OLTP ожидания
"Просто я писатель. .. Я пишу книгу после которой книг писать больше буден не нужно"
(С) Г.Миллер "Тропик Рака"
Всю свою сознательную рабочую деятельность я разрабатываю OLTP системы различного назначения. Это системы, основным назначением которых является ввод различных фактов - клиенты, счета, заказы, накладные, платежи ..
Системы меняются - в основном названия, мало смысл, может немного уточняется предметная область. Вся сила в деталях, поэтому подобные системы будут делаться снова и снова.
Я как человек по сути ленивый долго искал подходы как можно сократить работу по их написанию и хочу поделиться здесь своими изысканиями и далее предложить вариант их быстрого производства.
Начнем с фактов
1. Приложения состоят из набора модулей в терминах предметной области (счета, покупатели, поставщики, платежи ...).
2. Каждый модуль представляет пользователю некоторые функции.
3. 80% модулей это таблицы данных и стандартные функции вставка/ редактирование/ удаление. К таблицам нужны фильтры и настройки отображения и сортировки колонок, выгрузка содержимого таблицы на диск, печать таблицы...
4. Разделение доступа - пользователи и роли
5. Каждой системе нужны отчеты, и крайне необходима возможность настройки существующих и добавление новых.
6. Для коробочных проектов очень важна возможность настройки под конечного пользователя
- дополнительные колонки данных с поддержкой всех функций из (3)
- скриптовой язык (доводка, интеграция с другими системами, ...)
О шаблонах проектирования
Сейчас буквально все работодатели хотят знания шаблонов проектирования. На последних собеседованиях связанных с Java практически всегда задают вопрос: "Какие шаблоны проектирования вы использовали в вашей системе и где" ...
Я к шаблонам доходил постепенно. По простому экстремальному "правилу трех ударов" Кенета Бэка. Правила я еще не знал, но как всякий ленивый человек, пытался уйти от повторяющегося кода. Это удавалось но с хорошим скрипом ...
Сразу после прочтения Фаулера "Рефакторинг. Улучшение существующего кода" купил GoF и наконец понял отличие наследования или кооперация. Сразу GoF не переварилась - понадобилась еще одна книжечка попроще и Мартин для пущего усвоения "Быстрая разработка программ"
Мартин - Сила! Шаблоны это лишь типичные решения постоянно решающихся задач. Хорошо бы спросить сначала о принципах проектирования. Если случится мне нанимать программистов на работу - это будет первый технический вопрос.
- принцип персональной ответственности
- принцип открытия-закрытия
- принцип подстановки Лискоу
- принцип инверсии зависимостей
- принцип отделения интерфейса
(тестом же будет построение скелета CRUD приложения из нескольких модулей для демонстрации этих принципов!)
Какие же шаблоны я использую ... в этом нужно разобраться, ибо терминология должна быть соблюдена (см. также пост из будущего OLTP проектирование).
Singleton - менеджер с БД; менеджер конфигурации; менеджер приложения
Менеджер модулей приложения - тут сразу все принципы используются - абстракция модуля системы, навигация строится по списку и зависит только от абстракции модуля.
DataGateway (Composite, ~DAO, ~модуль таблицы)
DataGatewayFactory class
class function CreateSomeDataGateway: TSomeDataGateway;
фабрика / фабричный метод / строитель / строитель по образцу ...
создает объект и комплектует его внутренностями (зависимые объекты, сиквелы, действия)
Decorator - CustomUI комплектуется декоратором, который собственно и рисует UI-представление объекта
Дополнение обязанностями при установке модуля - сортировки, сохранение, печать сеток; настройка отчетов и фильтров ... динамический декоратор!?
Strategy - стандартные фукции insert/update/delete в зависимости от типа объекта DataGateway комплектуется стандартными стратегиями (простое редактирование, редактирование с блокировкой)
В общем спасибо шаблонам проектирования. Иногда уже трудно понять что за шаблон тут скрывается. Но главное чтобы соблюдались принципы проектирования. А они при набитой руке рождаются сами собой - автоматически, минуя этап размышлений. Это для меня уже как с проектированием БД - не помню определение третьей нормально формы, но соблюдаю ее автоматически
Еще раз всем рекомендую (Я читал несколько книг...):
- Роберт К. Мартин, Быстрая разработка программ. Принципы, примеры, практика
- GoF
И о простоте - один из самых важнейших принципов - "вам не понадобится". Не стройте фреймворк, не используйте шаблоны, пока не настанет реальная необходимость. Дизайн рождается постепенно по мере все большего понимания векторов развития системы!
Читать далее »
Struts - техника создания CRUD приложений
Долго я мучился с примерами Struts и наконец нашел статью, которая все прекрасно объясняет "как это?"
http://www.learntechnology.net/struts-crud.do
---------------------------------------------
1. Model
--------
interface SomeDom - интерфейс объекта домена
class SomeDomImpl - реализация объекта домена
interface SomeDao - интерфейс объекта доступа к данным
class SomeDaoImpl - реализация объекта доступа к данным
interface SomeDom {
getProperty();
setProperty();
}
class SomeDomImpl implements SomeDom {
private property
public getProperty();
public setProperty();
}
//... для Spring это вообще шаблонный класс Don't repeat the DAO!
interface SomeDao<> {
List selectList();
selectItem(int id);
boolean insert( some);
boolean update( some);
boolean delete(int id);
}
class SomeDaoImpl implements SomeDao {
// JDBC/Hibernate/iBATIS
// ...
}
!!! SomeDaoImpl попробовать генерировать автоматически
!!! Hibernate по схеме БД, XDoclet по анатациям к объекту домена (SomeDom)
!!! .. но наверное для новых приложений, которые можно строить из uml-диаграммы!
2. Controler
------------
2.1. стандартные файлы конфигурации
-------------------------------------
web.xml
struts-config.xml
2.2. SomeActionForm - форма some_item
-------------------------------------
класс с данными формы, похожий на SomeDomImpl
2.3. SomeAction класс с действиями
-------------------------------------
Наследник DispatchAction, представляющий все действия, возможные для SomeDao
public class SomeAction extends DispatchAction {
private Log logger = LogFactory.getLog(this.getClass());
private static SomeDao someDao = new SomeDaoImpl();
public ActionForward getSomeList(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception
{}
public ActionForward getSomeItem(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception
{}
public ActionForward insertOrUpdate(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception
{
SomeActionForm someActionForm = (someActionForm)form;
if (validationSuccessful(request, employeeForm))
{ //...
}
}
public ActionForward delete(ActionMapping mapping,
ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws Exception
{}
// это предпочтительнее т.к. в одном месте! и эффективнее чем
// в struts-config.xml, особенно на нетривиальных проверках
private boolean validationSuccessful(HttpServletRequest request,
SomeActionForm form) {}
3. View
-------
some_list.jsp - представление для списка (постраничное с линками insert, update, delete)
some_item.jsp - представление для объекта (форма с Accept/Cancel и выводом ошибок валидации)
some_list.jsp - простая таблица по List, с использованием JSTL!
some_item.jsp - простая форма ввода данных, с использованием Struts.html? не хватает в JSTL тегов?
4. Что дальше? Или усовершенствования, которые просятся
-------------------------------------------------------
4.1. some_list.jsp
------------------
Как и в desktop с формами гридов, нужно попробовать сделать универсальную some_list.jsp
для отображения любых настроенных списков
listSomeDom - ссылка на список объектов
SomeDomFields - проперти для отображения в нужном порядке
listSomeDomActions - ссылки на действия, которые можно выполнить со списком (перед таблицей: insert)
itemSomeDomActions - ссылки на действия, которые можно выполнить с позицией (в строке таблицы: update, delete)
это должно быть прсто сделать - в итоге экономия на одну jsp, и конфигурации в struts-config.xml
- 2 jsp (list, item)
- 1 ActionForm
- 1 DispatchAction
- 1 SomeDom
- 1 SomeDao
4.2. some_item.jsp
------------------
для простых форм можно тоже чего-нибудь придумать ...
SomeDomFields - проперти для отображения в нужном порядке
динамическая jsp ...
и тогда можно сконцентрироваться исключительно на логике ..
4.3. some_list services
-----------------------
- настройка порядка колонок
- настройка сортировки
- фильтры
Вот и все - разобрали - в среднем 2-3 часа на простую сущность
Проекту Eclipse исполнилось 5 лет
"Мир, как мы его знали, подходит к концу ....и Бог с ним" (С) Б.Г.
Немного запоздалый пост ... уже давно хотел о нем вспомнить и вот
попались на глаза пару заметок
Five years. Less than 2,000 days. In that time, Eclipse has evolved from an IBM-controlled experiment to a de facto industry standard for Java development. A recent Evans Data Corp. (EDC) study deemed the Eclipse developer population, estimated at around 2.2 million, to be the
industry's second largest, just trailing Microsoft's .Net ecosystem.
Кирил Ранеев на последней киевской конференции Borland показал цифры каких-то исследований. На 2006 год в мире насчитывается 15 миллионов разработчиков по всему миру. 2.2 миллиона из них на Eclipse ...
"Мир всего, что не привязано к продукции Microsoft, дружно переносит продукты под Eclipse... бесплатные системы контроля версий Subversion и CVS, Bugzilla и авто синхронизация при помощи плагинов ... добавляем WTP (XML, CSS, xHTML, Webservices, JSP) .. лучший в мире JavaScript редактор JSEclipse .. ставим PHPEclipse – возможно, самую интеллектуальную PHP IDE из существующих .. Gmail клиент, RSS ридер, IM .. Zend разрабатывает следующую версию Zend Studio под Eclipse и возглавляет проект Eclispe
PHP IDE, который будет бесплатным. IBM, BEA, Yahoo, Oracle, Sybase, Google продвигают Open AJAX Initiative, которая тоже будет основана на Eclipse. Adobe сделала Flex Builder 2 на базе Eclipse, а бесплатный Flex 2 SDK будет ни чем иным, как плагином к Eclipse. .. не важно, фрилансер вы или руководитель отдела мультимедиа-разработок с десятками подчиненных, вы все равно работаете с несколькими инструментами и когда они лежат в одном ящике, на своих местах и хорошо подходят друг к другу – то такой набор хочется купить. А за него еще и денег не просят. И в перспективе – есть такое красивое слово – таких средств станет все больше, и они будут все совершеннее. Но в России почти все по старинке пользуются ворованными старыми-добрыми "оригинальными продуктами"."
(С) Майкл Клишин flash@novemberain.com
JSF погружение
Разбираюсь дальше с JavaServer Faces. Несколько дней пишу первое приложение на заказ - хочется сделать красиво - погружаюсь в дебри. Нашел еще один интересный линк, проясняющий основные моменты работы с данными.
Жаль что Eclipse WTP пока не поддерживает визуальную разработку JSF страниц. Вчера закачал NetBeans 5.5 и плагин для визуальной разработки JSF-страниц. На первый взгляд все отлично, но нужно разбираться, на что пока нету времени. В Eclipse WTP 2.0 врде бы обещают добавить. Есть также дизайнер в Oracle JDeveloper (видел на картинках в "JavaServer Faces in Action) и по слухам в IBM WebSphere.
После Delphi это наверное тот самый "правильный" и привычный метод разработки.
JSF приятная вещь.
Автоматическая генерация HTML, WML
Интеграция пользовательского интерфейса и бизнес-объектов (чтение и запись)
Компонентная модель пользовательского интерфейса (запущенного на сервере)
Серверная обработка событий пользовательского интерфейса
Преобразование типов
Навигация
Обработка форм и валидация
Расширенная локализация
MVC2
Интеграция с другими технологиями (JSTL, STRUTS)
По началу немного пугает конфигурационные файлы, но это проходит.
Читать далее »
JSF - сформировать в бине HtmlDataTable
Потратил некоторое продолжительное время на поиск решения ... Java мир domain objects не изобилует примерами по работе с ResultSet/RowSet, и после Delphi часто приходится тратить много времени на изучение как это может быть сделано. Литературы по JSF на русском языке тоже нету. Рабочее решение найдено в примерах Shale Framework и немного изменено для своих потребностей.
На самом деле примеров много, но они очень конкретные, что означает много лишней работы. Для того чтобы отобразить выборку предлагается создать объекты Domain, Dao и конкретную страницу для конкретной выборки. Часто же подобные страницы выборок не отличаются друг от друга. Зачем делать лишнюю работу!?
-------------------------------
"Dao" для получения RowSet:
-------------------------------
public class RowSetDao {
static public RowSet getRowSet(String sqlText, Object[] params) {
CachedRowSet rowSet = null;
try {
rowSet = new CachedRowSetImpl();
rowSet.setDataSourceName("java:/comp/env/jdbc/Dk");
rowSet.setCommand(sqlText);
if (params != null) {
for (int i = 0; i < params.length; i++) {
rowSet.setObject(i + 1, params[i]);
}
}
rowSet.execute();
} catch (SQLException e) {
e.printStackTrace();
}
return rowSet;
}
}
BackingBean
import edu.nvoynov.jsf.dao.RowSetDao;
public class SelectBean {
private RowSet rowSet = null;
private boolean completed = false;
private String sqlText = null;
private HtmlDataTable results = new HtmlDataTable();
private ResultSetDataModel resultSetDataModel = null;
public String execute() {
prerender();
return "result";
}
public void prerender() {
if (sqlText == null) return;
if (sqlText.equals("")) return;
FacesContext context = FacesContext.getCurrentInstance();
try {
rowSet = RowSetDao.getRowSet(sqlText, null);
ResultSetMetaData rsmd = rowSet.getMetaData();
resultSetDataModel = new ResultSetDataModel(rowSet);
results.setFirst(0);
List <Object> children = results.getChildren();
children.clear();
for (int i = 1; i <= rsmd.getColumnCount(); i++) {
UIColumn column = new UIColumn();
column.setId("column" + i);
children.add(column);
HtmlOutputText header = new HtmlOutputText();
String label = rsmd.getColumnLabel(i);
if ((label == null) || (label.length() < 1)) {
label = rsmd.getColumnName(i);
}
header.setValue(label);
column.setHeader(header);
HtmlOutputText data = new HtmlOutputText();
data.setId("data" + i);
data.setValueBinding("value", context.getApplication()
.createValueBinding(
"#{current['" + rsmd.getColumnName(i) + "']}"));
column.getChildren().add(data);
}
completed = true;
} catch (Exception e) {
context.addMessage(null, new FacesMessage(
"Exception executing this query: " + e));
while (e != null) {
context.getExternalContext().log(
"Exception executing this query", e);
if (e instanceof SQLException) {
e = ((SQLException) e).getNextException();
} else {
e = null;
}
}
setCompleted(false);
}
}
public void destroy() {
if (rowSet != null) {
try {
rowSet.close();
} catch (SQLException e) {
}
}
}
}
И наконец объявление таблицы в странице
<h:dataTable id="results" binding="#{selectBean.results}"
rendered="#{selectBean.completed}"
value="#{selectBean.resultSetDataModel}" var="current" />
Довольно просто все на самом деле оказалось. Теперь можно быстро sql-выборки без Dao/Domain объектов, где единственный параметр это сиквел! На основе этого бина можно сделать шаблон таблицы для любых страниц выборок, декорировать по желанию CRUD операциями и стандартными стилями.
PS. Форматирование кода это ужас. Как с этим справляться?
Решили перепланировать внутреннее пространство квартиры с целью создания уникального дизайна комнат? В этом деле вам поможет гипсокартон - универсальный материал для быстрого возведения стен и монтажа подвесных потолков.
OLTP+OOP
Пришла мысль по продолжению обещанной OLTP практики и решил тут же ее записать. Изложение возможно будет сумбурным, но я обязательно поработаю над этим позже. Ходите иногда по старым ссылкам - содержимое будет иногда меняться.
Итак типичное OLTP-приложение выглядит для программиста как пара десятков форм для вывода табличных данных. Большинство из этих форм имеют набор CRUD-операций (create/ update/ delete) и соответствующих им диалогов ввода данных.
Я буду пользоваться обозначениями степени взросления разработчиков "начинающий", "бывалый", "опытный" и "настоящий". При этом я никого не хочу обидеть - можно сказать что это рассказ про меня самого. Хотелось бы еще ввести "будущий", но кто он такой я пока не представляю .. может здесь
Начинающие программисты Delphi тут же начинают примерно так: "Создаем новую форму, кидаем на нее TDataSet, TDBGrid, TDataSource ... связываем это все вместе и готово!". Операции кодируются тут же в форме, устанавливаются прочные зависимости от форм редактирования и других компонентов. И все это по умолчанию уже ООП - среда Delphi и приложения, которые здесь делаются это также объектно-ориентированные приложения.
Далее эта форма должна войти в общий интерфейс, т.е. нужно дать возможность пользователю туда попасть. Для первого раза можно просто сделать метод вызова в главной форме. (Хорошая демонстрация подхода есть например у Пачеко и Текстейры в главе 33. SalesManager. Приложение конечно не такое показательное как хотелось бы, но по крайней мере там есть системность, которая помогала мне при написании первых приложений.) Таким же самым образом появляется вторая, третья и последняя форма. Разработчики дерутся за главную форму потому что нужно постоянно вносить в нее изменения при добавлении, удалении и изменении точки монтирования каждой конкретной формы. Но мы пока оставим тему зависимостей до рассмотрения принципов дизайна, описанных у Мартина. Да и для маленькой заметки это очень много.
Вторая, третья и особенно дальнейшие формы чем-то напоминают первую все больше и больше. И вместе с тем формы разные, некоторые имеют очень похожую функциональность, другие до наоборот (формы клиентов и товаров; формы приходов и расходов). Внешнее оформление, схожая функциональность начинают выделяется в общие формы. Как только разработчик это улавливает он становится опытным ООП-разработчиком и начинается тотальное наследование. В некоторых местах оно к месту и позволяет за считанные минуты создать вполне работоспособную нову форму, а в других не позволяет выполнить какое-то элементарное действие из за того что оно есть где-то на пару уровней ниже в объектной иерархии. Картонный домик принципа наследования ООП, опытные еще не знают настоящих принципов ООП - они считают это просто инкапсуляция, наследование полиморфизм и помнят наизусть определения. Появляются дикие статьи типа этой (Универсальная Форма)...
Функциональность, как правило, начинает наращиваться. Появляются отчеты, фильтры .. другие стандартные сервисы. Постоянно возникает вопрос на каком уровне их в форму вводить ... картонный домик наследования начинает потихоньку рушится... Но все ведь было так хорошо и быстро, да конечно не без некоторых проблем, но так или иначе они были преодолены (переработкой иерархии наследования, живой правкой всех форм в проекте, других хирургических методов, выкидыванием некоторой функциональности). И наконец приложение готово! Хоть и с большим опозданием, перерасходом средств, урезанием функциональности, полным истощением от сверхурочной работы но все-таки готово. Первый эпохальный проект заканчивает первый этап взросления нашего разработчика - он переходит в категорию бывалого. Первый проект крайне важен, лабы и программки на три формочки в прошлом, но что-то большее было сделать очень трудно.
Бывалые начинают думать как бы исправить эту ситуацию (переработку и хирургические вмешательства по всему коду), тем более, что демон одинаковости и простоты за углом уже прочно обосновался в их голове. Начинается ООП просветление в виде шаблонов проектирования и рефакторинга, шаблона MVC и разделения приложения на уровни. Итак понимание шаблонов проектирования, разделение обязанностей, разделение приложения на логику представление и данные становится вторым этапом взросления. Основным достижением можно считать знакомство с принципами организации объектного взаимодействия в стандартных часто случающихся ситуациях (это же шаблоны!). Миф наследования развенчан. Основное преимущество объектов это возможности инкапсуляции а не наследование. И в следующем проекте обязательно буду т шаблоны.
Логика переносится в модули данных, появляются фасады доступа к БД, если повезло - из фасадов появляются объекты. Формы начинают терять логику и становятся просто видами и контроллерами. Появляются одиночки соединения БД, хранения состояний настроек между сансами. Более прочный и более гибкий дизайн. Главное не увязнуть в хорошем проектировании и вовремя остановится, иначе вторую программу можно и не сделать, увязнув в постоянном перепроектировании
(смотри эффект второй системы у Брукса) Вдоволь побаловавшись с новинками заканчивается и второй эпохальный проект. Освоены новые технологии, ту же самую функциональность можно сделать несколькими способами - только выбирай. Бывалый становится опытным. Но и у опытного есть свои проблемы и его отличает то, что он ищет ответы дальше.
Настоящее понимание шаблонов приходит не сразу. За, казалось бы простыми, решениями повседневных проблем стоит несколько основополагающих принципов проектирования, которые не даются сразу. Почему-то получается что все идет от частностей к теории, наверное это нормальный подход естествоиспытателя и действительно без частных случаев трудно уловить абстрактную теорию. После того как понимаешь что такое хороший дизайн все станет на свои места, найдутся правильные абстракции - все происходит само собой.
После того как усвоены основные принципы проектирования и шаблоны мир уже никогда не будет таким как прежде. Основное достижение, на мой взгляд, в том, что начинаешь видеть реальные объекты составляющие программу и, соответственно, выделять правильные абстракции - основа хорошего проекта. Причем что-то я помню что меня учили этому в институте - нет, разве что выделению объектов предметной области типа выделением существительных для объектов и полей и глаголов для действий. Но между объектами предметной области и их воплощением в программном коде должен быть архитектурный клей, который позволит без лишних препятствий соединить все это вместе.
Что-то я расписался тут, перемахнул в двух абзацах кучу промежуточный решений по нашим формам. И писать уже расхотелось, и главное ждут проект и заказчик. Так что подробно о проектировании в следующий раз по настроению, а пока коротко.
Выделяются области в которых живут объекты программы. Прежде всего определяется абстракция представления данных пользователю. Есть некоторый модуль, который предоставляет пользователю доступ к объекту предметной области - для его визуализации и взаимодействию с ним.
Выделяется система навигации по всем объектам приложения, возможно правила переходов между ними. Система навигации зависит только от абстракции модуля системы, а не от конкретных модулей. Появляется низкоуровневая инфраструктура работы с модулями (регистрация, отображение, ...). Здесь все соединяется в приложение.
Выделяется абстракция объекта предметной области, его данных и предоставляемых им операций (клиент, заказ, платеж ...). И абстракция доступа к постоянному хранилищу для сохранения и загрузки объектов.
Выделяются абстракция сервиса (смотри определение feature в заметках о требованиях) который может предоставлять система для объектов предметной области (например интернационализация, печать, сохранить/загрузить на диск). Возможно сюда можно отнести и хранение настроек, но это пользовательский сервис ...
Проектируются объекты системы в соответствии с построенными абстракциями.
Выглядит в конце это примерно так. При загрузке программы первым делом создается объект навигации, который строит интерфейс системы. Система загружает некоторый модуль. Модуль создает объект предметной области и предоставляет к нему доступ. Система подгружает дополнительные сервисы, доступные в этом контексте системы.
Сервисы стандартны и готовы. Грубо говоря когда инфраструктура готова и разработчику остается работать только над объектами предметно области. Высока вероятность что они будут меняться, но инфраструктура будет меняться гораздо реже и она вполне может стать прочной основой инфраструктуры следующего проекта.
В общем это наверное последний этап взросления - "настоящий" разработчик. Есть полное понимание составляющий системы и процессов которые происходят, а главное причин и следствий этих решений, разумных компромисов и степеней свободы по расширению и модификации кода. Без этого понимания сплошные проблемы. Забавно что это не так уж и много информации. Для хорошего приложения не нужно много абстракций - нужны правильные. И правильные абстракции упрощают и ускоряют разработку и тестирование.
Вернемся к примеру с формами. В первом варианте сплошные проблемы. Самый главный бич неудач разработки ПО это зависимости - в требованиях, коде .. (1) - на форму свалено много обязанностей . (2) как следствие (1) у формы много зависимостей она прочно связанна с другими модулями. (3) Главная форма вообще зависит от всех высокоуровневых модулей. А значит от каждой детали каждого высокоуровневого модуля! Что такое зависимости в этом случае? Это означает что то, что при изменении любого модуля системы от которого зависит модуль представления, зависит также главная форма приложения - т.е. каждое изменение потенциальных крах всего приложения.
Настоящий разработчик владеет терминологией, приемами проектирования и реализации, быстро ориентируется в качестве решений, имеет готовый наработанный запас общей сервисной функциональности. Имеет связи в профессиональном мире и может расчитывать на быструю и качественную помощь коллег.
Есть наблюдение, что хороший разработчик часто отличается в десять раз по производительности от посредственного. Но об повышении производительности кака-нибудь в другой раз.
JasperReports
В процессе выполнения последнего проекта столкнулся с инструментами построения отчетов в Java. Альтернативы было две (1) BIRT и (2) JasperReport. По (1) смотрел как-то демонстрацию на флеше, (2) был под рукой (спасибо, Андрей!). Времени было мало, (1) молодой, по (2) был потенциальный консультант и литература в сети. Поэтому выбрал JasperReport.
Есть визуальный редактор iReport, стандарт де-факто для JasperReport, как пишут в сети. Ну мы то delphi-программисты люди бывалые - что такое отчеты знаем. Но после FastReport как-то с этим редактором не сложилось. Проще говоря глюкало оказался и непонятно почему отчеты переставали компилироваться. Решено было бросить эту визуальщину и перейти на "прямое" XML-проектирование. Нашел в сети "Jasperreports. Reporting for Java Developers" и отношение мое изменилось. Красивый отчетник, немного медленно конечно писать напрямую .jrxml но зато вполне надежно .. не вдаваясь в тонкости кончено (а я туда и не вдавался).
Вот все-таки кодо-ориентированный подход, медленно, но надежно. Очень помогают стили. Кстати FR по простым инструментам оптового и удобного размещения колонок на бендах, нужно бы обратить внимание. У JR их больше, и все они действительно нужны. Но конечно FR может не волноваться - качество исполнения дизайнера граздо лучше. Есть для JR и платные дизайнеры, но посмотреть я их не успел - может дело не совсем и гиблое.
Пришел к выводу что действительно можно работать и на уровне .jrxml, жаль что редактор XML Eclipse его не потянул. Но уже после нескольких часов изучения становится понятно что можно просто самому писать простые кодогенераторы для отчетов, по крайней мере для выборок. FR свой xml не расписывает ...
Да еще одна особенность это работа JavaBean и коллекциями, кроме JDBC. Я все собираюсь проработать для себя еще раз вопрос JDBC vs ORM и это один потенциальный плюс в пользу ORM - можно работать с объектами в отчетах.
У меня на счет отчетов собственное простое мнение. Отчеты есть двух типов - простые фактовые для всех визуальных выборок в системе - там где есть таблицы нужно дать возможность печатать и выгружать на диск. И отчеты анализа - где информация некоторым образом перерабатывается, подводятся какие-то агрегации и итоги.
Первые дожны строится по тому что пользователь види в текущий момент времени. Они должны быть независимые от конкретных видов, т.е. предоставляемые видом данные и есть даные и для отчета, но отчет не кодируется жестко - нет статических зависимостей.
Вторые имеют больше статических зависимостей, но не от объектов программы, а скорее от данных (от слоя БД). Функциональная часть системы не должна зависеть от этих отчетов. И их нужно кодировать где-то с боку и обязательно давать пользователю или внедренцу возможность делать новые и изменять существующие. Сам так делаю постоянно - сделал одни раз и все довольны.
Чуть не забыл добавить. Все гениальное просто - в конце заказчик сказал что хотел бы простые текстовые отчеты, как в существующей desktop-системе
И это позволило без особого труда функции формирования отчетов сделать вполне юзабельными в вэб. Форма ввода параметров с кнопками "Построить", "Сохранить", "Страница для печати". Кликаем "построить" и тут же внизу страницы ввода параметров видим отчет. Кликаем сохранить - предлагается загрузка на диск, ну и со страницы для печати можно прямо из браузера распечатать.
Вот кстати красивые новости по BIRT http://www.eclipse.org/birt/phoenix/project/notable2.2M4.php
нужно обязательно посмотреть более внимательно
JDBC и/или ORM
Уже как-то рассказывал, что на вопрос "как обновить ResultSet, если в сиквеле присутствует JOIN" от двух Java- разработчиков получил примерно одинаковый ответ: "JDBC это неудобно бери Hibernate". Введите например в строку поиска Google одно из слов "j2ee", "Struts", "Spring", "JSF" в сочетании с "Hibernate". Складывается впечатление что сначала был Hibernate ...
Проследить такую ситуацию к сожалению не могу (в Java я новичок), но думаю JOIN сыграл и там свою роль. Сначала ResultSet не был обновляемым, затем стал, но с сильными ограничениями. Сейчас JOIN уже не проблема - его легко разрешить для RowSet заменив стандартные провайдеры синхронизации.
Все вроде бы как логично - нужен доступ к данным берем JDBC и используем. Несколько привычных классов (даже для Delphi разработчика) ResultSet, Statement, Connection. Доступ к полям по индексу и по имени поля (не забывать следить за именами полей для агрегатов и вычисляемых полей). Но как не хватает нормальной реализации TDataSet типа TFIBDataSet (заслуженная реклама FIBPlus) - динамический ResultSet; пять сиквелов - select, insert, update, delete, refresh. Достаточно selectSQL, tableName и keyFields и в 99% больше не нужно заботится о написании сиквелов вставки, обновления и удалении.
Какой смысл менять сложности использования JDBC на сложности использования Hibernate(iBATIS, JDO, ... XORM)? Зачем писать дополнительные карты связи объектов, напарываться на собственные ограничения Hibernate, разбираться с особенностями и ограничениями HQL .. (неужели он лучше и гибче чем SQL?). Конечно это размышление прежде всего относится к OLTP-приложениям. Но пока что мне не удалось поучаствовать в разработке предметных областей с глубоким наследованием и прочими трудностями. Т.е. если работа идет в сильных объектных предметных областях, использование Hibernate может быть вполне оправданным ... посмотрите внимательно на свои приложения и на приложения вокруг ...
И как же он используется этот Hibernate? Очень просто - пишут люди DAO объекты доступа к данным
// объект предметной области
public class EntityX {
public int getIntProperty() {}
public void setIntProperty(int intProperty) {}
};
// объект доступа к данным
public class DaoEntityX {
public List<EntityX> getList(); {}
public List<EntityX> getItem(int id); {}
public int insert(EntityX entityX) {}
public int update(EntityX entityX) {}
public int delete((EntityX entityX) {}
}
Когда меняется схема БД - меняется все сразу и EntityX, DaoEntityX, hql-запросы, карты связи объектов на БД. А нужно было только отобразить список, вставит, изменить, удалить не вдаваясь во все сложности объекта - он простой и не нужно этих отдельных свойств.
Delphi-разработчики ищут компоненты. С них смеются Java- разработчики, они взрослые и сразу ищут фреймворк ...
Думаю что Delphi-разработчик меня поймет. В JDBC4.0 кстати появился java.sql.DataSet - в ближайшее время посмотрю внимательно и тогда дополню этот пост. Но уже сейчас ясно что даже тем у кого везде используются коллекции объектов можно вполне использовать DataSet. Пока можно посмотреть статью
* 12/02/2007
Вспомнил! Это ведь практически в каждой книжке по ООП написано - и Фаулера помню и у Мартина. Что нужно откладывать очевидную проблему долговременного хранения объектов на самую последнюю очередь.
"Несмотря на широкую поддержку ..., использование SQL сопряжено с определенными трудностями. Многие разработчики просто не владеют ... поскольку убежден в несомненной целесообразности четкого разделения кода SQL и бизнес-логики"
Фаулер "Архитектура корпоративных приложений"
И я просто привык работать с обратной стороны. Пока они проектируют бизнес-логику мы проектируем БД
И у нас проблема натянуть БД на объекты, а у них объекты на БД.
Генераторы кода
Генераторы кода и ООП - если вам нужно генерировать большое количество одинакового кода, то это скорее свидетельствует о плохом дизайне приложения. Не путать с генерацией кода по моделям, генерацией сопровождающего или описательного кода типа XDoclet для web.xml
Есть как бы хорошая иллюстрация к предыдущему посту JDBC/ORM. В 2002 году работал с "СУБД" DBISAM, embedded версии Firebird тогда еще небыло - появилась как раз к окончанию проекта. DBISAM была тогда штука страшная. Были там свои TTable и TQuery, и как только делаешь JOIN или ORDER BY - можно забыть про манипуляции типа (не напоминает JDBC?) Insert; FieldByName('zzz').AsInteger := 20; Post;
Нужно делать дополнительный обновляющий запрос и перечитывать исходную ReadOnly- выборку на предмет изменений произведенных запросом. Параметры обновляемого запроса нужно было также вставлять руками...
Появлялось большое количество очень похожего служебного кода изготовления из записи TDataSet объекта (буквально Value Object) и переноса полей этого объекта в параметры. Для автоматизации этого процесса был сделан скрипт в ErWin уровня таблицы, который делал генерировал код для всех этих манипуляций. Да служебный код оставался, но для него практически не нужно было прикладывать руки при изменениях. "Красивое решение" вызванное ограничением библиотеки доступа к данным.
Но в общем случае наверное такая генерация зло (хоть и по модели). Одно то, что код практически одинаковый должно подталкивать к поиску правильного решения .. правильной инкапсуляции .. нужно только найти что инкапсулировать.
7.02.2007 - Нашел книжку "Code Generation in Action" завтра посмотрю, может созреют еще мысли. На вид довольно интересная.