1、設計 BaseDao 與 BaseDaoImpl
1)設計接口 BaseDao
每個實體都應有一個對應的Dao接口,封裝了對這個實體的數據庫操作。在每個Dao接口中都應有一個基本的增刪改查的方法,但每個Dao接口中都寫一遍就是重復的代碼,可以把這些方法抽取到一個父接口中,定義為:
1 package com.atguigu.surveypark.dao; 2 import java.util.List; 3 /** 4 * BaseDao接口 5 */ 6 public interface BaseDao<T> { 7 //寫操作 8 public void saveEntity(T t); 9 public void saveOrUpdateEntity(T t); 10 public void updateEntity(T t); 11 public void deleteEntity(T t); 12 public void batchEntityByHQL(String hql,Object...objects); //按照HQL語句進行批量更新 13 //執行原生的sql語句 14 public void executeSQL(String sql,Object...objects); 15 16 //讀操作 17 public T loadEntity(Integer id); 18 public T getEntity(Integer id); 19 public List<T> findEntityByHQL(String hql,Object...objects); 20 //單值檢索,確保查詢結果有且只有一條記錄 21 public Object uniqueResult(String hql,Object...objects); 22 //執行原生的sql查詢(可以指定是否封裝成實體) 23 public List executeSQLQuery(Class clazz,String sql,Object...objects); 24 }
2)設計實現類 BaseDaoImpl
每個Dao的接口還要有相應的實現類才可以,在每個DaoImpl中都要實現Dao接口中定義的所有方法,當然也包括公共的增刪改查方法(BaseDao中定義的基本方法)。每個DaoImpl中都實現一遍公共方法顯示是重復的,所以也可以抽取出來為一個父類BaseDaoImpl,在BaseDaoImpl中實現了BaseDao接口的所有方法,我們的DaoImpl只需要繼承他就可以不用重復的寫公共方法的實現了。
聲明如下:
1 package com.atguigu.surveypark.dao.impl; 2 import java.lang.reflect.ParameterizedType; 3 import java.util.List; 4 import javax.annotation.Resource; 5 import org.hibernate.SessionFactory; 6 import com.atguigu.surveypark.dao.BaseDao; 7 /** 8 * 抽象的dao實現,專門用於繼承 9 */ 10 @SuppressWarnings("unchecked") 11 public abstract class BaseDaoImpl<T> implements BaseDao<T> { 12 @Resource 13 private SessionFactory sessionFactory ; // 讓Spring注入sessionFactory 14 private Class<T> clazz ; 15 public BaseDaoImpl(){ 16 //得到泛型化超類(返回值為參數化類型) 17 ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass(); 18 clazz = (Class<T>) type.getActualTypeArguments()[0]; 19 } 20 21 protected Session getSession() { //獲取Session 22 return sessionFactory.getCurrentSession(); 23 } 24 //省略了對接口中方法的實現 25 }
這里用到了泛型技術,需要在程序執行過程中確定泛型T的具體類型clazz。獲取clazz時有兩種方法:★★★★
方法一:
Ⅰ、把clazz聲明成protected修飾符的,這樣子類中就可以訪問了,
Ⅱ、在每個子類的構造方法中傳遞這個屬性的值,如:
public RoleDaoImpl() { clazz = Role.class; }
public UserDaoImpl() { clazz = User.class; }
方法二:使用反射的方式(即上述BaseDaoImpl中使用的方法):
Ⅰ、在BaseDaoImpl的默認構造方法中寫如下代碼就可以了:
ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
clazz = (Class) pt.getActualTypeArguments()[0];
Ⅱ、說明:即使BaseDaoImpl沒有被定義為抽象類也不能直接使用,只能使用他的子類,否則這段代碼就無效了。因為只能通過其子類指定了泛型的具體類型之后,才能夠在BaseDaoImpl的構造方法中獲取到clazz的具體類型。
2、設計 BaseService 與 BaseServiceImpl
1)設計接口BaseService
在設計BaseService時有個不成文的規定,就是將BaseDao中的基本方法全部都復制到BaseServic中即可,如有需要擴展的業務方法則再獨立設計相應的Service接口,然后繼承BaseService接口即可。
因此BaseService的定義如下:
1 package com.atguigu.surveypark.service; 2 import java.util.List; 3 /** 4 * 基本的Service接口 5 */ 6 public interface BaseService<T> { 7 //寫操作 8 public void saveEntity(T t); 9 public void saveOrUpdateEntity(T t); 10 public void updateEntity(T t); 11 public void deleteEntity(T t); 12 public void batchEntityByHQL(String hql,Object...objects); 13 //執行原生的sql語句 14 public void executeSQL(String sql,Object...objects); 15 16 //讀操作 17 public T loadEntity(Integer id); 18 public T getEntity(Integer id); 19 public List<T> findEntityByHQL(String hql,Object...objects); 20 //單值檢索,確保查詢結果有且只有一條記錄 21 public Object uniqueResult(String hql,Object...objects); 22 //查詢所有實體 23 public List<T> findAllEntities(); 24 public List executeSQLQuery(Class clazz,String sql,Object...objects); 25 }
2)設計實現類 BaseServiceImp
在BaseServiceImpl中同樣采用了泛型技術,方便程序的實現。在BaseServiceImpl中主要是通過Spring注入了dao,然后通過dao調用了BaseDaoImpl中的基本方法的實現,已完成基本的業務操作。
具體實現如下:
1 package com.atguigu.surveypark.service.impl; 2 import java.lang.reflect.ParameterizedType; 3 import java.util.List; 4 import javax.annotation.Resource; 5 import com.atguigu.surveypark.dao.BaseDao; 6 import com.atguigu.surveypark.service.BaseService; 7 /** 8 * 抽象的Service實現,專門用於繼承 9 */ 10 public abstract class BaseServiceImpl<T> implements BaseService<T> { 11 private BaseDao<T> dao ; 12 private Class<T> clazz ; 13 @SuppressWarnings("unchecked") 14 public BaseServiceImpl() { 15 ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass(); 16 clazz = (Class<T>) type.getActualTypeArguments()[0]; 17 } 18 @Resource //注入dao 19 public void setDao(BaseDao<T> dao) { 20 this.dao = dao; 21 } 22 //省略了對接口中方法的實現 23 }
注意:在這里注入的是BaseDao接口的實現類,並且注解@Resource是添加在setDao方法上的。這是因為,BaseDao有多個實現類,因此在Spring進行dao注入時會有多個匹配的BaseDao的實現類滿足條件,因此不能將@Resource注解添加在屬性上,而是添加到set方法上,這樣,在具體的Service實現類中可以覆寫setDao方法,在@Resource注解中添加name屬性指定要注入的具體的Dao實現類。如下所示:
1 //UserService接口 2 public interface UserService extends BaseService<User> {} 3 //UserServiceImpl實現類 4 @Service("userService") 5 public class UserServiceImpl extends BaseServiceImpl<User> implements UserService { 6 /** 7 * 重寫該方法,目的是為了覆蓋超類中該方法的注解,指明注入指定的Dao對象,否則spring 8 * 無法確定注入哪個Dao---有多個滿足條件的Dao. 9 */ 10 @Resource(name="userDao") 11 public void setDao(BaseDao<User> dao) { 12 super.setDao(dao); 13 } 14 }