Hibernate的Dao層通用設計


hibernate作為一款優秀的數據庫持久化框架,在現實的運用中是非常廣泛的。它的出現讓不熟悉sql語法的程序員能開發數據庫連接層成為一種可能,但是理想與現實永遠是有差距的。開發過程中如果只使用hql進行操作,並且表之間的關聯配置很復雜的話,這將成為一種噩夢。還好我們偉大的hibernate支持原生的sql操作,這也大大的增加了hibernate的靈活性。下面我們探討一下hibernate的dao層的通用設計。

首先闡明一下顯示情況中遇到的問題。假設數據庫中有一張表,表名叫tbl_user,項目中對應的實體User,根據MVC的分層設計,我們會有UerDao的接口層,以及UserDaoImpl的接口實現層。對數據庫最多的操作就是增刪改查,我們的UserDao和UserDaoImpl中會有增刪改查的方法。如果有多張表的時候,我們會發現Dao的接口層以及實現層充斥着大量的增刪改查的代碼,而且大部分情況這些代碼都是類似的,那我們能不能將這些代碼進行封裝呢?答案是肯定的。

下面進入正題,數據庫使用mysql,創建一張名為tbl_user的表。

CREATE TABLE `tbl_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`name` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL,
`login_name` varchar(30) COLLATE utf8_unicode_ci DEFAULT NULL,
`pass_word` char(32) COLLATE utf8_unicode_ci DEFAULT NULL,
`role_id` bigint(20) DEFAULT NULL,
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`last_login_time` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`del_flag` enum('0','1') COLLATE utf8_unicode_ci DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='用戶表';

然后我們創建一個實體User對應這張表。

@Entity
@Table(name = "tbl_user")
public class User extends IdEntity implements Serializable {
private static final long serialVersionUID = 1L;

// 創建時間
private Date createTime;

// 刪除標記
private String delFlag;

// 最后登錄時間
private Date lastLoginTime;

// 登錄名
private String loginName;

// 姓名
private String name;

// 密碼
private String passWord;

// 角色id
private Long roleId;

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "create_time")
public Date getCreateTime() {
return createTime;
}

public void setCreateTime(Date createTime) {
this.createTime = createTime;
}

@Column(name = "del_flag")
public String getDelFlag() {
return delFlag;
}

public void setDelFlag(String delFlag) {
this.delFlag = delFlag;
}

@Temporal(TemporalType.TIMESTAMP)
@Column(name = "last_login_time")
public Date getLastLoginTime() {
return lastLoginTime;
}

public void setLastLoginTime(Date lastLoginTime) {
this.lastLoginTime = lastLoginTime;
}

@Column(name = "login_name")
public String getLoginName() {
return loginName;
}

public void setLoginName(String loginName) {
this.loginName = loginName;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Column(name = "pass_word")
public String getPassWord() {
return passWord;
}

public void setPassWord(String passWord) {
this.passWord = passWord;
}

@Column(name = "role_id")
public Long getRoleId() {
return roleId;
}

public void setRoleId(Long roleId) {
this.roleId = roleId;
}

}

好了,下一步我們要進行要進行dao層代碼的編寫。但是現在不能寫,我們的解決方案還沒有理出來。首先我們可以肯定的是常規的CRUD代碼不能寫在UserDao和UserDaoImpl中了,那寫在哪里呢。遇到這種情況首先想到的就是繼承,如果有一個父類具有這種能力的話,再去繼承這個父類,問題就迎刃而解了。好,方案確定,動手操作吧。我們先創建一個接口,命名為BaseDao,這里用到了接口泛型的概念。

public interface BaseDao<T> {

/*
* 持久化對象的方法
*/
public long add(T t);

/*
* 修改對象
*/
public void update(T t);

/*
* 刪除對象
*/
public int delete(long id);

/*
* 查找所有對象的方法
*/
public List<T> getAll();


/*
* 根據id查找對象
*/
public T getById(long id);

/*
* 根據map條件查找對象
*/
public List<T> getByMap(Map<String,Object> map);


/*
* 查詢條數
*/
public int countBySql(String sql);

/*
* 執行hql語句
*/
public int executeHql(String hql);

/*
* 執行sql語句
*/
public int executeSql(String sql);


/*
*根據sql語句查詢map集合
*/
public List<Map<String,Object>> getMapListBySql(String sql);
}

上面的頂層接口BaseDao定義了一些常用的dao層操作。我們在接口層再定義一個實體User對應的dao接口UserDao。

public interface UserDao extends BaseDao<User> {

}

接口UserDao中什么方法都沒有定義,只是去繼承了BaseDao。接下來我們要編寫dao實現層的代碼了,首先我們編寫dao實現層整個父類的BaseDaoImpl的代碼,這個時候我們停一下整理一下思路。父類BaseDaoImpl要具有CRUD的操作,並且要是動態的,就是不同的操作要相應的操作不同的表,在hibernate中我們也可以說是操作不同的實體。如何實現這種想法呢?答案還是泛型,在繼承父類BaseDaoImpl的時候注入自己的真實類型。下面就是BaseDaoImpl的代碼, 這也是最核心的地方。

public class BaseDaoImpl<T> implements BaseDao<T>{


@Autowired
private SessionFactory sessionFactory;

protected Class<T> clazz;



/**
* 構造方法自動注入真實的對象類型
*/
public BaseDaoImpl() {
ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
clazz = (Class<T>) type.getActualTypeArguments()[0];
}

public Session getCurrentSession() {
return this.sessionFactory.getCurrentSession();
}

@Override
public long add(T t){
if (t!=null) {
return (Long) this.getCurrentSession().save(t);
}
return 0;
}



@Override
public void update(T t){
if (t!=null) {
this.getCurrentSession().update(t);
}
}


@Override
public int delete(long id){
String hql = "delete "+clazz.getSimpleName()+" where id="+id;
return this.getCurrentSession().createQuery(hql).executeUpdate();
}


@Override
public List<T> getAll(){
String hql = "from "+clazz.getSimpleName();
return this.getCurrentSession().createQuery(hql).list();
}



@Override
public T getById(long id){
String hql = "from "+clazz.getSimpleName()+" where id="+id;
return (T) this.getCurrentSession().createQuery(hql).uniqueResult();
}


@Override
public List<T> getByMap(Map<String,Object> map){
Set<String> set = map.keySet();
if (set.size()>0) {
List<String> list = new ArrayList<String>();
for (String string : set) {
list.add(string);
}
String hql = "from "+clazz.getSimpleName()+" where ";
for(int i=0;i<=list.size()-1;i++){
if (i==0) {
hql += list.get(i)+"='"+map.get(list.get(i))+"'";
}
else{
hql += " and "+list.get(i)+"='"+map.get(list.get(i))+"'";
}
}
return this.getCurrentSession().createQuery(hql).list();
}
return null;
}


@Override
public int countBySql(String sql){
SQLQuery q = this.getCurrentSession().createSQLQuery(sql);
return ((BigInteger) q.uniqueResult()).intValue();
}
@Override
public int executeHql(String hql){
Query q = this.getCurrentSession().createQuery(hql);
return q.executeUpdate();
}


@Override
public int executeSql(String sql){
SQLQuery q = this.getCurrentSession().createSQLQuery(sql);
return q.executeUpdate();
}



@Override
public List<Map<String, Object>> getMapListBySql(String sql) {
SQLQuery query = this.getCurrentSession().createSQLQuery(sql);
query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
return(List<Map<String, Object>>) query.list();
}


}

BaseDao中的方法上已經寫了注釋,所以下面的實現類我就偷懶沒有寫注釋了。我們重點研究其中一個方法


我們在BaseDaoImpl中定義了一個全局變量clazz,利用BaseDaoImpl的構造方法將通過泛型傳進來的真是類型的Class傳到此變量中。此時,在這里可能會產生一個疑問,如果使用了spring,所有的bean管理都是由spring來完成的話,此處的構造方法會得到調用嗎?答案是肯定的,spring創建bean的時候也是通過調用構造方法創建bean實例的。得到此class后我們就可以利用反射機制得到實體的真實名稱進行hql的拼接。好了,我們可以去實現UserDao這個接口了。

@Repository
public class UserDaoImpl extends BaseDaoImpl<User> implements UserDao {

}

我們只要在實現UserDao接口的同時同時繼承BaseDaoImpl這個父類,把實體的真實類型傳進去就行了。雖然UserDaoImpl中一個方法都沒有,實際上它已經具有父類中的所有方法了。BaseDao代碼中只是示例了幾個常用的方法,現實中可以進行擴充,比如分頁的方法之類的,在BaseDaoImpl去實現就可以了。如果UserDaoImpl需要的方法在父類中沒有,並且方法具有特殊性的話,可以在UserDao定義此方法,在UserDaoImpl中去實現就可以了。其他實體所對應的Dao層操作也是一樣。


————————————————
版權聲明:本文為CSDN博主「小哥被占用了」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。
原文鏈接:https://blog.csdn.net/qq_26800725/article/details/52032537


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM