Java-EE: как спроектировать поток данных в трехуровневой среде приложений?

В настоящее время я работаю над распределенным приложением, используя следующие компоненты:

1. Уровень: Сервер базы данных -> MySQL 5.5

2. Уровень: Сервер приложений -> Glassfish 3.1.2.2

3. уровень: Автономный Fat-Client -> Java-SE (JRE 6)

Я использую JPA 2.0 (eclipse-link) в качестве поставщика постоянства и в настоящее время передаю bean-компоненты @Entity между 2-м и 3-м уровнями с помощью сериализации. Я использую статическое плетение, чтобы извлечь выгоду из ленивой выборки, поддерживаемой JPA.

Моя проблема в том, что из-за сериализации между 2-м и 3-м уровнями ленивая выборка не будет работать! Это приводит к интенсивному сетевому трафику, поскольку каждая сериализация одного из моих компонентов управления данными требует, чтобы все отношения с другими объектами/таблицами извлекались перед отправкой по сети.

Как я могу извлечь выгоду из ленивой выборки JPA в такой настройке? Есть ли обходной путь без необходимости введения облегченного DTO/DAO (это усложнило бы мой проект)?

Большое спасибо за вашу помощь заранее!


person salocinx    schedule 21.09.2012    source источник


Ответы (2)


По моему опыту работы с подобной архитектурой, для довольно большого проекта DTO неизбежны и должны использоваться при необходимости.

Работа с отсоединенными сущностями, особенно когда они имеют несколько ассоциаций с другими сущностями, не только вызывает проблемы с производительностью из-за загрузки базы данных и сериализации, но и создает огромную сложность. Действительно, если код не очень хорошо задокументирован, на уровне представления трудно понять, загружена ли данная ассоциация или нет, что вызывает множество исключений ленивой инициализации.

Еще хуже, если вы отправляете обратно сериализованные отсоединенные сущности с уровня представления на уровень службы, потому что тогда вы получите код, использующий сочетание отсоединенных и присоединенных сущностей, что еще больше усложняет ситуацию.

Таким образом, мои советы будут такими:

  • переносить объекты со слоя службы на уровень пользовательского интерфейса, если объект простой, а уровень пользовательского интерфейса не требует слишком большого количества загруженных ассоциаций. Всегда документируйте, какие ассоциации инициализированы, а какие нет. Если ассоциаций слишком много, используйте DTO и специальные запросы для загрузки необходимых данных из базы данных.
  • переносить DTO с уровня службы на уровень пользовательского интерфейса, когда требуется большое количество сущностей (например, результат формы поиска, возвращающий O (100) результатов или более). Используйте специальный запрос для загрузки запрошенных данных.
  • избегайте переноса объектов с уровня представления на уровень обслуживания. Если вы это сделаете, убедитесь, что первое, что вы делаете на уровне службы, — это перезагрузка присоединенной версии объекта из базы данных или слияние полученного объекта, чтобы всегда работать с присоединенными объектами. Отладка исключения ленивой инициализации, возникшего на сервисном уровне и вызванного использованием отсоединенной сущности, которая была загружена несколько минут назад предыдущим экраном, не доставляет удовольствия.
person JB Nizet    schedule 22.09.2012

Мы решили эту проблему, используя группы загрузки и подсказки запросов, чтобы определить глубину отношений, которые должны быть извлечены в конкретном запросе. Это требует отложенной выборки и, следовательно, переплетения при использовании eclipse-link в качестве поставщика JPA.

Вот краткий пример:

Запрос на стороне сервера на конкретном фасаде:

@Override
public List<Contact> query(LoadGroup group) {
    TypedQuery<Contact> query = em.createQuery("SELECT c FROM Contact c", Contact.class);
    if(group!=null) { query.setHint(QueryHints.LOAD_GROUP, group); }
    return query.getResultList();
}

Запрос на стороне клиента на этом конкретном фасаде:

LoadGroup group = new LoadGroup();
group.addAttribute("telephone");
group.addAttribute("address.street");
List<Contact> contacts = remoteContactFacade.query(group);

В этом примере таблица «Контакты» имеет дополнительные связи «многие к одному» с таблицами «Адрес» и «Телефон». Используя точечную нотацию, вы можете определить глубину того, что должно быть извлечено.

Надеюсь это поможет.

person salocinx    schedule 27.10.2012