Hibernate 使用不同的现存 Java API,比如 JDBC,Java 事务 API(JTA),以及 Java 命名和目录界面(JNDI)。JDBC 提供了一个基本的抽象级别的通用关系数据库的功能, Hibernate 支持几乎所有带有 JDBC 驱动的数据库。JNDI 和 JTA 允许 Hibernate 与 J2EE 应用程序服务器相集成。下面的部分简要地描述了在 Hibernate 应用程序架构所涉及的每一个类对象,同时也讲解下HIbernate的运行原理和流程。
配置对象(Configuration对象)
配置对象是你在任何 Hibernate 应用程序中创造的第一个 Hibernate 对象,并且经常只在应用程序初始化期间创造。它代表了 Hibernate 所需一个配置或属性文件。配置对象提供了两种基础组件。
- 数据库连接:由 Hibernate 支持的一个或多个配置文件处理。这些文件是 hibernate.properties 和 hibernate.cfg.xml。配置文件位置为src根目录,可以用绝对路径。
Configuration cfg = new Configuration().configure("hibernate.cfg.xml");
hibernate.cfg.xml
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<!-- Generated by MyEclipse Hibernate Tools. -->
<hibernate-configuration>
<session-factory>
<!-- 对应DB Browser里配置的数据库信息 -->
<property name="myeclipse.connection.profile">demo</property>
<!-- 方言:为每一种数据库提供适配器,方便转换 -->
<property name="dialect">
org.hibernate.dialect.Oracle9Dialect
</property>
<!-- 数据库驱动 -->
<property name="connection.driver_class">
oracle.jdbc.driver.OracleDriver
</property>
<!-- 数据库名称 -->
<property name="connection.url">jdbc:oracle:thin:@192.168.70.10:1521:orcl</property>
<!-- 数据库的登陆用户名 -->
<property name="connection.username">masceshi</property>
<!-- 数据库的登陆密码 -->
<property name="connection.password">masceshi</property>
<!-- 打印SQL语句 -->
<property name="show_sql">true</property>
<!-- 打印SQL语句格式化输出 -->
<property name="format_sql">true</property>
<!-- 数据库与实体类对应配置 -->
<!-- 如果不加会报org.hibernate.MappingException: Unknown entity: com.hibernate.demo.entity.Student -->
<mapping class="com.hibernate.demo.entity.Student" />
<!-- 数据库与XML对应配置 -->
<!-- 如果不加会报org.hibernate.MappingException: Unknown entity: com.hibernate.demo.entity.Student -->
<mapping resource="Student.hbm.xml" />
</session-factory>
</hibernate-configuration>
数据库与方言对照表
数据库 | 方言属性 |
---|---|
DB2 | org.hibernate.dialect.DB2Dialect |
HSQLDB | org.hibernate.dialect.HSQLDialect |
HypersonicSQL | org.hibernate.dialect.HSQLDialect |
Informix | org.hibernate.dialect.InformixDialect |
Ingres | org.hibernate.dialect.IngresDialect |
Interbase | org.hibernate.dialect.InterbaseDialect |
Microsoft SQL Server 2000 | org.hibernate.dialect.SQLServerDialect |
Microsoft SQL Server 2005 | org.hibernate.dialect.SQLServer2005Dialect |
Microsoft SQL Server 2008 | org.hibernate.dialect.SQLServer2008Dialect |
MySQL | org.hibernate.dialect.MySQLDialect |
Oracle (any version) | org.hibernate.dialect.OracleDialect |
Oracle 11g | org.hibernate.dialect.Oracle10gDialect |
Oracle 10g | org.hibernate.dialect.Oracle10gDialect |
Oracle 9i | org.hibernate.dialect.Oracle9iDialect |
PostgreSQL | org.hibernate.dialect.PostgreSQLDialect |
Progress | org.hibernate.dialect.ProgressDialect |
SAP DB | org.hibernate.dialect.SAPDBDialect |
Sybase | org.hibernate.dialect.SybaseDialect |
Sybase Anywhere | org.hibernate.dialect.SybaseAnywhereDialec |
- 类映射设置:这个组件创造了 Java 类和数据库表格之间的联系。这些文件是Student.hbm.xml,如果使用的是注解形式的实体类,这些文件可以省略,使用注解或者xml文件在数据库连接配置文件(hibernate.cfg.xml)中有些许不同。
Student.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!-- Mapping file autogenerated by MyEclipse Persistence Tools -->
<hibernate-mapping>
<class name="com.hibernate.demo.entity.Student" table="Student">
<id name="id" type="java.lang.String">
<column name="ID" length="50" />
<generator class="assigned" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" length="50" />
</property>
<property name="age" type="java.lang.String">
<column name="AGE" length="50" />
</property>
</class>
</hibernate-mapping>
Student.java
package com.hibernate.demo.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
// 对实体注释。任何Hibernate映射对象都要有这个注释
@Entity
// 声明此对象映射到数据库的数据表,通过它可以为实体指定表(talbe),目录(Catalog)和schema的名字。该注释不是必须的,如果没有则系统使用默认值(实体的短类名)。
@Table(name = "Student")
public class Student {
private String id;
private String name;
private String age;
// 设置主键
@Id
@Column(name = "id")
//主键生成规则为32位UUID
@GeneratedValue(generator = "system-uuid")
@GenericGenerator(name = "system-uuid", strategy = "uuid")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
// 设置与数据库中的字段关联
@Column(name = "NAME")
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Column(name = "AGE")
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
SessionFactory 对象
配置对象被用于创造一个 SessionFactory 对象,使用提供的配置文件为应用程序依次配置 Hibernate,并允许实例化一个会话对象。SessionFactory 是一个线程安全对象并由应用程序所有的线程所使用。
SessionFactory 是一个重量级对象所以通常它都是在应用程序启动时创造然后留存为以后使用。每个数据库需要一个 SessionFactory 对象使用一个单独的配置文件。所以如果你使用多种数据库那么你要创造多种 SessionFactory 对象。所以SessionFactory 是单例模式。
package com.hibernate.demo.util;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class HibernateUtils {
private static SessionFactory factory;
static {
// 读取hibernate.cfg.xml配置文件
Configuration cfg = new Configuration().configure("hibernate.cfg.xml");
// 获取SessionFactory,相当于连接
factory = cfg.buildSessionFactory();
}
public static SessionFactory getSessionFactory() {
return factory;
}
public static Session getSession() {
return factory.openSession();
}
public static void closeSession(Session session) {
if (session != null) {
if (session.isOpen()) {
session.close();
}
}
}
}
获取sessionFactory方法在各个版本中各不相同。注意:笔者使用的Hibernate版本为myeclipse2017自带的5.1版本,其他低版本经测试在单元测试时无法正确获取sessionFactory,该问题未解决,可能是由于笔者使用的均为myeclipse自带的版本。
Session 对象
一个会话被用于与数据库的物理连接。Session 对象是轻量级的,并被设计为每次实例化都需要与数据库的交互。持久对象通过 Session 对象保存和检索。Session 对象不应该长时间保持开启状态因为它们通常情况下并非线程安全,并且它们应该按照所需创造和销毁。
获取Session实例有两种方法,一种是通过openSession()方法,另一种是通过getCurrentSession()方法。下面我们就通过代码来说明一下两者的区别。
@Test
public void SaveByOpensession() {
Session session = null;
try {
session = HibernateUtils.getSession();
session.beginTransaction();
Student student = new Student();
student.setAge("22");
student.setName("测试");
session.getTransaction().commit();
if (session.isOpen()) {
System.out.println("session未关闭");
} else {
System.out.println("session已关闭");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
}
打印结果为:session未关闭。
@Test
public void SaveByGetCurrentSession() {
Session session = null;
try {
session = HibernateUtils.getCurrentSession();
session.beginTransaction();
Student student = new Student();
student.setAge("22");
student.setName("测试");
session.getTransaction().commit();
if (session.isOpen()) {
System.out.println("session未关闭");
} else {
System.out.println("session已关闭");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
session.getTransaction().rollback();
} finally {
HibernateUtils.closeSession(session);
}
}
打印结果为session已关闭。
综上所述,采用openSession方法获取Session实例时,SessionFactory直接创建一个新的Session对象,并且在使用完成后需要调用close方法进行手动关闭。getCurrentSession方法创建的Session实例会被绑定到当前线程中,它在提交或回滚操作时会自动关闭。
注意:如果需要使用getCurrentSession方法需要在数据库连接配置文件(hibernate.cfg.xml)中增加一项配置。
<!-- 调用getCurrentSession获取session必备设置 -->
<!-- 否则会报错 org.hibernate.HibernateException: No CurrentSessionContext configured! -->
<property name="hibernate.current_session_context_class">thread</property>
* 如果使用的是本地事务(jdbc事务)
<property name="hibernate.current_session_context_class">thread</property>
* 如果使用的是全局事务(jta事务)
<property name="hibernate.current_session_context_class">jta</property>
Transaction 对象
一个事务代表了与数据库工作的一个单元并且大部分 RDBMS 支持事务功能。在 Hibernate 中事务由底层事务管理器和事务(来自 JDBC 或者 JTA)处理。这是一个选择性对象,Hibernate 应用程序可能不选择使用这个接口,而是在自己应用程序代码中管理事务。
//事务开启
session.beginTransaction();
//提交事务
session.getTransaction().commit();
//抛出异常时回滚事务
session.getTransaction().rollback();
Query 对象
Query 对象使用 SQL 或者 Hibernate 查询语言(HQL)字符串在数据库中来检索数据并创造对象。一个查询的实例被用于连结查询参数,限制由查询返回的结果数量,并最终执行查询。
Query对象分为query和sqlquery两种,现将两种对象的区别整理如下:
Session session = sessionFactory.getCurrentSession();
//返回的是当前操作实体的list集合
List list = session.createQuery(hql语句).list();
//返回要转为Long类型的,用的时候可通过count.intValue()得到int类型
Long count = (Long)session.createQuery("SELECT COUNT(*) FROM "+clazz.getSimpleName()).uniqueResult();
//返回的是普通list集合,和操作的实体没有关系
List list = session.createSQLQuery(sql语句).list();
//如果需要通过SQL查询出实体,则需要加上addEntity(xxxx.class),此时返回的是将数据封装到xxxx实体中的list集合
List list = session.createSQLQuery(sql语句).addEntity(xxxx.class).list();
//返回要转为BigInteger 类型,用到的时候可通过count.intValue()得到int类型
BigInteger count = (BigInteger) session().createSQLQuery("select count(*) from "+clazz.getSimpleName().toLowerCase()).uniqueResult();
Criteria 对象
Criteria 对象被用于创造和执行面向规则查询的对象来检索对象。这是HIbernate里面的高级查询功能对象。