上一节已经把项目的框架环境搭建起来了。并且做了简单的测试,这一节我们主要来设计Dao共用类的。在做这项工作之前我们先来看一个问题:在开发的过程中,我们发现DAO层的主要作用就是对数据库进行增删改查。例如有用户管理、部门管理、角色管理、文章管理这四个模块:
实体 | Dao接口 | 实现类 |
---|---|---|
User | UserDao | UserDaoImpl |
Role | RoleDao | RoleDaoImpl |
Department | DepartmentDao | DepartmentDaoImpl |
Article | ArticleDao | ArticleDaoImpl |
他们各自具有方法如下:
实体 | Dao | 方法 |
---|---|---|
User | UserDao | save(User user), update(), delete(), find() |
Role | RoleDao | save(Role role), update(), delete(), find() |
Student | StudentDao | save(Student student), update(), delete(), find() |
这四个Dao类的主要作用是对数据中相对应的表进行增删改查操作,这时候为了避免重复操作,可以将他们的基本操作方法提取出来作为一个基础类的共有方法。
类 | 方法 |
---|---|
BaseDao | save(T t), update(), delete(), find() |
类的模型如图:
基于Java的单继承多现实原理,我们可以设计一个通用的BaseDao接口,他包含了save(),delete(),updae(),getById(),getByIds(),findAll()等共用的方法,这个Dao有一个基本的实现类BaseDaoImpl,对这个共用接口所有的方法实现。User对应的dao实现类可以继承BaseDaoImpl也同时实现了UserDao接口,Role对应的Dao实现类可以也可以继承BaseDaoImpl同时可以实现了RoleDao的接口。共用的方法放在BaseDao接口里面,实体对应的个体的独特方法放在自己的接口里面,这样便减少重复工作量!
BaseDao:
import java.util.List;
/**
* 泛型接口
* @author shizongger
* @param <T>
*/
public interface BaseDao<T> {
public void add(T entity);
public void delete(Long id);
public void update(T entity);
public T selectById(Long id);
public List<T> selectAll();
}
BaseDaoImpl:
import java.lang.reflect.ParameterizedType;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.shizongger.oa.domain.User;
/**
* @author shizongger
* @param <T> 实际操作的daomain实体
*/
public abstract class BaseDaoImpl<T> implements BaseDao<T> {
private Log log = LogFactory.getLog(this.getClass());
/**
* sessionFactory工厂
*/
@Autowired
private SessionFactory sessionFactory;
private Class<T> clazz;
@SuppressWarnings("unchecked")
public BaseDaoImpl() {
// 使用反射技术得到T的真实类型
ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); // 获取当前new的对象的 泛型的父类 类型
this.clazz = (Class<T>) pt.getActualTypeArguments()[0]; // 获取第一个类型参数的真实类型
}
/**
* 增加
*/
@Override
public void add(T entity) {
log.info("add:" + entity.toString());
getSession().save(entity);
}
/**
* 删除
*/
@Override
public void delete(Long id) {
Object object = selectById(id);
if(object != null) {
getSession().delete(object);
}
}
/**
* 修改
*/
@Override
public void update(T entity) {
getSession().update(entity);
}
/**
* 根据id查询
*/
@Override
public T selectById(Long id) {
return (T) getSession().get(this.clazz, id);
}
/**
* 根据id数组查询
*/
@Override
public List<T> selectAll() {
List<T> list = getSession().createQuery("FROM " + this.clazz.getSimpleName()).list();
return list;
}
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
}
由于共有类无法判断具体的实体,只能使用泛型,在调用过程中具体传递进来的实体类型是什么类型需要用java反射机制来实现。关键代码在我们的构造方法中的这两行代码:
// 使用反射技术得到T的真实类型
ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass(); // 获取当前new的对象的 泛型的父类 类型
this.clazz = (Class<T>) pt.getActualTypeArguments()[0]; // 获取第一个类型参数的真实类型
本类中多次用到hibernate的session,所以直接把获取session做成一个protected级别的方法。为了更好的给子类继承使用该方法,不宜写为private私有级别。
protected Session getSession() {
return sessionFactory.getCurrentSession();
}
这里需要注意的是:基础实现类不能new,只能new他的子类,所以这里要写abstract抽象类型。
如此便可以在不同模块的Dao调用这个公共类了。
用户增删改查对应的UserDao接口
import com.shizongger.oa.base.BaseDao;
import com.shizongger.oa.domain.User;
public interface UserDao extends BaseDao<User> {
public User getUserByNamdAndPasswd(String username, String password);
}
userDaoImpl实现类
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.shizongger.oa.base.BaseDaoImpl;
import com.shizongger.oa.dao.UserDao;
import com.shizongger.oa.domain.User;
@Repository
public class UserDaoImpl extends BaseDaoImpl<User> implements UserDao {
private Log log = LogFactory.getLog(this.getClass());
/**
* sessionFactory工厂
*/
@Autowired
private SessionFactory sessionFactory;
/**
* 检测用户的合法性
*/
@Override
public User getUserByNamdAndPasswd(String username, String password) {
Session session = sessionFactory.getCurrentSession();
String hql = "FROM User WHERE loginName = ? AND password = ?";
User user = (User) session.createQuery(hql).setString(0, username)
.setString(1, password).uniqueResult();
return user;
}
}
部门管理增删改查对应的接口DepartmentDao
import java.util.List;
import com.shizongger.oa.base.BaseDao;
import com.shizongger.oa.domain.Department;
public interface DepartmentDao extends BaseDao<Department> {
List<Department> findTopDept();
List<Department> findChildren(String parentId);
}
DepartmentDaoImpl实现类
import java.util.List;
import com.shizongger.oa.base.BaseDao;
import com.shizongger.oa.domain.Department;
public interface DepartmentDao extends BaseDao<Department> {
List<Department> findTopDept();
List<Department> findChildren(String parentId);
}
下面我们来进行junit测试:
BaseJunitTest
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:*.xml"})
public class BaseJunitTest {
}
UserDaoImplTest
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.Rollback;
import org.springframework.transaction.annotation.Transactional;
import com.shizongger.oa.base.BaseJunitTest;
import com.shizongger.oa.dao.UserDao;
import com.shizongger.oa.domain.User;
public class UserDaoImplTest extends BaseJunitTest {
@Autowired
private UserDao userDao;
@Test
@Ignore
public void testGetUserByNamdAndPasswd() throws Exception {
//TODO
}
@Transactional
@Test
@Rollback(false)
public void testAddUser() throws Exception {
User user = new User();
user.setName("shizongger");
userDao.add(user);
}
@Transactional
@Test
@Rollback(false)
public void testDelete() throws Exception {
userDao.delete(6L);
}
@Transactional
@Test
@Rollback(false)
public void testUpdate() throws Exception {
User user = (User) userDao.selectById(7L);
if(user != null) {
user.setName("admin");
userDao.update(user);
}
}
@Transactional
@Test
public void testSelectAll() throws Exception {
List<User> list = userDao.selectAll();
for(final User user : list) {
System.out.println("id:" + user.getId() + ", name:" + user.getName());
}
}
}
我们发现调用到BaseDaoImpl的构造方法是,正确的输出了我们传递过去的javaBean类型。
项目代码托管到了GitHub,OA