相信目前所有的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. python 实现 Fortran的读取10*0以及换行读问题

    思路,用read来全部读取,然后替换带*的元素来解决.代码如下 import numpy as np import re inf = open('SF.usr') title = inf.readli ...

  2. 常用screen参数

    摘自:https://www.cnblogs.com/webnote/p/5749675.html screen -S yourname -> 新建一个叫yourname的sessionscre ...

  3. android开发笔记(3)

    在android环境开发过程中,我最陌生的是虚拟机的创建以及使用,下面是虚拟机的创建以及使用过程. 创建虚拟机: 点击创建虚拟机的按钮,create->填写虚拟机相关的信息,确认之后创建. 创建 ...

  4. delphi 中OutputDebugString 函数的妙用(转载)

    原文地址 https://www.peganza.com/delphi-and-outputdebugstring.html Ever wanted to monitor your Delphi ap ...

  5. python闭包的详细解析

    一.什么是闭包? 如果一个内嵌函数访问外部嵌套函数作用域的变量,并返回这个函数,则这个函数就是闭包 闭包必须满足三个条件: 1. 必须有一个内嵌函数    2. 内嵌函数必须引用外部嵌套函数中的变量  ...

  6. Django——用户认证和判断用户是否登录

    用户认证 必须通过认证之后才能login(request,user)这样才能保存会话到request中,注销后会话结束 注意 自定义的用户登陆时只不止需要验证用户名和密码的需要写认证,就例如在线教育平 ...

  7. CentOS操作系统防火墙添加例外端口

    http://blog.csdn.net/inrgihc/article/details/63392004 CentOS6与CentOS7添加防火墙例外端口的命令不用,需单独来说: (1)CentOS ...

  8. hdu 4034

    比赛时才发现自己基础算法都忘得光光了,逆向floyd i->j经过k点,只要i到j的距离大于或者等于,就把这边标记,实为去掉...此时整个图就减一条边 #include<iostream& ...

  9. nginx 502错误 upstream sent too big header while reading response header from upstream

    原本的设置是 proxy_buffer_size 4k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; 在这种配置下,使用fiddler进行抓包分 ...

  10. maya2016卸载/安装失败/如何彻底卸载清除干净maya2016注册表和文件的方法

    maya2016提示安装未完成,某些产品无法安装该怎样解决呢?一些朋友在win7或者win10系统下安装maya2016失败提示maya2016安装未完成,某些产品无法安装,也有时候想重新安装maya ...