Статьи

Шаблоны реализации JPA: объекты доступа к данным

JPA , короткие для Java Persistence API, является частью спецификации Java EE 5 , и реализуется с помощью Hibernate , TopLink , EclipseLink , OpenJPA , а также ряд других объектно-реляционного отображения (ORM) рамки. Поскольку JPA изначально был разработан как часть спецификации EJB 3.0 , вы можете использовать его в приложении EJB 3.0. Но он одинаково хорошо работает вне EJB 3.0, например, в приложении Spring . И даже когда Гэвин Кинг, дизайнер Hibernate, рекомендует использовать JPA во втором издании Hibernate in Action, известном как Java Persistence с Hibernate.Очевидно, что JPA здесь, чтобы остаться.

Как только вы избавитесь от страха перед аннотациями ;-), вы обнаружите, что существует множество литературы, которая объясняет объекты и методы в API, как эти объекты работают вместе и как вы можете ожидать их реализации. И когда вы придерживаетесь программ в стиле hello-world, все кажется довольно простым. Но когда вы начинаете писать свое первое реальное приложение, вы обнаруживаете, что все не так просто. Абстракция, предоставляемая JPA, довольно утечка и имеет последствия для большей части вашего приложения, чем просто для ваших объектов доступа к данным.(DAO) и ваши доменные объекты. Вам нужно принять решение о том, как обрабатывать транзакции, ленивую загрузку, отсоединенный объект (например, веб-фреймворки), наследование и многое другое. И оказывается, что книги и статьи не очень вам здесь помогают.


A: ссылка {цвет: синий;} A: посещение {цвет: # A000A0;} html body p {line-height: 120%;
} ul li, ul.menu li, .item-list ul li, li.leaf {list-style-type: disc; list-style-position: outside;} ol li, ol.menu li, .item-list ol li, li.leaf {list-style-type: decimal; список-стиль-позиция: снаружи;}

По крайней мере, это то, что я обнаружил, когда впервые начал использовать JPA. В ближайшие недели я хотел бы обсудить варианты, с которыми я столкнулся, и решения, которые я принял, и почему я их принял. Когда я закончу, у нас будет ряд вещей, которые я хотел бы не слишком скромно назвать шаблонами реализации JPA.;-)

Нам действительно нужен DAO?

Итак, давайте начнем с того, что вы, вероятно, сначала напишите в своем приложении JPA: объект доступа к данным (DAO). Интересный момент, который нужно решить, прежде чем мы начнем, — нужен ли вам DAO при использовании JPA . Заключение этой дискуссии больше года назад был «Это зависит» , и в то время как очень трудно спорить с таким выводом :-), я хотел бы придерживаться с идеей о том , что DAO имеет свое место в заявке JPA , Возможно, он обеспечивает только тонкий слой поверх JPA, но, что более важно, создание DAO для каждого типа сущности дает вам следующие преимущества:

  • Вместо того, чтобы выбирать правильный метод EntityManager каждый раз, когда вы хотите сохранить или загрузить данные, вы решаете, какой из них использовать один раз, и вы и вся ваша команда можете легко придерживаться этого выбора.
  • Вы можете запретить определенные операции для определенных типов объектов. Например, вы никогда не захотите, чтобы ваш код удалял записи журнала. При использовании DAO вы просто не добавляете метод удаления в LogEntry DAO.
  • Теоретически, используя DAO, вы можете переключиться на другую систему персистентности (например, обычную JDBC или iBATIS). Но так как JPA — такая утечка абстракции, я думаю, что это невозможно на практике даже для слегка сложного приложения. Вы делаете получить единую точку входа , где вы можете добавить функции трассировки или статистические данные о производительности Кип.
  • Вы можете централизовать все запросы определенного типа сущности, а не разбрасывать их по своему коду. Вы можете использовать именованные запросы для хранения запросов с типом сущности, но вам все равно понадобится какое-то центральное место, где установлены правильные параметры. Помещение запроса, кода, который устанавливает параметры, и приведение к правильному типу возврата в DAO кажется более простым делом. Например:
    public List<ChangePlan> findExecutingChangePlans() {      Query query = entityManager.createQuery(            "SELECT plan FROM ChangePlan plan where plan.state = 'EXECUTING'");      return (List<ChangePlan>) query.getResultList();}

Поэтому , когда вы решите , которые собираетесь использовать DAO, то как же вы идете о написании их? Выделенный (выделенный жирным шрифтом) комментарий в Javadoc для JpaTemplate Spring, похоже, предполагает, что нет особого смысла в использовании этого конкретного класса, что также делает JpaDaoSupport излишним. Вместо этого вы можете написать свой JPA DAO как POJO, используя аннотацию @PersistenceContext, чтобы получить ссылку EntityManager. Он будет работать в контейнере EJB 3.0 и будет работать в Spring 2.0 и выше, если вы добавите bean-компонент PersistenceAnnotationBeanPostProcessor в свой контекст Spring.

Типичный безопасный шаблон DAO

Поскольку каждый DAO имеет много общих функций с другими DAO, имеет смысл иметь базовый класс с общими функциями, а затем подкласс этого класса для каждого конкретного DAO. Есть много из блог там о таком типобезопасном родовом DAO шаблоне , и вы даже можете загрузить код из Google Code . Когда мы объединяем элементы из всех этих источников, мы получаем следующий шаблон реализации JPA для DAO.

Класс сущности

Допустим, мы хотим сохранить следующий класс Order:

@Entity@Table(name = "ORDERS")public class Order {@Id@GeneratedValueprivate int id;private String customerName;private Date date; public int getId() { return id; }public void setId(int id) { this.id = id; } public String getCustomerName() { return customerName; }public void setCustomerName(String customerName) { this.customerName = customerName; } public Date getDate() { return date; }public void setDate(Date date) { this.date = date;}}

Не беспокойтесь о деталях этого класса. Мы вернемся к специфике в других шаблонах реализации JPA. Аннотация @Table существует потому, что ORDER является зарезервированным ключевым словом в SQL.

Интерфейсы DAO

Сначала мы определяем общий интерфейс DAO с методами, которые мы хотели бы, чтобы все DAO разделяли:

public interface Dao<K, E> {      void persist(E entity);      void remove(E entity);      E findById(K id);}

Параметр первого типа, K, является типом для использования в качестве ключа, а параметр второго типа, E, является типом сущности. Помимо базовых методов persist, remove и findById, вы также можете добавить метод List findAll (). Но, как и сам класс сущности, мы вернемся к методам DAO в более поздних шаблонах реализации JPA.

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

public interface OrderDao extends Dao<Integer, Order> {List<Order> findOrdersSubmittedSince(Date date);}

Базовая реализация DAO

Третий шаг — создание базовой реализации JPA DAO. Он будет иметь базовую реализацию всех методов в стандартном интерфейсе Dao, который мы создали на шаге 1:

public abstract class JpaDao<K, E> implements Dao<K, E> {protected Class<E> entityClass; @PersistenceContextprotected EntityManager entityManager; public JpaDao() {ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();this.entityClass = (Class<E>) genericSuperclass.getActualTypeArguments()[1];} public void persist(E entity) { entityManager.persist(entity); } public void remove(E entity) { entityManager.remove(entity); } public E findById(K id) { return entityManager.find(entityClass, id); }}

Большая часть реализации довольно проста. Некоторые моменты, чтобы отметить, хотя:

  • Конструктор JpaDao включает в себя метод, предложенный моим коллегой Арджаном Блокзейлом для использования отражения для получения класса сущностей.
  • Аннотация @PersistenceContext приводит к тому, что контейнер EJB 3.0 или Spring внедряет менеджер сущностей.
  • Поля entityManager и entityClass защищены, чтобы подклассы, то есть конкретные реализации DAO, могли получить к ним доступ.

Конкретная реализация DAO

И, наконец, мы создаем такую ​​конкретную реализацию DAO. Он расширяет базовый класс JPA DAO и реализует специальный интерфейс DAO:

public class JpaOrderDao extends JpaDao<Integer, Order> implements OrderDao {public List<Order> findOrdersSubmittedSince(Date date) {Query q = entityManager.createQuery("SELECT e FROM " + entityClass.getName() + " e WHERE date >= :date_since");q.setParameter("date_since", date);return (List<Order>) q.getResultList();}}

Использование DAO

То, как вы получите ссылку на экземпляр вашего OrderDao, зависит от того, используем ли мы EJB 3.0 или Spring. В EJB 3.0 мы использовали бы аннотацию, подобную этой:

@EJB(name="orderDao")private OrderDao orderDao;

в то время как в Spring мы можем использовать файлы XML bean-компонентов или использовать автоматическую разводку следующим образом:

@Autowiredpublic OrderDao orderDao;

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

Order o = new Order();o.setCustomerName("Peter Johnson");o.setDate(new Date());orderDao.persist(o); 

Но мы также можем использовать специфический для сущности запрос, который мы добавили в интерфейс OrderDao:

List<Order> orders = orderDao.findOrdersSubmittedSince(date);for (Order each : orders) {System.out.println("order id = " + each.getId());}

С этим типобезопасным шаблоном DAO мы получаем следующие преимущества:

  • Нет прямой зависимости от API JPA от клиентского кода.
  • Безопасность типов за счет использования дженериков. Любые приведения, которые еще должны быть выполнены, обрабатываются в реализации DAO.
  • Одно логическое место для группировки всего кода объекта JPA.
  • Одно место для добавления маркеров транзакций, отладки, профилирования и т. Д. Хотя, как мы увидим позже, нам потребуется добавить маркеры транзакций и в других частях наших приложений.
  • Один класс для тестирования при тестировании кода доступа к базе данных. Мы вернемся к этому вопросу в более поздней схеме реализации JPA.

Я надеюсь , что вы это убеждает , что вы делаете потребность DAO с JPA.:-)

И это завершает первый шаблон реализации JPA. В следующем блоге этой серии мы будем использовать этот пример для обсуждения следующего паттерна. В то же время я хотел бы услышать от вас, как вы пишете свои DAO!

С http://blog.xebia.com