Вместе с тем, открываются перспективы по освоению других языков, так как работа на зарубежного заказчика внезапно вновь стала выгодной. Также возрос интерес к открытому программному обеспечению на всех уровнях технологического стека, а больше всего, к “импортозамещающим” СУБД типа PostgreSQL, MySQL.
Оказавшись в очередной раз на межпроектной развилке, я получил немного свободного времени, чтобы рассказать о своем опыте реализации нескольких проектов на Java, и о том, каково оно было, после многих лет разработки на 1С. Смысл послушать есть хотя бы потому, что количество просмотров резюме Java разработчика по моим оценкам сейчас раз в 5 больше резюме 1Сника.
Рассказать хочу на примере 2 моих OpenSource проектов, выкладываемых на GitHub:
№1. Реализует базовую функциональность быстрой разработки, доступную в 1С.
№2. Реализует механизм формирования отчетов с пользовательскими настройками типа “сводная таблица”, упрощенный аналог СКД (системы компоновки данных в 1С).
Для начала, по первому проекту. Я начал создавать базу данных для одной организации. И очень скоро встретился с первыми препятствиями. Не то чтобы освоить банальный CRUD было так сложно — Java я знал, да дело и не в языке: витруозом родился — виртуозом и помрешь, хоть как Мусоргский запейся — гениальность не пропьешь.
Но… как выйти на прежнюю скорость разработки? В мелких 1С проектах приходится постоянно модифицировать базу данных, добавляя реквизиты, сущности, а требования клиентов часто меняются по ходу игры. И будучи “ленивым 1Сником” (с) я как-то привык, что добавив сущность или внеся изменения в сущность, можно нажать 1 (одну) кнопку, после чего произойдет реструктуризация базы данных, запуск программы, и изменения можно будет увидеть в форме списка, и в форме элемента, самостоятельно сгенерированных платформой. Если же реквизит ссылается на новый справочник, для него автоматически создадутся новые формы списка, выбора, элемента.
Что же можно сказать про Java… На самом деле, можно добавить реквизит в код класса — Eclipse дает возможность в полуавтоматическом режиме создать геттер и сеттер, а после упомянутой 1 кнопки (F11) база данных под ORM Hibernate действительно дополнится новой таблицей или новым столбцом (если включить hibernate.hbm2ddl.auto=update, хотя многие и против такого подхода — понятно, что на продакшне его выключим).
Давайте рассмотрим пример. Допустим, у нас был класс “Контакты”, и мы решили добавить в него реквизит “Статус контакта”, перечень которых будет храниться в отдельном справочнике, который надо сейчас же дать редактировать пользователю. Тогда вносим изменения в класс (изменения помечены “плюсами”, геттеры и сеттеры созданы Eclipse):
Table(name=«contacts»)
@Synonym(text=«Контакты»)
@Forms(element="")
public class Contacts implements java.io.Serializable {
@Synonym(text=«Код»)
private int id;
@Synonym(text=«Фамилия»)
private String f;
@Synonym(text=«Имя»)
private String i;
@Synonym(text=«Отчество»)
private String o;
@Synonym(text=«Статус») //++++++++++++++++++++++++++
private Contact_Status status; //++++++++++++++++++++++++++
@Synonym(text=«Адрес»)
private String address;
@Synonym(text=«Телефон»)
private String phone;
@Synonym(text=«Прочее»)
private String description;
public Contacts() {
}
public Contacts(int id, String f, String i, String o,
Contact_Status status, //++++++++++++++++++++++++++
String address, String phone, String description) {
this.id = id;
this.f = f;
this.i = i;
this.o = o;
this.status = status; //++++++++++++++++++++++++++
this.address = address;
this.phone = phone;
this.description = description;
}
Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = «id»)
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
@Synonym(text=«Фамилия»)
public String getF() {
return this.f;
}
public void setF(String f) {
this.f = f;
}
public String getI() {
return this.i;
}
public void setI(String i) {
this.i = i;
}
public String getO() {
return this.o;
}
public void setO(String o) {
this.o = o;
}
//{++++++++++++++++++++++++++
@ManyToOne(targetEntity = Contact_Status.class,cascade={CascadeType.ALL}
NotFound(action=NotFoundAction.IGNORE)
@JoinColumn(name = «contact_status», referencedColumnName=«id»,nullable=true,insertable=false,updatable=true)
// by Eclipse
public Contact_Status getStatus() {
return this.status;
}
public void setStatus(Contact_Status status) {
this.status = status;
}
//}++++++++++++++++++++++++++
public String getAddress() {
return this.address;
}
public void setAddress(String address) {
this.address = address;
}
public String getPhone() {
return this.phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getDescription() {
return this.description;
}
public void setDescription(String description) {
this.description = description;
}
public String toString() {
return this.f+" "+this.i+" "+this.o;
}
}
Ну и добавляем саму сущность “Статусы”, простую как полено (я обычно копирую какую-нибудь и меняю названия):
Entity
Table(name=«contact_status»)
@Synonym(text=«Статусы»)
@Forms(element="")
public class Contact_Status implements java.io.Serializable {
@Synonym(text=«Код»)
private int id;
@Synonym(text=«Наименование»)
private String name;
Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name = «id»)
// by Eclipse
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String toString(){
return name;
}
}
//}++++++++++++++++++++++++++
Если отбросить код, созданный Eclipse, останется совсем немного, а так как работать с текстом быстрее, чем бегать по меню в Конфигураторе 1С: Предприятия, считаю, результат уже неплох. Это я про базу данных.
Но как же интерфейс?
В сообществе Java пользуется популярностью UI фреймворк Swing. Но все примеры под него выполняли конкретные задачи, универсального решения, которое можно было бы использовать везде, я почему-то не нашел.
Не было банально приличного редактора форм. NetBeans неплох, но блокирует настроечный код, что страшно раздражает, а в Eclipse плагин windowsBilder постоянно этот код перечитывает, и тормозит при этом неимоверно — да и вылетает частенько. Плюс внутри анонимных классов обработчиков, созданных им, остальные элементы формы не видны — гениальный ход. IDEA почему-то раздражала при подтормаживании еще больше — хотя казалось бы SSD на M.2 должен был решить подобные проблемы.
В итоге задача “добавь сущность и закинь ее на форму” требует очень много кода и времени, по сравнению с 1С.
Спросят: “много” — это сколько, не зажрались ли вы, батенька?
Отвечу. Проводя тренинги для РП, я в качестве предисловия к одному из блоков с нуля делал простейший контур складского учета за 4 минуты 35 секунд (4:35, Карл!), включая ввод в базу тестового примера из двух документов. Участники тренинга замеряли время, а потом я с пафосом спрашивал у них — если все так просто, куда же тратятся миллионные бюджеты?
Но я не представлял, как добиться сопоставимой скорости разработки готового приложения, даже используя шаблон проекта с уже подключенными драйверами СУБД (PostgreSQL, MySQL, Oracle DB или что там у вас), ORM Hibernate и Swing (на самом деле, Swingx — в чистом Swing нет приличных элементов типа JXTreeTable). Очевидно, шаблон должен включать мощные классы-настройщики. И я решил их сделать, на коленке.
Если проводить параллель с 1С, табличное поле, привязанная к реквизиту формы (например, динамическому списку), это JXTable или JXTreeTable, привязанное к так называемым TableModel. Недолго думая, я отнаследовался от стандартной табличной и древовидной модели, добавил класс сущности, с которой мы работаем и текст запроса:
public class BeanTableModel extends DefaultTableModel{
...
private Class beanClass; //Класс сущности, "Основная таблица" динамического списка 1С
private String qtext; //Текст запроса динамического списка 1С
...
}
Самое главное, чтобы JXTable могла вызывать метод getValueAt(int row,int col) у своей TableModel. Что же касается JXTreeTable, там это getValueAt(ArrayNode node,col), где node — public class ArrayNode extends DefaultMutableTreeTableNode — вершина дерева.
Согласно запросу qtext, данные вытягиваются из базы, это может происходить как сразу большим куском, так и по мере необходимости (при вызове getValueAt), с реализацией кеша и пейджирования. В выложенной демке пейджирования нет, оно реализуется через очередную добавленную аннотацию, например @Paging, и дополнение модельных классов буфером.
Вывод таблицы должен содержать эти данные, но ведь пользователю надо выводить ограниченный перечень колонок, с правильными именами, и определенной ширины. Как добиться чтобы русские названия были заданы по умолчанию?.. Для этого, при описании entity в дополнение к persistent аннотациям и пришлось завести свою аннотацию Synonym, которую вы уже видели в листинге (а версия на GitHub уже поддерживает он-лайн переключение между любым количеством языков путем указания text, textEng, textDe, и добавления в глобальных props элемента language “Eng”, “De”...).
Чтобы два раза не вставать, к Entity я добавил и название классов форм аннотацией Forms — element, list, select. Понятно, что это должны быть потомки JFrame.
Но задача стояла сделать, чтобы программа, подобно платформе 1С: Предприятие сама создавала дефолтные формы (иначе какие уж там 4:35 на новую сущность), поэтому в примере в классе Contacts анностация пустая — @Forms(element=""), хотя ранее ссылалась на класс формы VContacts.
Как уже догадались коллеги, создание колонок в BeanTableModel, равно как и создание реквизитов в автоматически создаваемой формах элементов наших сущностей, происходит с помощью рефлексии, то есть работы с метаданным сущности. Я сходу наткнулся на 2 способа перебирать элементы метаданных: обычным способом (в классе FormElement) и через Интроспектор (в моделях).
Итак, мы вывели данные в таблицу посредством модели. Можно сказать, такая таблица — основа формы списка или выбора. Теперь неплохо было бы реализовать ее поведение — открытие формы элемента (добавление и редактирование), удаление, выбор и прочее.
Связать действия кнопок на JToolbar с таблицей удалось крайне некрасивым способом — через сканирование элементов внутри общего родителя. Ужаснувшись, я вынес это в отдельный класс ut (от слова “утилиты”) — чтобы больше не видеть. Но цель была достигнута.
Мой велосипед позволил:
1. Продолжить пользоваться редактором форм для расположения элементов на форме.
2. Настроить поведение этих элементов небольшими блоками кода (здесь подошел бы xml, но мне не нравится большое количество файлов, я в них путаюсь):
ContactTable.setTreeTableModel(new BeanTreeTableModel("select * from contacts", null, Contacts.class, new ArrayNode(new Object[0])));
ArrayList<HashMap> tt = new ArrayList< >();
tt.add(ut.newHashMap(new ArrayList < >(Arrays.asList("name,title,width", "id", "№", 60))));
...
tt.add(ut.newHashMap(new ArrayList< >(Arrays.asList("name,title,width", "phone", "Телефон", 200))));
ut.TuneTreeColumns(ContactTable, tt);
ut.TuneToolbar(Contacts_toolBar, ContactTable);
А у полей ввода с кнопками выбора настройка в 1 строчку типа:
ut.linkFormObjectElements(FormElement.this,d.getName(),t1,t2);
3. Если лень, можно не создавать вообще никаких форм для второстепенных сущностей, с тем, чтобы программа делала их автоматически, на основе самих классов и аннотаций. И эти формы могут открываться, даже если там 20 связей, функционал CRUD “Выбрать, Добавить, Редактировать, Удалить, Обновить” будет работать. Значит, студент свою лабораторную работу с базой для библиотеки сможет сляпать за 10 минут не приходя в создание.
Что же дальше?.. Java предоставляет гораздо более обширные возможности, чем встроенный язык 1С, но тот более лаконичен. Так как большинство объектов (ArrayList, HashMap и прочие) одинаковы, в принципе даже можно попробовать написать некий интерпретатор, но гораздо больше мне интересно расширение языка запросов SQL разыменовыванием, итогами, и виртуальными таблицами. Очевидно, что такая задача решается уже не через ORM.
Во второй статье будет рассказываться про проект инструмента гибкого формирования пользовательских отчетов, что как раз созвучно с темой запросов, так что там про это будет рассказано подробнее.
До новых встреч.
Комментарии (34)
igor_suhorukov
19.10.2015 16:25Рад что у вас получается переход на новую технологию.
Но вы сравниваете немного разные вещи.
1С — специализированное решение.
Если хотите быстрый GRUD интерфейс на java, посмотрите на Spring Roo/ SculptorNikita001
19.10.2015 16:44Спасибо, посмотрю.
Neuronix
21.10.2015 10:03Лучше посмотрите на Play Framework. Только версии 1.3.
senia
22.10.2015 02:42Почему? Чем плохи 2.x? Они уже версию 3.0 готовят.
Neuronix
22.10.2015 09:07Потому что тут человек говорит о Java. 2.x — это уже Scala, как ни крути. Хоть и заявлено, что можно писать на Java, но шаблонизатор в виде Scala тоже наводит на мысль использовать Scala.
Я достаточно давно использую Play во многих проектах, много раз пробовал перейти на ветку 2, но толкового из этого ничего не вышло. Говорю за себя, может кому-то и удобнее писать на смеси Java + Scala, особенно, зная Scala, но удобнее опять же тогда писать уже всё на Scala :)
Scraelos
19.10.2015 16:41+1А приложение принципиально должно быть десктопным?
JavaFX не пробовали?Nikita001
19.10.2015 16:43-2Насколько понял Fx на линуксе не заработает, а я с этим заделом на будущее искал.
EvilBeaver
19.10.2015 16:53+2А почему не 1С все-таки? Я понимаю, если вас не устроила бы scalability или там, производительность… Но цена? В 1С есть линуксы, есть reporting, веб-клиент из коробки, есть отказоустойчивый кластер и вообще, новая версия обещает вагон плюшек для больших систем. Наверное, можно это написать с нуля, если 1С не устраивает. Но я сильно (вообще сильно) сомневаюсь, что это будет дешевле для заказчика.
Beholder
19.10.2015 17:07+2С чего вы так решили? Должно бы, если судить по списку совместимости.
Nikita001
19.10.2015 17:13Читал коменты к статьям, здесь же на Хабре кстати.
Кто-то сделал такое заявление, вроде не опровергли, сам проверять не стал, поверил на слово.
Fx был бы сильно лучше Swing'а?vedenin1980
19.10.2015 18:02Вообще, веб.интерфейсы, на мой взгляд, намного популярнее у разработчиков на Java, поэтому их развивают намного больше десктопных интерфейсов, впрочем FX это лишь верхушка айсберга из FX, GWT, jsp 2, Spring MVC и многих многих других технологий, которые обкатывались годами и десятилетиями.
vedenin1980
19.10.2015 17:24+1Ооо, это как? Во-первых, в JDK Java вообще крайне редко бывает что где-то что-то не заработает, это же Java. Во-вторых, линукс в Java наверное самая популярная платформа из всех возможных, программисты Java скорее смирятся что в винде что-то не работает, чем в линуксе. ИМХО
MAXXL
19.10.2015 17:19Для большинства контор существующих на рынке достаточно функционала базовых версий конфигураций 1С, это будет им стоить тысяч пять за коробку и тысяч скажем 12 в год за подписку на обновления. При этом они получат достаточно оперативно необходимые изменения в печатных формах и в регламентной отчетности. Сколько будет стоить этот же функционал в Вашей программе?
Nikita001
19.10.2015 17:26Боюсь вас разочаровать, но конкретно функционал регл. учета я никогда не стану реализовывать в этой программе. =)
И вряд ли на GitHub кто-то форкнет проект с такими целями.
Разве что интереса ради регистр бухгалтерии и план счетов реализую — но на коммерческий успех такой поделки планировать было бы по-детски наивно.
А мне 32 года уже все-таки.MAXXL
19.10.2015 17:35А в чем смысл тогда этого «велосипеда»? Нет, ну почти все проходили свои этапы в виде «я напишу свою ОС», или «я сделаю свою программу складского учета»… А какой практический смысл?
solver
19.10.2015 17:45Ну как дети маленькие, чесное слово))
В чем смысл, в чем смысл… а в чем смысл 1С?
И до нее писали люди учетки и после нее будут писать.
Такой же по глупости вопрос можно задать любой программе, у которой есть конкурент.
А зачем люди начали писать хром, когда уже были популярные браузеры?
Если вы лично, со своей жердочки не видите практический смысл, это не значит, что его нет.MAXXL
19.10.2015 18:08Ну если пишется конкурент, то значит он выполняет как минимум те же функции, а то и имеет какую-то свою изюминку. Если тут «функционала регл. учета не будет», то в чем фишка? Просто еще одна система, как тот-же самый «Ананас». Продолжая аналогию с 1с — можно быстро набросать интерфейс, вести данные и получить нужные итоги из соответствующих регистров, при этом не углубляясь в низкоуровневые дебри. Создавая какой-то аналог этой системы с нуля с каждым шагом затрачиваешь больше времени. Быстро нарисовал интерфейс? Но уже с сохранением данных в базу и получением обратно нужно написать прилично «обертки». И так с каждым шагом. Только если подразумевается, как написали выше привязка, клиента к себе, ибо поддерживать такую систему, да еще не имея исходного кода, стороннему человеку будет очень сложно, если не сказать невозможно.
vedenin1980
19.10.2015 17:57-2Кстати, если очень хочется делать программы для бизнес учета и не писать велосипедов, я бы посоветовал посмотреть мою статью «Триста пятьдесят самых популярных Java opensource проектов на github», категория " Фреймворки и библиотеки для создания бизнес приложений на Java", там есть открытые фреймворки для создания программ похожих на 1С, вполне можно подобрать себе что-нибудь полезное.
jreznot
19.10.2015 20:20Ох хо хо! Очень советую вам набежать на CUBA.platform. И Java, и CRUD, и быстро и даже импортозамещение. Да, реклама, но ссылок не даю.
sphinks
20.10.2015 12:00+2Давно я не читал подобных авантюр. Не понятно зачем… Ну и слово дауншифт это вы погорячились, вы открыли для себя следующего уровня программирования. Это скорее рост.
EvilBeaver
21.10.2015 09:57Да не, нормальное слово — «переход на уровень ниже». Просто в последнее время этот термин ассоциируется с бомжеванием. Но это просто другой смысл того же слова. Здесь явно имелось в виду не это.
EvilBeaver
Я вот только мораль не понял… Кризис, импортозамещение, 1С быстро и из коробки, цена на нее разумная, но мы пилим свой велосипед на JAVA, потому что <??> Вот там, где знак вопроса, я не понял, какой тезис вставляет автор…
Nikita001
конкретно эти клиенты рассчитывают очень сильно смасштабироваться, и цена на 1С для них перестает быть разумной.
как обычно, все из-за денег.
EvilBeaver
Странно… 1С для них дорого, а разрабатывать, отлаживать и сопровождать кастомное решение — как бы дешево?
madfly
Это смотря как считать. Написание и поддержка специфического софта масштаба крупного предприятия может выйти в разы дороже. Но если работодатель готов платить за то, чтобы у вас была возможность освоить новый язык программирования — почему бы и нет.
immaculate
Хорошо, когда есть такие клиенты. Потому что разработка и поддержка любого велосипеда обойдется значительно дороже.
Судя по вашему коду, прочный задел на будущее вы уже заложили: мало кто сможет и захочет разбираться в этом. Это еще и снаружи выглядит, как интерфейс 90-х, значит спустя некоторое время можно будет развести клиента на переписывание под Web.
PQR
Когда увидел скриншоты интерфейса (и понял, что речь всё-таки не о Web), сразу возник вопрос, а почему тогда не Delphi?!
caballero
Пользы от делфи тут не намного больше. Java по кравней мере кросплатформенная и к ей проще прикрутить какой нибудь скриптовый язык в виде DSL.
Но это по любому не спасает «отца русской демократии»
madfly
Потому что язык 1С расчитан на то, чтобы бухгалтер, не обделенный тягой к новым знаниям, был способен его освоить. Порог вхождения невысок — конкурентов много. Внедрив софтинку на Java отсекаем конкурентов с 1С (для них это будет более высокий уровень), но избегаем конкурентов со стороны Java-программистов (для них это будет скорее шаг назад). Т.е. создаем для себя нишу для заработка — profit! Так 25 лет назад в конторах кормились программисты на клиппере — разбираться в чужих исходниках этого ужаса никто не хотел, а софтину поддерживать нужно. Поэтому конторе приходилось платить.
caballero
Никакой бухгалтер не станет осваивать язык програмирования. Последняя версия 1С где я видел чтобы бухгалтер редактировал что то была 1С 2.0.
Попытка повторения 1С на делфи или яве заранее обречена на провал. Плюсов 1с не достигаем зато добавляем минусов в виде приложения которое еще и перекомпиливать приходится. То есть если для 1С требовался приходящий програмист то здесь придется держать штатного. Но в таком случае зачем построитель форм? Достаточнго было бы какого нибудь декларативного дизайна.
Впрочем, как раз готовлю статью про то как следует писать приложения-альтернативы 1С.
.