泛型理解及应用(二):使用泛型编写通用型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 (在集合中使用 ...
随机推荐
- zabbix安装(Ubuntu)
zabbix的安装 Zabbix监控架构至少需要server,agent,web模块.mysql.web部分和server安装在同一台机器上. Zabbix安装前服务器要做时间同步(ntp) 1.创建 ...
- application/json和application/x-www-form-urlencoded使用选择
一.参考资料 选application/x-www-form-urlencoded还是application/json? @RequestBody应用 二.理解 1.@RequestBody的作用 注 ...
- 安装配置python环境,并跑一个推荐系统的例子
1.官网下载python2.7,安装完后,在环境变量Path中加上这个路径 在控制台输入python,出现版本信息,就成功了. 2.我使用的是 pycharm,注册后,在 把自己的python.exe ...
- php memcache 基础操作
<?php/** * Memcache缓存操作 * @author hxm * @version 1.0 * @since 2015.05.04 */class MCache extends O ...
- 在linux中文件的权限讲解
1.d:directory(目录): 表示这个文件是个目录,其他的还有f(file文件)等等: 2.r:read(可读) 3.w:write(可写) 4 x :execute(可执行) 一般Linux ...
- python模块:time
# encoding: utf-8 # module time # from (built-in) # by generator 1.145 """ This modul ...
- JAVA 8 主要新特性 ----------------(二)JDK1.8优点概括
一.JDK1.8优点概括 1.速度更快 由于底层结构和JVM的改变,使得JDK1.8的速度提高. 2.代码更少(增加了新的语法 Lambda 表达式) 增加新特性Lambda表达式的 ...
- javascript的常用事件
- Ubuntu 16.04虚拟机调整窗口大小自适应Windows 7
Windows 7上Ubuntu 16.04虚拟机安装成功后,默认的虚拟机窗口比较小,需要适当调整,才能把虚拟机的屏幕放大, 适合使用,以下介绍调整方法. 安装VMware Tools 启动虚拟机, ...
- Java 实现字符串的加密与解密
package com.wangbo.util; import java.security.Key; import java.security.Security; import javax.crypt ...