最近有机会闲下来研究一下一直想学习的SpringSide,本人还是新手,还望各位路过牛人指教.
HelloWorld
1.了解HSQL数据库
配置:
第一步,导入jar包
第二步,编写helloworld.properties和helloworld.script
第三步,数据库url设为:jdbc.url=jdbc:hsqldb:res:/hsqldb/helloworld即可.
2.HelloWorld中的Hibernate Annotation
HelloWorld中的Annotation还是比较简单的.
持久化类只需要声明@Entity并且为ID配上@Id及相应的生成器就行了.
可以看到,在spring的配置文件中,Hibernate Session已经由LocalSessionFactory改成org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean,
下面是持久化类的配置 <property name="annotatedClasses">
<list></list>
</property>
在HelloWorld中,还给出了一个更为详细的User Hibernate Annotation配置
package org.springside.helloworld.model;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import org.hibernate.annotations.BatchSize;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
/**
* 用户.
* {@UniqueConstraint(columnNames = {"name"})})
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@BatchSize(size = 5)
public class UserFullVersion {
private Integer id;
* 这是一个Hibernate Annotation 式配置的详细版本.
* 包含JDK1.4下的JavaDoc式配置 与JDK5.0的annotation配置
*
* @author Schweigen
* @hibernate.class table="user"
*/
@Entity
@org.hibernate.annotations.Entity(dynamicInsert = true, dynamicUpdate = true)
@Table(name = "user", uniqueConstraints =
private String name;
private String email;
private String descn;
/**
* @hibernate.id generator-class="native"
* column="id"
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
/**
* @hibernate.property column="name"
*/
@Column(updatable = false, name = "name", nullable = false, length = 50)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
/**
* @hibernate.property column="email"
*/
@Column(name = "email", nullable = true, length = 50)
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
/**
* @hibernate.property column="descn"
*/
@Column(name = "descn", nullable = true, length = 200)
public String getDescn() {
return descn;
}
public void setDescn(String descn) {
this.descn = descn;
}
}
与简化版相比只是将一些默认的东西添上,并做了一些详细调整,比如动态添加和动态更新的设置,主键的设置等.
3.利用Spring mock进行单元测试
Hello World中有一个测试类UserManagerTest,它继承DaoTestCase,而DaoTestCase又继承自Spring mock包中的org.springframework.test.AbstractTransactionalDataSourceSpringContextTests,DaoTestCase重写了该类的getConfigLocations方法,设置了spring配置文件中的位置 .从而UserManagerTest便可以对spring配置后的pojo进行单元测试.
4.分析SpringSide Core的org.springside.core.dao包
该包一共5个类,如下
EntityDao是一个DAO接口,提供了一组DAO方法,HibernateEntityDao实现它,HibernateGenericDao继承了Spirng的HibernateDaoSupport,HibernateDaoSupport最有用的莫过于getSession(),以及getHibernateTemplate(),前者可以用来获得Critera,后者可以用来事务性的对持久化类进行增删改.
IBatis的处理和Hibernate的差不多,目前我只关心后者.
HibernateGenericDao:
其中,isUnique,getId,getIdName是辅助方法,get,getAll,save,remove,removeById,flush,clear,find,findBy,findUniqueBy均是对getHibernateTemplate方法基于范型进行简单封装,createQuery,createCriteria两个方法是为分页供分页函数调用获取Query和Criteria对象,pagedQuery是分页函数重载,其中后两个调用第二个,第一个是根据Query进行分页,第二个是根据Criteria进行分页.在实现时,真正进行分页的语句其实都很简单,
Query query = createQuery(hql, values);
List list = query.setFirstResult(startIndex).setMaxResults(pageSize).list();
或
List list = criteria.setFirstResult(startIndex).setMaxResults(pageSize).list();
费点劲的在于在分页时要得到totalCount--数据库中的记录总数,从而将这个参数传给page(org.springside.core.dao.support)构造器,totalCount在page中的作用是根据用户当前显示页来判断是否有下一页.而且这个参数对EC也是必须的.所以在分页时,才费劲的找到这个值:
在pagedQuery(String hql, int pageNo, int pageSize, Object... values)中,
String countQueryString = " select count (*) " + removeSelect(removeOrders(hql));//清空hql中多余的order和select
List countlist = getHibernateTemplate().find(countQueryString, values);
long totalCount = (Long) countlist.get(0);
在 pagedQuery(Criteria criteria, int pageNo, int pageSize)中,
CriteriaImpl impl = (CriteriaImpl) criteria;
// 先把Projection和OrderBy条件取出来,清空两者来执行Count操作
Projection projection = impl.getProjection();
List<criteriaimpl.orderentry></criteriaimpl.orderentry> orderEntries;
try {
orderEntries = (List) BeanUtils.forceGetProperty(impl, "orderEntries");
BeanUtils.forceSetProperty(impl, "orderEntries", new ArrayList());
} catch (Exception e) {
throw new InternalError(" Runtime Exception impossibility throw ");
}
// 执行查询
long totalCount = (Long) criteria.setProjection(Projections.rowCount()).uniqueResult();
// 将之前的Projection和OrderBy条件重新设回去
criteria.setProjection(projection);
if (projection == null) {
criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
}
try {
BeanUtils.forceSetProperty(impl, "orderEntries", orderEntries);
} catch (Exception e) {
throw new InternalError(" Runtime Exception impossibility throw ");
}
getIdName方法动态获得某个pojo的主键,想想也知道要用Hibernate 的org.hibernate.metadata.ClassMetadata
* 取得对象的主键名,辅助函数.
*/
public String getIdName(Class clazz) {
Assert.notNull(clazz);
ClassMetadata meta = getSessionFactory().getClassMetadata(clazz);
Assert.notNull(meta, "Class " + clazz + " not define in hibernate session factory.");
String idName = meta.getIdentifierPropertyName();
Assert.hasText(idName, clazz.getSimpleName() + " has no identifier property define.");
return idName;
}
HibernateEntityDao:
HibernateEntityDao继承自HibernateGenericDao并实现EntityDao,它大部分的方法均是对HibernateGenericDao的二次封装,与其说它是为了封装HibernateGenericDao,不如说是为了封装一次范型,从而子类DAO仅仅需要继承该类,并指定该DAO是哪个持久化类的DAO,像这样public class UserManager extends HibernateEntityDao<user></user>,对于这个类,主要是构造器里的反射部分:
protected Class<t></t> entityClass;// DAO所管理的Entity类型.
/**
* 在构造函数中将泛型T.class赋给entityClass.
*/
public HibernateEntityDao() {
entityClass = GenericsUtils.getSuperClassGenricType(getClass());
}
GenericsUtils是个工具类,里面就有个getSuperClassGenricType的重载方法,我们来关心一下
package org.springside.core.utils;
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 sshwsfc
*/
public class GenericsUtils {
private static final Log log = LogFactory.getLog(GenericsUtils.class);
private GenericsUtils() {
}
/**
* 通过反射,获得定义Class时声明的父类的范型参数的类型. 如public BookManager extends GenricManager<book></book>
*
* @param clazz The class to introspect
* @return the first generic declaration, or Object.class
if cannot be determined
*/
public static Class getSuperClassGenricType(Class clazz) {
return getSuperClassGenricType(clazz, 0);
}
/**
* 通过反射,获得定义Class时声明的父类的范型参数的类型. 如public BookManager extends GenricManager<book></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 Object.class
if cannot be determined
*/
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];
}
}
在getSuperClassGenricType(Class clazz,int index)中,此时穿过来的clazz是你的DAO对象,比如UserManager,开始先取得父对象实例,也就是org.springside.core.dao.HibernateEntityDao<org.springside.helloworld.model.user></org.springside.helloworld.model.user>,随后返回它的范型参数.这里就是User类字面常量.
可以看到,SS对DAO采用了两层封装,一级接口的战术,底1层封装只关注与Spring-Hibernate的集成,底2层封装利用范型搭建了底1层与用户DAO的桥梁,并且通过继承EntityDao接口,在设计其它类时(比如StrutsAction),还可以在使用DAO的时候进行解偶.