spring dao层注解
欢迎来到Spring教程的第三部分。 在这一部分中,我们将继续编写Timesheet应用程序,这次我们将实现DAO层,业务服务并编写一些测试。
在上一部分中,我们定义了GenericDao接口,该接口告诉我们需要对实体执行哪些操作。 现在我们需要提供实现。 我们将使用Hibernate的工具(使用SessionFactory)编写一个类来执行这些操作。 因此,任何提供的DAO都会自动继承这些基本操作。 我们稍后再讨论。
package org.timesheet.service.impl;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.timesheet.service.GenericDao;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.List;
/**
* Basic DAO operations dependent with Hibernate's specific classes
* @see SessionFactory
*/
@Transactional(propagation= Propagation.REQUIRED, readOnly=false)
public class HibernateDao<E, K extends Serializable> implements GenericDao<E, K> {
private SessionFactory sessionFactory;
protected Class<? extends E> daoType;
public HibernateDao() {
daoType = (Class<E>) ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
@Autowired
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
protected Session currentSession() {
return sessionFactory.getCurrentSession();
}
@Override
public void add(E entity) {
currentSession().save(entity);
}
@Override
public void update(E entity) {
currentSession().saveOrUpdate(entity);
}
@Override
public void remove(E entity) {
currentSession().delete(entity);
}
@Override
public E find(K key) {
return (E) currentSession().get(daoType, key);
}
@Override
public List<E> list() {
return currentSession().createCriteria(daoType).list();
}
}
我希望您注意有关此代码的几件事:
- 我们在类的顶部使用@Transcational批注。 这基本上意味着,DAO方法将在事务内运行。 为了使其正常工作,我们需要更改persistence-beans.xml文件,并在其中声明将处理事务的事务管理器。 只需添加以下行(新bean定义):
<bean id='transactionManager' class='org.springframework.orm.hibernate3.HibernateTransactionManager'> <property name='sessionFactory' ref='sessionFactory' /> </bean>
- 我们正在使用setter注入自动装配(@Autowired)SessionFactory。 如您所知,注入的种类更多(字段,setter,构造函数)。 使用Spring时,字段注入看起来最好,因为注释直接位于字段上,而不是构造函数或setter方法上。 另一方面,字段注入是最无用的,因为我们不能手动将其他依赖项设置为私有字段(例如在单元测试中)。 我尽可能地喜欢构造函数注入,因为我不必为依赖项使用mutator(setter)。 因此,以更安全的方式构造对象。 在此特定情况下,我们将使用setter注入,因为我们正在设计此类进行扩展。 如果选择构造函数注入,则所有扩展类都必须具有与超类匹配的构造函数。
如果您想了解更多有关此的内容,我建议您阅读 Dhanji R. Prasanna撰写的精彩著作 。
还要注意,构造函数的第一行正在做一些反射魔术。 那是因为Java在运行时没有泛型,只有在编译时才有泛型,因此它阻止我们编写类似E.class的东西。 因此,我们使用了这个丑陋的技巧。
现在我们有了一些DAO操作的基本模板。 在实际系统中,每个实体通常都有DAO。 这是因为有时那些继承的CRUD操作还不够,您还需要一些其他业务操作 。 我们将定义类型安全的接口(每个DAO的操作集),并且以后仅依赖于控制器中的接口。 我们将使用Hibernate实施它们,并使其自动接线。 创建新的包org.timesheet.service.dao,并在其中添加以下接口–每个实体的DAO:
package org.timesheet.service.dao;
import org.timesheet.domain.Employee;
import org.timesheet.service.GenericDao;
/**
* DAO of employee.
*/
public interface EmployeeDao extends GenericDao<Employee, Long> {
/**
* Tries to remove employee from the system.
* @param employee Employee to remove
* @return {@code true} if employee is not assigned to any task
* or timesheet. Else {@code false}.
*/
boolean removeEmployee(Employee employee);
}
package org.timesheet.service.dao;
import org.timesheet.domain.Manager;
import org.timesheet.service.GenericDao;
/**
* DAO of Manager.
*/
public interface ManagerDao extends GenericDao<Manager, Long> {
/**
* Tries to remove manager from the system.
* @param manager Manager to remove
* @return {@code true} if manager is not assigned to any task.
* Else {@code false}.
*/
boolean removeManager(Manager manager);
}
package org.timesheet.service.dao;
import org.timesheet.domain.Task;
import org.timesheet.service.GenericDao;
/**
* DAO of Task.
*/
public interface TaskDao extends GenericDao<Task, Long> {
/**
* Tries to remove task from the system.
* @param task Task to remove
* @return {@code true} if there is no timesheet created on task.
* Else {@code false}.
*/
boolean removeTask(Task task);
}
package org.timesheet.service.dao;
import org.timesheet.domain.Timesheet;
import org.timesheet.service.GenericDao;
/**
* DAO of Timesheet.
*/
public interface TimesheetDao extends GenericDao<Timesheet, Long> {
// no additional business operations atm
}
实施时间。 我们将扩展HibernateDao并实现coresponding接口。 我们需要这些具体的类,因为它将被注入到相应的字段(由接口声明)中。 也许您已经听说过有关此方法的一些内容–称为接口编程,并且您肯定希望使用它。
package org.timesheet.service.impl;
import org.hibernate.Query;
import org.springframework.stereotype.Repository;
import org.timesheet.domain.Employee;
import org.timesheet.service.dao.EmployeeDao;
@Repository('employeeDao')
public class EmployeeDaoImpl extends HibernateDao<Employee, Long> implements EmployeeDao {
@Override
public boolean removeEmployee(Employee employee) {
Query employeeTaskQuery = currentSession().createQuery(
'from Task t where :id in elements(t.assignedEmployees)');
employeeTaskQuery.setParameter('id', employee.getId());
// employee mustn't be assigned on no task
if (!employeeTaskQuery.list().isEmpty()) {
return false;
}
Query employeeTimesheetQuery = currentSession().createQuery(
'from Timesheet t where t.who.id = :id');
employeeTimesheetQuery.setParameter('id', employee.getId());
// employee mustn't be assigned to any timesheet
if (!employeeTimesheetQuery.list().isEmpty()) {
return false;
}
// ok, remove as usual
remove(employee);
return true;
}
}
package org.timesheet.service.impl;
import org.hibernate.Query;
import org.springframework.stereotype.Repository;
import org.timesheet.domain.Manager;
import org.timesheet.service.dao.ManagerDao;
@Repository('managerDao')
public class ManagerDaoImpl extends HibernateDao<Manager, Long> implements Manager