“Hibernate笔记 1:入门(开发步骤)”的版本间差异

来自Wikioe
跳到导航 跳到搜索
 
(未显示同一用户的10个中间版本)
第7行: 第7行:
步骤:
步骤:
# 导入相关 JAR 包;
# 导入相关 JAR 包;
## Hibernate 包;
## Hibernate 依赖包:(位于 Hibernate 源文件中:Hibernate/lib/required/)
## 日志包;(hibernate 本身没有日志输出相关的 jar 包)
##: [[File:Hibernate:JAR包:Hibernate依赖包.png|400px]]
## mysql 驱动包;
## 日志相关包:(hibernate 本身没有日志输出相关的 jar 包)
# 创建实体类;
##: [[File:Hibernate:JAR包:日志相关包.png|400px]]
#* 使用 hibernate 时候,不需要自己手动创建表,hibernate 帮把表创建。
## mysql 驱动包:
##: [[File:Hibernate:JAR包:数据库驱动包.png|400px]]
# '''创建持久化类'''(实体类);
#* 见:<big>'''[[Hibernate笔记_4:核心知识#持久化类的编写规则]]'''</big>
# 创建数据表;
#* 如果配置“hibernate.hbm2ddl.auto”为“update”,则不需要自己手动创建表。
# '''配置映射关系''';(如:“User.hbm.xml”)
# '''配置映射关系''';(如:“User.hbm.xml”)
#: 示例:
#: 示例:
第99行: 第104行:
session.close();
session.close();
sessionFactory.close();
sessionFactory.close();
</syntaxhighlight>
== 配置文件详解 ==
配置文件是 Hibernate 中相当重要的部分,对于 Hibernate、数据库、orm 等内容的都依赖于配置文件。
1、配置文件可以是: '''.xml文件'''、'''.properties文件'''(属性文件);
2、配置文件中均需要引入 '''dtd''' 约束;
    - (xml 的约束格式包括:dtd、schema,而 Hibernate 的配置文件中均使用 dtd)
=== “映射关系”配置文件 ===
“映射关系”,即:实体类和数据库表一一对应的关系。
关于“''映射关系''配置文件”:
1、''文件名称'' 和 ''位置'' 没有固定要求;
    - 建议:在实体类所在包里面创建,名称为“<实体类名称>.hbm.xml”;
“xxx.hbm.xml”格式:
: <syntaxhighlight lang="xml" highlight="">
<?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">
<hibernate-mapping>
<!-- class 标签:建立“类”和“表”的映射
* name 属性 :类中的全路径
* table 属性 :表名(如果类名和表名一致,则该属性可以省略)
* catalog 属性:数据库名称,可以省略
-->
<class name="XXX.XXX.XXX" table="ZZZ">
<!-- id 标签:建立“类中的唯一性属性”与“表中的主键”的映射
* name 属性 :类中的属性名
* column 属性:表中的字段名(如果“类的属性名”和“表中的字段名”一致,则可省略)
* length 属性:字段的长度
* type 属性:类型。写Java数据类型,Hibernate数据类型(默认),SQL类型
-->
<id name="xxx" column="zzz">
<!-- 主键生成策略 -->
<generator class="???"/>
</id>
<!-- id 标签: 建立“类中的普通属性”与“表中的字段”的映射
* name 属性 :类中的属性名
* column 属性:表中的字段名(如果“类的属性名”和“表中的字段名”一致,则可省略)
* length 属性:字段的长度
* type 属性:类型。写Java数据类型,Hibernate数据类型(默认),SQL类型
-->
<property name="xxx" column="zzz" type="???"/>
<property name="xxx" column="zzz" type="???"/>
<property name="xxx" column="zzz"/>
<property name="xxx" column="zzz"/>
</class>
</hibernate-mapping>
</syntaxhighlight>
# <code>'''<class>'''</code> 用于配置“实体类”与“表”的对应;
#* “name”属性值为“实体类的全路径”;
# <code>'''<id>'''</code> 用于配置“(具有唯一性的)实体类的属性”与“(具有唯一性的)表的字段”的对应;
#* “column”属性可以省略(与“name”一致);
#* <code>'''<generator>'''</code> 用于配置主键的“增长方式”;
#*# '''uuid''';
#*# '''native'''(自增);
# <code>'''<property>'''</code> 用于配置“实体类的属性”与“表的字段”的对应;
#* “column”属性可以省略(与“name”一致);
#* “type”属性用于设置生成表字段的类型(自动对应类型);
示例:“Customer.hbm.xml”
: <syntaxhighlight lang="xml" highlight="">
<?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">
<hibernate-mapping>
<class name="cn.itcast.domain.Customer" table="customer">
<id name="cust_id" column="id">
<generator class="native"/>
</id>
<property name="cust_name" column="name"/>
<property name="cust_source" column="source"/>
<property name="cust_industry" column="industry"/>
<property name= "cust_level" column= "level"/>
<property name="cust_phone" column="phone"/>
<property name="cust_mobile" column="mobile"/>
</class>
</hibernate-mapping>
</syntaxhighlight>
=== Hibernate 核心配置文件 ===
Hibernate 核心配置文件,其名称与位置均是固定的。
关于“Hibernate 核心配置文件”:
1、'''文件名称和位置固定''':
    - 位置:必须位于 src 下。
    - 名称:必须为“<span style="color: blue; font-size: 120%">'''hibernate.cfg.xml'''</span>”(或“<span style="color: blue; font-size: 120%">'''hibernate.properties'''</span>”)。
2、hibernate 操作过程中,只会加载核心配置文件,其他配置文件不会加载。
    - “映射关系配置文件”需要配置到“核心配置文件”中,才会被加载。
“hibernate.cfg.xml”格式:
: <syntaxhighlight lang="xml" highlight="">
<?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>
<!-- 第一部分 数据库信息 -->
...
<!-- 第二部分 hibernate信息 -->
...
<!-- 第三部分 映射文件 -->
<mapping resource="..."/>
<session-factory>
</hibernate-configuration>
</syntaxhighlight>
# 配置数据库信息:
#* 使用不同的数据库驱动(不同版本驱动)时,配置信息可能有差别。【可以从类似“mysql-connector”的包里去找相应内容】
#: 示例:
## 使用“mysql-connector-java-5.0.4-bin.jar”时:
##: <syntaxhighlight lang="xml" highlight="">
<property name="hibernate.connection.driver.class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///hibernate_day01</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">admin</property>
</syntaxhighlight>
## 使用“mysql-connector-java-8.0.11.jar”时:
##: <syntaxhighlight lang="xml" highlight="">
<property name="hibernate.connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///hibernate_day02?useSSL=false&amp;serverTimezone=UTC</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">admin</property>
</syntaxhighlight>
# 配置 Hibernate 信息:
#: <syntaxhighlight lang="xml" highlight="">
<!-- 控制台输出 sql 语句 -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化输出的 sql 语句 -->
<property name="hibernate.format_sql">true</property>
<!-- hibernate 的 DDL 规则(update:无则创建,有则更新) -->
<property name="hibernate.hbm2ddl.auto">update</property>
<!-- 配置数据库方言(让 hibernate 框架识别不同数据库特有的方言) -->
<property name="hibernate.dialect">org.hibernate.dialect.MySqlDialect</property>
</syntaxhighlight>
#* hibernate.hbm2ddl.auto 的取值:(DDL,即“数据库定义语言”,如:create drop alter 等)
#** <span style="color: blue">none</span>:不用 Hibernate 自动生成表;
#** <span style="color: blue">create</span>:每次都会创建新的表;【用于测试】
#** <span style="color: blue">create-drop</span>:每次都会创建新的表,执行程序结束后删除表;【用于测试】
#** <span style="color: blue">'''update'''</span>:如果数据库中有表则直接使用,否则创建新表,且可以更新表结构;
#** <span style="color: blue">validate</span>:只会使用原有的表,对映射关系进行校验;
# 配置“映射关系配置文件”:
#* 需要定位到(需要加载的)“映射关系配置文件”所在包的位置。
#: 示例:
#: <syntaxhighlight lang="xml" highlight="">
<mapping resource = "cn/itcast/entity/User.hbm.xml"/>
</syntaxhighlight>
==== 常用配置属性 ====
:{| class="wikitable"
! 名称 !! 用途
|-
| hibernate.dialect || 操作数据库方言
|-
| hibernate.connection.driver_class || 连接数据库驱动程序
|-
| hibernate.connection.url || 连接数据库 URL
|-
| hibernate.connection.username || 数据库用户名
|-
| hibernate.connection.password || 数据库密码
|-
| hibernate.show_sql || 在控制台上输出 SQL 语句
|-
| hibernate.format_sql || 格式化控制台输出的 SQL 语句
|-
| hibernate.hbm2ddl.auto || 当 SessionFactory 创建时是否根据映射文件自动验证表结构或自动创建、自动更新数据库表结构。
* 该参数的取值为: none、 validate、 update、 create 和 create-drop。
|-
| hibernate.connection.autocommit || 事务是否自动提交
|}
== 核心 API ==
=== Configuration 接口 ===
  Configuration 接口的作用是:对 Hibernate 进行配置、并启动 Hibernate 和连接数据库系统。
Configuration 仅仅是作为一个初始化时的对象,一个 Configeration 实例代表 Hibernate 所有 Java 类到 Sql 数据库映射的集合。
在 Hibernate 的启动过程中,Configuration 类的实例首先定位缺省 XML 配置文件(hibernate.cfg.xml),并读取关的配置项目,然后创建出一个 SessionFactory 对象。
使用:
# 根据默认的 XML 配置文件:
#* 默认加载:src/hibernate.cfg.xml。
#: <syntaxhighlight lang="Java" highlight="">
Configuration cfg = new Configuration();
cfg.configure();
SessionFactory sessionFactory = cfg.buildSessionFactory();
</syntaxhighlight>
# 根据自定义的 XML 配置文件:(少用)
#: <syntaxhighlight lang="Java" highlight="">
Configuration cfg = new Configuration();
cfg.configure("cn/config/hibernate2.cfg.xml");
</syntaxhighlight>
=== SessionFactory 接口 ===
SessionFactory 的主要作用是:产生和管理 Session。
通常情况下每一个应用只需要一个 SessionFactory,当需要操作多个数据库时,可以为每个数据库指定一个 SessionFactory。
即:'''“一个 SessionFactory 实例”对应“一个数据存储源”'''。——【'''重量级,整个应用中共享'''】
根据 Configuration 对象创建一个 SessionFactory 对象:
# 在 Hibernate 4 之前:
#: <syntaxhighlight lang="Java" highlight="">
Configuration cfg = new Configuration();
cfg.configure();
SessionFactory sessionFactory = cfg.buildSessionFactory();
</syntaxhighlight>
# 在 Hibernate 4 之后:【???】
#: <syntaxhighlight lang="Java" highlight="">
Configuration cfg = new Configuration().configure();
ServiceRegistry sr = new ServiceRegistryBuilder().applySettings(cfg.getProperties()).buildServiceRegistry();
SessionFactory sessionFactory = cfg.buildSessionFactory(sr);
</syntaxhighlight>
SessionFactory 可以通过两种方法产生 Session。
生成 Sessin:
# '''openSession()''':每次打开都是新的 Session,并且需要人为的调用 '''close''' 方法关闭 Session。
#: <syntaxhighlight lang="Java" highlight="">
Session session = sessionFactory.openSession();
</syntaxhighlight>
# '''getCurrentSession()''':从当前上下文中获取 Session 并且会绑定到当前线程:
#: 第一次调用会自动创建一个 Session 实例,如果未手动关闭多次获取的是同一个 Session,事物提交或者回滚时会'''自动关闭''' Session。
#: <syntaxhighlight lang="Java" highlight="">
Session session = sessionFactory.getCurrentSession();
</syntaxhighlight>
#* 需要在配置文件(hibernate.cfg.xml)中添加如下配置:【否则会报错“org.hibernate.HinerbateException: No CurrentSessionContext configured!”】
#*# 如果使用的是'''本地事务'''(JDBC 事务):
#*#: <syntaxhighlight lang="xml" highlight="">
<property name="current_session_context_class">thread</property>
</syntaxhighlight>
#*# 如果使用的是'''全局事务'''(JTA 事务):
#*#: <syntaxhighlight lang="xml" highlight="">
<property name="current_session_context_class">jta</property>
</syntaxhighlight>
'''全局事务''':资源管理器管理和协调的事务,可以跨越多个数据库和进程。资源管理器一般使用 XA 二阶段提交协议与“企业信息系统”(EIS)或数据库进行交互。   
'''本地事务''':在单个 EIS 或数据库的本地并且限制在单个进程内的事务。本地事务不涉及多个数据来源。
=== Session ===
Session 用于管理一个数据库的任务单元(增、删、改、查),它是 Java 应用和 Hibernate 之间主要运行接口,是抽象持久性服务概念的主要 API。
【类似于 JDBC 中的连接(“connection”)】
Session 对象是'''非线程安全'''的,因此最好是一个线程只创建一个Session对象(将它设计为局部对象)。
    ——【'''轻量级,不共享''':“一请求”>>“一线程”>>“一session”>>“一事务”】
==== 生命周期 ====
Session 的生命周期是以一个逻辑事物的开始和结束为边界,Session 的主要功能是提供创建、读取和删除映射的实体类的操作。
实体可能存在于三种状态:
# '''瞬时状态'''(transient):实体对象在内存是自由存在的(即为普通的 Java 对象),即:“该实体从未与任何持久化上下文关联过,它没有持久化标识”。
#* 与“Session对象”:无关联;
#* 与“数据库记录”:无关联;
# '''持久状态'''(persistent):实体对象处于 Hibernate 框架所管理的状态,即:“该实体在数据库中有对应的记录,并拥有一个持久化标识”。
#* 与“Session对象”:有关联;
#* 与“数据库记录”:有关联;
#* Hibernate 会:依据 persistent 状态的实体对象的属性变化,而改变数据库中相对应的记录;
#**在一个 Session 中,对持久对象的改变不会马上对数据库进行变更,而是发生在 Transaction 终止,执行 commit 之后。
# '''游离状态'''(detached):与持久状态的对象关联的 Session 被关闭后,该对象就变成游离状态,即:“游离态是由持久态实体对象转变(关联的 Session 被关闭)而来的”。
#* 与“Session对象”:无关联;
#* 与“数据库记录”:无直接关联;(可能有相关数据,但未关联)
#* 此状态下对游离对象的引用依然有效,也可以继续被修改;
#** 但,对其所做的任何修改将不会影响到到数据库中的数据。
* 三种状态可以互相转换:
*: [[File:Hibernate 生命周期图.png|600px]]
==== Session 缓存 ====
Session 中有一个缓存,被称为“Hibernate 的第一级缓存”。
Session 缓存,用于存放被当前工作单元加载的对象,这块缓存中有一个 '''map'''。
1、在执行 '''save()''' 方法后,会生成一个 '''id'''(<span style="color: blue">'''OID'''</span>),这个 id 会保存在 map 的 '''key''' 中;
2、与之对应的 '''value''' 值就是该是该对象的引用,执行 '''commit()''' 方法后,数据库就有对应这条数据了,此时该实体处于“持久状态”;
3、当执行完 Session.'''close()'''后,处于“游离状态”。
区分实体的三种状态:
:{| class="wikitable"
! 实体状态 !! OID !! Session缓存 !! 数据库
|-
| 瞬时状态 || null || 不存在 || 无记录
|-
| 持久状态 || 非null || 存在 || 有记录
|-
| 托管状态 || 非null || 不存在 || (可能)有记录
|}
<span style="color: blue; font-size: 150%;">'''OID'''</span>(object identifier),即:“对象标识符”,是 hibernate 用于区分两个对象是否是同一个对象的标识的方法。
OID 在对象持久化之前是 '''null''',持久化的时候 hibernate 或者我们手动指定一个 id('''被插入到数据库当做主键,在 session中 当做索引'''),所以,需要保证 OID 与主键的一致性,比如:类型、长度等。
OID 类型:
# '''自然主键''':带有业务含义的主键(比如:学号,工作编号)。
#* 需要手动的指定主键(合成或者拼接);如果不指定主键就提交缓存进行更新,则会报错!
#* 主键生成策略:
#*# <span style="color: blue">'''assinged'''</span>:Hibernate不维护主键,开发人员需要手动设置主键;
# '''代理主键''':通过编码自动生成的,无业务含义的主键。
#* 主键生成策略:
#*# <span style="color: blue">'''increment'''</span>:(由 Hibernate 提供的)'''自动增长'''。
#*#* 适用于:short、int、long 类型的主键;
#*#* 不适用:与并发访问数据库;
#*# <span style="color: blue">'''identity'''</span>:(由数据库提供的)'''自动增长'''。
#*#* 适用于:short、int、long 类型的主键;
#*#* 不适用:不支持自动增长的数据库(如:Oracle);
#*# <span style="color: blue">'''sequence'''</span>:(由数据库提供的)'''序列'''。
#*#* 适用于:short、int、long 类型的主键;
#*#* 适用于:支持序列的方式的数据库(如:Oracle、db2、sap、db、postgresql);
#*# <span style="color: blue">'''native'''</span>:本地策略,根据底层的数据库不同,'''自动选择'''使用 identity 还是 sequence。
#*#* 适用于:short、int、long 类型的主键;
#*# <span style="color: blue">'''uuid'''</span>:随机字符串。
#*#* 适用于:字符串类型的主键。
Session 缓存是由一系列的 Java 集合构成的:
    当一个对象被加入到 Session 缓存中,这个对象的引用就加入到了 Java 的集合中,即使以后应用程序中的引用变量不再引用该对象,只要 Session 缓存不被清空,这个对象一直处于生命周期中。
作用:
# 减少访问数据库的频率。
# 保证缓存中的对象与数据库中的相关记录保持同步。
Session 清理缓存的时机:
# 提交前:当调用 Transaction 的 commit() 方法时,commit() 方法先清理缓存(前提是:FlushMode.COMMIT/AUTO),然后再向数据库提交事务。
# 查询前:当应用程序调用 Session 的 find() 或者 iterate() 时,如果缓存中的持久化对象的属性发生了变化,就会先清理缓存,以保证查询结果能反映持久化对象的最新状态。
# flush:当应用程序显示调用 Session 的 flush() 方法的时候。
: 如下图:
:{| class="wikitable"
! FlashMode !! Session.find()/iterate() !! Session.commit() !! Session.flush()
|-
| FlashMode.AUTO || ✔ || ✔ || ✔
|-
| FlashMode.COMMIT || ✘ || ✔ || ✔
|-
| FlashMode.NEVER || ✘ || ✘ || ✔
|}
:* 清理(✔),不清理(✘)
==== 常用方法 ====
Session 的所有方法,见博客:[https://www.cnblogs.com/lukun/archive/2011/03/31/1968937.html Hibernate session的方法全集]
常见方法:
# '''save()''':向数据库中'''保存'''(持久化)实体。
#: 示例:
#: <syntaxhighlight lang="Java" highlight="">
session.save(Object);
</syntaxhighlight>
# '''delete()''':从数据库中'''删除'''实体。
#* 对象删除后,其状态为“Transistent”;
#* 对象需要有 ID;
#: 示例:
#: <syntaxhighlight lang="Java" highlight="">
session.delete(Object);
</syntaxhighlight>
# '''load()''':从数据库中'''加载'''实体。
#: <syntaxhighlight lang="Java" highlight="">
Session.load(Class arg0, Serializable arg1) throws HibernateException
</syntaxhighlight>
## 参数:
##*“arg0”:需要加载对象的类,例如:User.class;
##*“arg1”:查询条件(实现了序列化接口的对象),例如:"4028818a245fdd0301245fdd06380001" 字符串已经实现了序列化接口;
##** 如果是数值类类型,则 hibernate 会自动使用包装类。
## 返回:
##* 此方法返回类型为 Object,但返回的是“'''代理对象'''”(并非实际可用的对象,因为“延迟加载”)。【第一次使用对象时,session 不能关闭】
##* 如果数据库中没有相应的记录,则会抛出异常“找不到对象”('''org.hibernate.ObjectNotFoundException''')。【记录不存在时抛出异常】
## 执行:因为 load 方法实现了“'''延迟加载'''”(懒加载,“lazy load”),所以,只有在使用返回的对象时,它才发出查询 SQL 语句,加载对象。
##* 其延迟加载实现原理是代理方式。【???】
#: 示例:
#: <syntaxhighlight lang="Java" highlight="">
try {
session = sf.openSession();
session.beginTransaction();
User user = (User)session.load(User.class,1);
// 在使用对象时,才发出查询SQL语句,加载对象
System.out.println("user.name=" + user.getName());
// 此时 user 为 persistent 状态
user.setName("eijux");
session.getTransaction().commit();
} catch (HibernateException e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally{
if (session != null){
if (session.isOpen())
session.close();
}
</syntaxhighlight>
# '''get()''':从数据库中'''加载'''实体。
#: <syntaxhighlight lang="Java" highlight="">
Session.get(Class arg0, Serializable arg1)
</syntaxhighlight>
## 参数:
##*“arg0”:需要加载对象的类,例如:User.class;
##*“arg1”:查询条件(实现了序列化接口的对象),例如:"4028818a245fdd0301245fdd06380001" 字符串已经实现了序列化接口;
##** 如果是数值类类型,则 hibernate 会自动使用包装类。
## 返回:
##* 此方法返回类型为 Object,然后我们再强行转换为需要加载的对象就可以了。【返回值需要强制类型转换???】
##* 如果数据库中没有相应的记录,则返回 '''null'''。【不会抛出异常】
## 执行:该方法会'''立即发出查询''' SQL 语句,加载对象。
#: 示例:
#: <syntaxhighlight lang="Java" highlight="">
try {
session = sf.openSession();
session.beginTransaction();
// 方法返回类型为Object,需要再强行转换为需要加载的对象
// 方法会立即发出查询语句
User user = (User)session.get(User.class, 1);
// 数据加载完后的状态为 persistent 状态。数据将与数据库同步
System.out.println("user.name=" + user.getName());
// 此时 user 为 persistent 状态
user.setName("eijux");
 
session.getTransaction().commit();
} catch (HibernateException e) {
e.printStackTrace();
session.getTransaction().rollback();
} finally{
if (session != null){
if (session.isOpen()){
session.close();
}
}
</syntaxhighlight>
# '''update()''':向数据库中'''更新'''实体。
#* 用于更新“'''detached'''”对象,更新完成后转为为“persistent”状态;【默认更新全部字段】
#* 更新“transient”对象(没有 ID)会报错;
#** 更新自己设定 ID 的“transient”对象则可以。
#* “persistent”状态的对象,只要设定字段不同的值,在 session 提交时,会自动更新(默认更新全部字段)。
#* 如果需要更新部分字段,有两种方法:
#*# '''update / updatable 属性''':用于设置'''参与更新的字段'''。【适合 xml、注解 方式】【较少用,不灵活】
#*## xml 方式:(映射关系配置文件中)设置“<property>”标签的“update”属性,以设置在更新时是否参与更新。
#*##* false:不参与更新;true(默认):参与更新。
#*##: 示例:
#*##: <syntaxhighlight lang="xml" highlight="">
<property name="name" update="false"/>
</syntaxhighlight>
#*## annotation 方式:(映射关系类中)设定“@Column”的“updatable”属性值,以设置在更新时是否参与更新。
#*##: 示例:
#*##: <syntaxhighlight lang="Java" highlight="">
@Column(updatable=false)
public String getTitle() {return title;}
</syntaxhighlight>
#*# '''dynamic-update 属性''':用于设置'''仅更新变更的字段'''。【只适合 xml 方式,JAP1.0 annotation 没有对应属性】
#*#: 在实体类的映射文件(映射关系配置文件)中的“<class>”标签中,设置“dynamic-update”属性:true,表示修改了哪个字段就更新哪个字段,其它字段不更新。
#*#* 但要求是同一个 session(不能跨 session),如果跨了 session 同样会更新所有的字段内容。
#*#: 示例:
#*#: <syntaxhighlight lang="xml" highlight="">
...
<class name="com.bjsxt.Student" dynamic-update="true">
...
</syntaxhighlight>
#*#: <syntaxhighlight lang="Java" highlight="">
@Test
public void testUpdate() {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Student s = (Student)session.get(Student.class, 1);
s.setName("zhangsan");
// 提交时,会只更新 name 字段,因为此时的 s 为 persistent 状态
session.getTransaction().commit();
s.setName("zhangsi");
Session session2 = sessionFactory.getCurrentSession();
session2.beginTransaction();
// 更新时,会更新所有的字段,因为此时的 s 不是 persistent 状态
session2.update(s);
session2.getTransaction().commit();
}
</syntaxhighlight>
#*#* 如果需要跨 session 实现更新修改的部分字段,需要使用 session.merget() 方法,合并字段内容:
#*#*: 示例:
#*#*: <syntaxhighlight lang="Java" highlight="">
@Test
public void testUpdate() {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Student s = (Student)session.get(Student.class, 1);
s.setName("zhangsan");
// 提交时,会只更新 name 字段,因为此时的 s 为 persistent 状态
session.getTransaction().commit();
s.setName("zhangsi");
Session session2 = sessionFactory.getCurrentSession();
session2.beginTransaction();
// 使用s ession.merget() 方法,合并字段内容
session2.merge(s);
// 提交时,会只更新 name 字段
session2.getTransaction().commit()
}
</syntaxhighlight>
#*#*: 如上,虽然可以实现部分字段更新,但这样会多出一条 select 语句,因为在字段数据合并时,需要比较字段内容是否已变化,就需要从数据库中取出这条记录进行比较。
#*#*: 所以,'''跨 Session 更新部分字段''',建议使用 '''HQL'''(EJBQL)面向对象的查询语言:
#*#*: 示例:
#*#*: <syntaxhighlight lang="Java" highlight="">
@Test
public void testUpdate7() {
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Query q = session.createQuery("update Student s set s.name='z5' where s.id = 1");
q.executeUpdate();
session.getTransaction().commit();
}
</syntaxhighlight>
# '''saveOrUpdate()''':向数据库中'''保存/更新'''实体。
#* 在执行的时候 hibernate 会检查,如果对象在数据库中已经有对应的记录(存在同样主键的记录),则会更新(update),否则会添加数据(save)。
# '''clear()''':'''清除 session 缓存'''。
#* 无论是 load 还是 get,都会首先查找缓存('''一级缓存''',也叫“'''session 缓存'''”),如果没有,才会去数据库查找,调用 clear() 方法可以强制清除 session 缓存。
#: 示例:
#: <syntaxhighlight lang="Java" highlight="">
// 使用 getCurrentSession,所以不需要手动关闭 Session
Session session = sessionFactory.getCurrentSession();
session.beginTransaction();
Teacher t = (Teacher)session.load(Teacher.class, 1);
System.out.println(t.getName());
session.clear();
Teacher t2 = (Teacher)session.load(Teacher.class, 1);
System.out.println(t2.getName());
session.getTransaction().commit();
</syntaxhighlight>
#: 如上,两个 load 方法均会发送查询到数据库。如果注释掉 clear,则第二次 load 不发送查询到数据库,而是直接使用第一次 load 的缓存(两次 load 的是同一个记录)。
# '''flush()''':'''强制同步数据(从内存到数据库)'''。
#* 在默认的情况下 session.commit() 之前时,其实执行了一个 flush 命令。
#* 只有部分 SQL 语句(update、insert、delete),才需要 flush。
#** 对于“session.save()”方法:
#**: 如果主键生成策略是“由数据库生成”(native),则 session.save 的同时就发出 SQL 语句(insert)。否则(uuid),session.save 不会发出 SQL 语句。【???】
#* 可以通过 <syntaxhighlight lang="Java" inline>session.setFlushMode(FlushMode);</syntaxhighlight> 来设置“'''刷新模式'''”:
#*: FlushMode 的枚举值:
#*#“FlushMode.ALWAYS”:任务一条SQL语句,都会 flush 一次;
#*#“FlushMode.AUTO”:自动 flush(默认);
#*#“FlushMode.COMMIT”:只有在 commit 时才 flush;
#*#“FlushMode.MANUAL”:手动 flush;
#*#“FlushMode.NEVER”:永远不 flush。(此选项在性能优化时可能用,比如 session 取数据为只读时用,这样就不需要与数据库同步了)
#** 设置 flush 模式(FlushMode),应该在 session 开启事务之前。
# '''evict()''':'''从 session 缓存(EntityEntries属性)中逐出该对象'''。
#* 与 commit 同时使用,会抛出异常。【???】
#*: 示例:
#*: <syntaxhighlight lang="Java" highlight="">
session = HibernateUtils.getSession();
tx = session.beginTransaction();
User1 user = new User1();
user.setName("李四");
user.setPassword("123");
user.setCreateTime(new Date());
user.setExpireTime(new Date());
/* 利用 Hibernate 将实体类对象保存到数据库中。
因为 user 主键生成策略采用的是 uuid,所以调用完成 save 后,只是将 user 纳入 session 的管理,不会发出 insert 语句,但是 id 已经生成,session 中的 existsInDatabase 状态为 false
*/
session.save(user);
// 从session缓存(EntityEntries属性)中逐出该对象
session.evict(user);
/* 无法成功提交。
因为 hibernate 在清理缓存时,在 session 的临时集合(insertions)中取出 user 对象进行 insert 操作后,需要更新 entityEntries 属性中的 existsInDatabase 为 true,而我们采用 evict 已经将 user 从 session 中逐出了,所以找不到相关数据,无法更新,抛出异常。
*/
tx.commit();
</syntaxhighlight>
#** 解决在逐出 session 缓存中的对象不抛出异常的方法:在 session.evict() 之前进行显示的调用 session.flush() 方法就可以了。【???】
#**: 示例:
#**: <syntaxhighlight lang="Java" highlight="">
session.save(user);
/* flush 后 hibernate 会清理缓存,会将 user 对象保存到数据库中,将 session 中的 insertions 中的 user 对象清除,并且会设置 session 中的 existsInDatabase 状态为 false
*/
session.flush();
// 从session缓存(EntityEntries属性)中逐出该对象
session.evict(user);
/* 可以成功提交。
因为 hibernate 在清理缓存时,在 Session 的 insertions 中集合中无法找到 user 对象所以不会发出 insert 语句,也不会更新 session 中 existsInDatabase 的状态。
*/
tx.commit();
</syntaxhighlight>
# '''persist()''':做一个瞬态的实例持久化。
#* 在事务里执行 session.persist(),不会向数据库插数据,事务 commit 了才会插入数据。
load() 与 get():
1、不存在对应记录时:
    load 抛出异常。
    get 返回 null。
2、返回对象:
    load 返回的是代理对象,等到真正使用对象的内容时才发出 sql 语句,这样就要求在第一次使用对象时,要求 session 处于 open 状态,否则出错。
    get 返回的对象即为 Object。(需要进行强制类型转换,来得到需要的对象类型)
3、加载时机:
    load 进行延迟加载。
    get 进行立即加载。
   
此外,get() 和 load() '''只根据主键查询''',不能根据其它字段查询,如果想根据非主键查询,可以使用 '''HQL'''。
=== Transaction ===
Hibernate 的 Transaction 是底层的 '''JDBC Transaction''' 或者 '''JTA Transaction''' 的封装,具体取决于在 hibernate.properties(hibernate.cfg.xml 中 property “current_session_context_class”) 的配置。
配置事务管理器(hibernate.cfg.xml 中):
# 使用 JDBC Transaction:
#: <syntaxhighlight lang="xml" highlight="">
<property name="current_session_context_class">thread</property>
</syntaxhighlight>
# 使用 JTA Transaction:
#: <syntaxhighlight lang="xml" highlight="">
<property name="current_session_context_class">jta</property>
</syntaxhighlight>
Hibernate 进行持久化操作时(CRUD)必须进行事务控制。
示例:
: <syntaxhighlight lang="Java" highlight="">
Transaction tx = null;
try{
tx = session.beginTransaction();
session.save(obj);
tx.commit();
}catch(HibernateException e){
if(tx != null){
tx.rollback();
}
throw e;
}finally{
if(session != null){
session.close();
}
}
</syntaxhighlight>
</syntaxhighlight>

2022年6月11日 (六) 07:10的最新版本


入门

以下内容展示一个 Hibernate 项目的入门过程。

环境搭建

步骤:

  1. 导入相关 JAR 包;
    1. Hibernate 依赖包:(位于 Hibernate 源文件中:Hibernate/lib/required/)
      Hibernate:JAR包:Hibernate依赖包.png
    2. 日志相关包:(hibernate 本身没有日志输出相关的 jar 包)
      Hibernate:JAR包:日志相关包.png
    3. mysql 驱动包:
      Hibernate:JAR包:数据库驱动包.png
  2. 创建持久化类(实体类);
  3. 创建数据表;
    • 如果配置“hibernate.hbm2ddl.auto”为“update”,则不需要自己手动创建表。
  4. 配置映射关系;(如:“User.hbm.xml”)
    示例:
    ...
    
    <hibernate-mapping>
    	<!-- 实体类和数据库表对应 -->
    	<class name="cn.itcast.entity.User" table="t_user">
    		<id name="uid" column="uid">
    			<generator = "uuid"></generator>
    		</id>
    		<property name="username" column="username"></property>
    		<property name="password" column="password"></property>
    		<property name="address" column="address"></property>
    	</class>
    </hibernate-mapping>
    
  5. 创建 Hibernate 核心配置文件(“hibernate.cfg.xml”);
    示例:
    ...
    
    <hibernate-configuration>
    	<session-factory>
    		
    		<!-- 数据库信息 -->
    		<property name="hibernate.connection.driver.class">com.mysql.jdbc.Driver</property>
    		<property name="hibernate.connection.url">jdbc:mysql:///hibernate_day01</property>
    		<property name="hibernate.connection.username">root</property>
    		<property name="hibernate.connection.password">admin</property>
    
    
    		<!-- hibernate信息 -->
    		<property name="hibernate.show_sql">true</property>
    		<property name="hibernate.format_sql">true</property>
    		<property name="hibernate.hbm2ddl.auto">update</property>
    		<property name="hibernate.dialect">org.hibernate.dialect.MySqlDialect</property>
    		
    		
    		<!-- 映射文件 -->
    		<mapping resource = "cn/itcast/entity/User.hbm.xml"/>
    
    	<session-factory>
    </hibernate-configuration>
    

代码实现

步骤:

  1. 加载 hibernate 核心配置文件:
    	// 到src下面找到名称是hibernate.cfg.xml
    	// 在hibernate里面封装对象
    	Configuration cfg = new Configuration();
    	cfg.configure();
    
  2. 创建 SessionFactory 对象:
    	// 读取 hibernate 核心配置文件内容,创建 sessionFactory
    	// 在过程中,根据映射关系,在配置数据库里面把表创建
    	SessionFactory sessionFactory = cfg.buildSessionFactory();
    
  3. 使用 SessionFactory 创建 session 对象:
    	// 类似于连接
    	Session session = sessionFactory.openSession();
    
  4. 开启事务:
    	Transaction tx = session.beginTransaction();
    
  5. 写具体逻辑 crud 操作:
    	User user = new User();
    	user.setUsername("小马");
    	user.setPassword("123");
    	user.setAddress("美国");
    	
    	session.save(user);
    
  6. 提交事务:
    	tx.commit();
    
  7. 关闭资源:
    	session.close();
    	sessionFactory.close();