#java #rest #jakarta-ee #ejb #n-tier-architecture
#java #rest #джакарта-ee #ejb #n-tier-architecture
Вопрос:
Мне нужен совет по проектированию «Уровня интеграции» N-уровневой системы на Java. Этот уровень отвечает за сохранение и извлечение данных для «Бизнес-уровня» (расположенного на отдельном сервере). Я новичок в J2EE и прочитал несколько книг и блогов. Алфавитный набор технологических сокращений сбивает меня с толку, поэтому у меня есть несколько вопросов.
Во-первых, что у меня есть на данный момент: я использую JPA (через Hibernate) для сохранения и извлечения данных в базу данных. Я создал свои объекты доступа к данным EJB и планирую развертывание на сервере приложений (JBoss), что упрощает транзакции (они находятся на функциональном уровне моего DAO), и мне не нужно беспокоиться о получении дескриптора EntityManager (внедрение зависимостей). Вот пример того, как все выглядит:
@Entity
class A{
@Id
Long id;
@OneToMany
List<B> setOfBs = new ArrayList<B>;
}
@Entity
class B{
@Id
Long id;
}
@Remote
public interface ADAO{
public A getAById(Long id);
}
@Stateless
class ADAOImpl implements ADAO{
@PersistenceContext
EntityManager em;
public A getAById(Long id){ ... }
}
Мой вопрос: Как бизнес-уровень должен обмениваться данными с уровнем интеграции. Я прочитал о службах RESTful, и они кажутся достаточно простыми. Меня беспокоит производительность, когда увеличивается частота получения и установки (HTTP-связь не кажется особенно быстрой). Другой вариант — RMI. Мои DAO уже являются EJB. Могу ли я просто предоставить бизнес-уровню доступ к ним напрямую (через JNDI)? Если да, то что произойдет, если ссылка @OneToMany в приведенном выше примере загружается лениво?
Например, если бизнес-уровень выполняет что-то вроде следующего:
Context context = new InitialContext(propertiesForIntegrationTierLookup);
ADAOImpl aDao = (ADAOImpl) context.lookup("something");
A myA = aDao.getAById(0);
int numberOfBs = myA.setOfBs.size();
Если список setOfBs загружается медленно, когда бизнес-уровень (на отдельном сервере) обращается к списку, правильный ли размер? Список каким-то образом правильно загружается с помощью магии EJBs? Если нет (чего я ожидаю), то каково решение?
Извините за длинный пост. Как я уже сказал, я новичок в J2EE, и я прочитал достаточно, чтобы получить общее представление, но мне нужна помощь в сборке частей вместе.
Ответ №1:
Когда вы вызываете size () в lazy collection, он инициализируется, поэтому вы всегда получите правильный размер, независимо от того, какой интерфейс вы используете — удаленный или локальный.
Другая ситуация заключается в том, что вы пытаетесь использовать классы JPA в качестве объектов передачи данных (DTO) и запрашивать их через удаленный интерфейс. Я не помню здесь никаких проблем с отложенной инициализацией, потому что перед передачей все объекты должны быть сериализованы (с инициализацией отложенных коллекций) на стороне сервера. В результате весь объектный граф передается по сети, что может привести к серьезным перегрузкам процессора и сети. Кроме того, чтобы десериализация была возможной, вам придется совместно использовать классы JPA с удаленным приложением. И на этом «магия EJB» заканчивается 🙂
Итак, как только станут возможны удаленные вызовы, я бы предложил начать думать о стратегии передачи данных и объектах передачи данных, отличных от JPA, в качестве дополнительного уровня данных. В моем случае я аннотировал классы DTO для привязки XML (JAXB) и повторно использовал их в веб-сервисах.
Комментарии:
1. Я воспользовался вашим мнением и провел последние несколько дней, изучая шаблоны проектирования EE. Похоже, у меня есть два варианта передачи данных между уровнем интеграции и бизнес-уровнем: DTO или REST. Кажется, что использование DTO приведет к появлению большого количества дополнительного кода на уровне интеграции (преобразование из класса A Doman в DTO B и т.д.), Но DTO могут быть повторно использованы на бизнес-уровне. REST было бы легко для уровня интеграции, но бизнес-уровень должен был бы выполнять основную работу по синтаксическому анализу, и производительность может быть проблемой. Ни одно из решений не кажется идеальным. Есть ли третий вариант?
2. Упс, я не имел в виду REST. Двумя вариантами могут быть либо DTO, либо какой-либо текстовый метод, например XML.
Ответ №2:
Краткий ответ: Если вы используете подход «Уровня интеграции», то то, что вы должны интегрировать, должно быть слабосвязанными сервисами, следующими принципам SOA.
Это означает, что вы не должны разрешать удаленные вызовы методов в объектах, которые могли бы выполнять вызовы фреймворка под прикрытием на другом сервере. Если вы сделаете это, вы действительно создадите тесно связанное распределенное приложение, и вам придется беспокоиться о проблемах с отложенной загрузкой и области действия контекста сохранения. Если вы хотите этого, вы могли бы рассмотреть расширенные контексты сохраненияhttp://docs.jboss.org/ejb3/docs/tutorial/extended_pc/extended.html .
Вы говорили о «бизнес-уровне», но JPA не предоставляет бизнес-уровень. Он предоставляет сущности и допускает операции CRUD, но обычно это не бизнес-операции. операция «registerUser» — это не просто вопрос сохранения объекта «User». Ваш уровень DAO может предлагать более высокий уровень работы, но DAO обычно используются для размещения тонкого слоя поверх базы данных, но он по-прежнему очень ориентирован на данные.
Лучший подход — определить операции типа бизнес-сервиса и сделать их сервисами, которые вы предоставляете. Возможно, вам нужен другой уровень поверх вашего DAO или вы можете захотеть иметь один уровень (преобразовать ваш уровень DAO).
Ваш бизнес-уровень должен вызывать flush и обрабатывать любые исключения JPA и скрывать все это от вызывающего.
Остается вопрос о том, как передавать ваши данные. Во многих случаях параметры ваших запросов на бизнес-сервис будут похожи на ваши объекты JPA, но я думаю, вы заметите, что часто существуют достаточные различия, из-за которых требуется определить новые DTO. Например, бизнес-операция «registerUser» может обновлять таблицу «User» и «EmailAddresses». Пользовательская таблица может включать свойство «CreatedDate», которое не является частью операции «registerUser», но установлено на текущую дату.
Для создания DTO вам, возможно, захочется взглянуть на Project Lombok.
Чтобы скопировать DTO в объект, вы можете использовать Apache Commons BeanUtils (например, PropertyUtils.copyProperties), чтобы выполнить большую часть работы, которая работает, если имена свойств совпадают.
Лично я не вижу смысла в XML в этом случае, если вы не хотите полностью отделить свои реализации.