Статьи

Интеграция Hibernate с Spring

В этой статье мы рассмотрим, как Spring влияет на уровень доступа к данным приложения. Наше обсуждение начинается с шаблона объекта доступа к данным (DAO). Этот шаблон, популярный в мире Java, обеспечивает более управляемый и более обслуживаемый уровень доступа к данным. Затем мы обсудим, как Spring влияет на классы приложений DAO при интеграции с Hibernate.

Spring является универсальной средой, которая играет разные роли во многих областях архитектуры приложений. Одна из этих областей — настойчивость. Spring не обеспечивает свою собственную структуру постоянства. Вместо этого он обеспечивает уровень абстракции над JDBC и различные платформы отображения O / R, такие как iBATIS SQL Maps, Hibernate, JDO, Apache OJB и Oracle TopLink. Эта абстракция обеспечивает согласованную управляемую реализацию доступа к данным. 

Уровень абстракции Spring абстрагирует приложение от фабрики соединений, API транзакций и иерархий исключений, используемых базовой технологией персистентности. Код приложения всегда использует Spring API для работы с фабриками соединений, использует стратегии Spring для управления транзакциями и использует общую иерархию исключений Spring для обработки базовых исключений. Spring находится между классами приложения и инструментом отображения O / R, выполняет транзакции и управляет объектами соединения. Он переводит базовые исключения персистентности, выдаваемые Hibernate, в значимые, непроверенные исключения типа DataAccessException, Кроме того, Spring предоставляет IoC и AOP, которые можно использовать на уровне персистентности. Spring осуществляет транзакции Hibernate и предоставляет более мощный и всеобъемлющий подход к управлению транзакциями.

 

Шаблон объекта доступа к данным

Хотя вы можете получить объект Session и подключиться к Hibernate в любом месте приложения, рекомендуется, чтобы все взаимодействия с Hibernate осуществлялись только через отдельные классы. В связи с этим существует шаблон проектирования JEE, который называется шаблоном DAO. Согласно шаблону DAO все постоянные операции должны выполняться через специальные классы, технически называемые классами DAO . Эти классы используются исключительно для связи с уровнем данных. Целью этого шаблона является отделение кода, связанного с постоянством, от бизнес-логики приложения, что делает код более управляемым и обслуживаемым, позволяя гибко изменять стратегию постоянства без изменения бизнес-правил или логики рабочего процесса.

Шаблон DAO утверждает, что мы должны определить интерфейс DAO, соответствующий каждому классу DAO. Этот интерфейс DAO описывает структуру класса DAO, определяет все постоянные операции, которые необходимы бизнес-уровню, и (в приложениях на основе Spring) позволяет применять IoC для отделения бизнес-уровня от класса DAO.

 

План фасада обслуживания

При реализации уровня доступа к данным всегда используется шаблон Service Facade в дополнение к шаблону DAO. Этот шаблон указывает на использование промежуточного объекта, называемого сервисным объектом, между всеми объектами бизнес-уровня и объектами DAO. Служебный объект собирает методы DAO для управления как единицу работы. Обратите внимание, что только один класс обслуживания создается для всех DAO, которые реализуются в каждом случае использования.

Класс обслуживания использует экземпляры интерфейсов DAO для взаимодействия с ними. Эти экземпляры создаются из конкретных классов DAO контейнером IoC во время выполнения. Следовательно, объект службы не знает фактических деталей реализации DAO.

Независимо от стратегии персистентности, используемой вашим приложением (даже если оно использует прямой JDBC), настоятельно рекомендуется применять шаблоны DAO и Service Facade для разделения уровней приложений.

Реализация уровня данных с Hibernate

Давайте теперь посмотрим, как обсуждаемые шаблоны применяются к приложению, которое напрямую использует Hibernate. Следующий код показывает пример интерфейса DAO:

package com.packtpub.springhibernate.ch13;import java.util.Collection;public interface StudentDao {  public Student getStudent(long id);  public Collection getAllStudents();  public Collection getGraduatedStudents();  public Collection findStudents(String lastName);  public void saveStudent(Student std);  public void removeStudent(Student std);}

Следующий код показывает класс DAO, который реализует этот интерфейс DAO:

package com.packtpub.springhibernate.ch13;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.Transaction;import org.hibernate.HibernateException;import org.hibernate.Query;import java.util.Collection;public class HibernateStudentDao implements StudentDao {  SessionFactory sessionFactory;  public Student getStudent(long id) {    Student student = null;    Session session = HibernateHelper.getSession();    Transaction tx = null;    try {      tx = session.beginTransaction();      student = (Student) session.get(Student.class, new Long(id));      tx.commit();      tx = null;    } catch (HibernateException e) {      if (tx != null)        tx.rollback();      throw e;    } finally {      session.close();    }    return student;  }  public Collection getAllStudents(){    Collection allStudents = null;    Session session = HibernateHelper.getSession();    Transaction tx = null;    try {      tx = session.beginTransaction();      Query query = session.createQuery(                  "from Student std order by std.lastName, std.firstName");      allStudents = query.list();      tx.commit();      tx = null;    } catch (HibernateException e) {      if (tx != null)        tx.rollback();      throw e;    } finally {      session.close();    }    return allStudents;  }  public Collection getGraduatedStudents(){    Collection graduatedStudents = null;    Session session = HibernateHelper.getSession();    Transaction tx = null;    try {      tx = session.beginTransaction();      Query query = session.createQuery(                        "from Student std where std.status=1");      graduatedStudents = query.list();      tx.commit();      tx = null;    } catch (HibernateException e) {      if (tx != null)        tx.rollback();      throw e;    } finally {      session.close();    }    return graduatedStudents;  }  public Collection findStudents(String lastName) {    Collection students = null;    Session session = HibernateHelper.getSession();    Transaction tx = null;    try {      tx = session.beginTransaction();      Query query = session.createQuery(                  "from Student std where std.lastName like ?");      query.setString(1, lastName + "%");      students = query.list();      tx.commit();      tx = null;    } catch (HibernateException e) {      if (tx != null)        tx.rollback();      throw e;    } finally {      session.close();    }    return students;  }  public void saveStudent(Student std) {    Session session = HibernateHelper.getSession();    Transaction tx = null;    try {      tx = session.beginTransaction();      session.saveOrUpdate(std);      tx.commit();      tx = null;    } catch (HibernateException e) {      if (tx != null)        tx.rollback();      throw e;    } finally {      session.close();    }  }  public void removeStudent(Student std) {    Session session = HibernateHelper.getSession();    Transaction tx = null;    try {      tx = session.beginTransaction();      session.delete(std);      tx.commit();      tx = null;    } catch (HibernateException e) {      if (tx != null)        tx.rollback();      throw e;    } finally {      session.close();    }  }  public void setSessionFactory(SessionFactory sessionFactory) {    this.sessionFactory = sessionFactory;  }}

 

Как видите, все реализованные методы выполняют подпрограммы. Все сначала получают объект Session , получают объект Transaction , выполняют операцию сохранения, фиксируют транзакцию, откатывают транзакцию, если возникает исключение, и, наконец, закрывают объект Session . Каждый метод содержит много стандартного кода, который очень похож на другие методы.

Хотя применение шаблона DAO к постоянному коду приводит к более управляемому и обслуживаемому коду, классы DAO по-прежнему содержат много стандартного кода. Каждый метод DAO должен получить экземпляр Session , запустить транзакцию, выполнить операцию сохранения и зафиксировать транзакцию. Кроме того, каждый метод DAO должен включать свою собственную дублированную реализацию обработки исключений. Именно эти проблемы побуждают нас использовать Spring с Hibernate.

Шаблон шаблона. Чтобы очистить код и обеспечить более управляемый код, Spring использует шаблон под названием Шаблон шаблона . По этому шаблону объект шаблона оборачивает весь повторяющийся код. Затем этот объект делегирует постоянные вызовы как часть функциональности в шаблоне. В случае Hibernate HibernateTemplate извлекает весь шаблонный код, такой как получение сеанса, выполнение транзакции и обработка исключений.

Реализация уровня данных с помощью Spring

В Spring вам не нужно реализовывать код для получения объектов Session , запуска и фиксации транзакций и обработки исключений Hibernate. Вместо этого вы используете экземпляр HibernateTemplate для делегирования постоянных вызовов Hibernate без прямого взаимодействия с Hibernate.

Вот некоторые преимущества использования Spring в слое постоянства вместо прямого взаимодействия с Hibernate:

  • В Spring объект HibernateTemplate взаимодействует с Hibernate. Этот объект удаляет шаблонный код из реализаций DAO.
  • Любой вызов одного из методов HibernateTemplate генерирует общее исключение DataAccessException вместо HibernateException (специфичное для Hibernate исключение).
  • Spring позволяет декларативно разграничивать транзакции, вместо реализации дублированного кода управления транзакциями.

Класс HibernateTemplate использует экземпляр SessionFactory для получения объектов Session для взаимодействия Hibernate. Интересно, что вы можете настроить объект SessionFactory через контейнер IoC Spring для создания экземпляров и внедрения в объекты DAO.

В следующих разделах обсуждается, как HibernateTemplate используется в классах DAO и как он настраивается с помощью SessionFactory в контейнере Spring IoC. Сначала давайте посмотрим на иерархию исключений Spring.

 

Весеннее исключение перевода

Spring предоставляет свою собственную иерархию исключений, которая находится в иерархиях исключений поддерживаемых инструментов отображения O / R. Он перехватывает любое исключение или ошибку базы данных, которая может быть выдана через JDBC или базовый инструмент отображения O / R, и преобразует перехваченное исключение в соответствующее исключение в своей собственной иерархии. Иерархия исключений Spring определяется как подкласс org.springframework.dao.DataAccessException . Spring перехватывает все исключения, возникающие в базовой технологии персистентности, и переносит их в экземпляр DataAccessException . Объект DataAccessException является непроверенным исключением, поскольку он расширяет RuntimeException, и вам не нужно его перехватывать, если вы этого не хотите.

 

Рефакторинг классов DAO для использования Spring

Spring предоставляет различные базовые классы DAO для различных технологий доступа к данным, которые он поддерживает. Например, Spring предоставляет HibernateDaoSupport для Hibernate, SqlMapClientDaoSupport для карт iBATIS SQL и JdoDaoSupport для JDO. Эти классы обертывают общие свойства и методы, которые требуются во всех подклассах реализации DAO.

Когда вы используете Hibernate с Spring, классы DAO расширяют класс Spring org.springframework.orm.hibernate3.support.HibernateDaoSupport . Этот класс оборачивает экземпляр org.springframework.orm.hibernate3.HibernateTemplate , который, в свою очередь, оборачивает экземпляр org.hibernate.SessionFactory . Как вы скоро увидите в этой статье, расширение класса HibernateDaoSupport позволяет согласованно настраивать все реализации DAO в виде bean-компонентов внутри контейнера IoC Spring. Их свойство SessionFactory настраивается и настраивается через контекст Spring.

В следующем коде показан простой интерфейс DAO для реализации DAO на основе Spring:

package com.packtpub.springhibernate.ch13;import java.util.Collection;public interface StudentDao {  public Student getStudent(long id);  public Collection getAllStudents();  public Collection getGraduatedStudents();  public Collection findStudents(String lastName);  public void saveStudent(Student std);  public void removeStudent(Student std);}

 

Здесь StudentDao — это интерфейс DAO с той же структурой, что и интерфейс, показанный в следующем коде.

HibernateException выдается за любой сбой при непосредственном взаимодействии с Hibernate. Когда используется Spring, HibernateException перехватывается Spring и преобразуется в DataAccessException для любого сбоя персистентности. Оба исключения не проверены, поэтому вам не нужно ловить их, если вы не хотите это делать.

 

В следующем коде показана реализация DAO для этого интерфейса DAO, который теперь использует Spring для взаимодействия с Hibernate:

package com.packtpub.springhibernate.ch13;import org.springframework.orm.hibernate3.support.HibernateDaoSupport;import java.util.Collection;public class HibernateStudentDao extends HibernateDaoSupport implements StudentDao {  public Student getStudent(long id) {    return (Student) getHibernateTemplate().get(Student.class, new Long(id));  }  public Collection getAllStudents(){    return getHibernateTemplate().      find("from Student std order by std.lastName, std.firstName");  }  public Collection getGraduatedStudents(){    return getHibernateTemplate().find("from Student std where std.status=1");  }  public Collection findStudents(String lastName) {    return getHibernateTemplate().      find("from Student std where std.lastName like ?", lastName + "%");  }  public void saveStudent(Student std) {    getHibernateTemplate().saveOrUpdate(std);  }  public void removeStudent(Student std) {    getHibernateTemplate().delete(std);  }}

 

Как видите, все постоянные методы в классе DAO используют метод getHibernateTemplate () для доступа к объекту HibernateTemplate . Как вы видели в предыдущем разделе, HibernateTemplate — это удобный класс Spring, который делегирует вызовы DAO API Hibernate Session. Этот класс предоставляет все методы Session Hibernate, а также множество других удобных методов, которые могут понадобиться классам DAO. Поскольку удобные методы HibernateTemplate не предоставляются интерфейсом Session, вы можете использовать find () и findByCriteria ()когда вы хотите выполнить HQL или создать объект Criteria. Кроме того, он оборачивает все лежащие в основе исключения, создаваемые методом Session, экземплярами непроверенной org.springframework.dao.DataAccessException .

Для удобства я рекомендую использовать класс HibernateDaoSupport в качестве базового класса для всех реализаций DAO Hibernate, но вы можете игнорировать этот класс и работать напрямую с экземпляром HibernateTemplate в классах DAO. Для этого определите свойство HibernateTemplate в классе DAO, которое инициализируется и настраивается через контейнер IoC Spring.

Следующий код показывает класс DAO, который теперь напрямую использует HibernateTemplate . Обратите внимание, что этот подход не рекомендуется, поскольку он вовлекает вас в объект HibernateTemplate как в классе DAO, так и в конфигурации DAO в контексте Spring:

package com.packtpub.springhibernate.ch13;import org.springframework.orm.hibernate3.HibernateTemplate;import java.util.Collection;public class HibernateStudentDao implements StudentDao {      HibernateTemplate hibernateTemplate;  public Student getStudent(long id) {    return (Student) getHibernateTemplate().get(Student.class, new Long(id));  }  public Collection getAllStudents(){    return getHibernateTemplate().      find("from Student std order by std.lastName, std.firstName");  }  public Collection getGraduatedStudents(){    return getHibernateTemplate().find("from Student std where std.status=1");  }  public Collection findStudents(String lastName) {    return getHibernateTemplate().      find("from Student std where std.lastName like "+ lastName + "%");  }  public void saveStudent(Student std) {    getHibernateTemplate().saveOrUpdate(std);  }  public void removeStudent(Student std) {    getHibernateTemplate().delete(std);  }  public HibernateTemplate getHibernateTemplate() {    return hibernateTemplate;  }  public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {    this.hibernateTemplate = hibernateTemplate;  }}

Класс DAO теперь имеет метод setHibernateTemplate (), позволяющий Spring внедрить настроенный экземпляр HibernateTemplate в объект DAO.

Кроме того, класс DAO может отказаться от класса HibernateTemplate и напрямую использовать экземпляр SessionFactory для взаимодействия с Hibernate. В следующем коде показан HibernateStudentDao , который теперь работает непосредственно с объектом SessionFactory :

package com.packtpub.springhibernate.ch13;import org.hibernate.HibernateException;import org.hibernate.Session;import org.hibernate.Query;import org.hibernate.SessionFactory;import org.springframework.orm.hibernate3.SessionFactoryUtils;import java.util.Collection;public class HibernateStudentDao implements StudentDao {  SessionFactory sessionFactory;  public Student getStudent(long id) {    Session session = SessionFactoryUtils.getSession(this.sessionFactory, true);    try {      return (Student) session.get(Student.class, new Long(id));    } catch (HibernateException ex) {      throw SessionFactoryUtils.convertHibernateAccessException(ex);    } finally {      SessionFactoryUtils.closeSession(session);    }  }  public Collection getAllStudents(){    Session session = SessionFactoryUtils.getSession(this.sessionFactory, true);    try {            Query query = session.createQuery(          "from Student std order by std.lastName, std.firstName");      Collection allStudents = query.list();      return allStudents;    } catch (HibernateException ex) {      throw SessionFactoryUtils.convertHibernateAccessException(ex);    } finally {      SessionFactoryUtils.closeSession(session);    }  }  public Collection getGraduatedStudents(){    Session session = SessionFactoryUtils.getSession(this.sessionFactory, true);    try {      Query query = session.createQuery("from Student std where std.status=1");      Collection graduatedStudents = query.list();      return graduatedStudents;    } catch (HibernateException ex) {      throw SessionFactoryUtils.convertHibernateAccessException(ex);    } finally {      SessionFactoryUtils.closeSession(session);    }  }  public Collection findStudents(String lastName) {    Session session = SessionFactoryUtils.getSession(this.sessionFactory, true);    try {      Query query = session.createQuery(                  "from Student std where std.lastName like ?");      query.setString(1, lastName + "%");      Collection students = query.list();      return students;    } catch (HibernateException ex) {      throw SessionFactoryUtils.convertHibernateAccessException(ex);    } finally {      SessionFactoryUtils.closeSession(session);    }  }  public void saveStudent(Student std) {    Session session = SessionFactoryUtils.getSession(this.sessionFactory, true);    try {      session.save(std);    } catch (HibernateException ex) {      throw SessionFactoryUtils.convertHibernateAccessException(ex);    } finally {      SessionFactoryUtils.closeSession(session);    }  }  public void removeStudent(Student std) {    Session session = SessionFactoryUtils.getSession(this.sessionFactory, true);    try {      session.delete(std);    } catch (HibernateException ex) {      throw SessionFactoryUtils.convertHibernateAccessException(ex);    } finally {      SessionFactoryUtils.closeSession(session);    }  }  public void setSessionFactory(SessionFactory sessionFactory) {    this.sessionFactory = sessionFactory;  }}

Во всех описанных выше методах класс SessionFactoryUtils используется для получения объекта Session . Предоставленный объект Session затем используется для выполнения операции персистентности. SessionFactoryUtils также используется для преобразования HibernateException в DataAccessException в блоках catch и закрытия объектов Session в последних блоках. Обратите внимание, что эта реализация DAO обходит преимущества HibernateDaoSupport и HibernateTemplate . Вы должны управлять сессией Hibernate вручную (а также трансляцией исключений и управлением транзакциями) и реализовывать много стандартного кода.

org.springframework.orm.hibernate3.SessionFactoryUtils — это вспомогательный класс Spring для получения Session, повторного использования Session в транзакциях и преобразования HibernateException в универсальный DataAccessException .

Этот способ абсолютно не подходит для работы с Session in Spring. Всегда используйте класс HibernateTemplate для работы с объектами Session за кулисами. Однако в тех случаях, когда вам нужно работать непосредственно с объектами Session, вы можете использовать реализацию интерфейса org.springframework.orm.hibernate3.HibernateCallback в качестве обработчика для работы с Sessions. Следующий фрагмент кода показывает, как этот подход:

public void saveStudent(Student std) {  HibernateCallback callback = new HibernateCallback() {   public Object doInHibernate(Session session) throws HibernateException, SQLException {    return session.saveOrUpdate(std);   }  };  getHibernateTemplate().execute(callback);   }

 

В этом коде создается неявная реализация HibernateCallback, и реализуется ее единственный метод doInHibernate () . Метод doInHibernate () принимает объект Session и возвращает результат операции персистентности, ноль, если нет. Затем объект HibernateCallback передается методу execute () HibernateTemplate для выполнения. Метод doInHibernate () просто предоставляет обработчик для работы непосредственно с объектами Session, которые получены и используются за кулисами.

Настройка Hibernate в контексте Spring

Spring предоставляет класс LocalSessionFactoryBean в качестве фабрики для объекта SessionFactory . Объект LocalSessionFactoryBean настраивается как bean-компонент внутри контейнера IoC с локальным источником данных JDBC или общим источником данных из JNDI.

Локальный JDBC DataSource может быть в свою очередь настроен как объект org.apache.commons.dbcp.BasicDataSource в контексте Spring:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">  <property name="driverClassName">    <value>org.hsqldb.jdbcDriver</value>  </property>  <property name="url">    <value>jdbc:hsqldb:hsql://localhost/hiberdb</value>  </property>  <property name="username">    <value>sa</value>  </property>  <property name="password">    <value></value>  </property></bean>

 

В этом случае org.apache.commons.dbcp.BasicDataSource (пул соединений с базой данных Jakarta Commons) должен находиться в пути к классу приложения.

Точно так же общий источник данных можно настроить как объект org.springframework.jndi.JndiObjectFactoryBean . Это рекомендуемый способ, который используется, когда пул соединений управляется сервером приложений. Вот способ настроить это:

<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">  <property name="jndiName">    <value>java:comp/env/jdbc/HiberDB</value>  </property></bean>

 

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

<bean id="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">    <property name="dataSource">        <ref bean="dataSource"/>    </property>    ...</bean>

 

В качестве альтернативы вы можете настроить объект SessionFactory в качестве объекта ресурса на стороне сервера в контексте Spring. Этот объект связан как ресурс JNDI в среде JEE для совместного использования несколькими приложениями. В этом случае вам нужно использовать JndiObjectFactoryBean вместо LocalSessionFactoryBean :

<bean id="sessionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">  <property name="jndiName">    <value>java:comp/env/jdbc/hiberDBSessionFactory</value>  </property></bean>

 

JndiObjectFactoryBean — это еще один фабричный компонент для поиска любого ресурса JNDI.

При использовании JndiObjectFactoryBean для получения предварительно сконфигурированной SessionFactory объекта, то SessionFactory объект уже должен быть зарегистрирован в качестве JNDI ресурса. Для этой цели вы можете запустить серверный класс, который создает объект SessionFactory и регистрирует его как ресурс JNDI.

LocalSessionFactoryBean использует три свойства: источник данных , mappingResources и hibernateProperties . Эти свойства следующие:

  • источник данных относится к JDBC DataSource объекта , который уже определен как другой фасоли внутри контейнера.
  • mappingResources указывает файлы отображения Hibernate, расположенные в пути к классам приложения.
  • hibernateProperties определяет параметры конфигурации Hibernate.

У нас есть объект sessionFactory, настроенный следующим образом:

<bean id="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  <property name="dataSource">    <ref bean="dataSource"/>  </property>  <property name="mappingResources">   <list>    <value>com/packtpub/springhibernate/ch13/Student.hbm.xml</value>    <value>com/packtpub/springhibernate/ch13/Teacher.hbm.xml</value>    <value>com/packtpub/springhibernate/ch13/Course.hbm.xml</value>    </list>  </property>  <property name="hibernateProperties">    <props>      <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect      </prop>      <prop key="hibernate.show_sql">true</prop>      <prop key="hibernate.max_fetch_depth">2</prop>    </props>  </property></bean>

 

Свойство mappingResources загружает определения сопоставления в путь к классам. Вы можете использовать mappingJarLocations или mappingDirectoryLocations, чтобы загрузить их из файла JAR или из любого каталога файловой системы, соответственно.

По-прежнему можно настроить Hibernate с помощью hibernate.cfg.xml вместо настройки Hibernate, как показано ниже. Для этого настройте sessionFactory со свойством configLocation следующим образом:

<bean id="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  <property name="dataSource">    <ref bean="dataSource"/>  </property>  <property name="configLocation">    <value>/conf/hibernate.cfg.xml</value>  </property></bean>

 

Обратите внимание, что hibernate.cfg.xml определяет определения отображения Hibernate в дополнение к другим свойствам Hibernate.

Когда объект SessionFactory настроен, вы можете настроить реализации DAO как компоненты в контексте Spring. Эти компоненты DAO — это объекты, которые ищутся из контейнера IoC Spring и используются бизнес-уровнем. Вот пример конфигурации DAO:

<bean id="studentDao" class="com.packtpub.springhibernate.ch13.HibernateStudentDao">  <property name="sessionFactory">    <ref local="sessionFactory"/>  </property></bean>

 

 

Это конфигурация DAO для класса DAO, который расширяет HibernateDaoSupport или напрямую использует свойство SessionFactory . Когда класс DAO имеет свойство HibernateTemplate , настройте экземпляр DAO следующим образом:

<bean id="studentDao" class="com.packtpub.springhibernate.ch13.HibernateStudentDao">  <property name="hibernateTemplate">    <bean class="org.springframework.orm.hibernate3.HibernateTemplate">      <constructor-arg>        <ref local="sessionFactory"/>      </constructor-arg>    </bean>  </property></bean>

 

В соответствии с предыдущим объявлением класс HibernateStudentDao имеет свойство hibernateTemplate , которое настраивается через контейнер IoC и инициализируется посредством внедрения конструктора и экземпляра SessionFactory в качестве аргумента конструктора.

Теперь любой клиент реализации DAO может найти контекст Spring, чтобы получить экземпляр DAO. В следующем коде показан простой класс, который создает контекст приложения Spring, а затем ищет объект DAO из контейнера Spring IoC:

package com.packtpub.springhibernate.ch13;    public class DaoClient {  public static void main(String[] args) {     ApplicationContext ctx = new ClassPathXmlApplicationContext("com/packtpub/springhibernate/ch13/applicationContext.xml");    StudentDao stdDao = (StudentDao)ctx.getBean("studentDao");    Student std = new Student();    //set std properties    //save std    stdDao.saveStudent(std);  }}

Spring управление транзакциями

Одной из причин интеграции Hibernate с Spring является управление транзакциями. Spring предоставляет уровень абстракции транзакций через API транзакций Hibernate и позволяет постоянным операциям участвовать в глобальных транзакциях. Кроме того, Spring обеспечивает декларативное разграничение транзакций, которое создает более читаемый и поддерживаемый код Java. Декларативный подход позволяет нам легко менять стратегию транзакций, не меняя код.

API разграничения транзакций Spring имеет два класса для работы с приложениями Hibernate:

  • org.springframework.transaction.support.TransactionTemplate для программного подхода.
  • org.springframework.transaction.interceptor.TransactionProxyFactoryBean для декларативного подхода.

За этим API вы можете использовать один из двух менеджеров транзакций Spring:

  • org.springframework.orm.hibernate3.HibernateTransactionManager : используйте этот параметр, когда приложение использует один источник данных и требуется только Hibernate. Это охватывает локальные транзакции, выполняемые в одной SessionFactory. Этот менеджер обычно используется, так как большинство приложений Hibernate работают с одной базой данных.
  • org.springframework.transaction.jta.JtaTransactionManager : Это глобальный диспетчер транзакций JTA Spring. Используйте его, когда приложение участвует в глобальных транзакциях в среде Java EE, в которой участвуют несколько методов SessionFactory , и транзакции разбросаны по ним.

Обратите внимание, что мы можем разработать классы DAO, которые не участвуют в управлении транзакциями. Это преимущество, которое Spring предоставляет для приложения, так что все классы DAO работают с транзакционными объектами Session, предоставляемыми за кулисами контейнером Spring IoC.

Вы конфигурируете транзакции Spring, устанавливая экземпляр менеджера транзакций как компонент внутри контейнера IoC. Конфигурация bean-компонента зависит от используемой стратегии транзакции: локальной или глобальной.

Давайте посмотрим на конфигурацию транзакции в деталях.

Локальные транзакции

Когда приложение использует только один источник данных (один SessionFactory в Hibernate), вы можете определить диспетчер транзакций как экземпляр HibernateTransactionManager следующим образом:

<bean id="transactionManager"      class="org.springframework.orm.hibernate3.HibernateTransactionManager">  <property name="sessionFactory">    <ref bean="sessionFactory"/>  </property></bean>

 

Затем определите компоненты DAO и Service соответственно как экземпляры DAO и классов обслуживания. Вот пример:

<bean id="persistenceService" class="com.packtpub.springhibernate.ch13.PersistenceService">    <property name="studentDao">      <ref bean="studentDao"/>    </property>  </bean><bean id="studentDao"class="com.packtpub.springhibernate.ch13.HibernateStudentDao">  <property name="sessionFactory">    <ref bean="sessionFactory"/>  </property></bean>

 

Наконец, экземпляры DAO помещаются в прокси-транзакцию. Транзакции — это сквозные проблемы, которые не посвящены функции конкретного метода. Вместо этого они разбросаны по многим постоянным методам. В Spring функциональность DAO можно разделить на два модуля:

  • Реализации DAO , которые выполняют постоянные операции.
  • Советы по транзакциям , которые определяют, как операции сохранения выполняются в транзакциях.

Модуляризация реализаций DAO для выполнения операций персистентности, а не управления транзакциями, позволяет избежать стандартного кода управления транзакциями в каждом методе персистентности. Чтобы применить рекомендации по транзакциям к целевым методам в реализациях DAO, нам нужны прокси-объекты.

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

<bean     id="studentDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">  <property name="transactionManager">    <ref bean="transactionManager"/>  </property>  <property name="target">    <ref bean="persistenceService"/>  </property>  <property name="transactionAttributes">    <props>      <prop key="save*">PROPAGATION_REQUIRED</prop>      <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>    </props>  </property></bean>

 

Объект TransactionProxyFactoryBean имеет три свойства:

  • TransactionManager , объявленный как отдельный компонент, представляет собой объект, который обеспечивает абстрагирование API между кодом приложения и API транзакции Hibernate.
  • target представляет объект, к которому должна применяться транзакция.
  • транзакционный атрибут определяет, как выбирать транзакционные методы. Например, save * выбирает все методы целевого объекта, которые начинаются со слова сохранения .

В нашем примере, PROPAGATION_REQUIRED и доступно только для чтения указать, соответственно, как участие несколько сделок, и является ли сделка разрешена только читать данные.

 

Глобальные транзакции

Вы можете настроить Spring на использование глобальных транзакций для синхронизации постоянных операций, выполняемых в среде Java EE, между несколькими источниками данных. Процесс настройки аналогичен настройке локальной транзакции, но вам необходимо определить несколько источников данных, а также несколько SessionFactory и DAO.

Объект менеджера транзакций теперь определен как экземпляр JtaTransactionManager , а не HibernateTransactionManager . Вот пример:

<beans>  <!-- the datasource1 declartion, registered to node ds1 on the JNDI tree-->  <bean id="datasource1" class="org.springframework.jndi.JndiObjectFactoryBean">    <property name="jndiName">      <value>java:comp/env/jdbc/ds1</value>    </property>  </bean>  <bean id="datasource2" class="org.springframework.jndi.JndiObjectFactoryBean">    <property name="jndiName">      <value>java:comp/env/jdbc/ds2</value>    </property>  </bean>  <!-- the sessionFactory1 declaration, which uses datasource1-->  <bean id="sessionFactory1"  class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">    <property name="mappingResources">      <list>        <value>Student.hbm.xml</value>      </list>    </property>    <property name="hibernateProperties">      <props>        <prop key="hibernate.dialect">          org.hibernate.dialect.MySQLDialect        </prop>      </props>    </property>    <property name="dataSource">      <ref bean="dataSource1"/>    </property>  </bean>  <!-- the sessionFactory2 declartion, which uses datasource2-->  <bean id="sessionFactory2"  class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">    <property name="mappingResources">      <list>        <value>Teacher.hbm.xml</value>        <value>Course.hbm.xml</value>      </list>    </property>    <property name="hibernateProperties">      <props>        <prop key="hibernate.dialect">          org.hibernate.dialect.HSQLDialect        </prop>      </props>    </property>    <property name="dataSource">      <ref bean="dataSource2"/>    </property>  </bean>  <!-- TransactionManager declaration -->  <bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>  <!-- A DAO configuration with the sessionFactory1-->  <bean id="studentDao" class="com.packtpub.springhibernate.ch13.HibernateStudentDao">    <property name="sessionFactory">      <ref bean="sessionFactory1"/>    </property>  </bean>  <!-- A DAO configuration with the sessionFactory2-->  <bean id="teacherDao" class="com.packtpub.springhibernate.ch13.HibernateTeacherDao">    <property name="sessionFactory">      <ref bean="sessionFactory2"/>    </property>  </bean>  <!-- A service instance configuration which reside in the business layer -->  <!-- and works with both StudentDao and TeacherDao instances-->  <bean id="persistenceServiceTarget" class="com.packtpub.springhibernate.ch13.PersistenceService">    <property name="studentDao">      <ref bean="studentDao"/>    </property>    <property name="teacherDao">      <ref bean="teacherDao"/>    </property>  </bean>  <!-- A proxy configuration, which applies the transactions on the DAO methods-->  <bean    id="persistenceService"   class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">    <property name="transactionManager">      <ref bean="transactionManager"/>    </property>    <property name="target">      <ref bean="persistenceServiceTarget"/>    </property>    <property name="transactionAttributes">      <props>        <prop key="save*">PROPAGATION_REQUIRED</prop>        <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>      </props>    </property>  </bean></beans>

 

Кроме того, вы можете настроить диспетчер транзакций как компонент типа JndiObjectFactoryBean в контейнере IoC. JndiObjectFactoryBean — это фабричный компонент, который получает объект менеджера транзакций путем поиска ресурса JNDI. Если вы выберете эту стратегию, конфигурация SessionFactory изменится следующим образом:

<bean id="sessionFactory"class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">  <property name="dataSource">    <ref bean="dataSource"/>  </property>  <property name="jtaTransactionManager">    <ref bean="transactionManager"/>  </property>  <property name="mappingResources">   <list>    <value>com/packtpub/springhibernate/ch13/Student.hbm.xml</value>    <value>com/packtpub/springhibernate/ch13/Teacher.hbm.xml</value>    <value>com/packtpub/springhibernate/ch13/Course.hbm.xml</value>   </list>  </property>  <property name="hibernateProperties">    <props>      <prop key="hibernate.dialect">        org.hibernate.dialect.HSQLDialect      </prop>    </props>  </property></bean>

 

Вы также должны настроить объект менеджера транзакций:

<bean id="transactionManager"class="org.springframework.jndi.JndiObjectFactoryBean">  <property name="jndiName">    <value>java:comp/env/jdbc/mytm</value>  </property></bean>

 

Таким образом, мы настроили транзакциюManager как компонент, созданный JndiObjectFactoryBean . Этот фабричный компонент возвращает объекты диспетчера транзакций путем поиска местоположения JNDI для конкретного сервера.

Резюме

В этой статье вы узнали о шаблоне объекта доступа к данным (DAO) . Этот шаблон позволяет осуществлять любое взаимодействие с уровнем данных через определенные классы, называемые классами DAO. В соответствии с этим шаблоном интерфейс DAO определяется для соответствия каждому классу DAO, определяя все постоянные операции, которые необходимы бизнес-уровню. Основная цель — отделить бизнес-уровень от уровня данных. Интерфейсы DAO являются единственными ссылками, которые бизнес-уровень использует для выполнения постоянных операций. На этом этапе Spring настроен на создание этих ссылок с фактическими реализациями во время выполнения.

Вы можете интегрировать Hibernate с Spring, чтобы упростить реализацию DAO. Spring предоставляет уровень абстракции транзакций в API локальных транзакций, предоставляемый Hibernate, и позволяет декларативное разграничение транзакций. Кроме того, он преобразует HibernateException в общую иерархию DataAccessException в Spring и позволяет использовать IoC на уровне данных.

Spring поддерживает реализацию DAO с помощью двух вспомогательных классов: org.springframework.orm.hibernate3.HibernateTemplate и org.springframework.orm.hibernate3.support.HibernateDaoSupport . Все классы DAO расширяют HibernateDaoSupport и используют HibernateTemplate для выполнения постоянных операций. Spring предоставляет org.springframework.orm.hibernate3.LocalSessionFactoryBean в качестве фабричного компонента для настройки и настройки объекта SessionFactory в стиле IoC. Для управления транзакциями Spring предоставляет org.springframework.orm.hibernate3.HibernateTransactionManager и org.springframework.transaction.jta.JtaTransactionManager.для управления локальными и глобальными транзакциями соответственно. Кроме того, Spring предоставляет org.springframework.transaction.interceptor.TransactionProxyFactoryBean , который действует как прокси-сервер для применения проблем транзакций к постоянным операциям.

 

Ссылки

 Эта статья была взята из книги « Сохранение весны в Hibernate» (Packt Publishing, ноябрь 2009 г.).