Hibernate 架构
Hibernate 架构是分层的,作为数据访问层,你不必知道底层 API 。Hibernate 利用数据库以及配置数据来为应用程序提供持续性服务(以及持续性对象)。
下面是一个非常高水平的 Hibernate 应用程序架构视图。
下面是一个详细的 Hibernate 应用程序体系结构视图以及一些重要的类。
Hibernate 使用不同的现存 Java API,比如 JDBC,Java 事务 API(JTA),以及 Java 命名和目录界面(JNDI)。JDBC 提供了一个基本的抽象级别的通用关系数据库的功能, Hibernate 支持几乎所有带有 JDBC 驱动的数据库。JNDI 和 JTA 允许 Hibernate 与 J2EE 应用程序服务器相集成。
下面的部分简要地描述了在 Hibernate 应用程序架构所涉及的每一个类对象。
配置对象:
配置对象是你在任何 Hibernate 应用程序中创造的第一个 Hibernate 对象,并且经常只在应用程序初始化期间创造。它代表了 Hibernate 所需一个配置或属性文件。配置对象提供了两种基础组件。
- 数据库连接:由 Hibernate 支持的一个或多个配置文件处理。这些文件是 hibernate.properties 和 hibernate.cfg.xml。
- 类映射设置:这个组件创造了 Java 类和数据库表格之间的联系。
SessionFactory 对象
- 配置对象被用于创造一个 SessionFactory 对象,使用提供的配置文件为应用程序依次配置 Hibernate,并允许实例化一个会话对象。SessionFactory 是一个线程安全对象并由应用程序所有的线程所使用。
- SessionFactory 是一个重量级对象所以通常它都是在应用程序启动时创造然后留存为以后使用。每个数据库需要一个 SessionFactory 对象使用一个单独的配置文件。所以如果你使用多种数据库那么你要创造多种 SessionFactory 对象。
Session 对象
- 一个会话被用于与数据库的物理连接。Session 对象是轻量级的,并被设计为每次实例化都需要与数据库的交互。持久对象通过 Session 对象保存和检索。
- Session 对象不应该长时间保持开启状态因为它们通常情况下并非线程安全,并且它们应该按照所需创造和销毁。
Transaction 对象
- 一个事务代表了与数据库工作的一个单元并且大部分 RDBMS 支持事务功能。在 Hibernate 中事务由底层事务管理器和事务(来自 JDBC 或者 JTA)处理。
- 这是一个选择性对象,Hibernate 应用程序可能不选择使用这个接口,而是在自己应用程序代码中管理事务。
Query 对象
- Query 对象使用 SQL 或者 Hibernate 查询语言(HQL)字符串在数据库中来检索数据并创造对象。一个查询的实例被用于连结查询参数,限制由查询返回的结果数量,并最终执行查询。
Criteria 对象
- Criteria 对象被用于创造和执行面向规则查询的对象来检索对象。
Hibernate(自带Jar包除外的)一些常用 Jar 包介绍
Jar 包 / 类库 | 解释 |
---|---|
dom4j | XML 解析 www.dom4j.org/ |
Xalan | XSLT 处理器 http://xml.apache.org/xalan-j/ |
Xerces | The Xerces Java 解析器 http://xml.apache.org/xerces-j/ |
cglib | Java 类生成库http://cglib.sourceforge.net/ |
log4j | 日志控制 http://logging.apache.org/log4j |
Commons | 日志,邮件等 http://jakarta.apache.org/commons |
SLF4J | 简单日志门面 http://www.slf4j.org |
Hibernate 配置
Hibernate 需要事先知道在哪里找到映射信息,这些映射信息定义了 Java 类怎样关联到数据库表。Hibernate 也需要一套相关数据库和其它相关参数的配置设置。所有这些信息通常是作为一个标准的 Java 属性文件提供的,名叫 hibernate.properties。又或者是作为 XML 文件提供的,名叫 hibernate.cfg.xml。
我们将考虑 hibernate.cfg.xml 这个 XML 格式文件,来决定在我的例子里指定需要的 Hibernate 应用属性。这个 XML 文件中大多数的属性是不需要修改的。这个文件保存在应用程序的类路径的根目录里。
Hibernate 属性
下面是一个重要的属性列表,你可能需要表中的属性来在单独的情况下配置数据库。
属性 | 描述 |
---|---|
hibernate.dialect | 这个属性使 Hibernate 应用为被选择的数据库生成适当的 SQL。 |
hibernate.connection.driver_class | JDBC 驱动程序类。 |
hibernate.connection.url | 数据库实例的 JDBC URL。 |
hibernate.connection.username | 数据库用户名。 |
hibernate.connection.password | 数据库密码。 |
hibernate.connection.pool_size | 限制在 Hibernate 应用数据库连接池中连接的数量。 |
hibernate.connection.autocommit | 允许在 JDBC 连接中使用自动提交模式。 |
如果您正在使用 JNDI 和数据库应用程序服务器然后您必须配置以下属性
属性 | 描述 |
---|---|
hibernate.connection.datasource | 在应用程序服务器环境中您正在使用的应用程序 JNDI 名。 |
hibernate.jndi.class | JNDI 的 InitialContext 类。 |
hibernate.jndi. | 在 JNDI的 InitialContext 类中通过任何你想要的 Java 命名和目录接口属性。 |
hibernate.jndi.url | 为 JNDI 提供 URL。 |
hibernate.connection.username | 数据库用户名。 |
hibernate.connection.password | 数据库密码。 |
Hibernate 和 MySQL 数据库
MySQL 数据库是目前可用的开源数据库系统中最受欢迎的数据库之一。我们要创建 hibernate.cfg.xml 配置文件并将其放置在应用程序的 CLASSPATH 的根目录里。你要确保在你的 MySQL 数据库中 testdb 数据库是可用的,而且你要有一个用户 test 可用来访问数据库。
XML 配置文件一定要遵守 Hibernate 3 Configuration DTD,在 http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd. 这个网址中是可以找到的。
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume test is the database name -->
<property name="hibernate.connection.url">
jdbc:mysql://localhost/test
</property>
<property name="hibernate.connection.username">
root
</property>
<property name="hibernate.connection.password">
root123
</property>
<!-- List of XML mapping files -->
<mapping resource="Employee.hbm.xml"/>
</session-factory>
</hibernate-configuration>
上面的配置文件包含与 hibernate-mapping 文件相关的 标签,我们将在下章看看 hibernate mapping 文件到底是什么并且要知道为什么用它,怎样用它。以下是各种重要数据库同源语属性类型的列表:
数据库 | 方言属性 |
---|---|
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 |
对象持久化
什么是对象持久化
所谓持久化(Persistence),即把数据(如内存中的对象)保存到持久化设备,即可永
久保存的存储设备中(如磁盘)。
持久化的主要应用是将内存中的数据存储到关系型的数据库中,当然也可以存储在磁盘
文件中、XML 数据文件中等。
为什么要持久化
(1)内存不能持久;
(2)内存容量有限(内存是用于存放计算数据的);
(3)业务数据共享的需要(需要公共的持久设备);
(4)为了使用大规模的检索(所以要将数据改为适合大规模检索的格式);
(5)数据管理的需要(安全、备份)
怎样实现持久化
-
对象序列化
即实现了 Serializable 接口的类。适合于少量的对象进行暂时的持久化,适合于在网络上传输对象。但不符合企业级应用的需要。因为企业应用中对数据的要求是大量的、长时间保存的、需要进行大规模查询。
-
JDBC
优点:功能完备、从理论上说效率是最高的;可以存储海量的数据并且适合进行大规模检索;
缺点:开发效率和维护效率低;开发难度大,代码量大,占到到总代码量的 1/3,或 1/2;
第一个 Hibernate 应用程序
一个POJO类:
public class Student {
private int sid;
private String sname;
private int age;
private double score;
public Student() {
}
Set/get方法省略
核心配置文件:
<?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">
<hibernate-configuration>
<session-factory>
<!-- DB四要素 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.password">1234</property>
<property name="hibernate.connection.url">jdbc:mysql:///bwie</property>
<property name="hibernate.connection.username">root</property>
<!--方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!--生成sql-->
<property name="hibernate.hbm2ddl.auto">create</property>
<!--展示sql -->
<property name="hibernate.show_sql">true</property>
<!--格式化sql -->
<property name="hibernate.format_sql">true</property>
<!-- sesssion上下文 -->
<property name="hibernate.current_session_context_class">thread</property>
<!--关联 映射文件 -->
<mapping resource="com/bwie/bean/Student.hbm.xml"/>
</session-factory>
</hibernate-configuration>
它们的功能分别为:
-
create:每次加载主配置文件时都会删除上一次的生成的表,然后再生成新表,哪怕两
次表结构没有任何变化。
-
create-drop:每次加载主配置文件时会生成表,但是 sessionFactory 一旦关闭,表就自
动删除。
-
update:当表字段增加时,会添加字段;当表字段减少时,不会减少字段。若表结构没
变化,但数据变化时,会修改数据
映射配置文件代码:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated 2018-11-14 9:01:34 by Hibernate Tools 3.5.0.Final -->
<hibernate-mapping>
<class name="com.bwie.bean.Student" table="t_student">
<id name="sid">
<column name="sid"/>
<generator class="native" />
</id>
<property name="sname" type="java.lang.String">
<column name="SNAME" />
</property>
<property name="age" type="int">
<column name="AGE" />
</property>
<property name="score" type="double">
<column name="SCORE" />
</property>
</class>
</hibernate-mapping>
- <class/> 标签该标签用于设置 PO 类与数据表间的映射关系。
- name 属性:指定持久化类。若<hibernate-mapping/>标签设置了 package 属性,那么,此处的 name 属性只需是类名即可;否则,需要是含包名的完整类名。
- table 属性:指定与持久化类对应的数据表的名称。若不指定,Hibernate 将默认为表名与类名相同
- catalog 属性:指定数据库。默认为主配置文件中指定的 DB。
- <id/> 与<property/> 标签它们都是<class/>标签的子标签。用于指定 PO 类的 id 属性与表的主健间的映射关系。它们的属性基本相同。常用的有, name 属性:指定持久化类的属性名
- column 属性:指定数据表中与 name 属性对应的字段名。若不指定,默认为与 name 属性同名。
- length 属性:指定属性所映射字段的长度,单位字节。
- not-null 属性:为指定字段添加非空约束。
- unique 属性:为指定字段添加唯一性约束。
- type 属性:指定属性所映射的字段的类型。若省略 Hibernate 会自动从持久化类中检测到类型。这里的类型取值支持两类:Java 类型与 Hibernate 类型。
- Java 类型指的是 Java 代码中的类型。若是基本数据类型,如 int、double 等,直接写即可。但若是对象类型,则需要写上全类名,如 java.lang.String。
测试添加一条数据:
public class TestMain {
public static void main(String[] args) {
// 读取核心配置文件
Configuration cfg = new Configuration().configure();
// 构建核心SessionFactory
SessionFactory sessionFactory = cfg.buildSessionFactory();
// 获取session
Session session = sessionFactory.openSession();
// 开启事务
Transaction transaction = session.beginTransaction();
// 添加对象
Student s1 = new Student();
s1.setSname("Admin");
s1.setScore(98.0);
s1.setAge(18);
s1.setSid(10);
session.save(s1);
// 提交事务
transaction.commit();
}
}
单元测试Junit测试效果:
public class MyTest {
@Test
public void test() {
//读取核心配置文件
Configuration cfg=new Configuration().configure();
//构建核心SessionFactory
SessionFactory sessionFactory = cfg.buildSessionFactory();
//获取session
Session session = sessionFactory.openSession();
//开启事务
session.beginTransaction();
//添加对象
Student s1=new Student();
s1.setSname("Peter");
s1.setScore(98.0);
s1.setAge(18);
session.save(s1);
//提交事务
session.getTransaction().commit();
}
}
构建查询列表语句:
@Test
public void test01() {
//读取核心配置文件
Configuration cfg=new Configuration().configure();
//构建核心SessionFactory
SessionFactory sessionFactory = cfg.buildSessionFactory();
//获取session
Session session = sessionFactory.openSession();
//构建hql语句
String hql="from Student";
//查询
List<Student> list = session.createQuery(hql).list();
//展示一下情况
for (Student student : list) {
System.out.println(student);
}
}
查询的效果如下:
Hibernate 常用的内置主键生成策略:
主键生成策略,即主键的生成方式。不同的生成策略,其生成主键的方法与值不同。注意,以下在测试时,需要先将上一次测试时生成的表删除,这样才能测试出正确结果。
1)increment策略
该策略是 Hibernate 自己在维护主键的值。当准备在数据库表中插入一条新记录时,首先从数据库表中获取当前主键字段的最大值,然后在最大值基础上加 1,作为新插入记录的
主键值,这就是 increment 生成策略。用其生成的主键字段所对应的属性类型可以是 long、short、int 及其封装类的类型。这种生成策略只有在没有其他进程向同一张表中插入数据时才能使用。在高并发下或集群下不能使用。其测试情况是,后台会产生对当前最大 id 的查询语句。
2)identity 生成策略;
该策略使用数据库自身的自增长来维护主键值。如 mysql 使用 auto_increment 来维护。
用其生成的主键字段所对应的属性类型可以是 long、short、int 及其封装类的类型。
该策略在生成主键值时会出现以下情况:对于插入操作,即使最后的执行是回滚,DB中记录主键值的变量也会增一。因为该生成策略在发生回滚之前已经调用过DB的主键自增,所以无论是否提交,对于 DB 来说已经执行。其测试情况是,后台不会产生任何有关 id 生成值的语句。因为其使用的是 MySql 自身的 auto_increment 来为 id 赋值
3)sequence 生成策略
在 Oracle、DB2 和 PostgreSQL 等数据库中创建一个序列(sequence),然后 Hibernate 通过该序列为当前记录获取主键值,从而为实体对象赋予主键字段映射属性值。此即 sequence生成策略,用其生成的主键字段映射属性值的类型可以是 long、short、int 及其封装类的类型。对于 MySql 数据库,原本是不支持序列的。但稍作修改后,MySql 也支持该生成策略。
其测试情况是:在第一次执行时,后台会输出以下查询语句,并报错。该语句是,从 hibernae_sequence 序列表中查询字段值 next_val,该值将作为要插入数据的主键值。当然,该查询语句中的 for update 表示,对该表的操作使用了乐观锁。打开数据库,发现多生成了一张表 hibernate_sequence,打开该表,发现其值为空,无法进行自增运算。这就是报错的原因:没有初始值。手工为其赋初值 1 即可再运行了
4)native 生成策略
由 Hibernate 根据所使用的数据库支持能力从 identity、sequence 生成策略中选择一种。
使用这种标识符属性生成策略可以根据不同的数据库采用不同的生成策略,如 Oracle中使用 sequence,在MySQL 中使用 identity 便于 Hibernate 应用在不同的数据库之间移植。试情况是,没有生成任何与 id 生成值有关的 SQL 语句。说明使用的是 identity 生成策略
5)uuid 生成策略
uuid 生成策略采用 UUID( Universally Unique Identifier,通用唯一识别码) 算法来生成一个35字符串类型的主键值,该值使用 IP 地址、JVM 的启动时间(精确到 1/4 秒)、当前系统时间和一个计数器值(在当前的 JVM 中唯一)经过计算产生,可以用于分布式的 Hibernate 应用中。产生的标识符属性是一个 32 位长度的字符串。使用这种生成策略,要求属性的类型必须为 String 类型。这种标识符属性生成策略生成的数值可以保证多个数据库之间的唯一性,并且由于其生成与具体的数据库没有关系,所以其移植性较强。但由于该值是 32 位长的字符串,所以占用的数据库空间较大,并且检索速度较慢。不过,实际开发中使用这种生成策略较多。除了使用 Hibernate 外,在 JDBC 中也可以使用 uuid 生成主键。因为 UUID 是 java.util 包中的一个独立的类。可以打开项目的 JRE System Library 库中的 rt.jar,在其中找到 java.util包,即可看到 UUID 这个类。
6)assigned 生成策略
该生成策略的主键值来自于程序员的手工设置,即通过 setId()方法设置。属性类型可以是整型,也可以是 String,但一般为 String。
此生成策略,主要应用于业务相关主键。例如学号、身份证号做主键。