论坛首页 Java企业应用论坛

让SpringSide的DAO针对接口编程

浏览 3993 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-03-23  
考虑到项目将在全省各市部署,预想到在条件允许的市需要调其他市的档案,因此希望DAO能针对接口编程,方便以后扩展以及适应不同的市。
于是在SpringSide的examples.miniweb上作了些修改,希望能够抛砖引玉,更希望各位批评指正。

将IdEntity改为EntityObject,希望以后所有实体都继承它。Id不一定放在这,但要重写equals和hashCode等。
package org.springside.examples.miniweb.entity;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

/**
 * 统一定义id的entity基类.
 * 
 * 基类统一定义id的属性名称、数据类型、列名映射及生成策略.
 * 子类可重载getId()函数重定义id的列名映射和生成策略.
 * 
 * @author calvin
 */
//JPA 基类的标识
@MappedSuperclass
public abstract class EntityObject {

	protected Long id;

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	//@GeneratedValue(strategy = GenerationType.SEQUENCE)
	public Long getId() {
		return id;
	}

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

	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this,
			ToStringStyle.MULTI_LINE_STYLE);
	}

	@Override
	public boolean equals(Object o) {
		return o != null && o.getClass().equals(this.getClass())
				&& EqualsBuilder.reflectionEquals(this, o);
	}

	@Override
	public int hashCode() {
		return HashCodeBuilder.reflectionHashCode(this);
	}
}


定义DAO的通用接口,如下,还有很多常用通用的没有写出来。
package org.springside.examples.miniweb.dao;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

import org.springside.examples.miniweb.entity.EntityObject;

/**
 * 通用DAO接口<br>
 * 在能用此接口完成功能时,不必再派生出子接口<br>
 * 在此接口不能满足要求时,派生出相应的子接口<br>
 * 
 * @see org.springside.examples.miniweb.dao.security.RoleDao
 * @see org.springside.examples.miniweb.dao.security.ResourceDao
 * 
 * @author langshao
 * 
 */
public interface GenericDao {

	/**
	 * 根据实体的class及id获取相应的实体
	 * 
	 * @param <T>
	 * @param entityClass 实体的class, 如 <code>User.class</code>
	 * @param id 当为 <code>null</code> 时返回 <code>null</code>
	 * @return 找到则返回相应的实体, 找不到时返回 <code>null</code>
	 */
	<T extends EntityObject> T get(Class<T> entityClass, Serializable id);

	/**
	 * 根据实体的class获取所有的实体
	 * 
	 * @param <T>
	 * @param entityClass 实体的class, 如 <code>User.class</code>
	 * @return 没有数据时返回空的 List
	 */
	<T extends EntityObject> List<T> getAll(Class<T> entityClass);

	/**
	 * 新增或更新实体<br>
	 * 当entity的id为 <code>null</code> 时新增, 否则更新
	 * 
	 * @param entity 当为 <code>null</code> 时不保存
	 */
	void saveOrUpdate(EntityObject entity);

	/**
	 * 批量新增或更新实体<br>
	 * 当entity的id为 <code>null</code> 时新增, 否则更新
	 * 
	 * @param entities 当entities为 <code>null</code> 时不保存
	 * @exception IllegalArgumentException entities的成员含有 <code>null</code>
	 *                时会抛出异常
	 */
	void saveOrUpdateAll(Collection<? extends EntityObject> entities)
			throws IllegalArgumentException;

	/**
	 * 删除实体
	 * 
	 * @param entity 当为 <code>null</code> 时不删除
	 */
	void delete(EntityObject entity);

	/**
	 * 批量删除实体
	 * 
	 * @param entities 当entities为 <code>null</code> 时不删除
	 * @exception IllegalArgumentException entities的成员含有 <code>null</code>
	 *                时会抛出异常
	 */
	void deleteAll(Collection<? extends EntityObject> entities)
			throws IllegalArgumentException;
}


一个简单的实现类如下,我是比较喜欢用HibernateTemplate。
package org.springside.examples.miniweb.dao.impl;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;

import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.stereotype.Repository;

import org.springside.examples.miniweb.dao.GenericDao;
import org.springside.examples.miniweb.entity.EntityObject;

/**
 * 通用DAO实现
 * 
 * @author langshao
 * 
 */
@Repository
public class GenericDaoImpl extends HibernateDaoSupport implements GenericDao {

	/**
	 * 为了使sessionFactory能自动注入而写了这个方法
	 * 
	 * @param sessionFactory
	 */
	@Autowired
	public void setSessionFactory4Autowired(SessionFactory sessionFactory) {
		this.setSessionFactory(sessionFactory);
	}

	@SuppressWarnings("unchecked")
	public <T extends EntityObject> T get(Class<T> entityClass, Serializable id) {
		if (id != null)
			return (T) getHibernateTemplate().get(entityClass, id);
		return null;
	}

	@SuppressWarnings("unchecked")
	public <T extends EntityObject> List<T> getAll(Class<T> entityClass) {
		return getHibernateTemplate().loadAll(entityClass);
	}

	public void saveOrUpdate(EntityObject entity) {
		if (entity != null) {
			getHibernateTemplate().saveOrUpdate(entity);
		}
	}

	public void saveOrUpdateAll(Collection<? extends EntityObject> entities)
			throws IllegalArgumentException {
		if (entities != null && !entities.isEmpty()) {
			getHibernateTemplate().saveOrUpdateAll(entities);
		}
	}

	public void delete(EntityObject entity) {
		if (entity != null) {
			getHibernateTemplate().delete(entity);
		}
	}

	public void deleteAll(Collection<? extends EntityObject> entities)
			throws IllegalArgumentException {
		if (entities != null) {
			getHibernateTemplate().deleteAll(entities);
		}
	}
}


在删除角色时,需要将相应的关联表也删除,因此角色需要重写delete的实现,为了方便自动注入,建立一个新的接口:
package org.springside.examples.miniweb.dao.security;

import org.springside.examples.miniweb.dao.GenericDao;

/**
 * 角色DAO接口
 * 
 * @author langshao
 */
public interface RoleDao extends GenericDao {
}

实现如下:
package org.springside.examples.miniweb.dao.security.impl;

import java.util.List;

import org.springframework.stereotype.Repository;

import org.springside.examples.miniweb.dao.impl.GenericDaoImpl;
import org.springside.examples.miniweb.dao.security.RoleDao;
import org.springside.examples.miniweb.entity.EntityObject;
import org.springside.examples.miniweb.entity.security.User;

/**
 * 角色DAO实现
 * 
 * @author langshao
 * 
 */
@Repository
public class RoleDaoImpl extends GenericDaoImpl implements RoleDao {

	private static final String QUERY_USER_BY_ROLEID = "select u from User u left join u.roleList r where r.id = ?";

	/**
	 * 重载函数,因为Role中没有建立与User的关联,因此需要以较低效率的方式进行删除User与Role的多对多中间表.
	 */
	@Override
	@SuppressWarnings("unchecked")
	public void delete(EntityObject role) {
		// 查询出拥有该角色的用户,并删除该用户的角色.
		List<User> users = getHibernateTemplate().find(QUERY_USER_BY_ROLEID,
			role.getId());
		for (User u : users) {
			u.getRoleList().remove(role);
		}
		super.delete(role);
	}
}


这样做之后,GenericDao 的 @Autowired 便会报错,以 RoleDaoTest 来说明如何解决之。
在applicationContext-test.xml中加入一个bean:
<bean id="genericDao" class="org.springside.examples.miniweb.dao.impl.GenericDaoImpl" />


然后RoleDaoTest如下写:
package org.springside.examples.miniweb.integration.dao.security;

import javax.annotation.Resource;

import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springside.modules.test.DataUtils;
import org.springside.modules.test.spring.SpringTxTestCase;

import org.springside.examples.miniweb.dao.GenericDao;
import org.springside.examples.miniweb.dao.security.RoleDao;
import org.springside.examples.miniweb.entity.security.Role;
import org.springside.examples.miniweb.entity.security.User;

/**
 * UserDao的集成测试用例,测试ORM映射及特殊的DAO操作.
 * 
 * @author calvin
 */
public class RoleDaoTest extends SpringTxTestCase {
	@Autowired
	private RoleDao roleDao;

	@Resource(name = "genericDao")
	private GenericDao userDao;

	/**
	 * 测试删除角色时删除用户-角色的中间表.
	 */
	@Test
	public void deleteRole() {
		//新增测试角色并与admin用户绑定.
		Role role = new Role();
		role.setName(DataUtils.randomName("Role"));
		roleDao.saveOrUpdate(role);

		User user = userDao.get(User.class, 1L);
		user.getRoleList().add(role);
		userDao.saveOrUpdate(user);
		flush();

		int oldJoinTableCount = countRowsInTable("SS_USER_ROLE");
		int oldUserTableCount = countRowsInTable("SS_USER");

		//删除用户角色, 中间表将减少1条记录,而用户表应该不受影响.
		roleDao.delete(role);
		flush();

		int newJoinTableCount = countRowsInTable("SS_USER_ROLE");
		int newUserTableCount = countRowsInTable("SS_USER");
		assertEquals(1, oldJoinTableCount - newJoinTableCount);
		assertEquals(0, oldUserTableCount - newUserTableCount);
	}
}


再举个扩充通用接口的例子:
package org.springside.examples.miniweb.dao.security;

import java.util.List;

import org.springside.examples.miniweb.dao.GenericDao;
import org.springside.examples.miniweb.entity.security.Resource;

/**
 * 受保护资源对象的DAO接口
 * 
 * @author langshao
 */
public interface ResourceDao extends GenericDao {

	/**
	 * 查询URL类型的资源并预加载可访问该资源的授权信息.
	 */
	List<Resource> getUrlResourceWithAuthorities();
}


实现如下:
package org.springside.examples.miniweb.dao.security.impl;

import java.util.List;

import org.springframework.stereotype.Repository;

import org.springside.examples.miniweb.dao.impl.GenericDaoImpl;
import org.springside.examples.miniweb.dao.security.ResourceDao;
import org.springside.examples.miniweb.entity.security.Resource;

/**
 * 受保护资源对象的DAO实现
 * 
 * @author langshao
 */
@Repository
public class ResourceDaoImpl extends GenericDaoImpl implements ResourceDao {

	public static final String QUERY_BY_RESOURCETYPE_WITH_AUTHORITY = "select distinct r from Resource r "
			+ "left join fetch r.authorityList WHERE r.resourceType=? ORDER BY r.position ASC";

	@SuppressWarnings("unchecked")
	public List<Resource> getUrlResourceWithAuthorities() {
		return getHibernateTemplate().find(
			QUERY_BY_RESOURCETYPE_WITH_AUTHORITY,
			Resource.URL_TYPE);
	}
}


这样做主要是想针对接口编程,还有就是不想每新增一个Entity就要增加一个DAO,只要通用的够用就不用再写了。
Write less, do more.
   发表时间:2010-03-24  
我怎么觉得接口到底用啊.
0 请登录后投票
   发表时间:2010-03-24  
难道一个DAO的基本封装还有多种实现吗?今天使用hibernat明天就使用jdbc或者ibatis了吗?
0 请登录后投票
   发表时间:2010-03-24  
chirs 写道
难道一个DAO的基本封装还有多种实现吗?今天使用hibernat明天就使用jdbc或者ibatis了吗?

是今天访问本地数据库,明天可能还需要查其他市的数据库,可能通过WebService的方式。
项目有点特殊,在各地区部署,而数据又要共用。再者,数据量太大,每个市都几千万,还要集中到省就更加了,字段也上百个,估计将来要切分之类优化的,希望这些数据层的变化不影响到业务层。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics