泛型理解及应用(二):使用泛型编写通用型Dao层

   相信目前所有的IT公司网站在设计WEB项目的时候都含有持久层,同样地使用过Hibernate的程序员都应该看过或者了解过Hibernate根据数据库反向生成持久层代码的模板。对于Hibernate生成的这个通用型的模板,可以看一下了解Hibernate是怎么处理这一层的。笔者来到公司作为开发的时候,项目已经有了一个比较成型的Dao层代码。当然这个层级代码是用C#+NHibernate写的。在这里刚好用了泛型把整个代码改成使用Java+Hibernate去实现。

   首先,先大致阐述下整个通用Dao层是怎么设计的。

    具体的类图如下:

                

   大致的设计思路:

  1. 工厂类:用来注册Dao层里面的每一个接口,通过HibernateFactory获取到每个具体Dao的实例;实现面向接口式编程。

  2. 抽象层:使用泛型抽象出一个Dao层面的接口,使得每个具体的类操作都有自己的Dao实例进行操作。

  3. 使用虚拟类对抽象层进行重写,没必要为每个Dao编写一次重复的代码。

  对于这套模板,大部分的都是概念性的东西,说白了这个设计里面最重要的就是AbstractDao<T>这个类,因为所有的代码基本都囊括在这里面了。下面,来贴下这个类里面的代码。

 1 package com.template.dao.interfaces;
 2 
 3 import java.io.Serializable;
 4 import java.util.List;
 5 
 6 /**
 7  * @Author: Travelsky_CLSUN
 8  * @Date: Created on 17-5-18 
 9  * @Description: 抽象一个Dao层级内容,含有基本的增删改查功能
10  */
11 public interface IDao<T>
12 {
13 
14     void add(T t);
15 
16     void delete(Serializable id);
17 
18     void update(T t);
19 
20     T findById(Serializable id);
21 
22     List<T> findAll();
23 
24 }
IDao定义
 1 package com.template.dao.interfaces;
 2 
 3 import com.template.domain.Person;
 4 
 5 /**
 6  * @Author: Travelsky_CLSUN
 7  * @Date: Created on 17-5-18 
 8  * @Description: Dao工厂,用来注册每个不同的Dao
 9  */
10 public interface IDaoFactory
11 {
12     IPersonDao<? extends Person> getPersonDao();
13 }
IDao工厂

  Dao层工厂的接口,抽象出每个不同的接口,在这里主要用来作为不同接口的注册。在使用其实现工厂HibernateFactory时,必须先在该工厂里面注册。

 1 package com.template.dao.impl;
 2 
 3 import com.template.dao.interfaces.IDaoFactory;
 4 import com.template.dao.interfaces.IPersonDao;
 5 import com.template.domain.Person;
 6 
 7 public class HibernateFactory implements IDaoFactory
 8 {
 9     public static HibernateFactory factoryInstance = null;
10 
11     public static HibernateFactory getFactory()
12     {
13         if (factoryInstance == null)
14         {
15             factoryInstance = new HibernateFactory();
16         }
17         return factoryInstance;
18     }
19 
20     private HibernateFactory()
21     {
22 
23     }
24 
25     @Override
26     public IPersonDao<Person> getPersonDao()
27     {
28         return new PersonDaoImpl();
29     }
30 }
单例的HiberanateFactory,用来获取每个接口的实现

  HibernateFactory,主要是用来获取每个接口的实现,实现面向接口方式的编程。

  1 package com.template.dao.impl;
  2 
  3 import java.io.Serializable;
  4 import java.lang.reflect.ParameterizedType;
  5 import java.lang.reflect.Type;
  6 import java.util.List;
  7 
  8 import org.hibernate.Query;
  9 import org.hibernate.Session;
 10 import org.hibernate.Transaction;
 11 
 12 import com.template.dao.interfaces.IDao;
 13 import com.template.utils.HibernateUtil;
 14 
 15 /**
 16  * @Author: Travelsky_CLSUN
 17  * @Date: Created on 17-5-18 
 18  * @Description: 抽象的Dao层,用于实现每个不同Dao上面的实现,避免重复编写代码
 19  */
 20 public abstract class AbstractDao<T> implements IDao<T>
 21 {
 22     Type persistenType;
 23     Class<? extends Object> persistenClass;
 24 
 25     // 定义事务
 26     Transaction transaction = null;
 27     Session session = null;
 28 
 29     public AbstractDao()
 30     {
 31         Type superClassType = this.getClass().getGenericSuperclass();
 32         if (null != superClassType && superClassType instanceof ParameterizedType)
 33         {
 34             persistenType = ((ParameterizedType) superClassType).getActualTypeArguments()[0];
 35             persistenClass = (Class<?>) persistenType;
 36         }
 37         else
 38         {
 39             System.out.println("类:" + this.getClass().getName() + "尚未实现接口或者定义错误");
 40         }
 41     }
 42 
 43     @Override
 44     public void add(T t)
 45     {
 46 
 47         // 获取session,让每个进来的都应该有一个连接
 48         session = HibernateUtil.getSession();
 49         transaction = session.beginTransaction();
 50         try
 51         {
 52             // save把内容放进hibernate一级缓存
 53             session.save(t);
 54             // 没进行事务提交的话,hibernate不会把数据同步到数据库,即使是调用session.flush
 55             transaction.commit();
 56         } catch (Exception e)
 57         {
 58             e.printStackTrace();
 59             transaction.rollback();
 60         } finally
 61         {
 62             transaction = null;
 63             session.close();
 64         }
 65 
 66     }
 67 
 68     @Override
 69     public void delete(Serializable id)
 70     {
 71         // 获取session,让每个进来的都应该有一个连接
 72         session = HibernateUtil.getSession();
 73         transaction = session.beginTransaction();
 74         Object obj = session.get(persistenClass, id);
 75         try
 76         {
 77             if (null != obj)
 78             {
 79                 session.delete(obj);
 80                 transaction.commit();
 81             }
 82         } catch (Exception e)
 83         {
 84             transaction.rollback();
 85         } finally
 86         {
 87             transaction = null;
 88             session.close();
 89         }
 90     }
 91 
 92     @Override
 93     public void update(T t)
 94     {
 95         // 获取session,让每个进来的都应该有一个连接
 96         session = HibernateUtil.getSession();
 97         session.save(t);
 98     }
 99 
100     @Override
101     public T findById(Serializable id)
102     {
103         // 获取session,让每个进来的都应该有一个连接
104         session = HibernateUtil.getSession();
105         try
106         {
107             Object obj = session.get(persistenClass, id);
108             return (T) obj;
109         } catch (Exception e)
110         {
111             return null;
112         } finally
113         {
114             session.close();
115         }
116 
117     }
118 
119     @Override
120     public List<T> findAll()
121     {
122         // 获取session,让每个进来的都应该有一个连接
123         session = HibernateUtil.getSession();
124         try
125         {
126             Query query = session.createQuery("from " + persistenClass.getName());
127             return query.list();
128         } catch (Exception e)
129         {
130             return null;
131         } finally
132         {
133             session.close();
134         }
135 
136     }
137 }
AbstractDao源码

  这个类的代码最重要的就是构造函数。在第一篇泛型的应用里面说了,对于泛型实参的确定有三种方法:①在构造函数显式传入实参类型 ②通过接口显式定义泛型实参,通过反射获取 ③通过匿名子类的方式对泛型实参进行捕获。在这三种方法当中,显然第一种方式不适合使用,因为通用的模板不应该每定义一个接口的时候通过构造器传入泛型实参,这样会导致模板"不通用"。用第三种方法去实现的时候,使用接口编程的时候会让人感觉很突兀,如: IPerson<Person> person = new IPersonDaoImpl(){}; 这样会导致客户端使用难以理解,同时这种匿名子类的方式可能会导致编译器生成好多个匿名子类,这种匿名子类的方式比较适合在方法体里面用来捕获泛型实参。因此选用了第二种方式,同样地因为在接口定义上会以以下形式声明类,所以能够在函数中使用反射获取到签名上的泛型实参,如IPersonDao的定义:

 1 package com.template.dao.interfaces;
 2 
 3 /**
 4  * @Author: Travelsky_CLSUN
 5  * @Date: Created on 17-5-18 
 6  * @Description: 带泛型实参的具体接口定义
 7  */
 8 public interface IPersonDao<Person> extends IDao<Person>
 9 {
10 
11 }
IPerson<T>接口的声明

  正因为显式地声明了泛型实参,所以可以通过反射获取到实参类型。

 1 package com.template.dao.impl;
 2 
 3 import com.template.dao.interfaces.IPersonDao;
 4 import com.template.domain.Person;
 5 
 6 /**
 7  * @Author: Travelsky_CLSUN
 8  * @Date: Created on 17-5-18 
 9  * @Description: 接口的具体实现,继承Abstract里面的大部分方法的同时,可以在方法体内编写关于自身使用的方法
10  */
11 public class PersonDaoImpl extends AbstractDao<Person> implements IPersonDao<Person>
12 {
13 
14 }
PersonDaoImpl:接口方法的实现

  在调用PersonImpl的时候,代码会隐式调用父类的构造函数,在AbstractDao构造器能够收到指向PersonDaoImpl的this,所以在AbstractDao里面可以拿到抽象类的泛型实参签名。因此能够拿到Person这个实参。

  通过在AbstractDao里面加入以下两句:

  会有以下结果:

1 what is this? com.template.dao.impl.PersonDaoImpl@232934a1
2 what is this’s superClass? com.template.dao.impl.AbstractDao<com.template.domain.Person>
抽象类的初始化结果

  通过this关键字,巧妙地传递了相关的类型内容。

  同样地,这样设计也是有缺点的,①接口如果一直增加,代码声明会越来越多,Hibernatefactory里面的获取的方法也会越来越多。②HibernateFactory未能有效地控制具体的实现类的初始化,可以直接绕开HibernateFactory直接初始化,如可以使用: IPersonDao<Person> personDao = new PersonDaoImpl(); 直接初始化特定的dao,虽然可以在PersonDaoImpl里面把构造函数的修饰符设置成protected,但是每个接口在声明的时候都需要添加这样一个构造函数。

  另外,补充一些题外点:

    1. 与Hibernate的一级缓存相关的save、update方法必须要开启事务,否者没法同步数据到数据库,就算Hibernate执行的时候打印出了insert语句。

    2. 不要在抽象类里面通过属性频繁开关session,这样可能会由于并发导致异常。

  3. 使用Hibernate更新数据库的时候,可以通过行级锁把数据锁上,免得两个不同的线程同时修改数据(所以第1点Hibernate在save和update的时候必须使用事务就是这原因)

  最后,附上源码,期望对自己和各位的学习有帮助,有问题请及时联系我加以指正,谢谢。

通用Dao层设计源码

 

 

 

  

转载于:https://www.cnblogs.com/doucheyard/p/6913742.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: C++的STL(Standard Template Library,标准模板库)是一个强大的库,其中包含了许多容器、算法和迭代器等组件。其中的容器是实现数据管理和存储的基本组件,包括向量、列表、队列和映射等。而在STL中使用的容器,大多采用了 C++ 的泛型编程的方式,即采用了泛型泛型是一种基于类型参数化的编程方式,它的主要特点是可以忽略类型细节而将通用算法应用于不同的类型数据上。在STL中,泛型应用可以明显提高代码的复用性和灵活性,使得STL的容器可以应用于不同类型的数据。 在STL中,容器、算法和迭代器等组件都是以泛型的形式出现。泛型程序可以使用模板来定义不同类型的函数和类。例如,使用 vector 模板可以创建一个自动分配和管理内存的可变数组。使用迭代器就可以对容器中的元素进行遍历。而STL中的算法则可以对容器中的元素进行各种处理,如排序和查找等。 STL中的泛型应用使得程序员们不必为不同的数据类型写出不同的版本的代码,同时也使得算法重用更加容易。因此,STL成为了C++编程中不可或缺的一部分,它将数据结构和算法分离,使得程序变得更加简单、漂亮和容易理解。 ### 回答2: STL(标准模板库)是C++编程中的一种重要的程序库,它提供了一系列的模板类和模板函数,可以帮助开发者更加高效地进行编程。其中,泛型是STL中的重要概念,它可以实现代码的重用,提高程序的可读性和可维护性。 泛型是指在STL程序设计中,可以将容器的类型、算法的参数、迭代器的类型等抽象成具有灵活性的、可重用的模板。这种设计思想可以让程序员编写具有通用性的代码,无需为每种数据类型单独编写代码。同时,泛型还可以维护代码的一致性和可靠性,减少编程错误。 STL的泛型分为容器和算法两个方面。容器是指能够存储某种数据类型的数据结构,例如vector、list、deque、set、map等。它们的共同点是都提供了访问元素的迭代器接口,可以通过迭代器的方式对元素进行访问、添加、删除等操作。使用容器能够简化对元素的操作,提高代码的可读性。 算法是指对容器中的元素执行某些操作的函数,例如find、sort、copy等。在STL中,算法通常使用迭代器作为参数,允许程序员通过自定义函数对象来实现更灵活的算法。 STL采用有限的基本概念和范式,尝试构建一种抽象的“程序设计语言”,把现实世界中需要处理的数据组织成容器,用算法来操作容器中的数据,以及迭代器来遍历容器中的元素。这种设计使得编写代码变得简单、可读性强、可维护性好,具有很高的实用价值。 总之,STL泛型技术是C++中一个非常重要的概念,它能够提高程序的可读性和可维护性,使得程序员能够高效地开发各种应用程序。掌握STL泛型技术,不仅可以帮助程序员更好地理解C++编程,而且也可以让代码更加清晰、简洁和高效。 ### 回答3: STL(标准模板库)是C++中的一个重要组成部分,它包含很多的类模板和函数模板,而其中的泛型(generic programming)则是STL的核心理念。泛型是指在编写程序时抽象出类型,使得同一份代码适用于多种不同的数据类型,同时保持程序代码的高效和可维护性。STL采用了泛型编程的方法,利用了模板特性,实现了很多可以适用于广泛场景的标准算法和容器。以下是STL中常见的泛型及其应用。 1. 容器(Containers): STL提供了多种类型的容器,如vector、list、deque、map等等。它们都是通过模板类实现的,可以存储不同类型的数据并提供多种数据管理功能。容器可以存储基本类型或用户定义的对象,可用于解决很多实际问题。例如,vector是一种高效的数据结构,可以存储不同类型的数据,比如数组和连续的空间。list和deque是序列容器,可以方便地插入、删除和遍历数据。map是一种关联式容器,它提供了键值对的存储和查找功能,可以极大地简化一些算法。 2. 迭代器(Iterators): 迭代器是指指向容器中某个元素的指针或类似于指针的对象。迭代器可以按照顺序访问容器中的元素,并可以实现很多算法。STL中的迭代器被设计成可以与容器类型无关,使得同一份算法可以适用于不同类型的容器。例如,迭代器可以用于实现排序、搜索和复制等操作。 3. 算法(Algorithms): STL提供了很多通用算法,例如sort、find、copy等等。这些算法都是通过模板函数实现的,可以适用于不同类型的容器和迭代器。算法的实现通常采用泛型编程技术,使得代码复用率高,并且可以保证算法的高效性。 在实际应用中,STL的泛型编程理念优化了大部分的数据结构和算法的实现,并且使得代码更加清晰。STL容器除了能够存储不同类型的数据,还具有一定的自我维护机制,如动态增长、内存管理等。同时,STL也弥补了一些C++语言中的不足,如指针操作容易出错、STL提供了异常处理机制等。在实际使用中,STL容器和算法的复杂度较低,执行效率较高,因此在开发中应尽可能采用STL。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值