相信目前所有的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层设计源码

  

泛型理解及应用(二):使用泛型编写通用型Dao层的更多相关文章

  1. <十六>JDBC_使用 DBUtils 编写通用的DAO

    接口 : DAO<T>.java import java.sql.Connection;import java.sql.SQLException;import java.util.List ...

  2. java反射+java泛型,封装BaseDaoUtil类。供应多个不同Dao使用

    当项目是ssh框架时,每一个Action会对应一个Service和一个Dao.但是所有的Ation对应的Dao中的方法是相同的,只是要查的表不一样.由于封装的思想,为了提高代码的重用性.可以使用jav ...

  3. Map接口下的集合和泛型理解

    一.Map接口 1. Map接口就是最顶层了,上面没有继承了.Map是一个容器接口,它与前面学的List.Set容器不同的是前面学的这些容器,一次只能传入一个元素,但是Map容器一次可以传入一对元素( ...

  4. .net泛型理解

    泛型简介: 泛型(Generic Type)是.NET Framework2.0最强大的功能之一.泛型的主要思想是将算法与数据结构完全分离开,使得一次定义的算法能作用于多种数据结构,从而实现高度可重用 ...

  5. 转载.net泛型理解说明

    net泛型理解 泛型简介: 泛型(Generic Type)是.NET Framework2.0最强大的功能之一.泛型的主要思想是将算法与数据结构完全分离开,使得一次定义的算法能作用于多种数据结构,从 ...

  6. java基础(十二 )-----Java泛型详解

    本文对java的泛型的概念和使用做了详尽的介绍. 概述 泛型在java中有很重要的地位,在面向对象编程及各种设计模式中有非常广泛的应用. 什么是泛型?为什么要使用泛型? 泛型,即“参数化类型”.一提到 ...

  7. java泛型理解。代码更明了。

    泛型数据java基础,但真正理解需要悉心品尝.毕竟在工作中用到的是在是太多了. 不要以为new ArrayList<>这就是泛型,这只能属于会使用. 在工作中,相对于现有的项目源码的数据库 ...

  8. C#泛型理解(一)

    一.什么是泛型 泛型是C#语言和公共语言运行库(CLR)中的一个新功能,它将类型参数的概念引入.NET Framework.类型参数使得设计某些类和方法成为可能,例如,通过使用泛型类型参数T,可以大大 ...

  9. Java基础一篇过(二)泛型

    一.啥是泛型 概述 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,即所操作的数据类型被指定为一个参数. 格式 类名<类型名> 标记符 E - Element (在集合中使用 ...

随机推荐

  1. Django 跨域请求 解决 axios 未完待续

    import django import os # os.environ.setdefault("DJANGO_SETTINGS_MODULE", "untitled5. ...

  2. journalctl 清理journal日志

    在CentOS 7开始使用的systemd使用了journal日志,这个日志的管理方式和以往使用syslog的方式不同,可以通过管理工具维护. 使用df -h检查磁盘文件,可以看到/run目录下有日志 ...

  3. Effective C++ 笔记:条款 31 将编译关系降至最低

    31 : Minimize compilation dependencies between files 1 这关乎C++的类(或说都是类惹的祸) 1.1 C++类定义式的问题 C++类定义式不只叙述 ...

  4. day30

    作业 #__author : 'liuyang' #date : 2019/4/11 0011 下午 12:36 # 这两天 1.软件开发规范 不写 没法做新作业 #2. 认证+上传 +下载 + 校验 ...

  5. Vue入门---常用指令详解

    Vue入门 Vue是一个MVVM(Model / View / ViewModel)的前端框架,相对于Angular来说简单.易学上手快,近两年也也别流行,发展速度较快,已经超越Angular了.比较 ...

  6. OpenCV从2到3的过渡

    与版本2.4相比,OpenCV 3.0引入了许多新算法和功能.有些模块已被重写,有些已经重组.尽管2.4中的大多数算法仍然存在,但接口可能不同.本节描述了一般性的最显着变化,过渡操作的所有细节和示例都 ...

  7. [opentwebst]一个简单的登陆脚本

    这个是个简单的vbs脚本,使用opentwebst进行录制 'Use the command line below to launch the script (or just double click ...

  8. 初识Twisted(一)

    pip install Twisted 安装Twisted库 from twisted.internet import reactor #开启事件循环 #不是简单的循环 #不会带来任何性能损失 rea ...

  9. 记录使用 Cake 进行构建并制作 nuget 包

    书接上一回(https://www.cnblogs.com/h82258652/p/4898983.html)?[手动狗头] 前段时间折腾了一下,总算是把我自己的图片缓存控件(https://gith ...

  10. WeexSDK源码分析(iOS)

    0.从工作原理谈起 Weex 表面上是一个客户端技术,但实际上它串联起了从本地开发.云端部署到分发的整个链路.开发者首先可在本地像编写 web 页面一样编写一个 app 的界面,然后通过命令行工具将之 ...