自定义类 Hibernate 框架的基本过程
1. 前言
不要一味地迷信,要善于质疑,善于打破。这应该是我们在学习框架时应该保持的心态。
Hibernate 是所有 JDBC 框架中封装最高的、使用起来最便利的框架之一。对于初学者而言,要么在感叹它的神奇,要么敬畏它的存在。
但是,作为一个真正的开发者,应该有破有立的想法。
本节课程试图通过一个简易的 JDBC 框架的实现流程描述,让大家更清晰地了解 Hibernate 框架的设计过程。
TIps: 框架的编写是一个比较复杂的过程,并不是一言两语能说清楚的,本文中的代码介于伪代码和真实代码之间 ,仅仅用来帮助大家理解 Hibernate 的框架基础原理。
2. 读取配置信息
简易框架主要模拟 3 个组件,分别对应 Hibernate 中的 Configuration、SessionFactory、Session 组件。
Configuration 组件的作用:解析配置文件中的信息。
Tips: 简易框架中,保持配置文件格式及内容和 Hiberante 的主配置文件一致。
查看一下 Configuration 类的结构:
public class Configuration {
//保存配置文件中的 property 元素信息
private Map<String,String> pros;
//保存配置文件中的映射类
private List<String> mappings;
public Configuration() {
this.pros=new HashMap<String, String>();
this.mappings=new ArrayList<String>();
}
/**
* 读取配置文件
*/
public void configure() throws DocumentException {
configure("/hibernate.cfg.xml");
}
private void configure(String resource) throws DocumentException {
InputStream stream =Configuration.class.getResourceAsStream(resource);
doConfigure( stream);
}
/**
* 使用 DOM4j XML 解析器解决配置文件中的信息
*/
private void doConfigure(InputStream stream) throws DocumentException {
SAXReader saxReader=new SAXReader();
Document doc= saxReader.read(stream);
Element rootEle= doc.getRootElement();
Element sfEle= rootEle.addElement("session-factory");
List<Element> propertys= sfEle.elements("property");
for (Element ele : propertys) {
this.pros.put(ele.attributeValue("name"), ele.getText());
}
List<Element> mappings= sfEle.elements("mapping");
for (Element m : mappings) {
this.mappings.add(m.attributeValue("class"));
}
}
/**
*创建会话工厂
*
*/
public SessionFactory buildSessionFactory() {
return new SessionFactory(this.pros,this.mappings);
}
}
Hibernate 的主配置文件中有很多配置项,因受限于文章篇幅和本文初设目标,这里只解析 2 类信息:
- 数据库连接信息;
- 实体类路径信息。
基础好的学习者可以查阅 Hibernate 的源代码。
3. 会话工厂
会话工厂的核心作用:建立和数据库系统的连接,创建会话对象。
创建时需要依赖 Configuration 组件解析出来的信息。Hiberante 的会话工厂的源代码实现比较复杂,代码具有良好的结构性,会衍生出很多辅助性组件。
因为只是说明问题,本文中的工厂仅仅用来创建会话对象,不考虑性能、缓存、事务等各方面问题,也不刻意讲究代码的结构性。
简易框架中的会话工厂的代码简单但具有说明力:
public class SessionFactory {
private Map<String, String> pros;
private List<String> mappings;
private ThreadLocal<Session> threadLocal;
public SessionFactory(Map<String, String> pros, List<String> mappings) {
this.pros = pros;
this.mappings = mappings;
this.threadLocal = new ThreadLocal<Session>();
}
public Connection getConn() {
String driverClass = this.pros.get("connection.driver_class");
String url = this.pros.get("connection.url");
String userName = this.pros.get("connection.username");
String password = this.pros.get("connection.password");
try {
Class.forName(driverClass);
return DriverManager.getConnection(url, userName, password);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return null;
}
public Session openSession() {
return new Session(this.getConn(), this.mappings);
}
public Session getCurrentSession() {
Session session = this.threadLocal.get();
if (session == null) {
session = this.openSession();
this.threadLocal.set(session);
}
return session;
}
}
会话工厂类中有 2 个创建 Session 的方法,一个是普通的创建方法,另一个是从线程上下文中检查是否已经存在 Session 对象。
4. 会话对象
Session 对象提供具体的方法用来对数据库中的数据进行相关操作。
public class Session<T> {
private Connection conn;
private List<String> mappings;
public Session(Connection conn, List<String> mappings) {
this.conn=conn;
this.mappings=mappings;
}
public T get(Class<T> clz,Serializable ser) {
//构建 SQL
//得到结果集
//封装数据
return null;
}
public Object load(Class<T> clz,Serializable ser) {
return null;
}
public int save() {
return 0;
}
//其它方法
}
在前面的《Hibernate 自动生成 SQL 语句》和《 Hibernate 自动进行数据封装 》章节中已经讲解了如何通过反射构建 SQL 和数据封装。请查阅相关内容。
因为延迟加载是 Hibernate 中一个比较重要的特性,所以,这里讲解一下 load 方法的实现原理。load()方法调用时并没有直接访问数据库,而是返回给用户一个代理对象,只有当使用者调用代理对象的 getXXX()方法时,才会进入数据库。
做到这一点并不难,使用代理设计模式便可。
代理设计模式中创建代理对象的方案有 2 种:
- 静态代理对象;
- 动态代理对象。
一般都会使用动态方式创建代理对象,动态代理对象的常用创建方案有 2 种:
- 使用 JDK 中提供的 Proxy 类创建;
- 第三方实现,如 cglib 库。
使用 Proxy 创建时有一个前提条件,需要被代理的对象提供接口规范,使用 cglib 就不需要,因是第三方实现,则需要在项目中导入依赖包。
通过代理设计模式的回调机制,load()方法就能实现当需要时再触发对应的数据库操作。
5. 小结
本节课以简易代码的方式描述了 Configuration、SessionFactory、Session 3 个组件的功能实现,以及其相互之间的依赖关系。
但并没有更深入的接轨 Hibennate 的源代码,Hiberanate 源代码中有很多商业性的解决方案,值得大家深究参考。
希望通过本次课程,能让大家更进一步的认识 Hibernate 。