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

大致的设计思路:
1. 工厂类:用来注册Dao层里面的每一个接口,通过HibernateFactory获取到每个具体Dao的实例;实现面向接口式编程。
2. 抽象层:使用泛型抽象出一个Dao层面的接口,使得每个具体的类操作都有自己的Dao实例进行操作。
3. 使用虚拟类对抽象层进行重写,没必要为每个Dao编写一次重复的代码。
对于这套模板,大部分的都是概念性的东西,说白了这个设计里面最重要的就是AbstractDao<T>这个类,因为所有的代码基本都囊括在这里面了。下面,来贴下这个类里面的代码。
package com.template.dao.interfaces; import java.io.Serializable;
import java.util.List; /**
* @Author: Travelsky_CLSUN
* @Date: Created on 17-5-18
* @Description: 抽象一个Dao层级内容,含有基本的增删改查功能
*/
public interface IDao<T>
{ void add(T t); void delete(Serializable id); void update(T t); T findById(Serializable id); List<T> findAll(); }
IDao定义
package com.template.dao.interfaces; import com.template.domain.Person; /**
* @Author: Travelsky_CLSUN
* @Date: Created on 17-5-18
* @Description: Dao工厂,用来注册每个不同的Dao
*/
public interface IDaoFactory
{
IPersonDao<? extends Person> getPersonDao();
}
IDao工厂
Dao层工厂的接口,抽象出每个不同的接口,在这里主要用来作为不同接口的注册。在使用其实现工厂HibernateFactory时,必须先在该工厂里面注册。
package com.template.dao.impl; import com.template.dao.interfaces.IDaoFactory;
import com.template.dao.interfaces.IPersonDao;
import com.template.domain.Person; public class HibernateFactory implements IDaoFactory
{
public static HibernateFactory factoryInstance = null; public static HibernateFactory getFactory()
{
if (factoryInstance == null)
{
factoryInstance = new HibernateFactory();
}
return factoryInstance;
} private HibernateFactory()
{ } @Override
public IPersonDao<Person> getPersonDao()
{
return new PersonDaoImpl();
}
}
单例的HiberanateFactory,用来获取每个接口的实现
HibernateFactory,主要是用来获取每个接口的实现,实现面向接口方式的编程。
package com.template.dao.impl; import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List; import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.Transaction; import com.template.dao.interfaces.IDao;
import com.template.utils.HibernateUtil; /**
* @Author: Travelsky_CLSUN
* @Date: Created on 17-5-18
* @Description: 抽象的Dao层,用于实现每个不同Dao上面的实现,避免重复编写代码
*/
public abstract class AbstractDao<T> implements IDao<T>
{
Type persistenType;
Class<? extends Object> persistenClass; // 定义事务
Transaction transaction = null;
Session session = null; public AbstractDao()
{
Type superClassType = this.getClass().getGenericSuperclass();
if (null != superClassType && superClassType instanceof ParameterizedType)
{
persistenType = ((ParameterizedType) superClassType).getActualTypeArguments()[];
persistenClass = (Class<?>) persistenType;
}
else
{
System.out.println("类:" + this.getClass().getName() + "尚未实现接口或者定义错误");
}
} @Override
public void add(T t)
{ // 获取session,让每个进来的都应该有一个连接
session = HibernateUtil.getSession();
transaction = session.beginTransaction();
try
{
// save把内容放进hibernate一级缓存
session.save(t);
// 没进行事务提交的话,hibernate不会把数据同步到数据库,即使是调用session.flush
transaction.commit();
} catch (Exception e)
{
e.printStackTrace();
transaction.rollback();
} finally
{
transaction = null;
session.close();
} } @Override
public void delete(Serializable id)
{
// 获取session,让每个进来的都应该有一个连接
session = HibernateUtil.getSession();
transaction = session.beginTransaction();
Object obj = session.get(persistenClass, id);
try
{
if (null != obj)
{
session.delete(obj);
transaction.commit();
}
} catch (Exception e)
{
transaction.rollback();
} finally
{
transaction = null;
session.close();
}
} @Override
public void update(T t)
{
// 获取session,让每个进来的都应该有一个连接
session = HibernateUtil.getSession();
session.save(t);
} @Override
public T findById(Serializable id)
{
// 获取session,让每个进来的都应该有一个连接
session = HibernateUtil.getSession();
try
{
Object obj = session.get(persistenClass, id);
return (T) obj;
} catch (Exception e)
{
return null;
} finally
{
session.close();
} } @Override
public List<T> findAll()
{
// 获取session,让每个进来的都应该有一个连接
session = HibernateUtil.getSession();
try
{
Query query = session.createQuery("from " + persistenClass.getName());
return query.list();
} catch (Exception e)
{
return null;
} finally
{
session.close();
} }
}
AbstractDao源码
这个类的代码最重要的就是构造函数。在第一篇泛型的应用里面说了,对于泛型实参的确定有三种方法:①在构造函数显式传入实参类型 ②通过接口显式定义泛型实参,通过反射获取 ③通过匿名子类的方式对泛型实参进行捕获。在这三种方法当中,显然第一种方式不适合使用,因为通用的模板不应该每定义一个接口的时候通过构造器传入泛型实参,这样会导致模板"不通用"。用第三种方法去实现的时候,使用接口编程的时候会让人感觉很突兀,如: IPerson<Person> person = new IPersonDaoImpl(){}; 这样会导致客户端使用难以理解,同时这种匿名子类的方式可能会导致编译器生成好多个匿名子类,这种匿名子类的方式比较适合在方法体里面用来捕获泛型实参。因此选用了第二种方式,同样地因为在接口定义上会以以下形式声明类,所以能够在函数中使用反射获取到签名上的泛型实参,如IPersonDao的定义:
package com.template.dao.interfaces; /**
* @Author: Travelsky_CLSUN
* @Date: Created on 17-5-18
* @Description: 带泛型实参的具体接口定义
*/
public interface IPersonDao<Person> extends IDao<Person>
{ }
IPerson<T>接口的声明
正因为显式地声明了泛型实参,所以可以通过反射获取到实参类型。
package com.template.dao.impl; import com.template.dao.interfaces.IPersonDao;
import com.template.domain.Person; /**
* @Author: Travelsky_CLSUN
* @Date: Created on 17-5-18
* @Description: 接口的具体实现,继承Abstract里面的大部分方法的同时,可以在方法体内编写关于自身使用的方法
*/
public class PersonDaoImpl extends AbstractDao<Person> implements IPersonDao<Person>
{ }
PersonDaoImpl:接口方法的实现
在调用PersonImpl的时候,代码会隐式调用父类的构造函数,在AbstractDao构造器能够收到指向PersonDaoImpl的this,所以在AbstractDao里面可以拿到抽象类的泛型实参签名。因此能够拿到Person这个实参。
通过在AbstractDao里面加入以下两句:

会有以下结果:
what is this? com.template.dao.impl.PersonDaoImpl@232934a1
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层的更多相关文章
- <十六>JDBC_使用 DBUtils 编写通用的DAO
接口 : DAO<T>.java import java.sql.Connection;import java.sql.SQLException;import java.util.List ...
- java反射+java泛型,封装BaseDaoUtil类。供应多个不同Dao使用
当项目是ssh框架时,每一个Action会对应一个Service和一个Dao.但是所有的Ation对应的Dao中的方法是相同的,只是要查的表不一样.由于封装的思想,为了提高代码的重用性.可以使用jav ...
- Map接口下的集合和泛型理解
一.Map接口 1. Map接口就是最顶层了,上面没有继承了.Map是一个容器接口,它与前面学的List.Set容器不同的是前面学的这些容器,一次只能传入一个元素,但是Map容器一次可以传入一对元素( ...
- .net泛型理解
泛型简介: 泛型(Generic Type)是.NET Framework2.0最强大的功能之一.泛型的主要思想是将算法与数据结构完全分离开,使得一次定义的算法能作用于多种数据结构,从而实现高度可重用 ...
- 转载.net泛型理解说明
net泛型理解 泛型简介: 泛型(Generic Type)是.NET Framework2.0最强大的功能之一.泛型的主要思想是将算法与数据结构完全分离开,使得一次定义的算法能作用于多种数据结构,从 ...
- java基础(十二 )-----Java泛型详解
本文对java的泛型的概念和使用做了详尽的介绍. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”.一提到 ...
- java泛型理解。代码更明了。
泛型数据java基础,但真正理解需要悉心品尝.毕竟在工作中用到的是在是太多了. 不要以为new ArrayList<>这就是泛型,这只能属于会使用. 在工作中,相对于现有的项目源码的数据库 ...
- C#泛型理解(一)
一.什么是泛型 泛型是C#语言和公共语言运行库(CLR)中的一个新功能,它将类型参数的概念引入.NET Framework.类型参数使得设计某些类和方法成为可能,例如,通过使用泛型类型参数T,可以大大 ...
- Java基础一篇过(二)泛型
一.啥是泛型 概述 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数. 格式 类名<类型名> 标记符 E - Element (在集合中使用 ...
随机推荐
- MySQL开发——【数据库、数据表的基本操作】
启动MySQL服务器端 CMD启动MySQL服务器端 net start(启动)|stop(停止)|restart(重启)服务名称(mysql) 连接MySQL服务器端 CMD连接MySQL服务器端 ...
- centos free详解
CentOS 6及以前 $ free total used free shared buffers cached Mem: 4040360 4012200 28160 0 176628 3571348 ...
- 利用maven将项目打包成一个可以运行的独立jar包
目标:希望把Java项目打包成一个完整的jar包,可以独立运行,不需要再依赖其他jar包. 我们在用eclipse中mvn创建mvn项目的时候,选择非webapp,会默认的以jar打包形式,如下图: ...
- 搭建Fabric网络(三)artifacts是怎么生成的:cryptogen和configtxgen
在first-network里,./byfn.sh generate可以生成artifacts文件. generate参数其实是使用了cryptogen和configtxgen这两个工具,这两个工具分 ...
- activeMq-2 高可用以及集群搭建
Activemq 的集群方法可以有多种实现方式,我们这里使用zookeeper来实现 要搭建集群,请确保已经搭建好zookeeper环境.这里不再演示. 基本原理: 使用ZooKeeper(集群)注册 ...
- SpringMVC 学习 十一 springMVC控制器向jsp或者别的控制器传递参数的四种方法
以后的开发,大部分是发送ajax,因此这四种传递参数的方法,并不太常用.作为了解吧 第一种:使用原生 Servlet 在控制器的响应的方法中添加Servlet中的一些作用域:HttpRequestSe ...
- 小米open-falcon监控系统接入手册
一.新项目接入 0.官方文档: https://book.open-falcon.org/zh_0_2/usage/getting-started.html 1.联系运维人员确定可以使用监控系统: ( ...
- CentOS7 启动中文输入法
CentOS 系统中是带有中文输入法的(智能拼音),启动方式如下: Applications --> System Tools --> Setting --> Region &am ...
- 用Rider写一个有IOC容器Autofac的.net core的程序
一:Autofac是一个和Java里的Spring IOC容器一样的东西,不过它确实没有Spring里的那么方便,主要是在于它没有提供足够的Api和扫描方式等等,不过优点是它比Spring要快很多,而 ...
- pyinstaller打包程序 带图片
首选说一下,这种打包方式只能在本电脑上使用运行正常 准备:.py文件:你的程序 gif文件:你要用的图片 第一步: 在上面文件所在目录打开cmd 输入 pyi-makespec FP.py 会生成一个 ...