Hibernate 模板设计模式
1. 前言
本节课和大家一起使用模板设计模式重构 Hibernate 操作流程,通过本节课程内容,你将了解到:
- 如何运用模板设计模式重构 Hibernate 操作流程;
- 持久化对象与序列化接口;
2. 模板设计模式
学习 Hibernate 的过程,如同训练场上练兵。终有一天,你要奔赴真正的战场(真实项目)。
所以,随时随地,要审视代码、重构代码。
2.1 什么是模板化操作
OOP 中有一个编码原则 : 写仅写一次 。翻译过来就是,不要重复,要重用。
如何简化测试实例:
答案是:使用模板设计模式进一步封装 Hibernate 的操作。
在真实项目中,Hibernate 仅仅只是完成项目中的一部分工作,需要和其它,如 Spring 等框架联合工作,一起承担整体项目的开发。
Spring 框架中就提供的有 Hibernate 模板对象。
模板设计模式的应用场景:
一个常规的、频繁的操作代码中,大部分代码不需要变动,只有小部分代码需要根据需求变动。这种场景下的代码,可认为是模板化操作流程代码,可使用模板设计模式进行重构。
JDBC 就是一个标准化的模板化操作过程。可以说,Hibernate 是一个操作 JDBC 的模板化框架。
Hiberante 虽然高度简化 JDBC 操作流程,但使用期间,还是需要按部就班的:
- 创建配置对象;
- 创建会话工厂;
- 创建会话对象;
- 创建事务对象;
- 终于轮到完成需求;
- 嘿,记得…… 关闭会话对象!谢谢提醒,差点忘记了。
好无聊呀,重复的事情,总是让人很容易麻木。内心挣扎时刻,便是想办法应对时刻。
本文中把不需要改变的代码称为模板代码。
好了,现在开始,一起使用模板设计模式继续简化 Hibernate 的操作流程。这种感觉就像风一样自由。
2.2 模板化流程
先画一个图,简要说一下模板化的基本思路:
一个模板对象中有 2 种类型代码:
- 模板代码: 公共的代码,不需要变更的代码;
- 通知代码: 告诉调用者,此处应该是你来做了。一般采用接口的方式进行通知。
可以得出一个结论,对于一件事情,原来完全是靠调用者独立完成,现在分摊到了两个对象上,模板对象完成公共部分代码,调用者完成属于自己需求的代码。
有了上面的理解基础,便知,一个完整的模板调用过程,会涉及到 3 个角色:
- 调用者角色: 使用者;
- 模板角色: 封装公共代码,简化使用者的操作;
- 接口角色: 调用者和模板角色之间的连接通道,互相通信;
下面进行具体实例编写:
- 构建一个 HibernateTemplate 类,模板角色:
public class HibernateTemplate<T extends Serializable > {
private SessionFactory sessionFactory;
public HibernateTemplate() {
// 模板代码
Configuration configuration = new Configuration().configure();
// 模板代码
ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties())
.buildServiceRegistry();
// 模板代码
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
}
public T template(Notify<T> notify) {
// 模板代码
Session session = sessionFactory.openSession();
// 模板代码
Transaction transaction = null;
try {
// 模板代码
transaction = session.beginTransaction();
// 通知代码
T result = notify.action(session);
transaction.commit();
return result;
} catch (Exception e) {
transaction.rollback();
} finally {
session.close();
}
return null;
}
}
- 接口角色:
public interface Notify<T extends Serializable> {
T action(Session session);
}
- 调用者角色:此处为测试实例:
HibernateTemplate<Student> hibernateTemplate=new HibernateTemplate<Student>();
Student stu= hibernateTemplate.template(new Notify<Student>() {
@Override
public Student action(Session session) {
return (Student)session.get(Student.class, new Integer(1));
}
});
System.out.println(stu.getStuName());
- 控制台输出结果不再贴出。不要怀疑,结果一定是你所期望的。
模板对象中的接口非常重要:
- 通知作用,并等待调用者的响应。接口中的方法也可称为回调方法;
- 模板对象通过接口的方式暴露出 Session 给调用者,调用者不需要关心 Session 是怎么来的,安心使用便是。
模板对象中可以进一步封装 Session 对象中的相关方法,如:
public T get(Class<?> clz, Serializable ser) {
// 模板代码
Session session = sessionFactory.openSession();
// 模板代码
Transaction transaction = null;
try {
// 模板代码
transaction = session.beginTransaction();
T result = (T) session.get(clz, ser);
transaction.commit();
return result;
} catch (Exception e) {
transaction.rollback();
} finally {
session.close();
}
return null;
}
测试实例:
HibernateTemplate<Student> hibernateTemplate=new HibernateTemplate<Student>();
Student stu= hibernateTemplate.get(Student.class, new Integer(1));
System.out.println(stu.getStuName());
是不是开心的不要不要的,除了在模板类中需要写一次 Hibernate 常规流程外,具体操作时,直接上模板对象。
对于任何知识不要理所当然地接受,要善于发现代码中冗余的地方,逐步形成代码意识,随时改进代码。
3. 序列化
不知道大家发现没有,模板对象的泛型声明:
public class HibernateTemplate<T extends Serializable>{}
前面课程使用 Session 对象中的方法时,Serializable 接口时不时的就蹦出来,为什么 Hibernate 要求持久化类实现 Serializable 接口?
为什么使用 Sesssion 的方法,某些参数也需要一个实现 Serializable 接口的对象。
public Object load(Class theClass, Serializable id);
public Object get(Class clazz, Serializable id);
原因很简单,如果你真正了解什么是序列化。
所谓序列化,通俗理解:
以二进制的形式存储对象中的数据,这个过程就叫序列化。相反的,把存储的二进制数据恢复成对象数据,这个过程是反序列化。
序列化的目的,就是要以对象为单元进行数据存储,存储并不限于本地磁盘,可以是网络等环境。
序列化屏蔽底层繁琐的编码、解码过程,完全以一种面向对象的理念进行数据存储。提高开发效率。
Hiberante 为什么要求持久化对象实现序列化?
- 缓存数据: 如先把一个查询出来的对象数据以序列化的方式存储到内存或磁盘中,需要时再读出来,再持久化到数据库中;
- 网络数据传输: 需要把持久化数据从一个系统传到另一个系统时,可能两个系统是基于两个平台,在异构化的系统中通过二进制进行数据传递,可打破这种壁垒。
不管怎样,让对象具有序列化能力,有点像《终结者》中的液态机器人,随时把自己液态化,来去自由。适应不同的需求场景。
4. 小结
本节课也要到说再见的时候,留一个问题给大家:如果在持久化类中重写 equals 方法,为什么也要求重写 hashCode 方法。
答案其实就在于你对这两个方法的理解了。
好了!本节课,和大家一起使用模板设计模式封装了 Hibernate 的操作代码。让 Hibernate 的使用过程变得更简单,更是为了适应真实项目需求。
本节课程也和大家一起聊到了持久化对象为什么要实现序列化接口。