目录
- gitee地址
- 1. 尚硅谷_佟刚_Hibernate_概述&安装 Hibernatetools 插件
- 2. 尚硅谷_佟刚_Hibernate_HelloWorld 代码实现部分
- 3. 尚硅谷_佟刚_Hibernate_HelloWorld 详解
- 4. 尚硅谷_佟刚_Hibernate_Session 概述
- 5. 尚硅谷_佟刚_Hibernate_Session 缓存(Hibernate 一级缓存)
- 6. 尚硅谷_佟刚_Hibernate_Session 核心方法(1)
- 7. 尚硅谷_佟刚_Hibernate_Session 核心方法(2)
- 8. 尚硅谷_佟刚_Hibernate_再讲 hibernate.cfg.xml
- 9. 尚硅谷_佟刚_Hibernate_详解 Hibernate 映射文件(1)
- 10. 尚硅谷_佟刚_Hibernate_详解 Hibernate 映射文件(2)
- 11. 尚硅谷_佟刚_Hibernate_单向多对一映射
- 12. 尚硅谷_佟刚_Hibernate_双向一对多映射
- 13. 尚硅谷_佟刚_Hibernate_set 的 3 个属性
- 14. 尚硅谷_佟刚_Hibernate_基于外键映射的1-1关联关系
- 15. 尚硅谷_佟刚_Hibernate_基于主键映射的1-1关联关系
- 16. 尚硅谷_佟刚_Hibernate_映射多对多关联关系
- 17. 尚硅谷_佟刚_Hibernate_映射继承关系
- 18. 尚硅谷_佟刚_Hibernate_检索策略(lazy、fetch、batch-size等)
- 19. 尚硅谷_佟刚_Hibernate_HQL查询(1)
- 20. 尚硅谷_佟刚_Hibernate_HQL查询(2)
- 21. 尚硅谷_佟刚_Hibernate_HQL查询(3)
- 22. 尚硅谷_佟刚_Hibernate_QBC&本地SQL查询
- 23. 尚硅谷_佟刚_Hibernate_二级缓存(1)
- 24. 尚硅谷_佟刚_Hibernate_二级缓存(2)
- 25. 尚硅谷_佟刚_Hibernate_管理Session&批量操作
gitee地址
https://gitee.com/gao_tian_hao/category-java-javaee-hibernate.git
1. 尚硅谷_佟刚_Hibernate_概述&安装 Hibernatetools 插件
什么是 Hibernate ?
- 一个框架
- 一个 Java 领域的持久化框架
- 一个 ORM 框架
对象的持久化
- 狭义的理解,“持久化”仅仅指把对象永久保存到数据库中
- 广义的理解,“持久化”包括和数据库相关的各种操作:
- 保存:把对象永久保存到数据库中。
- 更新:更新数据库中对象(记录)的状态。
- 删除:从数据库中删除一个对象。
- 查询:根据特定的查询条件,把符合查询条件的一个或多个对象从数据库加载到内存中。
- 加载:根据特定的OID,把一个对象从数据库加载到内存中。
- OID: 为了在系统中能够找到所需对象,需要为每一个对象分配一个唯一的标识号。在关系数据库中称之为主键,而在对象术语中,则叫做对象标识(Object identifier-OID).
ORM
ORM(Object/Relation Mapping): 对象/关系映射
-
ORM 主要解决对象-关系的映射
-
ORM的思想:将关系数据库中表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作。
-
ORM 采用元数据来描述对象-关系映射细节, 元数据通常采用 XML 格式, 并且存放在专门的对象-关系映射文件中.
-
图示意:
-
流行的ORM框架
- Hibernate:
- 非常优秀、成熟的 ORM 框架。
- 完成对象的持久化操作
- Hibernate 允许开发者采用面向对象的方式来操作关系数据库。
- 消除那些针对特定数据库厂商的 SQL 代码
- myBatis:
- 相比 Hibernate 灵活高,运行速度快
- 开发速度慢,不支持纯粹的面向对象操作,需熟悉sql语句,并且熟练使用sql语句优化功能
- TopLink
- OJB
Hibernate 与 Jdbc 代码对比
安装Hibernate插件
是在eclipse上的安装
- 安装方法说明(hibernatetools-4.1.1.Final):
- Help --> Install New Software…
- Click Add…
- In dialog Add Site dialog, click Archive…
- Navigate to hibernatetools-Update-4.1.1.Final_2013-12-08_01-06-33-B605.zip and click Open
- Clicking OK in the Add Site dialog will bring you back to the dialog ‘Install’
- Select the Jboss Tools hibernatetools Nightly Build Update Site that has appeared
- Click Next and then Finish
- Approve the license
- Restart eclipse when that is asked
2. 尚硅谷_佟刚_Hibernate_HelloWorld 代码实现部分
代码实现
- 实体类
package com.atguigu.hibernate.helloworld;
import java.sql.Blob;
import java.util.Date;
public class News {
private Integer id; //field
private String title;
private String author;
private String desc;
//使用 title + "," + content 可以来描述当前的 News 记录.
//即 title + "," + content 可以作为 News 的 desc 属性值
private String content;
private Blob picture;
public Blob getPicture() {
return picture;
}
public void setPicture(Blob picture) {
this.picture = picture;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
private Date date;
public Integer getId() { //property
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public News(String title, String author, Date date) {
super();
this.title = title;
this.author = author;
this.date = date;
}
public News() {
// TODO Auto-generated constructor stub
}
@Override
public String toString() {
return "News [id=" + id + ", title=" + title + ", author=" + author
+ ", date=" + date + "]";
}
}
- 测试类
package com.atguigu.hibernate.helloworld;
import java.sql.Date;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.Test;
public class HibernateTest {
@Test
public void test() {
System.out.println("test...");
//1. 创建一个 SessionFactory 对象
SessionFactory sessionFactory = null;
//1). 创建 Configuration 对象: 对应 hibernate 的基本配置信息和 对象关系映射信息
Configuration configuration = new Configuration().configure();
//4.0 之前这样创建
// sessionFactory = configuration.buildSessionFactory();
//2). 创建一个 ServiceRegistry 对象: hibernate 4.x 新添加的对象
//hibernate 的任何配置和服务都需要在该对象中注册后才能有效.
ServiceRegistry serviceRegistry =
new ServiceRegistryBuilder().applySettings(configuration.getProperties())
.buildServiceRegistry();
//3).
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
//2. 创建一个 Session 对象
Session session = sessionFactory.openSession();
//3. 开启事务
Transaction transaction = session.beginTransaction();
//4. 执行保存操作
News news = new News("Java12345", "ATGUIGU", new Date(new java.util.Date().getTime()));
session.save(news);
//5. 提交事务
transaction.commit();
//6. 关闭 Session
session.close();
//7. 关闭 SessionFactory 对象
sessionFactory.close();
}
}
- hibernate.cfg.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 配置连接数据库的基本信息 -->
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql:///hibernate5</property>
<!-- 配置 hibernate 的基本信息 -->
<!-- hibernate 所使用的数据库方言 -->
<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!-- 执行操作时是否在控制台打印 SQL -->
<property name="show_sql">true</property>
<!-- 是否对 SQL 进行格式化 -->
<property name="format_sql">true</property>
<!-- 指定自动生成数据表的策略 -->
<property name="hbm2ddl.auto">update</property>
<!-- 指定关联的 .hbm.xml 文件 -->
<mapping resource="com/atguigu/hibernate/helloworld/News.hbm.xml"/>
</session-factory>
</hibernate-configuration>
- News.hbm.xml配置文件
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.atguigu.hibernate.helloworld">
<class name="News" table="NEWS" dynamic-insert="true">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<!-- 指定主键的生成方式, native: 使用数据库本地方式 -->
<generator class="native" />
</id>
<property name="title" not-null="true" unique="true"
index="news_index" length="50"
type="java.lang.String" column="TITLE" >
</property>
<property name="author" type="java.lang.String"
index="news_index">
<column name="AUTHOR" />
</property>
<property name="date" type="date">
<column name="DATE" />
</property>
<property name="desc"
formula="(SELECT concat(title, ',', author) FROM NEWS n WHERE n.id = id)"></property>
<property name="content">
<column name="CONTENT" sql-type="text"></column>
</property>
<property name="picture" column="PICTURE" type="blob"></property>
</class>
</hibernate-mapping>
- 运行结果
-
控制台
-
-
数据库
-
-
3. 尚硅谷_佟刚_Hibernate_HelloWorld 详解
准备hibernate环境
-
导入hibernate必须的jar包
-
-
加入数据库驱动的jar包
-
hibernate开发步骤
图示意:
描述
-
创建持久化java类
- 提供一个无参的构造器:使Hibernate可以使用Constructor.newInstance() 来实例化持久化类
- 提供一个标识属性(identifier property): 通常映射为数据库表的主键字段. 如果没有该属性,一些功能将不起作用,如:Session.saveOrUpdate()
- 为类的持久化类字段声明访问方法(get/set): Hibernate对JavaBeans 风格的属性实行持久化。
- 使用非 final 类: 在运行时生成代理是 Hibernate 的一个重要的功能. 如果持久化类没有实现任何接口, Hibnernate 使用 CGLIB 生成代理. 如果使用的是 final 类, 则无法生成 CGLIB 代理.
- 重写 eqauls 和 hashCode 方法: 如果需要把持久化类的实例放到 Set 中(当需要进行关联映射时), 则应该重写这两个方法。
- Hibernate 不要求持久化类继承任何父类或实现接口,这可以保证代码不被污染。这就是Hibernate被称为低侵入式设计的原因
-
创建对象关系映射文件
- Hibernate 采用 XML 格式的文件来指定对象和关系数据之间的映射. 在运行时 Hibernate 将根据这个映射文件来生成各种 SQL 语句。
- 映射文件的扩展名为 .hbm.xml
-
创建hibernate配置文件
-
Hibernate 从其配置文件中读取和数据库连接的有关信息, 这个文件应该位于应用的 classpath 下.
-
-
-
通过hibernate API 编写访问数据库的代码
-
测试代码
-
-
下面是控制台输出的SQL语句
-
-
helloworld
使用 Hibernate 进行数据持久化操作,通常有如下步骤:
- 编写持久化类: POJO + 映射文件
- 获取 Configuration 对象
- 获取 SessionFactory 对象
- 获取 Session,打开事务
- 用面向对象的方式操作数据库
- 关闭事务,关闭 Session
Configuration类
- Configuration 类负责管理 Hibernate 的配置信息。包括如下内容:
- Hibernate 运行的底层信息:数据库的URL、用户名、密码、JDBC驱动类,数据库Dialect,数据库连接池等(对应 hibernate.cfg.xml 文件)。
- 持久化类与数据表的映射关系(*.hbm.xml 文件)
- 创建 Configuration 的两种方式
- 属性文件(hibernate.properties):
- Configuration cfg = new Configuration();
- Xml文件(hibernate.cfg.xml)
- Configuration cfg = new Configuration().configure();
- Configuration 的 configure 方法还支持带参数的访问:
- File file = new File(“simpleit.xml”);
- Configuration cfg = new Configuration().configure(file);
- 属性文件(hibernate.properties):
SessionFactory接口
- 针对单个数据库映射关系经过编译后的内存镜像,是线程安全的。
- SessionFactory 对象一旦构造完毕,即被赋予特定的配置信息
- SessionFactory是生成Session的工厂
- 构造 SessionFactory 很消耗资源,一般情况下一个应用中只初始化一个 SessionFactory 对象。
- Hibernate4 新增了一个 ServiceRegistry 接口,所有基于 Hibernate 的配置或者服务都必须统一向这个 ServiceRegistry 注册后才能生效
- Hibernate4 中创建 SessionFactory 的步骤
Session接口
-
Session 是应用程序与数据库之间交互操作的一个单线程对象,是 Hibernate 运作的中心,所有持久化对象必须在 session 的管理下才可以进行持久化操作。此对象的生命周期很短。Session 对象有一个一级缓存,显式执行 flush 之前,所有的持久层操作的数据都缓存在 session 对象处。相当于 JDBC 中的 Connection。
-
-
持久化类与 Session 关联起来后就具有了持久化的能力。
-
Session 类的方法:
- 取得持久化对象的方法: get() load()
- 持久化对象的保存,更新和删除:save(),update(),saveOrUpdate(),delete()
- 开启事务: beginTransaction().
- 管理 Session 的方法:isOpen(),flush(), clear(), evict(), close()等
Transaction(事务)
- 代表一次原子操作,它具有数据库事务的概念。所有持久层都应该在事务管理下进行,即使是只读操作。
- Transaction tx = session.beginTransaction();
- 常用方法:
- commit():提交相关联的session实例
- rollback():撤销事务操作
- wasCommitted():检查事务是否提交
hibernate配置文件的两个配置项
- hbm2ddl.auto:该属性可帮助程序员实现正向工程, 即由 java 代码生成数据库脚本, 进而生成具体的表结构. 。取值 create | update | create-drop | validate
- create : 会根据 .hbm.xml 文件来生成数据表, 但是每次运行都会删除上一次的表 ,重新生成表, 哪怕二次没有任何改变
- create-drop : 会根据 .hbm.xml 文件生成表,但是SessionFactory一关闭, 表就自动删除
- update : 最常用的属性值,也会根据 .hbm.xml 文件生成表, 但若 .hbm.xml 文件和数据库中对应的数据表的表结构不同, Hiberante 将更新数据表结构,但不会删除已有的行和列
- validate : 会和数据库中的表进行比较, 若 .hbm.xml 文件中的列在数据表中不存在,则抛出异常
- format_sql:是否将 SQL 转化为格式良好的 SQL . 取值 true | false
4. 尚硅谷_佟刚_Hibernate_Session 概述
Session 概述
- Session 接口是 Hibernate 向应用程序提供的操纵数据库的最主要的接口, 它提供了基本的保存, 更新, 删除和加载 Java 对象的方法.
- Session 具有一个缓存, 位于缓存中的对象称为持久化对象, 它和数据库中的相关记录对应. Session 能够在某些时间点, 按照缓存中对象的变化来执行相关的 SQL 语句, 来同步更新数据库, 这一过程被称为刷新缓存(flush)
- 站在持久化的角度, Hibernate 把对象分为 4 种状态: 持久化状态, 临时状态, 游离状态, 删除状态. Session 的特定方法能使对象从一个状态转换到另一个状态.
代码
- 测试类
public class HibernateTest {
private SessionFactory sessionFactory;
private Session session;
private Transaction transaction;
@Before
public void init() {
Configuration configuration = new Configuration().configure();
ServiceRegistry serviceRegistry =
new ServiceRegistryBuilder().applySettings(configuration.getProperties())
.buildServiceRegistry();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);
session = sessionFactory.openSession();
transaction = session.beginTransaction();
}
@After
public void destroy() {
transaction.commit();
session.close();
sessionFactory.close();
}
@Test
public void test() {
}
}
- 实体类
public class News {
private Integer id;
private String title;
private String author;
private Date date;
}
- hibernate.cfg.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Hibernate 连接数据库的基本信息 -->
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql:///hibernate5</property>
<!-- Hibernate 的基本配置 -->
<!-- Hibernate 使用的数据库方言 -->
<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!-- 运行时是否打印 SQL -->
<property name="show_sql">true</property>
<!-- 运行时是否格式化 SQL -->
<property name="format_sql">true</property>
<!-- 生成数据表的策略 -->
<property name="hbm2ddl.auto">update</property>
<!-- 需要关联的 hibernate 映射文件 .hbm.xml -->
<mapping resource="com/atguigu/hibernate/entities/News.hbm.xml"/>
</session-factory>
</hibernate-configuration>
- News.hbm.xml 配置文件
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.atguigu.hibernate.entities">
<class name="News" table="NEWS" dynamic-update="true">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<property name="title" type="string" column="TITLE"
unique="true" update="false" index="news_index" length="20">
</property>
<property name="author" type="java.lang.String" index="news_index">
<column name="AUTHOR" />
</property>
<property name="date" type="time">
<column name="DATE" />
</property>
</class>
</hibernate-mapping>
- 运行空的@Test方法后,数据库自动生成表
5. 尚硅谷_佟刚_Hibernate_Session 缓存(Hibernate 一级缓存)
- 在数据库添加一条记录
- 测试:获取该记录
@Test
public void testSessionCache(){
News news = (News) session.get(News.class, 1);
System.out.println(news);
}
-
结果
-
-
测试2 :
-
-
问题: 会发送几条SQL语句
-
结果: 只发送一个SQL语句
-
Session缓存
- 在 Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存. 只要 Session 实例没有结束生命周期, 且没有清理缓存,则存放在它缓存中的对象也不会结束生命周期
- Session 缓存可减少 Hibernate 应用程序访问数据库的频率。
操作Session缓存
flush()
/**
* flush: 使数据表中的记录和 Session 缓存中的对象的状态保持一致. 为了保持一致, 则可能会发送对应的 SQL 语句.
* 1. 在 Transaction 的 commit() 方法中: 先调用 session 的 flush 方法, 再提交事务
* 2. flush() 方法会可能会发送 SQL 语句, 但不会提交事务.
* 3. 注意: 在未提交事务或显式的调用 session.flush() 方法之前, 也有可能会进行 flush() 操作.
* 1). 执行 HQL 或 QBC 查询, 会先进行 flush() 操作, 以得到数据表的最新的记录
* 2). 若记录的 ID 是由底层数据库使用自增的方式生成的, 则在调用 save() 方法时, 就会立即发送 INSERT 语句.
* 因为 save 方法后, 必须保证对象的 ID 是存在的!
*/
@Test
public void testSessionFlush2(){
News news = new News("Java", "SUN", new Date());
session.save(news);
}
@Test
public void testSessionFlush(){
News news = (News) session.get(News.class, 1);
news.setAuthor("Oracle");
// session.flush();
// System.out.println("flush");
News news2 = (News) session.createCriteria(News.class).uniqueResult();
System.out.println(news2);
}
refresh()
/**
* refresh(): 会强制发送 SELECT 语句, 以使 Session 缓存中对象的状态和数据表中对应的记录保持一致!
*/
@Test
public void testRefresh(){
News news = (News) session.get(News.class, 1);
System.out.println(news);
session.refresh(news);
System.out.println(news);
}
clear()
/**
* clear(): 清理缓存
*/
@Test
public void testClear(){
News news1 = (News) session.get(News.class, 1);
session.clear();
News news2 = (News) session.get(News.class, 1);
}
flush 缓存
- flush:Session 按照缓存中对象的属性变化来同步更新数据库
- 默认情况下 Session 在以下时间点刷新缓存:
- 显式调用 Session 的 flush() 方法
- 当应用程序调用 Transaction 的 commit()方法的时, 该方法先 flush ,然后在向数据库提交事务
- 当应用程序执行一些查询(HQL, Criteria)操作时,如果缓存中持久化对象的属性已经发生了变化,会先 flush 缓存,以保证查询结果能够反映持久化对象的最新状态
- flush 缓存的例外情况: 如果对象使用 native 生成器生成 OID, 那么当调用 Session 的 save() 方法保存对象时, 会立即执行向数据库插入该实体的 insert 语句.
- commit() 和 flush() 方法的区别:flush 执行一系列 sql 语句,但不提交事务;commit 方法先调用flush() 方法,然后提交事务. 意味着提交事务意味着对数据库操作永久保存下来。
hibernate主键生成策略
设置刷新缓存的时间点
- 若希望改变 flush 的默认时间点, 可以通过 Session 的 setFlushMode() 方法显式设定 flush 的时间点
数据库隔离级别
- 对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没有采取必要的隔离机制, 就会导致各种并发问题:
- 脏读: 对于两个事物 T1, T2, T1 读取了已经被 T2 更新但还没有被提交的字段. 之后, 若 T2 回滚, T1读取的内容就是临时且无效的.
- 不可重复读: 对于两个事物 T1, T2, T1 读取了一个字段, 然后 T2 更新了该字段. 之后, T1再次读取同一个字段, 值就不同了.
- 幻读: 对于两个事物 T1, T2, T1 从一个表中读取了一个字段, 然后 T2 在该表中插入了一些新的行. 之后, 如果 T1 再次读取同一个表, 就会多出几行.
- 数据库事务的隔离性: 数据库系统必须具有隔离并发运行各个事务的能力, 使它们不会相互影响, 避免各种并发问题.
- 一个事务与其他事务隔离的程度称为隔离级别. 数据库规定了多种事务隔离级别, 不同隔离级别对应不同的干扰程度, 隔离级别越高, 数据一致性就越好, 但并发性越弱
- 数据库提供的 4 种事务隔离级别:
- Oracle 支持的 2 种事务隔离级别:READ COMMITED, SERIALIZABLE. Oracle 默认的事务隔离级别为: READ COMMITED
- Mysql 支持 4 中事务隔离级别. Mysql 默认的事务隔离级别为: REPEATABLE READ
在MySQL中设置隔离级别
- 每启动一个 mysql 程序, 就会获得一个单独的数据库连接. 每个数据库连接都有一个全局变量 @@tx_isolation, 表示当前的事务隔离级别. MySQL 默认的隔离级别为 Repeatable Read
- 查看当前的隔离级别: SELECT @@tx_isolation;
- 设置当前 mySQL 连接的隔离级别:
- set transaction isolation level read committed;
- 设置数据库系统的全局的隔离级别:
- set global transaction isolation level read committed;
在hibernate中设置隔离级别
- JDBC 数据库连接使用数据库系统默认的隔离级别. 在 Hibernate 的配置文件中可以显式的设置隔离级别. 每一个隔离级别都对应一个整数:
- 1 . READ UNCOMMITED
- 2 . READ COMMITED
- 4 . REPEATABLE READ
- 8 . SERIALIZEABLE
- Hibernate 通过为 Hibernate 映射文件指定 hibernate.connection.isolation 属性来设置事务的隔离级别
6. 尚硅谷_佟刚_Hibernate_Session 核心方法(1)
持久化对象的状态
- 站在持久化的角度, Hibernate 把对象分为 4 种状态: 持久化状态, 临时状态, 游离状态, 删除状态. Session 的特定方法能使对象从一个状态转换到另一个状态.
- 临时对象(Transient):
- 在使用代理主键的情况下, OID 通常为 null
- 不处于 Session 的缓存中
- 在数据库中没有对应的记录
- 持久化对象(也叫”托管”)(Persist):
- OID 不为 null
- 位于 Session 缓存中
- 若在数据库中已经有和其对应的记录, 持久化对象和数据库中的相关记录对应
- Session 在 flush 缓存时, 会根据持久化对象的属性变化, 来同步更新数据库
- 在同一个 Session 实例的缓存中, 数据库表中的每条记录只对应唯一的持久化对象
- 删除对象(Removed)
- 在数据库中没有和其 OID 对应的记录
- 不再处于 Session 缓存中
- 一般情况下, 应用程序不该再使用被删除的对象
- 游离对象(也叫”脱管”) (Detached):
- OID 不为 null
- 不再处于 Session 缓存中
- 一般情况需下, 游离对象是由持久化对象转变过来的, 因此在数据库中可能还存在与它对应的记录
对象的状态转换图
Session的save()方法
- Session 的 save() 方法使一个临时对象转变为持久化对象
- Session 的 save() 方法完成以下操作:
- 把 News 对象加入到 Session 缓存中, 使它进入持久化状态
- 选用映射文件指定的标识符生成器, 为持久化对象分配唯一的 OID. 在 使用代理主键的情况下, setId() 方法为 News 对象设置 OID 使无效的.
- 计划执行一条 insert 语句:在 flush 缓存的时候
- Hibernate 通过持久化对象的 OID 来维持它和数据库相关记录的对应关系. 当 News 对象处于持久化状态时, 不允许程序随意修改它的 ID
- persist() 和 save() 区别:
- 当对一个 OID 不为 Null 的对象执行 save() 方法时, 会把该对象以一个新的 oid 保存到数据库中; 但执行 persist() 方法时会抛出一个异常.
代码
/**
* 1. save() 方法
* 1). 使一个临时对象变为持久化对象
* 2). 为对象分配 ID.
* 3). 在 flush 缓存时会发送一条 INSERT 语句.
* 4). 在 save 方法之前的 id 是无效的
* 5). 持久化对象的 ID 是不能被修改的!
*/
@Test
public void testSave(){
News news = new News();
news.setTitle("CC");
news.setAuthor("cc");
news.setDate(new Date());
news.setId(100);
System.out.println(news);
session.save(news);
System.out.println(news);
// news.setId(101);
}
/**
* persist(): 也会执行 INSERT 操作
*
* 和 save() 的区别 :
* 在调用 persist 方法之前, 若对象已经有 id 了, 则不会执行 INSERT, 而抛出异常
*/
@Test
public void testPersist(){
News news = new News();
news.setTitle("EE");
news.setAuthor("ee");
news.setDate(new Date());
news.setId(200);
session.persist(news);
}
Session的get()和load()方法
- 都可以根据跟定的 OID 从数据库中加载一个持久化对象
- 区别:
- 当数据库中不存在与 OID 对应的记录时, load() 方法抛出 ObjectNotFoundException 异常, 而 get() 方法返回 null
- 两者采用不同的延迟检索策略:load 方法支持延迟加载策略。而 get 不支持。
代码
/**
* get VS load:
*
* 1. 执行 get 方法: 会立即加载对象.
* 执行 load 方法, 若不适用该对象, 则不会立即执行查询操作, 而返回一个代理对象
*
* get 是 立即检索, load 是延迟检索.
*
* 2. load 方法可能会抛出 LazyInitializationException 异常: 在需要初始化
* 代理对象之前已经关闭了 Session
*
* 3. 若数据表中没有对应的记录, Session 也没有被关闭.
* get 返回 null
* load 若不使用该对象的任何属性, 没问题; 若需要初始化了, 抛出异常.
*/
@Test
public void testLoad(){
News news = (News) session.load(News.class, 10);
System.out.println(news.getClass().getName());
// session.close();
// System.out.println(news);
}
@Test
public void testGet(){
News news = (News) session.get(News.class, 1);
// session.close();
System.out.println(news);
}
7. 尚硅谷_佟刚_Hibernate_Session 核心方法(2)
Session的update()方法
- Session 的 update() 方法使一个游离对象转变为持久化对象, 并且计划执行一条 update 语句.
- 若希望 Session 仅当修改了 News 对象的属性时, 才执行 update() 语句, 可以把映射文件中 元素的 select-before-update 设为 true. 该属性的默认值为 false
- 当 update() 方法关联一个游离对象时, 如果在 Session 的缓存中已经存在相同 OID 的持久化对象, 会抛出异常。
- 当 update() 方法关联一个游离对象时, 如果在数据库中不存在相应的记录, 也会抛出异常.
代码
/**
* update:
* 1. 若更新一个持久化对象, 不需要显示的调用 update 方法. 因为在调用 Transaction
* 的 commit() 方法时, 会先执行 session 的 flush 方法.
* 2. 更新一个游离对象, 需要显式的调用 session 的 update 方法. 可以把一个游离对象
* 变为持久化对象
*
* 需要注意的:
* 1. 无论要更新的游离对象和数据表的记录是否一致, 都会发送 UPDATE 语句.
* 如何能让 updat 方法不再盲目的出发 update 语句呢 ? 在 .hbm.xml 文件的 class 节点设置
* select-before-update=true (默认为 false). 但通常不需要设置该属性.
*
* 2. 若数据表中没有对应的记录, 但还调用了 update 方法, 会抛出异常
*
* 3. 当 update() 方法关联一个游离对象时,
* 如果在 Session 的缓存中已经存在相同 OID 的持久化对象, 会抛出异常. 因为在 Session 缓存中
* 不能有两个 OID 相同的对象!
*
*/
@Test
public void testUpdate(){
News news = (News) session.get(News.class, 1);
transaction.commit();
session.close();
// news.setId(100);
session = sessionFactory.openSession();
transaction = session.beginTransaction();
// news.setAuthor("SUN");
News news2 = (News) session.get(News.class, 1);
session.update(news);
}
Session的saveOrUpdate()方法
-
Session 的 saveOrUpdate() 方法同时包含了 save() 与 update() 方法的功能
-
-
判定对象为临时对象的标准
- Java 对象的 OID 为 null
- 映射文件中为 设置了 unsaved-value 属性, 并且 Java 对象的 OID 取值与这个 unsaved-value 属性值匹配
代码
/**
* 注意:
* 1. 若 OID 不为 null, 但数据表中还没有和其对应的记录. 会抛出一个异常.
* 2. 了解: OID 值等于 id 的 unsaved-value 属性值的对象, 也被认为是一个游离对象
*/
@Test
public void testSaveOrUpdate(){
News news = new News("FFF", "fff", new Date());
news.setId(11);
session.saveOrUpdate(news);
}
Session的merge()方法
说明
- 在hibernate中不常用,在JPA中常用。
Session的delete()方法
- Session 的 delete() 方法既可以删除一个游离对象, 也可以删除一个持久化对象
- Session 的 delete() 方法处理过程
- 计划执行一条 delete 语句
- 把对象从 Session 缓存中删除, 该对象进入删除状态.
- Hibernate 的 cfg.xml 配置文件中有一个 hibernate.use_identifier_rollback 属性, 其默认值为 false, 若把它设为 true, 将改变 delete() 方法的运行行为: delete() 方法会把持久化对象或游离对象的 OID 设置为 null, 使它们变为临时对象
代码
/**
* delete: 执行删除操作. 只要 OID 和数据表中一条记录对应, 就会准备执行 delete 操作
* 若 OID 在数据表中没有对应的记录, 则抛出异常
*
* 可以通过设置 hibernate 配置文件 hibernate.use_identifier_rollback 为 true,
* 使删除对象后, 把其 OID 置为 null
*/
@Test
public void testDelete(){
// News news = new News();
// news.setId(11);
News news = (News) session.get(News.class, 163840);
session.delete(news);
System.out.println(news);
}
<!-- 删除对象后, 使其 OID 置为 null -->
<property name="use_identifier_rollback">true</property>
Session的evict()对象
代码
/**
* evict: 从 session 缓存中把指定的持久化对象移除
*/
@Test
public void testEvict(){
News news1 = (News) session.get(News.class, 1);
News news2 = (News) session.get(News.class, 2);
news1.setTitle("AA");
news2.setTitle("BB");
session.evict(news1);
}
通过hibernate调用存储过程
- Work 接口: 直接通过 JDBC API 来访问数据库的操作
- Session 的 doWork(Work) 方法用于执行 Work 对象指定的操作, 即调用 Work 对象的 execute() 方法. Session 会把当前使用的数据库连接传递给 execute() 方法.
代码
@Test
public void testDoWork(){
session.doWork(new Work() {
@Override
public void execute(Connection connection) throws SQLException {
System.out.println(connection);
//调用存储过程.
}
});
}
hibernate与触发器协同工作
- Hibernate 与数据库中的触发器协同工作时, 会造成两类问题
- 触发器使 Session 的缓存中的持久化对象与数据库中对应的数据不一致:触发器运行在数据库中, 它执行的操作对 Session 是透明的
- Session 的 update() 方法盲目地激发触发器: 无论游离对象的属性是否发生变化, 都会执行 update 语句, 而 update 语句会激发数据库中相应的触发器
- 解决方案:
-
在执行完 Session 的相关操作后, 立即调用 Session 的 flush() 和 refresh() 方法, 迫使 Session 的缓存与数据库同步(refresh() 方法重新从数据库中加载对象)
-
-
在映射文件的的 元素中设置 select-before-update 属性: 当 Session 的 update 或 saveOrUpdate() 方法更新一个游离对象时, 会先执行 Select 语句, 获得当前游离对象在数据库中的最新数据, 只有在不一致的情况下才会执行 update 语句
-
8. 尚硅谷_佟刚_Hibernate_再讲 hibernate.cfg.xml
hibernate的配置文件
- hibernate配置文件主要用于配置数据库连接和 Hibernate 运行时所需的各种属性
- 每个 Hibernate 配置文件对应一个 Configuration 对象
- Hibernate配置文件可以有两种格式:
- hibernate.properties
- hibernate.cfg.xml
hibernate.cfg.xml的常用属性
- JDBC 连接属性
- connection.url:数据库URL
- connection.username:数据库用户名
- connection.password:数据库用户密码
- connection.driver_class:数据库JDBC驱动
- dialect:配置数据库的方言,根据底层的数据库不同产生不同的 sql 语句,Hibernate 会针对数据库的特性在访问时进行优化
- C3P0 数据库连接池属性
- hibernate.c3p0.max_size: 数据库连接池的最大连接数
- hibernate.c3p0.min_size: 数据库连接池的最小连接数
- hibernate.c3p0.timeout: 数据库连接池中连接对象在多长时间没有使用过后,就应该被销毁
- hibernate.c3p0.max_statements: 缓存 Statement 对象的数量
- hibernate.c3p0.idle_test_period: 表示连接池检测线程多长时间检测一次池内的所有链接对象是否超时. 连接池本身不会把自己从连接池中移除,而是专门有一个线程按照一定的时间间隔来做这件事,这个线程通过比较连接对象最后一次被使用时间和当前时间的时间差来和 timeout 做对比,进而决定是否销毁这个连接对象。
- hibernate.c3p0.acquire_increment: 当数据库连接池中的连接耗尽时, 同一时刻获取多少个数据库连接
- 其他
- show_sql:是否将运行期生成的SQL输出到日志以供调试。取值 true | false
- format_sql:是否将 SQL 转化为格式良好的 SQL . 取值 true | false
- hbm2ddl.auto:在启动和停止时自动地创建,更新或删除数据库模式。取值 create | update | create-drop | validate
- hibernate.jdbc.fetch_size
- hibernate.jdbc.fetch_size:实质是调用 Statement.setFetchSize() 方法设定 JDBC 的 Statement 读取数据的时候每次从数据库中取出的记录条数。
- 例如一次查询1万条记录,对于Oracle的JDBC驱动来说,是不会 1 次性把1万条取出来的,而只会取出 fetchSize 条数,当结果集遍历完了这些记录以后,再去数据库取 fetchSize 条数据。因此大大节省了无谓的内存消耗。Fetch Size设的越大,读数据库的次数越少,速度越快;Fetch Size越小,读数据库的次数越多,速度越慢。Oracle数据库的JDBC驱动默认的Fetch Size = 10,是一个保守的设定,根据测试,当Fetch Size=50时,性能会提升1倍之多,当 fetchSize=100,性能还能继续提升20%,Fetch Size继续增大,性能提升的就不显著了。并不是所有的数据库都支持Fetch Size特性,例如MySQL就不支持
- hibernate.jdbc.batch_size
- hibernate.jdbc.batch_size:设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小,类似于设置缓冲区大小的意思。batchSize 越大,批量操作时向数据库发送sql的次数越少,速度就越快。
- 测试结果是当Batch Size=0的时候,使用Hibernate对Oracle数据库删除1万条记录需要25秒,Batch Size = 50的时候,删除仅仅需要5秒!Oracle数据库 batchSize=30 的时候比较合适。
代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- Hibernate 连接数据库的基本信息 -->
<property name="connection.username">root</property>
<property name="connection.password">root</property>
<property name="connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql:///hibernate5</property>
<!-- Hibernate 的基本配置 -->
<!-- Hibernate 使用的数据库方言 -->
<property name="dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<!-- 运行时是否打印 SQL -->
<property name="show_sql">true</property>
<!-- 运行时是否格式化 SQL -->
<property name="format_sql">true</property>
<!-- 生成数据表的策略 -->
<property name="hbm2ddl.auto">update</property>
<!-- 设置 Hibernate 的事务隔离级别 -->
<property name="connection.isolation">2</property>
<!-- 删除对象后, 使其 OID 置为 null -->
<property name="use_identifier_rollback">true</property>
<!-- 配置 C3P0 数据源 -->
<property name="hibernate.c3p0.max_size">10</property>
<property name="hibernate.c3p0.min_size">5</property>
<property name="c3p0.acquire_increment">2</property>
<property name="c3p0.idle_test_period">2000</property>
<property name="c3p0.timeout">2000</property>
<property name="c3p0.max_statements">10</property>
<!-- 设定 JDBC 的 Statement 读取数据的时候每次从数据库中取出的记录条数 -->
<property name="hibernate.jdbc.fetch_size">100</property>
<!-- 设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小 -->
<property name="jdbc.batch_size">30</property>
<!-- 需要关联的 hibernate 映射文件 .hbm.xml -->
<mapping resource="com/atguigu/hibernate/entities/News.hbm.xml"/>
<!-- <mapping resource="com/atguigu/hibernate/entities/Worker.hbm.xml"/>-->
<!--
<mapping resource="com/atguigu/hibernate/entities/n21/Customer.hbm.xml"/>
<mapping resource="com/atguigu/hibernate/entities/n21/Order.hbm.xml"/>
-->
<!--
<mapping resource="com/atguigu/hibernate.entities/n21/both/Customer.hbm.xml"/>
<mapping resource="com/atguigu/hibernate.entities/n21/both/Order.hbm.xml"/>
-->
</session-factory>
</hibernate-configuration>
9. 尚硅谷_佟刚_Hibernate_详解 Hibernate 映射文件(1)
对象关系映射文件
POJO 类和数据库的映射文件*.hbm.xml
- POJO 类和关系数据库之间的映射可以用一个XML文档来定义。
- 通过 POJO 类的数据库映射文件,Hibernate可以理解持久化类和数据表之间的对应关系,也可以理解持久化类属性与数据库表列之间的对应关系
- 在运行时 Hibernate 将根据这个映射文件来生成各种 SQL 语句
- 映射文件的扩展名为 .hbm.xml
映射文件说明
- hibernate-mapping
- 类层次:class
- 主键:id
- 基本类型:property
- 实体引用类: many-to-one | one-to-one
- 集合:set | list | map | array
- one-to-many
- many-to-many
- 子类:subclass | joined-subclass
- 其它:component | any 等
- 类层次:class
- 查询语句:query(用来放置查询语句,便于对数据库查询的统一管理和优化)
- 每个Hibernate-mapping中可以同时定义多个类. 但更推荐为每个类都创建一个单独的映射文件
hibernate-mapping
class
映射对象标识符
- Hibernate 使用对象标识符(OID) 来建立内存中的对象和数据库表中记录的对应关系. 对象的 OID 和数据表的主键对应. Hibernate 通过标识符生成器来为主键赋值
- Hibernate 推荐在数据表中使用代理主键, 即不具备业务含义的字段. 代理主键通常为整数类型, 因为整数类型比字符串类型要节省更多的数据库空间.
- 在对象-关系映射文件中, 元素用来设置对象标识符. 子元素用来设定标识符生成器.
- Hibernate 提供了标识符生成器接口: IdentifierGenerator, 并提供了各种内置实现
id
主键生成策略generator
代码
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
increment标识符生成器
- increment 标识符生成器由 Hibernate 以递增的方式为代理主键赋值
- Hibernate 会先读取 NEWS 表中的主键的最大值, 而接下来向 NEWS 表中插入记录时, 就在 max(id) 的基础上递增, 增量为 1.
- 适用范围:
- 由于 increment 生存标识符机制不依赖于底层数据库系统, 因此它适合所有的数据库系统
- 适用于只有单个 Hibernate 应用进程访问同一个数据库的场合, 在集群环境下不推荐使用它
- OID 必须为 long, int 或 short 类型, 如果把 OID 定义为 byte 类型, 在运行时会抛出异常
- 适用范围:
会有并发问题: 因为需要先从数据库表中查询,然后加1后再插入。此时如果有同时查询的化,就会查到同样的值,而导致插入失败。
identity 标识符生成器
- identity 标识符生成器由底层数据库来负责生成标识符, 它要求底层数据库把主键定义为自动增长字段类型
- 适用范围:
- 由于 identity 生成标识符的机制依赖于底层数据库系统, 因此, 要求底层数据库系统必须支持自动增长字段类型. 支持自动增长字段类型的数据库包括: DB2, Mysql, MSSQLServer, Sybase 等
- OID 必须为 long, int 或 short 类型, 如果把 OID 定义为 byte 类型, 在运行时会抛出异常
sequence 标识符生成器
- sequence 标识符生成器利用底层数据库提供的序列来生成标识符.
- Hibernate 在持久化一个 News 对象时, 先从底层数据库的 news_seq 序列中获得一个唯一的标识号, 再把它作为主键值
- 适用范围:
- 由于 sequence 生成标识符的机制依赖于底层数据库系统的序列, 因此, 要求底层数据库系统必须支持序列. 支持序列的数据库包括: DB2, Oracle 等
- OID 必须为 long, int 或 short 类型, 如果把 OID 定义为 byte 类型, 在运行时会抛出异常
hilo 标识符生成器
- hilo 标识符生成器由 Hibernate 按照一种 high/low 算法*生成标识符, 它从数据库的特定表的字段中获取 high 值.
- Hibernate 在持久化一个 News 对象时, 由 Hibernate 负责生成主键值. hilo 标识符生成器在生成标识符时, 需要读取并修改 HI_TABLE 表中的 NEXT_VALUE 值.
- 适用范围:
- 由于 hilo 生存标识符机制不依赖于底层数据库系统, 因此它适合所有的数据库系统
- OID 必须为 long, int 或 short 类型, 如果把 OID 定义为 byte 类型, 在运行时会抛出异常
hilo高低算法
hilo 和 seqhilo生成器给出了两种hi/lo算法的实现
第一种情况:
<id name="id" type="id" column="id">
<generator class="hilo">
<param name="table">zhxy_hilo_tbl</param>
<param name="column">next_value</param>
<param name="max_lo">100</param>
</generator>
</id>
第二种情况
<id name="id" type="long" column="cat_id">
<generator class="seqhilo">
<param name="sequence">hi_value</param>
<param name="max_lo">100</param>
</generator>
</id>
第二种情况需要sequence的支持,这里只讨论更通用的第一种情况
默认请况下使用的表是
hibernate_unique_key,默认字段叫作next_hi。next_hi必须有一条记录否则会出现错误。
几个简写解释:
hi:高值-从数据库取得的那个值
lo:低值-hibernate自动维护,取值1到max_low
max_low:映射文件中配置的那个值
那hibernate怎样生成主键呢?
1.从数据库中取得hi值,数据库的next_value值加1
2.hibernate取得lo值(0到max_lo-1循环,lo到max_lo时,执行步骤1,然后lo继续从0到max_lo-1循环)
根据下面的公式计算值:
hi*(max_lo+1)+lo;
例如hi初始为2,max_lo为3
生成的值依次是:
读取hi为2,写到数据库为3
2*(3+1)+0=8
2*(3+1)+1=9
2*(3+1)+2=10
2*(3+1)+3=11
这有次读写表zhxy_hilo_tbl操作,hi变为3,数据库成为4
3*(3+1)+0=12
3*(3+1)+1=13
关闭数据库,下次打开时,读取hi值为4,数据库变为5
4*(3+1)+0=16
但是有一种特殊情况,就是hi是0的时候,那么第一个值不是0*(max_lo+1)+0=0
而是跳过0,直接就是1
native 标识符生成器
跨平台,通常使用它就挺好
- native 标识符生成器依据底层数据库对自动生成标识符的支持能力, 来选择使用 identity, sequence 或 hilo 标识符生成器.
- 适用范围:
- 由于 native 能根据底层数据库系统的类型, 自动选择合适的标识符生成器, 因此很适合于跨数据库平台开发
- OID 必须为 long, int 或 short 类型, 如果把 OID 定义为 byte 类型, 在运行时会抛出异常
Property
java类型,hibernate映射类型及SQL类型之间的对应关系
10. 尚硅谷_佟刚_Hibernate_详解 Hibernate 映射文件(2)
时间日期
Java时间和日期类型的hibernate映射
- 在 Java 中, 代表时间和日期的类型包括: java.util.Date 和 java.util.Calendar. 此外, 在 JDBC API 中还提供了 3 个扩展了 java.util.Date 类的子类: java.sql.Date, java.sql.Time 和 java.sql.Timestamp, 这三个类分别和标准 SQL 类型中的 DATE, TIME 和 TIMESTAMP 类型对应。
- 在标准 SQL 中, DATE 类型表示日期, TIME 类型表示时间, TIMESTAMP 类型表示时间戳, 同时包含日期和时间信息.
如何进行映射 ?
- 因为 java.util.Date 是 java.sql.Date, java.sql.Time 和 java.sql.Timestamp 的父类, 所以 java.util.Date可以对应标准 SQL 类型中的 DATE, TIME 和 TIMESTAMP
- 基于 此,所以在设置持久化类的 Date 类型是, 设置为 java.util.Date.
如何把 java.util.Date 映射为 DATE, TIME 和 TIMESTAMP ?
-
可以通过 property 的 type 属性来进行映射: 如下
-
<property name="date" type="timestamp"> <column name="DATE" /> </property>
-
<property name="date" type="data"> <column name="DATE" /> </property>
-
<property name="date" type="time"> <column name="DATE" /> </property>
-
其中 timestamp, date, time 既不是 Java 类型, 也不是标准 SQL 类型, 而是 hibernate 映射类型.
使用hibernate内置映射类型
- 以下情况下必须显式指定 Hibernate 映射类型
- 一个 Java 类型可能对应多个 Hibernate 映射类型. 例如: 如果持久化类的属性为 java.util.Date 类型, 对应的 Hibernate 映射类型可以是 date, time 或 timestamp. 此时必须根据对应的数据表的字段的 SQL 类型, 来确定 Hibernate 映射类型. 如果字段为 DATE 类型, 那么 Hibernate 映射类型为 date; 如果字段为 TIME 类型, 那么 Hibernate 映射类型为 time; 如果字段为 TIMESTATMP 类型, 那么 Hibernate 映射类型为 timestamp.
java大对象类型的Hibernate映射
-
在 Java 中, java.lang.String 可用于表示长字符串(长度超过 255), 字节数组 byte[] 可用于存放图片或文件的二进制数据. 此外, 在 JDBC API 中还提供了 java.sql.Clob 和 java.sql.Blob 类型, 它们分别和标准 SQL 中的 CLOB 和 BLOB 类型对应. CLOB 表示字符串大对象(Character Large Object), BLOB表示二进制对象(Binary Large Object)
-
-
Mysql 不支持标准 SQL 的 CLOB 类型, 在 Mysql 中, 用 TEXT, MEDIUMTEXT 及 LONGTEXT 类型来表示长度操作 255 的长文本数据
-
在持久化类中, 二进制大对象可以声明为 byte[] 或 java.sql.Blob 类型; 字符串可以声明为 java.lang.String 或 java.sql.Clob
-
实际上在 Java 应用程序中处理长度超过 255 的字符串, 使用 java.lang.String 比 java.sql.Clob 更方便
代码
//大文本
private String content;
//二进制数据
private Blob image;
<!-- 映射大对象 -->
<property name="content" type="clob"></property>
<property name="image" type="blob"></property>
- 此时生成的数据库表字段为
<!-- 映射大对象 -->
<!-- 若希望精确映射 SQL 类型, 可以使用 sql-type 属性. -->
<property name="content">
<column name="CONTENT" sql-type="mediumtext"></column>
</property>
<property name="image">
<column name="IMAGE" sql-type="mediumblob"></column>
</property>
- 此时对于的数据库表字段为
对于二进制数据,如何保存?
@Test
public void testBlob() throws Exception{
//保存二进制和大文本
// News news = new News();
// news.setAuthor("cc");
// news.setContent("CONTENT");
// news.setDate(new Date());
// news.setDesc("DESC");
// news.setTitle("CC");
//
// InputStream stream = new FileInputStream("Hydrangeas.jpg");
// Blob image = Hibernate.getLobCreator(session)
// .createBlob(stream, stream.available());
// news.setImage(image);
//
// session.save(news);
// 获取二进制和大文本
News news = (News) session.get(News.class, 1);
Blob image = news.getImage();
InputStream in = image.getBinaryStream();
System.out.println(in.available());
}
二进制文件的保存仅作为了解内容,一般开发中都会使用将文件放在某个路径下,然后在数据库中保存该路径
映射组成关系
-
建立域模型和关系数据模型有着不同的出发点:
-
域模型: 由程序代码组成, 通过细化持久化类的的粒度可提高代码的可重用性, 简化编程。
-
-
在没有数据冗余的情况下, 应该尽可能减少表的数目, 简化表之间的参照关系, 以便提高数据的访问速度
-
-
Hibernate 把持久化类的属性分为两种:
- 值(value)类型: 没有 OID, 不能被单独持久化, 生命周期依赖于所属的持久化类的对象的生命周期。
- 实体(entity)类型: 有 OID, 可以被单独持久化, 有独立的生命周期。
-
显然无法直接用 property 映射 pay 属性。
-
Hibernate 使用
<component>
元素来映射组成关系, 该元素表名 pay 属性是 Worker 类一个组成部分, 在 Hibernate 中称之为组件。 -
component
代码
- Pay.java
package com.atguigu.hibernate.entities;
public class Pay {
private int monthlyPay;
private int yearPay;
private int vocationWithPay;
private Worker worker;
public Worker getWorker() {
return worker;
}
public void setWorker(Worker worker) {
this.worker = worker;
}
public int getMonthlyPay() {
return monthlyPay;
}
public void setMonthlyPay(int monthlyPay) {
this.monthlyPay = monthlyPay;
}
public int getYearPay() {
return yearPay;
}
public void setYearPay(int yearPay) {
this.yearPay = yearPay;
}
public int getVocationWithPay() {
return vocationWithPay;
}
public void setVocationWithPay(int vocationWithPay) {
this.vocationWithPay = vocationWithPay;
}
}
- Worker.java
package com.atguigu.hibernate.entities;
public class Worker {
private Integer id;
private String name;
private Pay pay;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Pay getPay() {
return pay;
}
public void setPay(Pay pay) {
this.pay = pay;
}
}
- Worker.hbm.xml
<?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 2014-1-2 16:14:33 by Hibernate Tools 3.4.0.CR1 -->
<hibernate-mapping package="com.atguigu.hibernate.entities">
<class name="Worker" table="WORKER">
<id name="id" type="java.lang.Integer">
<column name="ID" />
<generator class="native" />
</id>
<property name="name" type="java.lang.String">
<column name="NAME" />
</property>
<!-- 映射组成关系 -->
<component name="pay" class="Pay">
<parent name="worker"/>
<!-- 指定组成关系的组件的属性 -->
<property name="monthlyPay" column="MONTHLY_PAY"></property>
<property name="yearPay" column="YEAR_PAY"></property>
<property name="vocationWithPay" column="VOCATION_WITH_PAY"></property>
</component>
</class>
</hibernate-mapping>
- hibernate.cfg.xml中增加映射
<mapping resource="com/atguigu/hibernate/entities/Worker.hbm.xml"/>
- 测试
@Test
public void testComponent(){
Worker worker = new Worker();
Pay pay = new Pay();
pay.setMonthlyPay(1000);
pay.setYearPay(80000);
pay.setVocationWithPay(5);
worker.setName("ABCD");
worker.setPay(pay);
session.save(worker);
}
- 生成结果
11. 尚硅谷_佟刚_Hibernate_单向多对一映射
- 在领域模型中, 类与类之间最普遍的关系就是关联关系.
- 在 UML 中, 关联是有方向的.
- 以 Customer 和 Order 为例: 一个用户能发出多个订单, 而一个订单只能属于一个客户. 从 Order 到 Customer 的关联是多对一关联; 而从 Customer 到 Order 是一对多关联.
- 单向关联
- 双向关联
单向n-1
-
单向 n-1 关联只需从 n 的一端可以访问 1 的一端
-
域模型: 从 Order 到 Customer 的多对一单向关联需要在Order 类中定义一个 Customer 属性, 而在 Customer 类中无需定义存放 Order 对象的集合属性。
-
-
关系数据模型:ORDERS 表中的 CUSTOMER_ID 参照 CUSTOMER 表的主键。
-
-
显然无法直接用 property 映射 customer 属性
-
Hibernate 使用 元素来映射多对一关联关系
-
-
many-to-one
-
代码
- 实体类
public class Customer {
private Integer customerId;
private String customerName;
public Integer getCustomerId() {
return customerId;
}
public void setCustomerId(Integer customerId) {
this.customerId = customerId;
}
public String getCustomerName() {
return customerName;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
}
public class Order {
private Integer orderId;
private String orderName;
private Customer customer;
public Integer getOrderId() {
return orderId;
}
public void setOrderId(Integer orderId) {
this.orderId = orderId;
}
public String getOrderName() {
return orderName;
}
public void setOrderName(String orderName) {
this.orderName = orderName;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
- 映射文件 :
- Customer.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="com.atguigu.hibernate.entities.n21.Customer" table="CUSTOMERS">
<id name="customerId" type="java.lang.Integer">
<column name="CUSTOMER_ID" />
<generator class="native" />
</id>
<property name="customerName" type="java.lang.String">
<column name="CUSTOMER_NAME" />
</property>
</class>
</hibernate-mapping>
- Order.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.atguigu.hibernate.entities.n21">
<class name="Order" table="ORDERS">
<id name="orderId" type="java.lang.Integer">
<column name="ORDER_ID" />
<generator class="native" />
</id>
<property name="orderName" type="java.lang.String">
<column name="ORDER_NAME" />
</property>
<!--
映射多对一的关联关系。 使用 many-to-one 来映射多对一的关联关系
name: 多这一端关联的一那一端的属性的名字
class: 一那一端的属性对应的类名
column: 一那一端在多的一端对应的数据表中的外键的名字
-->
<many-to-one name="customer" class="Customer" column="CUSTOMER_ID"></many-to-one>
</class>
</hibernate-mapping>
- 配置文件 : hibernate.cfg.xml
<mapping resource="com/atguigu/hibernate/entities/n21/Customer.hbm.xml"/>
<mapping resource="com/atguigu/hibernate/entities/n21/Order.hbm.xml"/>
- 生成数据库表
@Test
public void testMany2One(){
}
- 生成的表
- 开始做增删改查操作
- 增
@Test
public void testMany2OneSave(){
Customer customer = new Customer();
customer.setCustomerName("BB");
Order order1 = new Order();
order1.setOrderName("ORDER-3");
Order order2 = new Order();
order2.setOrderName("ORDER-4");
//设定关联关系
order1.setCustomer(customer);
order2.setCustomer(customer);
//执行 save 操作: 先插入 Customer, 再插入 Order, 3 条 INSERT
//先插入 1 的一端, 再插入 n 的一端, 只有 INSERT 语句.
// session.save(customer);
//
// session.save(order1);
// session.save(order2);
//先插入 Order, 再插入 Customer. 3 条 INSERT, 2 条 UPDATE
//先插入 n 的一端, 再插入 1 的一端, 会多出 UPDATE 语句!
//因为在插入多的一端时, 无法确定 1 的一端的外键值. 所以只能等 1 的一端插入后, 再额外发送 UPDATE 语句.
//推荐先插入 1 的一端, 后插入 n 的一端
session.save(order1);
session.save(order2);
session.save(customer);
}
- 查
@Test
public void testMany2OneGet(){
//1. 若查询多的一端的一个对象, 则默认情况下, 只查询了多的一端的对象. 而没有查询关联的
//1 的那一端的对象!
Order order = (Order) session.get(Order.class, 1);
System.out.println(order.getOrderName());
System.out.println(order.getCustomer().getClass().getName());
session.close();
//2. 在需要使用到关联的对象时, 才发送对应的 SQL 语句.
Customer customer = order.getCustomer();
System.out.println(customer.getCustomerName());
//3. 在查询 Customer 对象时, 由多的一端导航到 1 的一端时,
//若此时 session 已被关闭, 则默认情况下
//会发生 LazyInitializationException 异常
//4. 获取 Order 对象时, 默认情况下, 其关联的 Customer 对象是一个代理对象!
}
- 改
@Test
public void testUpdate(){
Order order = (Order) session.get(Order.class, 1);
order.getCustomer().setCustomerName("AAA");
}
- 删
@Test
public void testDelete(){
//在不设定级联关系的情况下, 且 1 这一端的对象有 n 的对象在引用, 不能直接删除 1 这一端的对象
Customer customer = (Customer) session.get(Customer.class, 1);
session.delete(customer);
}
12. 尚硅谷_佟刚_Hibernate_双向一对多映射
双向1-n
-
双向 1-n 与 双向 n-1 是完全相同的两种情形
-
双向 1-n 需要在 1 的一端可以访问 n 的一端, 反之依然.
-
域模型:从 Order 到 Customer 的多对一双向关联需要在Order 类中定义一个 Customer 属性, 而在 Customer 类中需定义存放 Order 对象的集合属性。
-
-
关系数据模型:ORDERS 表中的 CUSTOMER_ID 参照 CUSTOMER 表的主键。
-
-
当 Session 从数据库中加载 Java 集合时, 创建的是 Hibernate 内置集合类的实例, 因此在持久化类中定义集合属性时必须把属性声明为 Java 接口类型
-
Hibernate 的内置集合类具有集合代理功能, 支持延迟检索策略
-
事实上, Hibernate 的内置集合类封装了 JDK 中的集合类, 这使得 Hibernate 能够对缓存中的集合对象进行脏检查, 按照集合对象的状态来同步更新数据库。
-
在定义集合属性时, 通常把它初始化为集合实现类的一个实例. 这样可以提高程序的健壮性, 避免应用程序访问取值为 null 的集合的方法抛出 NullPointerException
-
-
Hibernate 使用 元素来映射 set 类型的属性
-
<set>
key
13. 尚硅谷_佟刚_Hibernate_set 的 3 个属性
<set> 元素的 inverse 属性
- 在hibernate中通过对 inverse 属性的来决定是由双向关联的哪一方来维护表和表之间的关系. inverse = false 的为主动方,inverse = true 的为被动方, 由主动方负责维护关联关系
- 在没有设置 inverse=true 的情况下,父子两边都维护父子关系
- 在 1-n 关系中,将 n 方设为主控方将有助于性能改善(如果要国家元首记住全国人民的名字,不是太可能,但要让全国人民知道国家元首,就容易的多)
- 在 1-N 关系中,若将 1 方设为主控方
- 会额外多出 update 语句。
- 插入数据时无法同时插入外键列,因而无法为外键列添加非空约束
cascade 属性
- 在对象 – 关系映射文件中, 用于映射持久化类之间关联关系的元素,
<set>, <many-to-one> 和 <one-to-one>
都有一个 cascade 属性, 它用于指定如何操纵与当前对象关联的其他对象.
在数据库中对集合排序
<set>
元素有一个 order-by 属性, 如果设置了该属性, 当 Hibernate 通过 select 语句到数据库中检索集合对象时, 利用 order by 子句进行排序- order-by 属性中还可以加入 SQL 函数
14. 尚硅谷_佟刚_Hibernate_基于外键映射的1-1关联关系
- 域模型
- 关系数据模型
-
按照外键映射
-
-
按照主键映射
-
-
基于外键约束的1-1
- 对于基于外键约束的1-1关联,其外键可以存放在任意一边,在需要存放外键一端,增加many-to-one元素。为many-to-one元素增加unique=“true” 属性来表示为1-1关联
- 另一端需要使用one-to-one元素,该元素使用 property-ref 属性指定使用被关联实体主键以外的字段作为关联字段
- 不使用 property-ref 属性的 sql
- 使用 property-ref 属性的 sql
两边都使用外键映射的1-1
无法保证是真正的1-1,比如下图
15. 尚硅谷_佟刚_Hibernate_基于主键映射的1-1关联关系
-
基于主键的映射策略:指一端的主键生成器使用 foreign 策略,表明根据”对方”的主键来生成自己的主键,自己并不能独立生成主键. 子元素指定使用当前持久化类的哪个属性作为 “对方”。
-
采用foreign主键生成器策略的一端增加 one-to-one 元素映射关联属性,其one-to-one属性还应增加 constrained=“true” 属性;另一端增加one-to-one元素映射关联属性。
-
constrained(约束):指定为当前持久化类对应的数据库表的主键添加一个外键约束,引用被关联的对象(“对方”)所对应的数据库表主键