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,

下面是持久化類的配置

   

在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的時候進行解偶.

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章