Hibernate初始

以下内容引用尚硅谷,如有侵权请联系删除
中文文档:https://hibernate.net.cn/

第一章 什么是Hibernate

1.1 对象的持久化

狭义的理解,“持久化"仅仅指把对象永久保存到数据库中

广义的理解,“持久化"包括和数据库相关的各种操作
(1)保存∶把对象永久保存到数据库中。
(2)更新∶更新数据库中对象(记录)的状态。
(3)删除︰从数据库中删除一个对象。
(4)查询:根据特定的查询条件,把符合查询条件的一个或多个对象从数据库加载到内存中。
(5)加载∶根据特定的OID,把一个对象从数据库加载到内存中。

OID--为了在系统中能够找到所需对象,需要为每一个对象分配一个唯一的标识号。在关系数据库中称之为主键,而在对象术语中,则叫做对象标识(Objectidentifier-OID).

OID对于数据库来说是主键,对于对象来说是主键对应的属性

1.2 ORM

ORM(Object/Relation Mapping):对象/关系映射
在这里插入图片描述
ORM的思想:将关系数据库中表中的记录映射成为对象,以对象的形式展现,程序员可以把对数据库的操作转化为对对象的操作。

ORM采用元数据来描述对象-关系映射细节,元数据通常采用XML格式,并且存放在专门的对象-关系映射文件中.

第二章 HelloWorld案例

在这里插入图片描述

(1)maven依赖

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
        <!--hibernate包--> 	
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.2.4.Final</version>
        </dependency>
		
		<!--mysql包-->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
		
		<!--lombok包-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
		
		<!--单元测试包-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

(2)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">1230</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.MySQLInnoDBDialect</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="News.hbm.xml"/>
	
	</session-factory>

</hibernate-configuration>

(3)实体类

package com.atguigu.hibernate.helloworld;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.sql.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class News {
    private Integer id;
    private String title;
    private String author;
    private Date date;

    public News(String title, String author, Date date) {
        this.title = title;
        this.author = author;
        this.date = date;
    }
}

(4)实体映射xml文件: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>
		
    </class>
    
</hibernate-mapping>

(5)测试

import com.atguigu.hibernate.helloworld.News;
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;

import java.util.Date;

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.sql.Date().getTime()));
		session.save(news);
		
		//5. 提交事务 
		transaction.commit();
		
		//6. 关闭 Session
		session.close();
		
		//7. 关闭 SessionFactory 对象
		sessionFactory.close();
	}
	
}

Configuration类

在这里插入图片描述

SessionFactory接口
在这里插入图片描述
Session接口
在这里插入图片描述
Transaction(事务)
在这里插入图片描述
Hibernate配置文件的两个配置项
在这里插入图片描述

第三章 通过Session操纵对象

3.1 Session概述

Session接口是Hibernate向应用程序提供的操纵数据库的最主要的接口,它提供了基本的保存,更新,删除和加载Java对象的方法.

Session具有一个缓存,位于缓存中的对象称为持久化对象,它和数据库中的相关记录对应. Session能够在某些时间点,按照缓存中对象的变化来执行相关的SQL语句,来同步更新数据库,这一过程被称为刷新缓存(flush)

站在持久化的角度, Hibernate把对象分为4种状态:持久化状态,临时状态,游离状态,删除状态. Session的特定方法能使对象从一个状态转换到另一个状态.

3.2 Session详解

Session缓存--flush

Session缓存解释

在Session接口的实现中包含一系列的Java集合,这些Java集合构成了Session缓存.只要Session实例没有结束生命周期,且没有清理缓存,则存放在它缓存中的对象也不会结束生命周期

Session缓存可减少Hibernate应用程序访问数据库的频率。
在这里插入图片描述
flush解释
(1)flush : Session按照缓存中对象的属性变化来同步更新数据库

(2)默认情况下Session在以下时间点刷新缓存:

  • 显式调用Session的flush()方法
  • 当应用程序调用Transaction的commit ( )方法的时,该方法先 flush,然后在向数据库提交事务
  • 当应用程序执行一些查询(HQL, Criteria)操作时,如果缓存中持久化对象的属性已经发生了变化,会先flush缓存,以保证查询结果能够反映持久化对象的最新状杰

(3)flush 缓存的例外情况:如果对象使用native 生成器生成OID,那么当调用Session的save()方法保存对象时,会立即执行向数据库插入该实体的insert语句

(4)commit()和flush()方法的区别:flush 执行一系列sql语句,但不提交事务;commit方法先调用flush()方法,然后提交事务.意味着提交事务意味着对数据库操作永久保存下来。
向容实扫相

Hibernate主键生成策略
在这里插入图片描述
设定刷新缓存的时间点
若希望改变flush的默认时间点,可以通过Session的setFlushMode()方法显式设定flush的时间点
在这里插入图片描述
数据库的隔离级别

(1)对于同时运行的多个事务,当这些事务访问数据库中相同的数据时如果没有采取必要的隔离机制,就会导致各种并发问题:

  • 脏读:对于两个事物T1,T2,T1读取了已经被T2更新但还没有被提交的字段之后,若T2回滚,T1读取的内容就是临时且无效的
  • 不可重复读:对于两个事物T1,T2,T1读取了一个字段,然后T2更新了该字段之后,T1再次读取同一个字段,值就不同了.
  • 幻读:对于两个事物T1,T2,T1从一个表中读取了一个字段,然后T2在该表中插入了一些新的行.之后,如果T1再次读取同一个表,就会多出几行.

(2)数据库事务的隔离性:数据库系统必须具有隔离并发运行各个事务的能力使它们不会相互影响,避免各种并发问题.

(3)一个事务与其他事务隔离的程度称为隔离级别.数据库规定了多种事务隔离级别,不同隔离级别对应不同的干扰程度,隔离级别越高,数据一致性就越好,但并发性越弱

在这里插入图片描述
Oracle支持的2种事务隔离级别: READ COMMITED,SERIALIZABLE.Oracle默认的事务隔离级别为:READCOMMITED

Mysql支持4中事务隔离级别. Mysal默认的事务隔离级别为:REPEATABLE READ

在MySql中设置隔离级别

(1)每启动一个mysql程序,就会获得一个单独的数据库连接.每个数据库连接都有一个全局变量@@tx_isolation,表示当前的事务隔离级别.MySQL默认的隔离级别为Repeatable Read

(2)查看当前的隔离级别: SELECT @@tx_isolation;

(3)设置当前mySQL 连接的隔离级别:

  • set transaction isolation level read committed;

(4)设置数据库系统的全局的隔离级别:

  • 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属性来设置事务的隔离级别

持久化对象的状态

站在持久化的角度, Hibernate把对象分为4种状态:持久化状态,临时状态,游离状态,删除状态.Session的特定方法能使对象从一个状态转换到另一个状态.

临时对象(Transient):

  • 在使用代理主键的情况下,OID通常为null
  • 不处于Session的缓存中
  • 在数据库中没有对应的记录

持久化对象(也叫“托管”)(Persist):

  • OID不为null
  • 位于Session缓存中
  • 若在数据库中已经有和其对应的记录,持久化对象和数据库中的相关记录对应
  • Session在flush缓存时,会根据持久化对象的属性变化来同步更新数据库
  • 在同一个Session实例的缓存中,数据库表中的每条记录只对应唯一的持久化对象

删除对象(Removed)

  • 在数据库中没有和其OID对应的记录
  • 不再处于Session缓存中
  • 般情况下,应用程序不该再使用被删除的对象

游离对象(也叫“脱管”)(Detached)

  • OID不为null
  • 不再处于Session缓存中
  • —般情况需下,游离对象是由持久化对象转变过来的,因此在数据库中可能还存在与它对应的记录

在这里插入图片描述
Session的save()方法
(1)Session的save()方法使一个l临时对象转变为持久化对象

(2)Session的save()方法完成以下操作:

  • 把News 对象加入到Session缓存中,使它进入持久化状态
  • 选用映射文件指定的标识符生成器,为持久化对象分配唯一的OID.在使用代理主键的情况下,setld()方法为News对象设置OID使无效的.
  • 计划执行一条insert语句:在flush 缓存的时候

(3)Hibernate通过持久化对象的OID来维持它和数据库相关记录的对应关系.当News 对象处于持久化状态时,不允许程序随意修改它的ID

(4)persist(和save()区别:当对一个OID不为Null的对象执行save()方法时,会把该对象以一个新的oid 保存到数据库中;但执行persist()方法时会抛出一个异常

Session的get()和load()方法

load方法返回的是一个代理对象,而get方法返回的就是对象本身

(1)都可以根据跟定的OID从数据库中加载一个持久化对象

(2)区别:a、当数据库中不存在与OID对应的记录时, load()方法抛出ObjectNotFoundException异常,而get()方法返回null;b、两者采用不同的延迟检索策略: load方法支持延迟加载策略。而get 不支持。

Session的update()方法

(1)Session的update()方法使一个游离对象转变为持久化对象,并且计划执行一条update语句.

(2)若希望Session仅当修改了News 对象的属性时,才执行update()语句,可以把映射文件中元素的select-before-update 设为true.该属性的默认值为false

(3)update()方法关联一个游离对象时,如果在Session的缓存中已经存在相同OID的持久化对象,会抛出异常

(4)当update()方法关联一个游离对象时,如果在数据库中不存在相应的记录,也会抛出异常.

Session的saveOrUpdate()方法

(1)Session的saveOrUpdate()方法同时包含了save()与update()方法的功能
在这里插入图片描述
(2)判定对象为临时对象的标准

  • Java 对象的OID为null
  • 映射文件中为标签id设置了unsaved-value属性并且Java对象的OID取值与这个unsaved-value属性值匹配

Session的delete()方法
(1)Session的delete()方法既可以删除一个游离对象,也可以删除一个持久化对象

(2)Session的delete()方法处理过程:a、计划执行一条delete语句;b、把对象从Session 缓存中删除,该对象进入删除状态.

(3)Hibernate的cfg.xml配置文件中有一个hibernate.use. .identifier rollback属性,其默认值为false,若把它设为true,将改变delete()方法的运行行为: delete()方法会把持久化对象或游离对象的OID设置为null,使它们变为临时对象

Session的merge()方法
在这里插入图片描述

个人总结

临时状态--OID为null
持久化--OID不为null,存在于session中,数据库中存在对应记录
游离--OID不为null,不位于session中,数据库中存在对应记录
删除--OID为null,不位于session中
save--临时变为持久(如果对象OID人为赋值,save以后,OID会变化)
persist--临时变为持久(如果对象OID人为赋值,persist抛出异常)
get,load--延迟加载
update--OID不为null;db中存在对应记录,update;db中不存在对应记录,抛出异常
delete--游离或持久化变为删除,等待垃圾回收,执行删除操作(只要 OID 和数据表中一条记录对应, 就会准备执行 delete 操作,若 OID 在数据表中没有对应的记录, 则抛出异常)
evict--从session缓存中移除

第四章 配置文件

4.1 Hibernate的配置文件

Hibernate配置文件主要用于配置数据库连接和Hibernate运行时所需的各种属性

每个Hibernate配置文件对应一个Configuration 对象

Hibernate配置文件可以有两种格式:
(1)hibernate.propertles(2)hibernate.cfg.xml

JDBC连接属性

connection.url :数据库URL
connection.username :数据库用户名
connection.passsword :数据库用户密码
connection.driver_class:数据库JDBC驱动
dialect :配置数据库的方言,根据底层的数据库不同产生不同的sql语句,Hibernate会针对数据库的特性在访问时进行优化

C3P0数据库连接池属性

在这里插入图片描述

其他

在这里插入图片描述
在这里插入图片描述

4.2 对象关系映射文件

(1)POJO类和数据库的映射文件*.hbm.xml
(2)POJO类和关系数据库之间的映射可以用一个XML文档来定义
(3)通过POJO类的数据库映射文件,Hibernate可以理解持久化类和数据表之间的对应关系,也可以理解持久化类属性与数据库表列之间的对应关系
(4)在运行时 Hibernate 将根据这个映射文件来生成各种SQL语句
(5)映射文件的扩展名为.hbm.xml

介绍

在这里插入图片描述

hibernate-mapping

在这里插入图片描述

class

在这里插入图片描述

映射对象标识符--id

Hibernate使用对象标识符(OID)来建立内存中的对象和数据库表中记录的对应关系.对象的OID和数据表的主键对应.Hibernate通过标识符生成器来为主键赋值

Hibernate推荐在数据表中使用代理主键,即不具备业务含义的字段.代理主键通常为整数类型,因为整数类型比字符串类型要节省更多的数据库空间.

在对象-关系映射文件中,id元素用来设置对象标识符.generator子元素用来设定标识符生成器.

Hibernate提供了标识符生成器接口: ldentifierGenerator,并提供了各种内置实现

id
在这里插入图片描述
generator
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

属性--Property

在这里插入图片描述
在这里插入图片描述

Java、Hibernate、SQL映射类型

在这里插入图片描述
在这里插入图片描述

Java时间和日期类型的Hibernate映射

在这里插入图片描述

使用Hibernate内置映射类型

在这里插入图片描述

Java 大对象类型的Hibernate映射

在这里插入图片描述

映射组成关系--component

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

通过Hibernate调用存储过程

在这里插入图片描述

Hibernate与触发器协同工作

在这里插入图片描述

第五章 映射一对多关联关系

5.1 介绍

在这里插入图片描述

5.2 多对一

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

5.3 双向一对多

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第六章 代码阶段性总结

在这里插入图片描述
maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>hibernate-2</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
		
		<!--hibernate包-->
        <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.2.4.Final</version>
        </dependency>
		
		<!--mysql包-->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
		
		<!--lombok包-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
		
		<!--单元测试包-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
		
		<!--c3p0包-->
        <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
		
		<!--hibernate-c3p0包-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-c3p0</artifactId>
            <version>4.2.5.Final</version>
        </dependency>
    </dependencies>
</project>
<?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.driver_class">com.mysql.jdbc.Driver</property>
    	<property name="connection.url">jdbc:mysql://172.16.19.104:3306/hibernate5</property>
		<property name="connection.username">springcloud</property>
		<property name="connection.password">springcloud</property>

		<!-- Hibernate 的基本配置 -->
		<!-- Hibernate 使用的数据库方言 -->
		<!-- 在MYSQL5.5及以后版本中type=InnoDB 由ENGINE=InnoDB  代替-->
		<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="hibernate.c3p0.acquire_increment">2</property>
    	
    	<property name="hibernate.c3p0.idle_test_period">2000</property>
    	<property name="hibernate.c3p0.timeout">2000</property>
    	
    	<property name="hibernate.c3p0.max_statements">10</property>
    	
    	<!-- 设定 JDBC 的 Statement 读取数据的时候每次从数据库中取出的记录条数 -->
    	<property name="hibernate.jdbc.fetch_size">100</property>
    	
    	<!-- 设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小 -->
    	<property name="hibernate.jdbc.batch_size">30</property>
    	
    	<!-- 需要关联的 hibernate 映射文件 .hbm.xml -->
    	<mapping resource="News.hbm.xml"/>
    	<mapping resource="Worker.hbm.xml"/>

    	<mapping resource="Customer.hbm.xml"/>
    	<mapping resource="Order.hbm.xml"/>


    	<mapping resource="both/Customer.hbm.xml"/>
    	<mapping resource="both/Order.hbm.xml"/>
    
    </session-factory>
</hibernate-configuration>

6.1 对象状态、映射组成关系--com.atguigu.hibernate.entities

package com.atguigu.hibernate.entities;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.sql.Blob;
import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class News {

    private Integer id;
    private String title;
    private String author;

    private Date date;

    private String desc;

    private String content;

    private Blob image;

    public News(String title, String author, Date date) {
        super();
        this.title = title;
        this.author = author;
        this.date = date;
    }

}

package com.atguigu.hibernate.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Pay {
	
	private int monthlyPay;

	private int yearPay;

	private int vocationWithPay;
	
	private Worker worker;

	
}

package com.atguigu.hibernate.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Worker {
	
	private Integer id;

	private String name;
	
	private Pay pay;

}

<?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>
        
        <!-- 映射派生属性 -->
        <property name="desc" formula="(SELECT concat(author, ': ', title) FROM NEWS n WHERE n.id = id)"></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>
        
    </class>
    
</hibernate-mapping>

<?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>

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.jdbc.Work;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Date;

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();
    }

    /**
     * 只会生成一条select语句
     * 缺陷:在第一个news获取好以后,此时人为的修改数据库的数据,导致缓存的数据news和数据库中不一致
     * 解决:使用refresh方法
     */
    @Test
    public void testOne() {
        News news = (News) session.get(News.class, 1);
        System.out.println(news);

        News news2 = (News) session.get(News.class, 1);
        System.out.println(news2);
    }

    //flush:如果对象的属性改变,会自动生成相应的更新语句

    /**
     * flush:使数据表中的记录和 Session 缓存中的对象的状态保持一致. 为了保持一致, 则可能会发送对应的 SQL 语句
     * 默认在commit方法之前会调用flush方法,如果发现对象的属性改变,会生成相应的更新语句,保证一致
     */
    @Test
    public void testSessionFlush(){
        News news = (News) session.get(News.class, 1);
        news.setAuthor("Oracle");//会生成update语句
    }

    /**
     * flush: 使数据表中的记录和 Session 缓存中的对象的状态保持一致. 为了保持一致, 则可能会发送对应的 SQL 语句.
     * 1. 在 Transaction 的 commit() 方法中: 先调用 session 的 flush 方法, 再提交事务
     * 2. flush() 方法会可能会发送 SQL 语句, 但不会提交事务.
     * 3. 注意: 还没有执行到commit方法或不显式的调用 session.flush() ,也有可能会进行 flush() 操作.
     * 1). 执行 HQL 或 QBC 查询, 会先进行 flush() 操作, 以得到数据表的最新的记录
     * 2). 若记录的 ID 是由底层数据库使用自增的方式生成的, 则在调用 save() 方法时, 就会立即发送 INSERT 语句.
     * 因为 save 方法后, 必须保证对象的 ID 是存在的!
     */

    //执行 HQL 或 QBC 查询, 会先进行 flush() 操作, 以得到数据表的最新的记录
    @Test
    public void testSessionFlush2() {

        News news = (News) session.get(News.class, 1);
        //news.setAuthor("123");

        //自动执行flush方法(上面对象属性可能改变)
        News news1 = (News)session.createCriteria(News.class).uniqueResult();
    }

    /**
     * 若记录的 ID 是由底层数据库使用自增的方式生成的, 则在调用 save() 方法时, 就会立即发送 INSERT 语句.
     * 因为对象的ID是数据库生成,需要获取对象的ID属性值,所以会立即发送
     */
    @Test
    public void testSessionFlush3(){
        News news = new News("Java", "SUN", new Date());
        session.save(news);
    }

    /**
     * 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(): 清理缓存
     */
    @Test
    public void testClear(){
        News news1 = (News) session.get(News.class, 1);

        session.clear();

        News news2 = (News) session.get(News.class, 1);
    }

    /**
     * 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);
    }

    @Test
    public void testGet(){
        News news = (News) session.get(News.class, 1);
        //session.close();
        System.out.println(news);
    }

    /**
     * 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);
    }

    /**
     * update:
     * 1. 若更新一个持久化对象, 不需要显示的调用 update 方法. 因为在调用 Transaction
     * 的 commit() 方法时, 会先执行 session 的 flush 方法.
     * News news = (News) session.get(News.class, 1); news.setAuthor("SUN");
     *
     * 2. 更新一个游离对象, 需要显式的调用 session 的 update 方法. 可以把一个游离对象
     * 变为持久化对象
     * News news = (News) session.get(News.class, 1);
     * transaction.commit();
     * session.close();//运行完这行代码代码,news成为游离对象(不在session的缓存中)
     * session = sessionFactory.openSession();
     * transaction = session.beginTransaction();
     * news.setAuthor("SUN");
     * session.update(news);
     *
     * 更新一个游离对象, 需要显式的调用 session 的 update 方法. 可以把一个游离对象
     * 变为持久化对象
     * 需要注意的:
     * 1. 无论要更新的游离对象和数据表的记录是否一致, 都会发送 UPDATE 语句.
     *    如何能让 updat 方法不再盲目的出发 update 语句呢 ? 在 .hbm.xml 文件的 class 节点设置
     *    select-before-update=true (默认为 false). 但通常不需要设置该属性.
     *
     * 2. 若数据表中没有对应的记录, 但还调用了 update 方法, 会抛出异常
     * News news = (News) session.get(News.class, 1);
     * transaction.commit();
     * session.close();//运行完这行代码代码,news成为游离对象(不在session的缓存中)
     * news.setId(100);
     * session = sessionFactory.openSession();
     * transaction = session.beginTransaction();
     * news.setAuthor("SUN");
     * session.update(news);
     *
     * 3. 当 update() 方法关联一个游离对象时,
     * 如果在 Session 的缓存中已经存在相同 OID 的持久化对象, 会抛出异常. 因为在 Session 缓存中不能有两个 OID 相同的对象!
     * News news = (News) session.get(News.class, 1);
     * transaction.commit();
     * session.close();//运行完这行代码代码,news成为游离对象(不在session的缓存中)
     * session = sessionFactory.openSession();
     * transaction = session.beginTransaction();
     * News news2 = (News) session.get(News.class, 1);
     * session.update(news);
     *
     */
    @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);
    }

    /**
     * 注意:
     * 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);
    }

    /**
     * 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);
        news.setAuthor("123321");

        System.out.println(news);
    }

    /**
     * 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);
    }

    @Test
    public void testDoWork(){
        session.doWork(new Work() {

            public void execute(Connection connection) throws SQLException {
                System.out.println(connection);

                //调用存储过程.
            }
        });
    }

    @Test
    public void testHe() {
        News o = (News)session.get(News.class, 1);
        System.out.println(o);
    }


}

6.2 多对一--com.atguigu.hibernate.entities.n21

package com.atguigu.hibernate.entities.n21;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@AllArgsConstructor
@NoArgsConstructor
@Data
public class Customer {
    private Integer customerId;
    private String customerName;
}

package com.atguigu.hibernate.entities.n21;

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;
	}
		
}

<?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>

<?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>

package n21;


import com.atguigu.hibernate.entities.n21.Customer;
import com.atguigu.hibernate.entities.n21.Order;
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.After;
import org.junit.Before;
import org.junit.Test;

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 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);
    }

}

6.3 双向一对多--com.atguigu.hibernate.entities.n21.both

package com.atguigu.hibernate.entities.n21.both;

import java.util.HashSet;
import java.util.Set;

public class Customer {

	private Integer customerId;
	private String customerName;
	
	/*
	 * 1. 声明集合类型时, 需使用接口类型, 因为 hibernate 在获取
	 * 集合类型时, 返回的是 Hibernate 内置的集合类型, 而不是 JavaSE 一个标准的
	 * 集合实现. 
	 * 2. 需要把集合进行初始化, 可以防止发生空指针异常
	 */
	private Set<Order> orders = new HashSet<Order>();

	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 Set<Order> getOrders() {
		return orders;
	}

	public void setOrders(Set<Order> orders) {
		this.orders = orders;
	}

}

package com.atguigu.hibernate.entities.n21.both;

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;
	}
}

<?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.both">
    
    <class name="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>
        
        <!-- 映射 1 对多的那个集合属性 -->
        <!-- set: 映射 set 类型的属性, table: set 中的元素对应的记录放在哪一个数据表中. 该值需要和多对一的多的那个表的名字一致 -->
        <!-- inverse: 指定由哪一方来维护关联关系. 通常设置为 true, 以指定由多的一端来维护关联关系 -->
        <!-- cascade 设定级联操作. 开发时不建议设定该属性. 建议使用手工的方式来处理 -->
        <!-- order-by 在查询时对集合中的元素进行排序, order-by 中使用的是表的字段名, 而不是持久化类的属性名  -->
        <set name="orders" table="ORDERS" inverse="true" order-by="ORDER_NAME DESC">
        	<!-- 执行多的表中的外键列的名字 -->
        	<key column="CUSTOMER_ID"></key>
        	<!-- 指定映射类型 -->
        	<one-to-many class="Order"/>
        </set>
        
    </class>
    
</hibernate-mapping>

<?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.both">

    <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>

package n21.both;

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.After;
import org.junit.Before;
import org.junit.Test;

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 testMany2OneSave(){
		Customer customer = new Customer();
		customer.setCustomerName("AA");

		Order order1 = new Order();
		order1.setOrderName("ORDER-1");

		Order order2 = new Order();
		order2.setOrderName("ORDER-2");

		//设定关联关系
		order1.setCustomer(customer);
		order2.setCustomer(customer);

		customer.getOrders().add(order1);
		customer.getOrders().add(order2);

		//执行  save 操作: 先插入 Customer, 再插入 Order, 3 条 INSERT, 2 条 UPDATE
		//因为 1 的一端和 n 的一端都维护关联关系. 所以会多出 UPDATE
		//可以在 1 的一端的 set 节点指定 inverse=true, 来使 1 的一端放弃维护关联关系!
		//建议设定 set 的 inverse=true, 建议先插入 1 的一端, 后插入多的一端
		//好处是不会多出 UPDATE 语句
		session.save(customer);

//		session.save(order1);
//		session.save(order2);

		//先插入 Order, 再插入 Cusomer, 3 条 INSERT, 4 条 UPDATE
//		session.save(order1);
//		session.save(order2);
//
//		session.save(customer);
	}

	@Test
	public void testOne2ManyGet(){
		//1. 对 n 的一端的集合使用延迟加载
		Customer customer = (Customer) session.get(Customer.class, 7);
		System.out.println(customer.getCustomerName());
		//2. 返回的多的一端的集合时 Hibernate 内置的集合类型.
		//该类型具有延迟加载和存放代理对象的功能.
		System.out.println(customer.getOrders().getClass());

		//session.close();
		//3. 可能会抛出 LazyInitializationException 异常

		System.out.println(customer.getOrders().size());

		//4. 再需要使用集合中元素的时候进行初始化.
	}

	@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 testUpdat2(){
		Customer customer = (Customer) session.get(Customer.class, 1);
		customer.getOrders().iterator().next().setOrderName("GGG");
	}

	@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);
	}


	@Test
	public void testCascade(){
		Customer customer = (Customer) session.get(Customer.class, 3);
		customer.getOrders().clear();
	}
}

第七章 映射一对一关联关系

7.1 介绍

在这里插入图片描述

7.2 基于外键映射的1-1

在这里插入图片描述

7.3 基于主键映射的1-1

在这里插入图片描述

第八章 映射多对多关联关系

8.1 单向n-n

在这里插入图片描述
在这里插入图片描述

8.2 双向n-n

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第九章 映射继承关系

9.1 介绍

在这里插入图片描述
在这里插入图片描述

9.2 采用subclass元素的继承映射

在这里插入图片描述
在这里插入图片描述

9.3 采用joined-subclass元素的继承映射

在这里插入图片描述
在这里插入图片描述

9.4 采用union-subclass元素的继承映射

在这里插入图片描述
在这里插入图片描述

9.5 三种继承映射方式的比较

在这里插入图片描述

第十章 Hibernate检索策略

10.1 概述

在这里插入图片描述

10.2 类级别的检索策略

在这里插入图片描述
在这里插入图片描述

10.3 —对多和多对多的检索策略

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

10.4 多对—和一对一关联的检索策略

在这里插入图片描述
在这里插入图片描述

第十一章 代码阶段性总结

在这里插入图片描述

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>hibernate-3</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
    	<!--hibernate包-->
        <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.2.4.Final</version>
        </dependency>
		
		<!--mysql包-->
        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
		
		<!--lombok包-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
		
		 <!--单元测试包-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
		
		<!--c3p0包-->
        <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
		
		<!--hibernate-c3p0包-->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-c3p0</artifactId>
            <version>4.2.5.Final</version>
        </dependency>
    </dependencies>
</project>
<?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.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://172.16.19.104:3306/hibernate5</property>
        <property name="connection.username">springcloud</property>
        <property name="connection.password">springcloud</property>

        <!-- Hibernate 的基本配置 -->
        <!-- Hibernate 使用的数据库方言 -->
        <!-- 在MYSQL5.5及以后版本中type=InnoDB 由ENGINE=InnoDB  代替-->
        <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="hibernate.c3p0.acquire_increment">2</property>

        <property name="hibernate.c3p0.idle_test_period">2000</property>
        <property name="hibernate.c3p0.timeout">2000</property>

        <property name="hibernate.c3p0.max_statements">10</property>

        <!-- 设定 JDBC 的 Statement 读取数据的时候每次从数据库中取出的记录条数 -->
        <property name="hibernate.jdbc.fetch_size">100</property>

        <!-- 设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小 -->
        <property name="hibernate.jdbc.batch_size">30</property>

        <!-- 需要关联的 hibernate 映射文件 .hbm.xml -->
    </session-factory>
</hibernate-configuration>

11.1 外键1-1:com.atguigu.hibernate.one2one.foreign

package com.atguigu.hibernate.one2one.foreign;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Department {

	private Integer deptId;
	private String deptName;
	
	private Manager mgr;
	
}

package com.atguigu.hibernate.one2one.foreign;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Manager {

	private Integer mgrId;
	private String mgrName;
	
	private Department dept;

}

<?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.one2one.foreign.Manager" table="MANAGERS">
        
        <id name="mgrId" type="java.lang.Integer">
            <column name="MGR_ID" />
            <generator class="native" />
        </id>
        
        <property name="mgrName" type="java.lang.String">
            <column name="MGR_NAME" />
        </property>
        
        <!-- 映射 1-1 的关联关系: 在对应的数据表中已经有外键了, 当前持久化类使用 one-to-one 进行映射 -->
        <!-- 
        	没有外键的一端需要使用one-to-one元素,该元素使用 property-ref 属性指定使用被关联实体主键以外的字段作为关联字段
        	如果不使用property-ref指定,默认使用主键也就是MGR_ID与Department进行左外关联
         -->
        <one-to-one name="dept" 
        	class="com.atguigu.hibernate.one2one.foreign.Department"
        	property-ref="mgr"></one-to-one>
        
    </class>
    
</hibernate-mapping>

<?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.one2one.foreign.Department" table="DEPARTMENTS">

        <id name="deptId" type="java.lang.Integer">
            <column name="DEPT_ID" />
            <generator class="native" />
        </id>
        
        <property name="deptName" type="java.lang.String">
            <column name="DEPT_NAME" />
        </property>
		
		<!-- 使用 many-to-one 的方式来映射 1-1 关联关系 -->
		<many-to-one name="mgr" class="com.atguigu.hibernate.one2one.foreign.Manager" 
			column="MGR_ID" unique="true"></many-to-one>
			        
    </class>
</hibernate-mapping>

package one2one.foreign;


import com.atguigu.hibernate.one2one.foreign.Department;
import com.atguigu.hibernate.one2one.foreign.Manager;
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.After;
import org.junit.Before;
import org.junit.Test;

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 testSave() {
        Department department = new Department();
        department.setDeptName("DEPT-BB");

        Manager manager = new Manager();
        manager.setMgrName("MGR-BB");

        //设定关联关系
        department.setMgr(manager);
        manager.setDept(department);

        //保存操作
        //建议先保存没有外键列的那个对象. 这样会减少 UPDATE 语句
        session.save(department);
        session.save(manager);
    }

    @Test
    public void testGet() {
        //1. 默认情况下对关联属性使用懒加载
        Department dept = (Department)session.get(Department.class, 1);
        System.out.println(dept.getDeptName());

        //2. 所以会出现懒加载异常的问题.
		//session.close();
		//Manager mgr = dept.getMgr();
		//System.out.println(mgr.getClass());
		//System.out.println(mgr.getMgrName());

        //3. 查询 Manager 对象的连接条件应该是 dept.manager_id = mgr.manager_id
        //而不应该是 dept.dept_id = mgr.manager_id
        Manager mgr = dept.getMgr();
        System.out.println(mgr.getMgrName());
    }

    @Test
    public void testGet2(){
        /**
         * 在查询没有外键的实体对象时, 使用的左外连接查询, 一并查询出其关联的对象
         * 并已经进行初始化.
         */
        Manager mgr = (Manager) session.get(Manager.class, 1);
        System.out.println(mgr.getMgrName());
        System.out.println(mgr.getDept().getDeptName());
    }

}

11.2 主键1-1:com.atguigu.hibernate.one2one.primary

package com.atguigu.hibernate.one2one.primary;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Department {

	private Integer deptId;
	private String deptName;
	
	private Manager mgr;

}

package com.atguigu.hibernate.one2one.primary;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Manager {

	private Integer mgrId;
	private String mgrName;
	
	private Department dept;

}

<?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.one2one.primary.Manager" table="MANAGERS">
        
        <id name="mgrId" type="java.lang.Integer">
            <column name="MGR_ID" />
            <generator class="native" />
        </id>
        
        <property name="mgrName" type="java.lang.String">
            <column name="MGR_NAME" />
        </property>

        <one-to-one name="dept" class="com.atguigu.hibernate.one2one.primary.Department" ></one-to-one>
        
    </class>
    
</hibernate-mapping>

<?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.one2one.primary">

    <class name="Department" table="DEPARTMENTS">

        <id name="deptId" type="java.lang.Integer">
            <column name="DEPT_ID" />
            <!-- 使用外键的方式来生成当前的主键 -->
            <generator class="foreign">
                <!-- property 属性指定使用当前持久化类的哪一个属性的主键作为外键 -->
                <param name="property">mgr</param>
            </generator>
        </id>

        <property name="deptName" type="java.lang.String">
            <column name="DEPT_NAME" />
        </property>

        <!--
        采用 foreign 主键生成器策略的一端增加 one-to-one 元素映射关联属性,
        其 one-to-one 节点还应增加 constrained=true 属性, 以使当前的主键上添加外键约束
        -->
        <one-to-one name="mgr" class="Manager" constrained="true"></one-to-one>

    </class>
</hibernate-mapping>

package one2one.primary;


import com.atguigu.hibernate.one2one.primary.Department;
import com.atguigu.hibernate.one2one.primary.Manager;
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.After;
import org.junit.Before;
import org.junit.Test;

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 testSave(){

        Department department = new Department();
        department.setDeptName("DEPT-DD");

        Manager manager = new Manager();
        manager.setMgrName("MGR-DD");

        //设定关联关系
        manager.setDept(department);
        department.setMgr(manager);

        //保存操作
        //先插入哪一个都不会有多余的 UPDATE
        session.save(department);
        session.save(manager);

    }

}

11.3 多对多n-n:com.atguigu.hibernate.n2n

package com.atguigu.hibernate.n2n;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.HashSet;
import java.util.Set;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Category {

	private Integer id;
	private String name;
	
	private Set<Item> items = new HashSet<Item>();
}

package com.atguigu.hibernate.n2n;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.HashSet;
import java.util.Set;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Item {

	private Integer id;
	private String name;
	
	private Set<Category> categories = new HashSet<Category>();
	
}

<?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.n2n">

    <class name="Category" table="CATEGORIES">
    
        <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>
        
        <!-- table: 指定中间表 -->
        <set name="items" table="CATEGORIES_ITEMS">
            <key>
                <column name="C_ID" />
            </key>
            <!-- 使用 many-to-many 指定多对多的关联关系. column 执行 Set 集合中的持久化类在中间表的外键列的名称  -->
            <many-to-many class="Item" column="I_ID"></many-to-many>
        </set>
        
    </class>
</hibernate-mapping>

<?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.n2n.Item" table="ITEMS">
        
        <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>
        
        <set name="categories" table="CATEGORIES_ITEMS" inverse="true">
        	<key column="I_ID"></key>
        	<many-to-many class="com.atguigu.hibernate.n2n.Category" column="C_ID"></many-to-many>
        </set>
        
    </class>
</hibernate-mapping>

package n2n;

import com.atguigu.hibernate.n2n.Category;
import com.atguigu.hibernate.n2n.Item;
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.After;
import org.junit.Before;
import org.junit.Test;

import java.util.Set;

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 testSave(){
		Category category1 = new Category();
		category1.setName("C-AA");

		Category category2 = new Category();
		category2.setName("C-BB");

		Item item1 = new Item();
		item1.setName("I-AA");

		Item item2 = new Item();
		item2.setName("I-BB");

		//设定关联关系
		category1.getItems().add(item1);
		category1.getItems().add(item2);

		category2.getItems().add(item1);
		category2.getItems().add(item2);

		item1.getCategories().add(category1);
		item1.getCategories().add(category2);

		item2.getCategories().add(category1);
		item2.getCategories().add(category2);

		//执行保存操作
		session.save(category1);
		session.save(category2);

		session.save(item1);
		session.save(item2);
	}
	
	@Test
	public void testGet(){
		Category category = (Category) session.get(Category.class, 1);
		System.out.println(category.getName()); 
		
		//需要连接中间表
		Set<Item> items = category.getItems();
		System.out.println(items.size()); 
	}

}

11.4 subclass:com.atguigu.hibernate.subclass

package com.atguigu.hibernate.subclass;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
	
	private Integer id;
	private String name;
	private int age;

}

package com.atguigu.hibernate.subclass;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student extends Person{

	private String school;

}

<?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.subclass">

    <class name="Person" table="PERSONS" discriminator-value="PERSON">

        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        
        <!-- 配置辨别者列 -->
		<discriminator column="TYPE" type="string"></discriminator>

        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
        <property name="age" type="int">
            <column name="AGE" />
        </property>
        
        <!-- 映射子类 Student, 使用 subclass 进行映射 -->
        <subclass name="Student" discriminator-value="STUDENT">
        	<property name="school" type="string" column="SCHOOL"></property>
        </subclass>
        
    </class>
</hibernate-mapping>

package subclass;

import com.atguigu.hibernate.subclass.Person;
import com.atguigu.hibernate.subclass.Student;
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.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

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();
	}

	/**
	 * 插入操作:
	 * 1. 对于子类对象只需把记录插入到一张数据表中.
	 * 2. 辨别者列有 Hibernate 自动维护.
	 */
	@Test
	public void testSave(){

		Person person = new Person();
		person.setAge(11);
		person.setName("AA");

		session.save(person);

		Student stu = new Student();
		stu.setAge(22);
		stu.setName("BB");
		stu.setSchool("ATGUIGU");

		session.save(stu);

	}
	
	/**
	 * 缺点:
	 * 1. 使用了辨别者列.
	 * 2. 子类独有的字段不能添加非空约束.
	 * 3. 若继承层次较深, 则数据表的字段也会较多. 
	 */
	
	/**
	 * 查询:
	 * 1. 查询父类记录, 只需要查询一张数据表
	 * 2. 对于子类记录, 也只需要查询一张数据表
	 */
	@Test
	public void testQuery(){
		List<Person> persons = session.createQuery("FROM Person").list();
		System.out.println(persons.size()); 
		
		List<Student> stus = session.createQuery("FROM Student").list();
		System.out.println(stus.size()); 
	}

}

11.5 joinedsubclass:com.atguigu.hibernate.joinedsubclass

package com.atguigu.hibernate.joinedsubclass;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
    private Integer id;
    private String name;
    private int age;
}

package com.atguigu.hibernate.joinedsubclass;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student extends Person {
    private String school;
}

<?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.joinedsubclass">

    <class name="Person" table="PERSONS">

        <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>
        
        <property name="age" type="int">
            <column name="AGE" />
        </property>
        
        <joined-subclass name="Student" table="STUDENTS">
        	<key column="STUDENT_id"></key>
        	<property name="school" type="string" column="SCHOOL"></property>
        </joined-subclass>
        
    </class>
</hibernate-mapping>

package joinedsubclass;

import com.atguigu.hibernate.joinedsubclass.Person;
import com.atguigu.hibernate.joinedsubclass.Student;
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.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

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();
	}

	/**
	 * 插入操作:
	 * 1. 对于子类对象至少需要插入到两张数据表中.
	 */
	@Test
	public void testSave(){

		Person person = new Person();
		person.setAge(11);
		person.setName("AA");

		session.save(person);

		Student stu = new Student();
		stu.setAge(22);
		stu.setName("BB");
		stu.setSchool("ATGUIGU");

		session.save(stu);

	}
	
	/**
	 * 优点:
	 * 1. 不需要使用了辨别者列.
	 * 2. 子类独有的字段能添加非空约束.
	 * 3. 没有冗余的字段. 
	 */
	
	/**
	 * 查询:
	 * 1. 查询父类记录, 做一个左外连接查询
	 * 2. 对于子类记录, 做一个内连接查询. 
	 */
	@Test
	public void testQuery(){
		List<Person> persons = session.createQuery("FROM Person").list();
		System.out.println(persons.size()); 
		
		List<Student> stus = session.createQuery("FROM Student").list();
		System.out.println(stus.size()); 
	}

}

11.6 unionsubclass:com.atguigu.hibernate.unionsubclass

package com.atguigu.hibernate.unionsubclass;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Person {
	
	private Integer id;
	private String name;
	private int age;

}

package com.atguigu.hibernate.unionsubclass;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student extends Person {

	private String school;

}

<?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.unionsubclass">

    <class name="Person" table="PERSONS">

        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="hilo" />
        </id>
        
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
        <property name="age" type="int">
            <column name="AGE" />
        </property>
	
		<union-subclass name="Student" table="STUDENTS">
			<property name="school" column="SCHOOL" type="string"></property>
		</union-subclass>        
        
    </class>
</hibernate-mapping>

package unionsubclass;

import com.atguigu.hibernate.unionsubclass.Person;
import com.atguigu.hibernate.unionsubclass.Student;
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.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

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();
	}

	/**
	 * 插入操作:
	 * 1. 对于子类对象只需把记录插入到一张数据表中.
	 */
	@Test
	public void testSave(){

		Person person = new Person();
		person.setAge(11);
		person.setName("AA");

		session.save(person);

		Student stu = new Student();
		stu.setAge(22);
		stu.setName("BB");
		stu.setSchool("ATGUIGU");

		session.save(stu);

	}
	
	/**
	 * 优点:
	 * 1. 无需使用辨别者列.
	 * 2. 子类独有的字段能添加非空约束.
	 * 
	 * 缺点:
	 * 1. 存在冗余的字段
	 * 2. 若更新父表的字段, 则更新的效率较低
	 */
	
	/**
	 * 查询:
	 * 1. 查询父类记录, 需把父表和子表记录汇总到一起再做查询. 性能稍差. 
	 * 2. 对于子类记录, 也只需要查询一张数据表
	 */
	@Test
	public void testQuery(){
		List<Person> persons = session.createQuery("FROM Person").list();
		System.out.println(persons.size()); 
		
		List<Student> stus = session.createQuery("FROM Student").list();
		System.out.println(stus.size()); 
	}

	@Test
	public void testUpdate(){
		String hql = "UPDATE Person p SET p.age = 20";
		session.createQuery(hql).executeUpdate();
	}

}

11.7 策略:com.atguigu.hibernate.strategy

package com.atguigu.hibernate.strategy;

import java.util.HashSet;
import java.util.Set;

public class Customer {

	private Integer customerId;
	private String customerName;
	
	private Set<Order> orders = new HashSet<Order>();

	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 Set<Order> getOrders() {
		return orders;
	}

	public void setOrders(Set<Order> orders) {
		this.orders = orders;
	}
	
	

}

package com.atguigu.hibernate.strategy;

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;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((orderId == null) ? 0 : orderId.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj){
			return true;
		}
		if (obj == null){
			return false;
		}
		if (getClass() != obj.getClass()){
			return false;
		}
		Order other = (Order) obj;
		if (orderId == null) {
			if (other.orderId != null){
				return false;
			}
		} else if (!orderId.equals(other.orderId)){
			return false;
		}
		return true;
	}
	
	
	
}

<?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.strategy">
    
    <class name="Customer" table="CUSTOMERS" lazy="true" batch-size="5">
    
        <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>
        
        <set name="orders" table="ORDERS" 
        	inverse="true" order-by="ORDER_NAME DESC" lazy="true"
        	batch-size="2" fetch="subselect">
        	<key column="CUSTOMER_ID"></key>
        	<one-to-many class="Order"/>
        </set>
        
    </class>
    
</hibernate-mapping>

<?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.strategy">

    <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="customer" class="Customer" 
			column="CUSTOMER_ID"
			lazy="false"
			fetch="join"></many-to-one>

    </class>
</hibernate-mapping>

package strategy;

import com.atguigu.hibernate.strategy.Customer;
import com.atguigu.hibernate.strategy.Order;
import org.hibernate.Hibernate;
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.After;
import org.junit.Before;
import org.junit.Test;

import java.util.List;

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 testClassLevelStrategy(){
		Customer customer = (Customer) session.load(Customer.class, 1);
		System.out.println(customer.getClass()); 
		
		System.out.println(customer.getCustomerId()); 
		System.out.println(customer.getCustomerName()); 
	}

	@Test
	public void testOne2ManyLevelStrategy(){
		Customer customer = (Customer) session.get(Customer.class, 1);
		System.out.println(customer.getCustomerName());

		System.out.println(customer.getOrders().size());
		Order order = new Order();
		order.setOrderId(1);
		System.out.println(customer.getOrders().contains(order));

		Hibernate.initialize(customer.getOrders());

		//---------------set 的 lazy 属性------------------
		//1. 1-n 或 n-n 的集合属性默认使用懒加载检索策略.
		//2. 可以通过设置 set 的 lazy 属性来修改默认的检索策略. 默认为 true
		//并不建议设置为  false.
		//3. lazy 还可以设置为 extra. 增强的延迟检索. 该取值会尽可能的延迟集合初始化的时机!
	}

	@Test
	public void testSetBatchSize(){
		List<Customer> customers = session.createQuery("FROM Customer").list();

		System.out.println(customers.size());

		for(Customer customer: customers){
			if(customer.getOrders() != null)
				System.out.println(customer.getOrders().size());
		}

		//set 元素的 batch-size 属性: 设定一次初始化 set 集合的数量.
	}

	@Test
	public void testSetFetch(){
		List<Customer> customers = session.createQuery("FROM Customer").list();

		System.out.println(customers.size());

		for(Customer customer: customers){
			if(customer.getOrders() != null)
				System.out.println(customer.getOrders().size());
		}

		//set 集合的 fetch 属性: 确定初始化 orders 集合的方式.
		//1. 默认值为 select. 通过正常的方式来初始化 set 元素
		//2. 可以取值为 subselect. 通过子查询的方式来初始化所有的 set 集合. 子查询
		//作为 where 子句的 in 的条件出现, 子查询查询所有 1 的一端的 ID. 此时 lazy 有效.
		//但 batch-size 失效.
		//3. 若取值为 join. 则
		//3.1 在加载 1 的一端的对象时, 使用迫切左外连接(使用左外链接进行查询, 且把集合属性进行初始化)的方式检索 n 的一端的集合属性
		//3.2 忽略 lazy 属性.
		//3.3 HQL 查询忽略 fetch=join 的取值
	}

	@Test
	public void testSetFetch2(){
		Customer customer = (Customer) session.get(Customer.class, 1);
		System.out.println(customer.getOrders().size());
	}

	@Test
	public void testMany2OneStrategy(){
//		Order order = (Order) session.get(Order.class, 1);
//		System.out.println(order.getCustomer().getCustomerName());

		List<Order> orders = session.createQuery("FROM Order o").list();
		for(Order order: orders){
			if(order.getCustomer() != null){
				System.out.println(order.getCustomer().getCustomerName());
			}
		}

		//1. lazy 取值为 proxy 和 false 分别代表对应对应的属性采用延迟检索和立即检索
		//2. fetch 取值为 join, 表示使用迫切左外连接的方式初始化 n 关联的 1 的一端的属性
		//忽略 lazy 属性.
		//3. batch-size, 该属性需要设置在 1 那一端的 class 元素中:
		//<class name="Customer" table="CUSTOMERS" lazy="true" batch-size="5">
		//作用: 一次初始化 1 的这一段代理对象的个数.
	}

}

第十二章 Hibernate检索方式

12.1 概述

在这里插入图片描述

12.2 HQL检索方式

HQL(Hibernate Query Language)是面向对象的查询语言,它和SQL查询语言有些相似.在 Hibernate 提供的各种检索方式中,HQL是使用最广的一种检索方式。

介绍
在这里插入图片描述
迫切连接的好处是,例如,一个部门下有多个人员,也就是一个Department有多个Employee,也就是Department有一个Set属性,使用迫切连接查询,可以将每个部门下的Employee封装到Set属性上,如果不使用迫切连接,不会封装到Set属性上,会生成一条条记录,是Object[]数组;什么意思哪?例如1部门下有a、b、c三个员工,使用迫切连接返回的结果是一条记录,也就是一个Department对象,a、b、c会自动封装到set上;如果不使用,会生成三条记录,每条是一个Object[]数组,也就1a、1b、1c三条记录

HQL(迫切)左外连接
在这里插入图片描述

HQL(迫切)内连接
在这里插入图片描述

关联级别运行时的检索策略
如果在HQL中没有显式指定检索策略,将使用映射文件配置的检索策略.

HQL会忽略映射文件中设置的迫切左外连接检索策略,如果希望HQL采用迫切左外连接策略,就必须在HQL查询语句中显式的指定它

若在HQL代码中显式指定了检索策略,就会覆盖映射文件中配置的检索策略

QBC检索和本地SQL检索
QBC查询就是通过使用Hibernate提供的Query By
Criteria API来查询对象,这种API封装了SQL语句的动态拼装,对查询提供了更加面向对象的功能接口

本地SQL查询来完善HQL不能涵盖所有的查询特性

第十三章 Hibernate的二级缓存

Hibernate中提供了两个级别的缓存
第一级别的缓存是Session级别的缓存,它是属于事务范围的缓存。这一级别的缓存由hibernate管理的

第二级别的缓存是SessionFactory级别的缓存,它是属于进程范围的缓存(缓存一定会存在的问题就是缓存的数据库和数据库数据的一致性,这也导致高并发问题;二级缓存存在高并发问题,如果项目部署了集群,相当于有多个SessionFactory缓存,如果这多个SessionFactory缓存了同一张表的数据,一个SessionFactory缓存改变了这一张表的数据,另一个SessionFactory缓存并改变,导致两个缓存中数据不一致)

13.1 SessionFactory的缓存可以分为两类

内置缓存:Hibernate自带的,不可卸载.通常在Hibernate 的初始化阶段, Hibernate会把映射元数据和预定义的SQL语句放到SessionFactory的缓存中,映射元数据是映射文件中数据( .hbm.xml文件中的数据)的复制.该内置缓存是只读的.

外置缓存(二级缓存):一个可配置的缓存插件.在默认情况下,SessionFactory不会启用这个缓存插件.外置缓存中的数据是数据库数据的复制,外置缓存的物理介质可以是内存或硬盘

适合放入二级缓存中的数据
很少被修改
不是很重要的数据,允许出现偶尔的并发问题

不适合放入二级缓存中的数据
经常被修改
财务数据,绝对不允许出现并发问题
与其他应用程序共享的数据

13.2 Hibernate二级缓存的架构

在这里插入图片描述

13.3 二级缓存的并发访问策略

在这里插入图片描述
在这里插入图片描述

13.4 管理Session

在这里插入图片描述
在这里插入图片描述

13.5 批量处理数据

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

第十四章 代码总结

注意,代码是第十二章与十三章内容

在这里插入图片描述

1. 使用 Hibernate 二级缓存的步骤:

1). 加入二级缓存插件的 jar 包及配置文件:

I. 复制 \hibernate-release-4.2.4.Final\lib\optional\ehcache\*.jar 到当前 Hibrenate 应用的类路径下.
II. 复制 hibernate-release-4.2.4.Final\project\etc\ehcachexml 到当前 WEB 应用的类路径下

2). 配置 hibernate.cfg.xml 

I.   配置启用 hibernate 的二级缓存
<property name="cache.use_second_level_cache">true</property>

II.  配置hibernate二级缓存使用的产品
<property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

III. 配置对哪些类使用 hibernate 的二级缓存
<class-cache usage="read-write" class="com.atguigu.hibernate.entities.Employee"/>
		
实际上也可以在 .hbm.xml 文件中配置对哪些类使用二级缓存, 及二级缓存的策略是什么. 

2). 集合级别的二级缓存的配置

I. 配置对集合使用二级缓存

<collection-cache usage="read-write" collection="com.atguigu.hibernate.entities.Department.emps"/>

也可以在 .hbm.xml 文件中进行配置

<set name="emps" table="GG_EMPLOYEE" inverse="true" lazy="true">
	<cache usage="read-write"/>
    <key>
        <column name="DEPT_ID" />
    </key>
    <one-to-many class="com.atguigu.hibernate.entities.Employee" />
</set>

II. 注意: 还需要配置集合中的元素对应的持久化类也使用二级缓存! 否则将会多出 n 条 SQL 语句. 

3). ehcache 的 配置文件: ehcache.xml

4).  查询缓存: 默认情况下, 设置的缓存对 HQL 及 QBC 查询时无效的, 但可以通过以下方式使其是有效的

I.  在 hibernate 配置文件中声明开启查询缓存

<property name="cache.use_query_cache">true</property>

II. 调用 Query 或 Criteria 的 setCacheable(true) 方法

III. 查询缓存依赖于二级缓存
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>hibernate-4</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
            <version>4.2.4.Final</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-c3p0</artifactId>
            <version>4.2.5.Final</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/net.sf.ehcache/ehcache -->
        <dependency>
            <groupId>net.sf.ehcache</groupId>
            <artifactId>ehcache</artifactId>
            <version>2.4.3</version>
            <type>pom</type>
        </dependency>

        <!-- hibernate-ehcache -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-ehcache</artifactId>
            <version>4.2.4.Final</version>
        </dependency>

    </dependencies>
</project>
<?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.driver_class">com.mysql.jdbc.Driver</property>
        <property name="connection.url">jdbc:mysql://172.16.19.104:3306/hibernate5</property>
        <property name="connection.username">springcloud</property>
        <property name="connection.password">springcloud</property>

        <!-- Hibernate 的基本配置 -->
        <!-- Hibernate 使用的数据库方言 -->
        <!-- 在MYSQL5.5及以后版本中type=InnoDB 由ENGINE=InnoDB  代替-->
        <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="hibernate.c3p0.acquire_increment">2</property>

        <property name="hibernate.c3p0.idle_test_period">2000</property>
        <property name="hibernate.c3p0.timeout">2000</property>

        <property name="hibernate.c3p0.max_statements">10</property>

        <!-- 设定 JDBC 的 Statement 读取数据的时候每次从数据库中取出的记录条数 -->
        <property name="hibernate.jdbc.fetch_size">100</property>

        <!-- 设定对数据库进行批量删除,批量更新和批量插入的时候的批次大小 -->
        <property name="hibernate.jdbc.batch_size">30</property>

        <!-- 启用二级缓存 -->
        <property name="cache.use_second_level_cache">true</property>

        <!-- 配置使用的二级缓存的产品 -->
        <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

        <!-- 配置启用查询缓存 -->
        <property name="cache.use_query_cache">true</property>

        <!-- 配置管理 Session 的方式 -->
        <property name="current_session_context_class">thread</property>

        <!-- 需要关联的 hibernate 映射文件 .hbm.xml -->
        <mapping resource="Employee.hbm.xml"></mapping>
        <mapping resource="Department.hbm.xml"></mapping>

        <class-cache class="com.atguigu.hibernate.entities.Employee" usage="read-write"/>
        <class-cache class="com.atguigu.hibernate.entities.Department" usage="read-write"/>
        <collection-cache usage="read-write" collection="com.atguigu.hibernate.entities.Department.emps"/>

    </session-factory>
</hibernate-configuration>


<ehcache>

    <!-- Sets the path to the directory where cache .data files are created.

         If the path is a Java System Property it is replaced by
         its value in the running VM.

         The following properties are translated:
         user.home - User's home directory
         user.dir - User's current working directory
         java.io.tmpdir - Default temp file path -->
    <!--  
    	指定一个目录:当 EHCache 把数据写到硬盘上时, 将把数据写到这个目录下.
    -->     
    <diskStore path="d:\\tempDirectory"/>


    <!--Default Cache configuration. These will applied to caches programmatically created through
        the CacheManager.

        The following attributes are required for defaultCache:

        maxInMemory       - Sets the maximum number of objects that will be created in memory
        eternal           - Sets whether elements are eternal. If eternal,  timeouts are ignored and the element
                            is never expired.
        timeToIdleSeconds - Sets the time to idle for an element before it expires. Is only used
                            if the element is not eternal. Idle time is now - last accessed time
        timeToLiveSeconds - Sets the time to live for an element before it expires. Is only used
                            if the element is not eternal. TTL is now - creation time
        overflowToDisk    - Sets whether elements can overflow to disk when the in-memory cache
                            has reached the maxInMemory limit.

        -->
    <!--  
    	设置缓存的默认数据过期策略 
    -->    
    <defaultCache
        maxElementsInMemory="10000"
        eternal="false"
        timeToIdleSeconds="120"
        timeToLiveSeconds="120"
        overflowToDisk="true"
        />

   	<!--  
   		设定具体的命名缓存的数据过期策略。每个命名缓存代表一个缓存区域
   		缓存区域(region):一个具有名称的缓存块,可以给每一个缓存块设置不同的缓存策略。
   		如果没有设置任何的缓存区域,则所有被缓存的对象,都将使用默认的缓存策略。即:<defaultCache.../>
   		Hibernate 在不同的缓存区域保存不同的类/集合。
			对于类而言,区域的名称是类名。如:com.atguigu.domain.Customer
			对于集合而言,区域的名称是类名加属性名。如com.atguigu.domain.Customer.orders
   	-->
   	<!--  
   		name: 设置缓存的名字,它的取值为类的全限定名或类的集合的名字 
		maxElementsInMemory: 设置基于内存的缓存中可存放的对象最大数目 
		
		eternal: 设置对象是否为永久的, true表示永不过期,
		此时将忽略timeToIdleSeconds 和 timeToLiveSeconds属性; 默认值是false 
		timeToIdleSeconds:设置对象空闲最长时间,以秒为单位, 超过这个时间,对象过期。
		当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。 
		timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。
		如果此值为0,表示对象可以无限期地存在于缓存中. 该属性值必须大于或等于 timeToIdleSeconds 属性值 
		
		overflowToDisk:设置基于内存的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中 
   	-->
    <cache name="com.atguigu.hibernate.entities.Employee"
        maxElementsInMemory="1"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="true"
        />

    <cache name="com.atguigu.hibernate.entities.Department.emps"
        maxElementsInMemory="1000"
        eternal="true"
        timeToIdleSeconds="0"
        timeToLiveSeconds="0"
        overflowToDisk="false"
        />

</ehcache>

<?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.Department" table="GG_DEPARTMENT">
        
        <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>
        
        <set name="emps" table="GG_EMPLOYEE" inverse="true" lazy="true">
            <key>
                <column name="DEPT_ID" />
            </key>
            <one-to-many class="com.atguigu.hibernate.entities.Employee" />
        </set>
        
    </class>
</hibernate-mapping>

<?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.Employee" table="GG_EMPLOYEE">
    	
    	<!--  
    	<cache usage="read-write"/>
    	-->
    		
        <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>
        
        <property name="salary" type="float">
            <column name="SALARY" />
        </property>
        
        <property name="email" type="java.lang.String">
            <column name="EMAIL" />
        </property>
        
        <many-to-one name="dept" class="com.atguigu.hibernate.entities.Department">
            <column name="DEPT_ID" />
        </many-to-one>
        
    </class>
    
    <query name="salaryEmps"><![CDATA[FROM Employee e WHERE e.salary > :minSal AND e.salary < :maxSal]]></query>
    
</hibernate-mapping>

package com.atguigu.hibernate.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.HashSet;
import java.util.Set;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Department {

	private Integer id;
	private String name;
	
	private Set<Employee> emps = new HashSet<Employee>();

	@Override
	public String toString() {
		return "Department [id=" + id + "]";
	}	
}
package com.atguigu.hibernate.entities;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {

	private Integer id;
	private String name;
	private float salary;
	private String email;
	
	private Department dept;

	@Override
	public String toString() {
		return "Employee [id=" + id + "]";
	}

	public Employee(String email, float salary, Department dept) {
		super();
		this.salary = salary;
		this.email = email;
		this.dept = dept;
	}

	
}

package com.atguigu.hibernate.hibernate;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;

public class HibernateUtils {
	
	private HibernateUtils(){}
	
	private static HibernateUtils instance = new HibernateUtils();
	
	public static HibernateUtils getInstance() {
		return instance;
	}

	private SessionFactory sessionFactory;

	public SessionFactory getSessionFactory() {
		if (sessionFactory == null) {
			Configuration configuration = new Configuration().configure();
			ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
					.applySettings(configuration.getProperties())
					.buildServiceRegistry();
			sessionFactory = configuration.buildSessionFactory(serviceRegistry);
		}
		return sessionFactory;
	}
	
	public Session getSession(){
		return getSessionFactory().getCurrentSession();
	}

}

package com.atguigu.hibernate.dao;

import org.hibernate.Session;

import com.atguigu.hibernate.entities.Department;
import com.atguigu.hibernate.hibernate.HibernateUtils;

public class DepartmentDao {

	public void save(Department dept){
		//内部获取 Session 对象
		//获取和当前线程绑定的 Session 对象
		//1. 不需要从外部传入 Session 对象
		//2. 多个 DAO 方法也可以使用一个事务!
		Session session = HibernateUtils.getInstance().getSession();
		System.out.println(session.hashCode());
		
		session.save(dept);
	}
	
	/**
	 * 若需要传入一个 Session 对象, 则意味着上一层(Service)需要获取到 Session 对象.
	 * 这说明上一层需要和 Hibernate 的 API 紧密耦合. 所以不推荐使用此种方式. 
	 */
	public void save(Session session, Department dept){
		session.save(dept);
	}
	
}

import com.atguigu.hibernate.dao.DepartmentDao;
import com.atguigu.hibernate.entities.Department;
import com.atguigu.hibernate.entities.Employee;
import com.atguigu.hibernate.hibernate.HibernateUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.criterion.Conjunction;
import org.hibernate.criterion.Disjunction;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.jdbc.Work;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;

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 testHQL(){

        //1. 创建 Query 对象
        //基于位置的参数.
        String hql = "FROM Employee e WHERE e.salary > ? AND e.email LIKE ? AND e.dept = ? "
                + "ORDER BY e.salary";
        Query query = session.createQuery(hql);

        //2. 绑定参数
        //Query 对象调用 setXxx 方法支持方法链的编程风格.
        Department dept = new Department();
        dept.setId(80);
        query.setFloat(0, 6000)
                .setString(1, "%A%")
                .setEntity(2, dept);

        //3. 执行查询
        List<Employee> emps = query.list();
        System.out.println(emps.size());
    }

    @Test
    public void testHQLNamedParameter(){

        //1. 创建 Query 对象
        //基于命名参数.
        String hql = "FROM Employee e WHERE e.salary > :sal AND e.email LIKE :email";
        Query query = session.createQuery(hql);

        //2. 绑定参数
        query.setFloat("sal", 7000)
                .setString("email", "%A%");

        //3. 执行查询
        List<Employee> emps = query.list();
        System.out.println(emps.size());
    }

    @Test
    public void testPageQuery() {
        String hql="FROM Employee";
        Query query = session.createQuery(hql);

        int pageNo=22;
        int pageSize=5;

        List<Employee> emps = query.setFirstResult((pageNo - 1) * pageSize).setMaxResults(pageSize).list();

        System.out.println(emps);
    }

    @Test
    public void testNamedQuery(){
        Query query = session.getNamedQuery("salaryEmps");

        List<Employee> emps = query.setFloat("minSal", 5000)
                .setFloat("maxSal", 10000)
                .list();

        System.out.println(emps.size());
    }

    @Test
    public void testFieldQuery() {
        String hql = "SELECT e.email, e.salary, e.dept FROM Employee e WHERE e.dept = :dept";
        Query query = session.createQuery(hql);
        Department dept = new Department();
        dept.setId(80);
        List<Object[]> result = query.setEntity("dept", dept).list();

        for (Object[] objs : result) {
            System.out.println(Arrays.asList(objs));
        }
    }

    @Test
    public void testFieldQuery2(){
        String hql = "SELECT new Employee(e.email, e.salary, e.dept) "
                + "FROM Employee e "
                + "WHERE e.dept = :dept";
        Query query = session.createQuery(hql);

        Department dept = new Department();
        dept.setId(80);
        List<Employee> result = query.setEntity("dept", dept)
                .list();

        for(Employee emp: result){
            System.out.println(emp.getId() + ", " + emp.getEmail()
                    + ", " + emp.getSalary() + ", " + emp.getDept());
        }
    }

    @Test
    public void testGroupBy(){
        String hql = "SELECT min(e.salary), max(e.salary) "
                + "FROM Employee e "
                + "GROUP BY e.dept "
                + "HAVING min(salary) > :minSal";

        Query query = session.createQuery(hql)
                .setFloat("minSal", 8000);

        List<Object []> result = query.list();
        for(Object [] objs: result){
            System.out.println(Arrays.asList(objs));
        }
    }

    @Test
    public void testLeftJoinFetch(){
//		String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN FETCH d.emps";
        String hql = "FROM Department d INNER JOIN FETCH d.emps";
        Query query = session.createQuery(hql);

        List<Department> depts = query.list();
        depts = new ArrayList<Department>(new LinkedHashSet(depts));
        System.out.println(depts.size());

        for(Department dept: depts){
            System.out.println(dept.getName() + "-" + dept.getEmps().size());
        }
    }

    @Test
    public void testLeftJoin(){
        String hql = "SELECT DISTINCT d FROM Department d LEFT JOIN d.emps";
        Query query = session.createQuery(hql);

		List<Object []> result = query.list();
		result = new ArrayList<Object []>(new LinkedHashSet<Object []>(result));
		System.out.println(result);

		for(Object [] objs: result){
			System.out.println(Arrays.asList(objs));
		}
    }

    @Test
    public void testLeftJoinFetch2(){
        String hql = "SELECT e FROM Employee e INNER JOIN e.dept";
        Query query = session.createQuery(hql);

        List<Employee> emps = query.list();
        System.out.println(emps.size());

        for(Employee emp: emps){
            System.out.println(emp.getName() + ", " + emp.getDept().getName());
        }
    }

    @Test
    public void testQBC(){
        //1. 创建一个 Criteria 对象
        Criteria criteria = session.createCriteria(Employee.class);

        //2. 添加查询条件: 在 QBC 中查询条件使用 Criterion 来表示
        //Criterion 可以通过 Restrictions 的静态方法得到
        criteria.add(Restrictions.eq("email", "SKUMAR"));
        criteria.add(Restrictions.gt("salary", 5000F));

        //3. 执行查询
        Employee employee = (Employee) criteria.uniqueResult();
        System.out.println(employee);
    }

    @Test
    public void testQBC2(){
        Criteria criteria = session.createCriteria(Employee.class);

        //1. AND: 使用 Conjunction 表示
        //Conjunction 本身就是一个 Criterion 对象
        //且其中还可以添加 Criterion 对象
        Conjunction conjunction = Restrictions.conjunction();
        conjunction.add(Restrictions.like("name", "a", MatchMode.ANYWHERE));
        Department dept = new Department();
        dept.setId(80);
        conjunction.add(Restrictions.eq("dept", dept));
        System.out.println(conjunction);

        //2. OR
        Disjunction disjunction = Restrictions.disjunction();
        disjunction.add(Restrictions.ge("salary", 6000F));
        disjunction.add(Restrictions.isNull("email"));

        criteria.add(disjunction);
        criteria.add(conjunction);

        criteria.list();
    }

    @Test
    public void testQBC3(){
        Criteria criteria = session.createCriteria(Employee.class);

        //统计查询: 使用 Projection 来表示: 可以由 Projections 的静态方法得到
        criteria.setProjection(Projections.max("salary"));

        System.out.println(criteria.uniqueResult());
    }

    @Test
    public void testQBC4(){
        Criteria criteria = session.createCriteria(Employee.class);

        //1. 添加排序
        criteria.addOrder(Order.asc("salary"));
        criteria.addOrder(Order.desc("email"));

        //2. 添加翻页方法
        int pageSize = 5;
        int pageNo = 3;
        criteria.setFirstResult((pageNo - 1) * pageSize)
                .setMaxResults(pageSize)
                .list();
    }

    @Test
    public void testNativeSQL(){
        String sql = "INSERT INTO gg_department VALUES(?, ?)";
        Query query = session.createSQLQuery(sql);

        query.setInteger(0, 280)
                .setString(1, "ATGUIGU")
                .executeUpdate();
    }

    @Test
    public void testHQLUpdate(){
        String hql = "DELETE FROM Department d WHERE d.id = :id";

        session.createQuery(hql).setInteger("id", 280)
                .executeUpdate();
    }

    @Test
    public void testHibernateSecondLevelCache(){
        Employee employee = (Employee) session.get(Employee.class, 100);
        System.out.println(employee.getName());

        transaction.commit();
        session.close();

        session = sessionFactory.openSession();
        transaction = session.beginTransaction();

        Employee employee2 = (Employee) session.get(Employee.class, 100);
        System.out.println(employee2.getName());
    }

    @Test
    public void testCollectionSecondLevelCache(){
        Department dept = (Department) session.get(Department.class, 80);
        System.out.println(dept.getName());
        System.out.println(dept.getEmps().size());

        transaction.commit();
        session.close();

        session = sessionFactory.openSession();
        transaction = session.beginTransaction();

        Department dept2 = (Department) session.get(Department.class, 80);
        System.out.println(dept2.getName());
        System.out.println(dept2.getEmps().size());
    }

    @Test
    public void testQueryCache() {
        Query query = session.createQuery("FROM Employee");

        //没有这行代码,下面两个list()会生成两条sql语句
        query.setCacheable(true);

        List<Employee> emps = query.list();
        System.out.println(emps.size());

        emps = query.list();
        System.out.println(emps.size());

        Criteria criteria = session.createCriteria(Employee.class);
        criteria.setCacheable(true);
    }

    @Test
    public void testUpdateTimeStampCache(){
        Query query = session.createQuery("FROM Employee");
        query.setCacheable(true);

        List<Employee> emps = query.list();
        System.out.println(emps.size());

        Employee employee = (Employee) session.get(Employee.class, 100);
        employee.setSalary(30000);

        emps = query.list();
        System.out.println(emps.size());
    }

    @Test
    public void testQueryIterate(){
        Department dept = (Department) session.get(Department.class, 70);
        System.out.println(dept.getName());
        System.out.println(dept.getEmps().size());

        Query query = session.createQuery("FROM Employee e WHERE e.dept.id = 80");
//		List<Employee> emps = query.list();
//		System.out.println(emps.size());

        Iterator<Employee> empIt = query.iterate();
        while(empIt.hasNext()){
            System.out.println(empIt.next().getName());
        }
    }

    @Test
    public void testManageSession(){

        //获取 Session
        //开启事务
        Session session = HibernateUtils.getInstance().getSession();
        System.out.println("-->" + session.hashCode());
        Transaction transaction = session.beginTransaction();

        DepartmentDao departmentDao = new DepartmentDao();

        Department dept = new Department();
        dept.setName("ATGUIGU");

        departmentDao.save(dept);
        departmentDao.save(dept);
        departmentDao.save(dept);

        //若 Session 是由 thread 来管理的, 则在提交或回滚事务时, 已经关闭 Session 了.
        transaction.commit();
        System.out.println(session.isOpen());
    }

    @Test
    public void testBatch(){
        session.doWork(new Work() {

            public void execute(Connection connection) throws SQLException {
                //通过 JDBC 原生的 API 进行操作, 效率最高, 速度最快!
            }
        });
    }




}

posted @ 2022-01-24 10:02  KeepArlen  阅读(74)  评论(0)    收藏  举报