воскресенье, 16 октября 2011 г.

Расширим DAO c Hibernate Generic D.A.O. Framework

Когда я только начал изучать Hibernate я делал на каждую свою сущность отдельный DAO. Вскоре я наткнулся на популярную в сети статью "Не повторяйте DAO", которая используя Spring и дженерики создает обобщенный типизированный DAO. И через некоторое время данный подход возродился в некий фреймворк, который может работать с любой сущностью или коллекцией сущностей. Но смысла нету показывать мои костыли, если есть Hibernate Generic D.A.O. Framework, про который можно прочитать здесь, а я постараюсь показать пример его использования. Правда DAO нужно будет всеравно создавать для каждой сущности, но зато мы получим общие методы и интересный подход к составлению запросов в БД.

Я не буду описывать что такое Spring и Hibernate и как их первоначально добавить в проект и настроить, так что этот пост предозначен для тех, что уже ознакомился с этими фреймворками. Если же кому не довелось с ними столкнутся, то в интернете статей на эту тему больше чем достаточно (можно начать с http://habrahabr.ru/blogs/java/111102/ )

1. Для начала добавим фреймворк в проект. 
Сделать это можно подложив скачанные здесь jar'ы в проект или используя maven, добавить след. зависимости
<dependency>
<groupId>com.googlecode.genericdao</groupId>
<artifactId>dao-hibernate</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>com.googlecode.genericdao</groupId>
<artifactId>search-jpa-hibernate</artifactId>
<version>1.0.0</version>
</dependency>
2. Создадим класс сущность.
Я использую hibernate-annotation. Кому по душе писать xml-маппинги, я думаю будет не сложно понять по коду структуру сущности.
@Entity
@Table(name = "user", uniqueConstraints = @UniqueConstraint(columnNames = "account"))
public class User implements Serializable {

 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private Long id;
 @Column(name = "first_name")
 private String firstName;
 @Column(name = "last_name")
 private String lastName;
 @Column(name = "account", unique = true, nullable = false, length = 64)
 private String account;

 @Column(name = "is_active", nullable = false)
 private boolean isActive;

 public String getAccount() {
  return account;
 }

 public void setAccount(String account) {
  this.account = account;
 }

 public String getFirstName() {
  return firstName;
 }

 public void setFirstName(String firstName) {
  this.firstName = firstName;
 }

 public Long getId() {
  return id;
 }

 public void setId(Long id) {
  this.id = id;
 }

 public boolean isIsActive() {
  return isActive;
 }

 public void setIsActive(boolean isActive) {
  this.isActive = isActive;
 }

 public String getLastName() {
  return lastName;
 }

 public void setLastName(String lastName) {
  this.lastName = lastName;
 }
}
3. Конфигурим в контексте Spring'a
Опишем DataSource и SessionFactory.
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost/test?useUnicode=true&amp;characterEncoding=UTF-8" />
<property name="username" value="test" />
<property name="password" value="test" />
</bean>

<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
  <value>net.dirong.hgs.demo.entity.User</value>
</list>
</property>
<property name="annotatedPackages">
<list>
  <value>net.dirong.hgs.demo.entity</value>
</list>
</property>
<property name="configurationClass" value="org.hibernate.cfg.AnnotationConfiguration" />
<property name="hibernateProperties">
<props>
  ..........................................................
</props>
</property>
</bean>

<tx:annotation-driven transaction-manager="transactionManager"/>

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

4. Создадим DAO.
Для того, чтобы получить прелести этого фреймворка мы для интерфейса наследуем  GenericDAO с коробки и для класса - GenericDAOImpl
public interface UserDAO extends GenericDAO<User, Long> {
}
public class UserDAOImpl extends GenericDAOImpl<User, Long> implements UserDAO{
}

И добавим его в контекст Spring'a
<bean id="userDAO" class="net.dirong.hgs.demo.UserDAOImpl">
  <property name="sessionFactory" ref="sessionFactory"></property>
</bean>

5. Использование
Теперь наш UserDAO имеет стандартные методы получения и модификации объекта в БД.
Но на этом возможности фреймворка не заканчиваются. В нем есть интересный подход к постоению запросов с помощью класса Search.
Добавим в наш DAO несколько методов.
public class UserDAOImpl extends GenericDAOImpl<User, Long> implements UserDAO{

  public User searchByAccount(String account) throws Exception{
    Search search = new Search();
    search.addFilterEqual("account", account);
    return searchUnique(search);
  }

  public List<User> getActiveUsers(){
    Search search = new Search();
    search.addFilterEqual("isActive", true);
    search.addFilterNotNull("createDate");
    search.addSortDesc("firstName");
    return search(search);
  }

  @Override
  public boolean save(User entity) {
    if(entity.getId()==null){
      entity.setCreateDate(new Date());
    }
    return super.save(entity);
  }
}
Вот так вот легко можно строить запросы. Но если нам нужно, как в большенстве случаев, использовать несколько сущностей в запросе, то на помощь приходит SearchFacade
<bean id="searchFacade" class="com.trg.search.hibernate.HibernateSearchFacade">
  <property name="sessionFactory" ref="sessionFactory"/>
</bean>

И теперь его можно использовать в своих бинах.
  @Autowired
  private SearchFacade searchFacade;

  @Transactional
  public List<Role> getRolesByUser(User user) {
    Search search = new Search(Role.class);
    search.addFilterEqual("user", user);
    search.addField("role");
    search.addSort("role.name", false);
    return searchFacade.search(search);
  }
Надеюсь пост будет полезен. Если что-то не понятно описал, то сорри, могу позже описать некоторые детали более подробно. Кому стало интерестно, то на вики фреймворка описано более подробно.

1 комментарий:

  1. @Override
    public boolean save(User entity) {
    if(entity.getId()==null){
    entity.setCreateDate(new Date());
    }
    return super.save(entity);
    }

    госпади,что это за копипаста?)

    ОтветитьУдалить