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,
下面是持久化類的配置
在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 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,對於這個類,主要是構造器裏的反射部分:
protected Class 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
*
* @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
*
* @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,隨後返回它的範型參數.這裏就是User類字面常量.
可以看到,SS對DAO採用了兩層封裝,一級接口的戰術,底1層封裝只關注與Spring-Hibernate的集成,底2層封裝利用範型搭建了底1層與用戶DAO的橋樑,並且通過繼承EntityDao接口,在設計其它類時(比如StrutsAction),還可以在使用DAO的時候進行解偶.