以前从springside2.0上搞下来的很好用的,基本实现dao零编码只要配置xml文件就行了。
先看图:
一共4层,com.demonstration.hibernate.basedao是我加的用来进一步解耦hibernate和spring的耦合。
原来的官方解释如下:
SpringSide对Hibernate做了三层封装:
第一层:HibernateGenericDao,基于spring的HibernateDaoSupport,但加入了分页函数与各种Finder函数,并使用泛型避免了返回值强制类型转换。
第二层:HibernateEntityDao,基于HibernateGenericDao,用泛型声明Dao所管理的Entity类,默认拥有该entity的CRUD方法。
第三层:HibernateExtendDao,基于HibernateEntityDao,主要扩展各种选择性的功能。
关于三个类的详细注解请看JavaDoc,大致描述如下:
1 HibernateGenericDao
在Spring HibernateDaoSupport基础上封装的DAO,功能如下:
1.应用泛型:使得find(), get() 这些函数不再返回Object,而是返回T,不再需要强制类型转换。
2.提供各种finder的简便函数
应用了JDK5可变参数的hsql查询函数:List find(String hql, Object... values),支持find(hql),find(hql, param1); find(hql,param1,param2);find(hql,new Object[] {param1,param2}) 四种接口。
简单查询的简化函数:findBy(Class entityClass,String name,Object value) ,findUniqueBy(Class entityClass,String name, Object value),findByLike(Class entityClass,String name,Object value)
3.获得设置好的Query和Criteria:createQuery(String hql,Object... values) 和 createCriteria(Class<T> entityClass,Criterion... criterions)
Spring并没有很好的接口封装支持firstResult, maxResult, fetchsize,cache,cacheRegion 等多个查询参数,所以springside宁愿返回已设置好查询条件的Query和Criteria,让大家继续剩下的参数设置,最后再执行 list(),注意那几个参数可以连续设置的,如:
createQuery(hql,param1).setFirstResult(10).setMaxResult(20).list(); 4.分页函数:Page pagedQuery(Criteria criteria, int pageNo, int pageSize) 和Page pagedQuery(String hql, int pageNo, int pageSize, Object... args)
Page是SpringSide自行封装的一个典型Page类,pagedQuery与hibernate自身分页查询的差别是先运行一次count,获得符合条件的总记录数。
如果查询不需要总记录数,用普通的hibernate API,加上setFirstResult(),setMaxResult()就解决,不需要pagedQuery()。
5.判别对象属性在数据库中唯一的函数:isUnique(Class<T> entityClass,Object entity,String names)。
2. HibernateEntityDao
所有UserManager, ProductManager之类只管理一类对象的Manager类的基类,只需要在类定义处声明Entity类型即可
public class BookManager extends HibernateEntityDao<Book> {
} 通过<Book>的定义,避免了HibernateGenericDao类各方法中必有的Class entityClass参数。
如果需要操作其他的Entity,比如BookManager可能需要处理Category(图书目录),可以注入CategoryManager。无需担心事务的问题,JavaEE的默认事务模型已能很好处理。
如果没有对应的CategoryManager,或者各种原因不想注入的话,可以使用BookManager继承自 HibernateGenericDao的带entityClass参数的函数来操作Category的增删改,如Category category= this.get(Category.class, 1);
3. HibernateExtendDao
此类演示SpringSide 所作的一些扩展,大家可以按照自己的需要进行修改和扩展。
1. 支持对象不能被直接删除,只能设置状态列为无效。
接口UndeleteableEntityOperation,定义了要支持此功能必须实现的函数。
可以有接口(UndeletableEntity)和annotation(@Undeletable)两种形式来定义无效列,annotation列形式还可以定义标识对象已删除的状态属性的名称,用接口则必须实现setStatus()接口,在里面操作实际的状态属性。
第四层就是把HibernateEntityDao和HibernateExtendDao以属性注入的方式注入到basedao,IBasedao就全局接口程序中使用的就是它,这个接口的实现全调用HibernateEntityDao和HibernateExtendDao的方法。方便以后的更改和替换,这样IBasedao接口不变就不要修改业务层的代码了。
代码如下(从下到上):
Page.java
package com.demonstration.hibernate.dao.support;
import java.io.Serializable;
import java.util.ArrayList;
/**
* 分页对象. 包含当前页数据及分页信息如总记录数.
*
* @author springside
*
*/
@SuppressWarnings("serial")
public class Page implements Serializable {
private static int DEFAULT_PAGE_SIZE = 20;
private int pageSize = DEFAULT_PAGE_SIZE; // 每页的记录数
private long start; // 当前页第一条数据在List中的位置,从0开始
private Object data; // 当前页中存放的记录,类型一般为List
private long totalCount; // 总记录数
/**
* 构造方法,只构造空页.
*/
@SuppressWarnings("unchecked")
public Page() {
this(0, 0, DEFAULT_PAGE_SIZE, new ArrayList());
}
/**
* 默认构造方法.
*
* @param start 本页数据在数据库中的起始位置
* @param totalSize 数据库中总记录条数
* @param pageSize 本页容量
* @param data 本页包含的数据
*/
public Page(long start, long totalSize, int pageSize, Object data) {
this.pageSize = pageSize;
this.start = start;
this.totalCount = totalSize;
this.data = data;
}
/**
* 取总记录数.
*/
public long getTotalCount() {
return this.totalCount;
}
/**
* 取总页数.
*/
public long getTotalPageCount() {
if (totalCount % pageSize == 0)
return totalCount / pageSize;
else
return totalCount / pageSize + 1;
}
/**
* 取每页数据容量.
*/
public int getPageSize() {
return pageSize;
}
/**
* 取当前页中的记录.
*/
public Object getResult() {
return data;
}
/**
* 取该页当前页码,页码从1开始.
*/
public long getCurrentPageNo() {
return start / pageSize + 1;
}
/**
* 该页是否有下一页.
*/
public boolean hasNextPage() {
return this.getCurrentPageNo() < this.getTotalPageCount() - 1;
}
/**
* 该页是否有上一页.
*/
public boolean hasPreviousPage() {
return this.getCurrentPageNo() > 1;
}
/**
* 获取任一页第一条数据在数据集的位置,每页条数使用默认值.
*
* @see #getStartOfPage(int,int)
*/
protected static int getStartOfPage(int pageNo) {
return getStartOfPage(pageNo, DEFAULT_PAGE_SIZE);
}
/**
* 获取任一页第一条数据在数据集的位置.
*
* @param pageNo 从1开始的页号
* @param pageSize 每页记录条数
* @return 该页第一条数据
*/
public static int getStartOfPage(int pageNo, int pageSize) {
return (pageNo - 1) * pageSize;
}
}
GenericsUtils.java
package com.demonstration.hibernate.dao.support;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Generics的util类.
*
* @author springside
*
*/
public class GenericsUtils {
private static final Log log = LogFactory.getLog(GenericsUtils.class);
private GenericsUtils() {
}
/**
* 通过反射,获得定义Class时声明的父类的范型参数的类型. 如public BookManager extends GenricManager<Book>
*
* @param clazz The class to introspect
* @return the first generic declaration, or <code>Object.class</code> if cannot be determined
*/
@SuppressWarnings("unchecked")
public static Class getSuperClassGenricType(Class clazz) {
return getSuperClassGenricType(clazz, 0);
}
/**
* 通过反射,获得定义Class时声明的父类的范型参数的类型. 如public BookManager extends GenricManager<Book>
*
* @param clazz clazz The class to introspect
* @param index the Index of the generic ddeclaration,start from 0.
* @return the index generic declaration, or <code>Object.class</code> if cannot be determined
*/
@SuppressWarnings("unchecked")
public static Class getSuperClassGenricType(Class clazz, int index) {
Type genType = clazz.getGenericSuperclass();
if (!(genType instanceof ParameterizedType)) {
log.warn(clazz.getSimpleName() + "'s superclass not ParameterizedType");
return Object.class;
}
Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
if (index >= params.length || index < 0) {
log.warn("Index: " + index + ", Size of " + clazz.getSimpleName() + "'s Parameterized Type: "
+ params.length);
return Object.class;
}
if (!(params[index] instanceof Class)) {
log.warn(clazz.getSimpleName() + " not set the actual class on superclass generic parameter");
return Object.class;
}
return (Class) params[index];
}
}
BeanUtils.java
package com.demonstration.hibernate.dao.support;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* 扩展Apache Commons BeanUtils, 提供一些反射方面缺失功能的封装.
* @author springside
*
*/
public class BeanUtils extends org.apache.commons.beanutils.BeanUtils {
protected static final Log logger = LogFactory.getLog(BeanUtils.class);
private BeanUtils() {
}
/**
* 循环向上转型,获取对象的DeclaredField.
*
* @throws NoSuchFieldException 如果没有该Field时抛出.
*/
public static Field getDeclaredField(Object object, String propertyName) throws NoSuchFieldException {
Assert.notNull(object);
Assert.hasText(propertyName);
return getDeclaredField(object.getClass(), propertyName);
}
/**
* 循环向上转型,获取对象的DeclaredField.
*
* @throws NoSuchFieldException 如果没有该Field时抛出.
*/
@SuppressWarnings("unchecked")
public static Field getDeclaredField(Class clazz, String propertyName) throws NoSuchFieldException {
Assert.notNull(clazz);
Assert.hasText(propertyName);
for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
return superClass.getDeclaredField(propertyName);
} catch (NoSuchFieldException e) {
// Field不在当前类定义,继续向上转型
}
}
throw new NoSuchFieldException("No such field: " + clazz.getName() + '.' + propertyName);
}
/**
* 暴力获取对象变量值,忽略private,protected修饰符的限制.
*
* @throws NoSuchFieldException 如果没有该Field时抛出.
*/
public static Object forceGetProperty(Object object, String propertyName) throws NoSuchFieldException {
Assert.notNull(object);
Assert.hasText(propertyName);
Field field = getDeclaredField(object, propertyName);
boolean accessible = field.isAccessible();
field.setAccessible(true);
Object result = null;
try {
result = field.get(object);
} catch (IllegalAccessException e) {
logger.info("error wont' happen");
}
field.setAccessible(accessible);
return result;
}
/**
* 暴力设置对象变量值,忽略private,protected修饰符的限制.
*
* @throws NoSuchFieldException 如果没有该Field时抛出.
*/
public static void forceSetProperty(Object object, String propertyName, Object newValue)
throws NoSuchFieldException {
Assert.notNull(object);
Assert.hasText(propertyName);
Field field = getDeclaredField(object, propertyName);
boolean accessible = field.isAccessible();
field.setAccessible(true);
try {
field.set(object, newValue);
} catch (IllegalAccessException e) {
logger.info("Error won't happen");
}
field.setAccessible(accessible);
}
/**
* 暴力调用对象函数,忽略private,protected修饰符的限制.
*
* @throws NoSuchMethodException 如果没有该Method时抛出.
*/
@SuppressWarnings("unchecked")
public static Object invokePrivateMethod(Object object, String methodName, Object... params)
throws NoSuchMethodException {
Assert.notNull(object);
Assert.hasText(methodName);
Class[] types = new Class[params.length];
for (int i = 0; i < params.length; i++) {
types[i] = params[i].getClass();
}
Class clazz = object.getClass();
Method method = null;
for (Class superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
try {
method = superClass.getDeclaredMethod(methodName, types);
break;
} catch (NoSuchMethodException e) {
// 方法不在当前类定义,继续向上转型
}
}
if (method == null)
throw new NoSuchMethodException("No Such Method:" + clazz.getSimpleName() + methodName);
boolean accessible = method.isAccessible();
method.setAccessible(true);
Object result = null;
try {
result = method.invoke(object, params);
} catch (Exception e) {
ReflectionUtils.handleReflectionException(e);
}
method.setAccessible(accessible);
return result;
}
/**
* 按Filed的类型取得Field列表.
*/
@Su