1. 程式人生 > >通過hibernate封裝數據庫持久化過程回顧泛型/繼承/實現等概念

通過hibernate封裝數據庫持久化過程回顧泛型/繼承/實現等概念

ring 改進 具體類 額外 generated 父類 etc 字段 是你

  前言

在開發過程中,我們不難發現,客戶的需求以及產品的定位對開發內容的走向有很大的決策作用,而這些往往需要在一開始就盡可能考慮周全和設計完善。為什麽說是盡可能,因為我們都知道,需求這種東西,一言難盡...作為開發者,既然無法掌控需求的變更等因素,那我們就要把握好自身能決定的工具資源等,架構設計、技術選型等等。有的人可能會說,我才多久經驗之類,架構什麽的不都是leader們大佬們的事情麽,選什麽數據庫用什麽技術又不是我能決定的。如果你有這種想法,我只能覺得你說得很對。不知道大家在開發過程中,有沒有遇到,某個場景或者效果用現有的工具技術你覺得無法實現或者很難實現,又或者你知道又可以實現的技術框架卻不會用?echarts、POI等,對於我來說,都符合上述場景。總的來說,有的東西在開發過程中可以當作學習,有些東西卻需要你額外花時間在工作之外去研究。當然,如果你說你工作中不管需要什麽新技術,你一天之類就可以掌握,那也是能力。而且這不是嘲諷什麽的,是實實在在的,我現在愈發覺得學習能力的重要性,他對一個人的潛在能力增長曲線的影響太大,當然你有能力卻不學習,那也註定是一條直線射穿。Spring、Strus 2、Hibernate、Mybatis、Spring MVC是比較常見的Web項目會用到的框架,持久層目前來看主要有Hibernate、Mybatis、Spring data jpa等,目前我在用Spring data jpa,這個東西真的厲害,用起來比我以前的想法又上了一個臺階,而且跟hibernate支持的也很好,Hibernate本身也是jpa規範的實現。接下來文中項目中類與方法設計的思路是之前基於Hibernate的,正好回顧一下,認識下其中的不足,好結合現在用的Jpa改進。

  項目結構一覽

以web項目技術框架選型為Spring+Hibernate+Spring MVC為例,忽略配置以及web前端文件,後臺結果最精簡情況大致如下:

技術分享

從數據庫方向依次向前介紹:

1.domain層,這個東西的標準解釋是領域模型,我們orm對象關系映射的實體類一般就放在該層下,即實體層,類似的叫法,entity、model、pojo等作用大抵如此;

2.repository層,數據庫訪問層,這個就是dao層,存放數據庫訪問操作的方法;

3.service層,業務邏輯層,用於存放業務邏輯方法的地方,與dao層相比,有的人可能開始會覺得這個service層沒有什麽必要,或者不太清楚兩者的區別。首先關於兩者區別:業務邏輯層,故名思意,他的重要定位是業務,業務需求什麽,他就要提供什麽,舉個例子,dao層提供了你刪除商品的方法和添加日誌的方法,這兩個方法分別對應有兩個實體對象商品和日誌,操作的也是數據庫相應的單個表,但是實際情況是你通過單個方法總感覺哪裏不對勁,業務場景下,如果需要你刪除東西後會有相應的日誌記錄,而日誌又不會憑空捏造,需要有事件對應。這時候,service層的作用就體現出來了,簡單的可以這麽理解,復雜的數據庫處理你靠單個dao方法無法實現的時候,你在service層去構造這個方法就行,通過SpringIOC特性,在service層註入你需要的調用的dao方法所在的類或者接口,實際場景一般都是接口。其次關於必要性,我曾經也想過,直接在controller裏面註入多個dao類或者接口不是也能達到想要的效果嗎?Controller顧名思義,控制器,它在web結果中,主要起到一個接受和轉發請求並控制的作用,當你的業務邏輯相對簡單的時候,你覺得看不出多大區別,當實際項目業務邏輯相對復雜很多的情況下,這個controller就有點炸了,像個身兼多職操勞過度的苦工,另外結合上一個疑惑,還有一個很重要的概念就是事務,而Spring的事務管理做得也很到位,通過編程式事務管理或者聲明式事務管理都可以實現,而事務一般就設置在業務邏輯也就是service層上,關於事務也是很重要和精髓的一塊,需要學習也值得學習。

4.controller層,控制層,用struts 2也許會叫action層吧,或者通用點,叫web層其實也可以。他可以接收不同的請求url,調用相應的service層代碼,操作數據庫,跳轉到制定頁面,也可以不跳轉,直接返回數據,這裏的數據目前用json的比較多,典型的應用場景:ajax發起異步請求,DispatcherServlet捕獲請求後對url進行解析,分發到相應的控制類中的相應方法中執行其中代碼,該方法上加上@ResponseBody即可。

  關於Hibernate在項目中的定位

Hibernate是一個典型的ORM持久層框架,ORM即Object Relational Mapping(對象關系映射),即實體類與數據庫表之間的關系映射,通俗的講,一張表對應一個實體類,一條記錄對應一個實體類對象,字段對應實體類屬性。對數據庫的操作在Hibernate中有一個重要對象session,通過session封裝一系列數據庫操作方法在數據庫訪問層,所以在上面的結構中,Hibernate主要操作和作用的有domain層和repository層,下面示例也省略其他層代碼。

  示例

domain層,這裏簡單使用,Mysql數據庫主鍵也用的int自增型,實際應用數據量較大等情況下會用varchar,保存使用UUID賦值主鍵。

 1 @Entity
 2 @Table(name="TBL_USER")
 3 public class User {
 4     private Integer id;
 5     private String username;
 6     private String password;
 7     @Id
 8     @GeneratedValue(strategy=GenerationType.AUTO)
 9     public Integer getId() {
10         return id;
11     }
12     public void setId(Integer id) {
13         this.id = id;
14     }
15     public String getUsername() {
16         return username;
17     }
18     public void setUsername(String username) {
19         this.username = username;
20     }
21     public String getPassword() {
22         return password;
23     }
24     public void setPassword(String password) {
25         this.password = password;
26     }
27     @Override
28     public String toString() {
29         return "User [id=" + id + ", username=" + username + ", password=" + password + "]";
30     }
31     
32     
33     
34 }

repository層

BaseDao泛型接口

public interface BaseDao<T> {
    public void edit(Object obj);        //添加或者更新一條記錄 
    public void delete(int id);            //根據主鍵刪除一條記錄
    public T load(int id);                //根據主鍵查找一條記錄(懶加載)
    public T get(int id);                //根據主鍵查找一條記錄(非懶加載)
}

BaseDao泛型實現類

public class BaseDaoImpl<T> implements BaseDao<T> {
    @Resource
    private SessionFactory factory;
    private Class<T> clazz = GeneriscUtil.getGenericType(this.getClass());
    
    protected Session getSession() {
        return factory.getCurrentSession();
    }
    
    public void edit(Object obj) {
        getSession().saveOrUpdate(obj);
    }

    public void delete(int id) {
        Object object = getSession().get(clazz, id);
        if(object != null) {
            getSession().delete(object);
        }
    }

    public T load(int id) {
        return (T) getSession().load(clazz, id);
    }

    public T get(int id) {
        return (T) getSession().get(clazz, id);
    }

}

泛型工具類

public class GeneriscUtil {
    @SuppressWarnings("rawtypes")
    public static Class getGenericType(Class clazz){
        Type genType = clazz.getGenericSuperclass();//獲取泛型父類  
        Type[] types = ((ParameterizedType) genType).getActualTypeArguments();
        if (!(types[0] instanceof Class)) {
            return Object.class;   
        } 
        return (Class) types[0];
    }
}

最後:UserDao接口和UserDaoImpl實現類

public interface UserDao extends BaseDao<User> {
    User login(String username, String password);
}
@Repository("UserDao")
public class UserDaoImpl extends BaseDaoImpl<User> implements UserDao {

    public User login(String username, String password) {
        String hql = "from User u where u.username=:un and u.password=:pwd";
        Query query = getSession().createQuery(hql);
        query.setString("un", username) 
            .setString("pwd", password);
        Object obj = query.uniqueResult();
        return obj != null ? (User)obj : null;
    }
    
}

  還有其的XXXDao和XXXDaoImpl,通過繼承和實現,可以讓全部Impl實現類很輕松地就擁有基本的CRUD等通用數據庫操作方法,如果某個實體有特殊的Dao操作只需要在相應Dao接口實現,然後在Impl實現類中實現該特殊方法就行,以前認識到的知識點在這裏有了深入理解。

  泛型是一種思想,也是一種技術,動態的獲取類型,讓編程更加靈活,這裏利用泛型,我們可以先不制定實體類對象具體類型,構造一個泛型的Dao接口和實現類,讓後面各自具體的實體Dao去繼承泛型的時候再確定類型,從而也得到了泛型中定義的基本方法。

  繼承體現的是代碼重用思想,當一個方法被構造多次的時候我們就要思考代碼重用的問題了,在這裏每個實體對象都需要基本的數據庫操作方法,如果一個個的去定義將會十分繁瑣和枯燥,通過繼承我們可以省去很多代碼。

  實現不同於繼承,它沒有節省代碼,而且又涉及到抽象的概念,在單個父類與多個子類的繼承關系中,父類某個方法對應的子類實現有差異時,我們對於父類的該方法,即可定義為抽象方法,子類各自實現具體細節即可。舉個例子:動物父類,子類有鳥、魚等,定義動物時,會定義睡覺方法,但是每種動物睡覺情況都不一樣,鳥睡樹上,魚睡水下,你沒法指定具體實現細節,所以就可以定義一個抽象的睡覺方法void sleep();具體的動物實現接口後,重寫該抽象方法,展示實現細節。

  總結繼承和實現:繼承通用的,實現特殊的。在文中構造的持久層中,我們的所有實體通過繼承輕松地擁有了所有的通用數據庫操作方法,各自特殊的操作需求可在各自接口中生命然後在實現類中實現即可。

  最後,文中的項目中其實還存在許多不足的地方,主鍵的類型其實也可以用泛型,dao層有通用組件,service層是否應該也有通用的,若是有該如何構造等等,一些問題我在現在用的spring data jpa中得到了答案,所以應該會另外梳理一下寫出來。

通過hibernate封裝數據庫持久化過程回顧泛型/繼承/實現等概念