Hibernate(了解与使用)

Hiberante的了解

框架的概述

框架(framework)是一个框子——指其约束性,也是一个架子——指其支撑性。是一个基本概念上的结构,用于去解决或者处理复杂的问题。

什么是框架

框架:指的是软件的半成品,已经完成了部分功能。

EE的三层架构

EE的经典三层结构

Hibernate的概述

Hibernate 是一个开放源代码的对象关系映射框架,它对JDBC进行了非常轻量级的对象封装,它将POJO与数据库表建立映射关系,是一个全自动的orm框架,hibernate可以自动生成SQL语句,自动执行,使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。 Hibernate可以应用在任何使用JDBC的场合,既可以在Java的客户端程序使用,也可以在Servlet/JSP的Web应用中使用,最具革命意义的是,Hibernate可以在应用EJB的JaveEE架构中取代CMP,完成数据持久化的重任。

什么是Hibernate

Hibernate是一种ORM框架,在Java对象与关系数据库之间建立某种映射,以实现直接存取Java对象!

什么是ORM

ORM:Object Relational Mapping(对象关系映射)。指的是将一个Java中的对象与关系型数据库中的表建立一种映射关系,从而操作对象就可以操作数据库中的表。

为什么要学习Hibernate

与其他操作数据库的技术相比,Hibernate具有以下几点优势

  • Hibernate对 JBDC 访问数据库的代码做了轻量级封装,大大简化了数据访问层繁琐的重复性代码,并且减少了内存消耗,加快了运行效率。
  • Hibernate 是一个基于 JDBC 的主流持久化框架,是一个优秀的ORM实现,它很大程度的简化 DAO(Date Access Object,数据访问对象)层编码工作。
  • Hibernate 的性能非常好,映射的灵活性很出色。它支持很多关系型数据库,从一对一到多对多的各种复杂关系。
  • 可扩展性强,由于源代码的开源以及API的开放,当本身功能不够用时,可以自行编码进行扩展。

Hibernate的入门

下载Hibernate开发环境

Hibernate3.x   Hibernate4.x   Hibernate5.x

https://sourceforge.net/projects/hibernate/files/hibernate-orm/5.0.7.Final/

解压 Hibernate

解压完成会看到三个文件夹:

  1. documentation :Hibernate开发的文档
  2. lib :Hibernate开发包
    • required :Hibernate开发的必须的依赖包
    • optional :Hibernate开发的可选的jar包
  3. project :Hibernate提供的项目

创建一个项目,引入jar包

  • 数据库驱动包
  • Hibernate开发的必须的jar包
  • Hibernate引入日志记录包

创建表

1
2
3
4
5
6
7
8
9
10
CREATE TABLE `cst_customer` (
`cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
`cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
`cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
`cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
`cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
`cust_phone` varchar(64) DEFAULT NULL COMMENT '固定电话',
`cust_mobile` varchar(16) DEFAULT NULL COMMENT '移动电话',
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

创建实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class Customer {
private Long cust_id;
private String cust_name;
private String cust_source;
private String cust_industry;
private String cust_level;
private String cust_phone;
private String cust_mobile;
public Long getCust_id() {
return cust_id;
}
public void setCust_id(Long cust_id) {
this.cust_id = cust_id;
}
public String getCust_name() {
return cust_name;
}
public void setCust_name(String cust_name) {
this.cust_name = cust_name;
}
public String getCust_source() {
return cust_source;
}
public void setCust_source(String cust_source) {
this.cust_source = cust_source;
}
public String getCust_industry() {
return cust_industry;
}
public void setCust_industry(String cust_industry) {
this.cust_industry = cust_industry;
}
public String getCust_level() {
return cust_level;
}
public void setCust_level(String cust_level) {
this.cust_level = cust_level;
}
public String getCust_phone() {
return cust_phone;
}
public void setCust_phone(String cust_phone) {
this.cust_phone = cust_phone;
}
public String getCust_mobile() {
return cust_mobile;
}
public void setCust_mobile(String cust_mobile) {
this.cust_mobile = cust_mobile;
}
@Override
public String toString() {
return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + ", cust_source=" + cust_source
+ ", cust_industry=" + cust_industry + ", cust_level=" + cust_level + ", cust_phone=" + cust_phone
+ ", cust_mobile=" + cust_mobile + "]";
}

}

创建映射

映射需要通过XML的配置文件来完成,这个配置文件可以任意命名。尽量统一命名规范(类名.hbm.xml)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<!-- 建立类与表的映射 -->
<class name="com.hibernate.demo1.Customer" table="cst_customer">
<!-- 建立类中的属性与表中的主键对应 -->
<id name="cust_id" column="cust_id" >
<generator class="native"/>
</id>

<!-- 建立类中的普通的属性和表的字段的对应 -->
<property name="cust_name" column="cust_name" length="32" />
<property name="cust_source" column="cust_source" length="32"/>
<property name="cust_industry" column="cust_industry"/>
<property name="cust_level" column="cust_level"/>
<property name="cust_phone" column="cust_phone"/>
<property name="cust_mobile" column="cust_mobile"/>
</class>
</hibernate-mapping>

创建一个Hibernate的核心配置文件

Hibernate的核心配置文件的名称:hibernate.cfg.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 连接数据库的基本参数 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///hibernate_day01</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">abc</property>
<!-- 配置Hibernate的方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

<!-- 可选配置================ -->
<!-- 打印SQL -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化SQL -->
<property name="hibernate.format_sql">true</property>
<!-- 自动创建表 -->
<property name="hibernate.hbm2ddl.auto">update</property>

<!-- 配置C3P0连接池 -->
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!--在连接池中可用的数据库连接的最少数目 -->
<property name="c3p0.min_size">5</property>
<!--在连接池中所有数据库连接的最大数目 -->
<property name="c3p0.max_size">20</property>
<!--设定数据库连接的过期时间,以秒为单位,
如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
<property name="c3p0.timeout">120</property>
<!--每3000秒检查所有连接池中的空闲连接 以秒为单位-->
<property name="c3p0.idle_test_period">3000</property>

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

编写测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class HibernateDemo1 {

@Test
// 保存客户的案例
public void demo1(){
// 1.加载Hibernate的核心配置文件
Configuration configuration = new Configuration().configure();
// 手动加载映射
// configuration.addResource("com/itheima/hibernate/demo1/Customer.hbm.xml");
// 2.创建一个SessionFactory对象:类似于JDBC中连接池
SessionFactory sessionFactory = configuration.buildSessionFactory();
// 3.通过SessionFactory获取到Session对象:类似于JDBC中Connection
Session session = sessionFactory.openSession();
// 4.手动开启事务:
Transaction transaction = session.beginTransaction();
// 5.编写代码

Customer customer = new Customer();
customer.setCust_name("王西");

session.save(customer);

// 6.事务提交
transaction.commit();
// 7.资源释放
session.close();
sessionFactory.close();
}
}

Hibernate的常见配置

Hibernate的映射的配置

映射的配置

  1. 【class标签的配置】

    • 标签用来建立类与表的映射关系
    • 属性:
      • name :类的全路径
      • table :表名(类名与表名一致,table可以省略)
      • catalog :数据库名
  2. 【id标签的配置】

    • 标签用来建立类中的属性与表中的主键的对应关系
    • 属性:
      • name :类中的属性名
      • column :表中的字段名(类中的属性名和表中的字段名如果一致,column可以省略)
      • length :长度
      • type :类型
  3. 【property标签的配置】

    • 标签用来建立类中的普通属性与表的字段的对应关系
    • 属性:
      • name :类中的属性名
      • column :表中的字段名
      • length :长度
      • type :类型
      • not-null :设置非空
      • unique :设置唯一

Hibernate的核心的配置

Hibernate的核心配置方式(了解)

  1. 一种方式:属性文件的方式

    • hibernate.properties
      • hibernate.connection.driver_class=com.mysql.jdbc.Driver
      • hibernate.show_sql=true
    • 属性文件的方式不能引入映射文件(手动编写代码加载映射文件)
  2. 二种方式:XML文件的方式

    • hibernate.cfg.xml

核心的配置

  1. 必须的配置
    • 连接数据库的基本的参数
      • 驱动类
      • url路径
      • 用户名
      • 密码
    • 方言
  2. 可选的配置

    • 显示SQL :hibernate.show_sql
    • 格式化SQL :hibernate.format_sql
    • 自动建表 :hibernate.hbm2ddl.auto
      • none :不使用hibernate的自动建表
      • create :如果数据库中已经有表,删除原有表,重新创建,如果没有表,新建表。(测试)
      • create-drop :如果数据库中已经有表,删除原有表,执行操作,删除这个表。如果没有表,新建一个,使用完了删除该表。(测试)
      • update :如果数据库中有表,使用原有表,如果没有表,创建新表(更新表结构)
      • validate :如果没有表,不会创建表。只会使用数据库中原有的表。(校验映射和表结构)。
  3. 映射文件的引入

    • 引入映射文件的位置

Hibernate的核心API

Hibernate的API

Configuration:Hibernate的配置对象

Configuration
configuration类的作用是对Hibernate进行配置,以及对它进行启动,在启动过程中,Configuration类的实例首先定位映射文档的位置,读取这些配置,然后创建一个SessionFactory对象,虽然Configuration类在整个Hibernate项目中只扮演着一个很小的角色,但它是启动hibernate时所遇到的第一个对象

  • 作用:
    • 加载核心配置文件
      • hibernate.properties
        Configuration cfg = new Configuration();
      • hibernate.cfg.xml
        Configuration cfg = new Configuration().configure();
    • 加载映射文件
      1
      2
      // 手动加载映射
      configuration.addResource("com/itheima/hibernate/demo1/ Customer.hbm.xml");

SessionFactory:Session工厂

SessionFactory
SessionFactory接口负责初始化Hibernate,它充当数据存储源的代理,并负责创建Session对象,这里用到了工厂模式。需要注意的事SessionFactory并不是轻量级的,因为在一般情况下,一个项目通常只需要一个SessionFactory就够,当需要操作多个数据库时,可以为每个库指定一个SessionFactory。

SessionFactory内部维护了Hibernate的连接池和Hibernate的二级缓存。是线程安全的对象。一个项目创建一个对象即可。

  • 配置连接池:(了解)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    <!-- 配置C3P0连接池 -->
    <property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
    <!--在连接池中可用的数据库连接的最少数目 -->
    <property name="c3p0.min_size">5</property>
    <!--在连接池中所有数据库连接的最大数目 -->
    <property name="c3p0.max_size">20</property>
    <!--设定数据库连接的过期时间,以秒为单位,
    如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
    <property name="c3p0.timeout">120</property>
    <!--每3000秒检查所有连接池中的空闲连接 以秒为单位-->
    <property name="c3p0.idle_test_period">3000</property>
  • 抽取工具类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class HibernateUtils {

    public static final Configuration cfg;
    public static final SessionFactory sf;

    static{
    cfg = new Configuration().configure();
    sf = cfg.buildSessionFactory();
    }

    public static Session openSession(){
    return sf.openSession();
    }
    }

Session:类似Connection对象是连接对象

Session接口负责执行被持久化对象的CRUD操作(CRUD的任务是完成与数据库的交流,包含很多藏剑的SQL语句)。但需要注意的事Session对象是非线程安全的。同时,Hibernate的Session不同于JSP应用中的HttpSession。这里当使用Session这个术语时,其实指的是Hibernate中的Session,而以后会将HttpSession对象称为用户Session。

Session代表的是Hibernate与数据库的链接对象。不是线程安全的。与数据库交互桥梁。

  • Session中的API
    • 保存方法:
    • Serializable save(Object obj);
  • 查询方法:

    • T get(Class c,Serializable id);
    • T load(Class c,Serializable id);
    • get方法和load方法的区别?
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      public void demo2(){
      Session session = HibernateUtils.openSession();
      Transaction tx = session.beginTransaction();
      /**
      * get方法
      * * 采用的是立即加载,执行到这行代码的时候,就会马上发送SQL语句去查询。
      * * 查询后返回是真实对象本身。
      * * 查询一个找不到的对象的时候,返回null
      *
      * load方法
      * * 采用的是延迟加载(lazy懒加载),执行到这行代码的时候,不会发送SQL语句,当真正使用这个对象的时候才会发送SQL语句。
      * * 查询后返回的是代理对象。javassist-3.18.1-GA.jar 利用javassist技术产生的代理。
      * * 查询一个找不到的对象的时候,返回ObjectNotFoundException
      */
      // 使用get方法查询
      /*Customer customer = session.get(Customer.class, 100l); // 发送SQL语句
      System.out.println(customer);*/

      // 使用load方法查询
      Customer customer = session.load(Customer.class, 200l);
      System.out.println(customer);

      tx.commit();
      session.close();
      }
  • 修改方法

    • void update(Object obj);
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      public void demo3(){
      Session session = HibernateUtils.openSession();
      Transaction tx = session.beginTransaction();

      // 直接创建对象,进行修改
      /*Customer customer = new Customer();
      customer.setCust_id(1l);
      customer.setCust_name("王聪");
      session.update(customer);*/

      // 先查询,再修改(推荐)
      Customer customer = session.get(Customer.class, 1l);
      customer.setCust_name("王小贱");
      session.update(customer);

      tx.commit();
      session.close();
      }
  • 删除方法

    • void delete(Object obj);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 删除操作
public void demo4(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();

// 直接创建对象,删除
/* Customer customer = new Customer();
customer.setCust_id(1l);
session.delete(customer);*/

// 先查询再删除(推荐)--级联删除
Customer customer = session.get(Customer.class, 2l);
session.delete(customer);

tx.commit();
session.close();
}
  • 保存或更新
    • void saveOrUpdate(Object obj)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void demo5(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();

/*Customer customer = new Customer();
customer.setCust_name("王凤");
session.saveOrUpdate(customer);*/

Customer customer = new Customer();
customer.setCust_id(3l);
customer.setCust_name("李如花");
session.saveOrUpdate(customer);

tx.commit();
session.close();
}
  • 查询所有
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 查询所有
public void demo6(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 接收HQL:Hibernate Query Language 面向对象的查询语言
/*Query query = session.createQuery("from Customer");
List<Customer> list = query.list();
for (Customer customer : list) {
System.out.println(customer);
}*/

// 接收SQL:
SQLQuery query = session.createSQLQuery("select * from cst_customer");
List<Object[]> list = query.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
tx.commit();
session.close();
}

Transaction:事务对象

Hibernate中管理事务的对象。

  • commit();
  • rollback();

主键生成策略&一级缓存&事物管理

Hibernate持久化类的编写规则

什么是持久化类

Hibernate 是持久层的ORM映射框架,专注于数据的持久化工作。所谓的持久化,就是将内存中的数据永久存储到关系型数据库中。那么知道了什么是持久化,什么又是持久化类呢?其实所谓的持久化类指的是一个Java类与数据库表建立了映射关系,那么这个类称为持久化类。其实你可以简单的理解为持久化类就是一个Java类有了一个映射文件与数据库的表建立了关系。

持久化类的编写规则

我们在编写持久化类的时候需要有以下几点需要注意:

  • 持久化类需要提供无参数的构造方法。因为在Hibernate的底层需要使用反射生成类的实例。
  • 持久化类的属性需要私有,对私有的属性提供公有的get和set方法。因为在Hibernate底层会将查询到的数据进行封装。
  • 持久化类的属性要尽量使用包装类的类型。因为包装类和基本数据类型的默认值不同,包装类的类型语义描述更清晰而基本数据类型不容易描述。
  • 持久化类要有一个唯一标识OID与表的主键对应。因为Hibernate中需要通过这个唯一标识OID区分在内存中是否是同一个持久化类。在Java中通过地址区分是否是同一个对象的,在关系型数据库的表中式通过主键区分是否同一条记录。那么Hibernate就是通过这个OID来进行区分的。Hibernate是不允许在内存中出现两个ODI相同的持久化对象的。
  • 持久化类尽量不要使用final进行修饰。因为Hibernate中有延迟加载的机制,这个机制中会产生代理对象,Hibernate产生代理对象使用的是字节码的增强技术完成的,其实就是产生了当前类的一个子类对象实现的。如果使用了final修饰持久化类。那么久不能产生子类,从而就不会产生代理对象,那么Hibernate的延迟加载策略(是一种优化手段)就会失效。

Hibernate主键生成策略

主键的类型

在讲解Hibernate的逐渐生成策略之前,先来了解两个概念,即自然主键和代理主键,具体如下:

  • 自然主键:把具有业务含义的字段作为主键,称之为自然主键。例如在customer表中,如果把那么字段作为主键,其前提条件必须是:每一个客户的姓名不允许为null,不允许客户重名,并且不允许修改客户姓名。尽管着也是可行的,但是不能满足不断变化的业务需求,一旦出现了允许客户重名的业务需求,就必须修改数据模型,重新定义表的主键,这给数据库的维护增加了难度。
  • 代理主键:把不具备业务含义的字段作为主键,称之为代理主键。该字段一般取名为“ID”,通常为整数类型,因为整数类型比字符串类型要节省更多的数据库空间。

Hibernate的主键生成策略:

Hibernate中,提供了几个内置的主键生成策略,其常用主键生成策略的名称和描述如下

名称描述
increment用于long、short、或int类型,由Hibernate自动以递增的方式生成唯一标识符,每次增量为1。只有当没有用其它进程想同一张表中插入数据时才可以使用,不能在集群环境下使用。适用于代理主键。
identity采用底层数据库本身提供的主键生成标识符,条件是数据库支持自动增长数据类型。在DB2、MySQL、MS SQL Server、Sybase和HypersonicSQL数据库中可以使用该生成器,该生成器要求在数据库中把主键定义成为自增长类型。适用于代理主键。
sequenceHiberante根据底层数据库序列生成标识符。条件是数据库支持序列。适用于代理主键。
native根据底层数据库对自动生成标识符的能力来选择identity、sequence、hilo三种生成器中的一种,适合跨数据库平台开发。适用于代理主键。
uuidHibernate采用128位的UUID算法来生成标识符。该算法能够在网络环境中生成唯一的字符串标识符,其UUID被编码为一个长度为32为的十六进制字符串。这种策略并不流行,因为字符串类型的主键比整数类型的主键占用更多的数据库空间。适用于代理主键。
assigned由java程序负责生成标识符,如果不指定id元素的generator属性,则默认使用该主键策略。适用于自然主键。

Hibernate的持久化对象的三种状态

持久化对象三种状态的概述

了解了主键的生成策略之后,我们可以进一步来了解持久化类了。Hibernate为了更好的来管理持久化类,特将持久化类分成了三种状态。在Hibernate中持久化的对象可以划分为三种状态,分别是瞬时态、持久态和脱管态,一个持久化类的实例可能处于三种不同状态中的某一种,三种状态详细介绍如下。

  1. 瞬时态(transient)也被称为临时态或者自由态,瞬时态的实例是由new命令创建、开辟内存空间的对象,不存在持久化标识OID(相当于主键值),尚未与Hiberante Session关联,在数据库中也米有记录,失去引用后将被JVM回收。瞬时状态的对象在内存中式孤立存在的,与数据库中的数据无如何关联,仅是一个信息携带的载体。
  2. 持久态(persistent)的对象存在持久化标识OID,加入到了Session缓存中,并且相关联的Session没有关闭,在数据库中有对应的记录,每条记录只对应唯一的持久化对象,需要注意的是,持久态对象是在事务还未提交前变成持久态的。
  3. 脱管态(detached)也被称为离线态或者游离态,当某个持久化状态的实例与Session的管理被关闭时就变成了脱管态。脱管态对象存在持久化标识OID,并且仍然与数据库中的数据存在关联,只是失去了与当前Session的关联,脱管状态对象发生改变时Hibernate不能检测到。

区分对象的三种状态

为了帮助大家更好的理解持久化对象的三种状态,接下来通过具体的案例来演示持久化对象的三种状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//测试对象的三种状态
public class Demo {

@Test
//查看三种状态
public void fun1(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作
Customer c = new Customer(); // 没有id, 没有与session关联 => 瞬时状态

c.setCust_name("联想"); // 瞬时状态

Serializable id = session.save(c); // 持久化状态, 有id,有关联

//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联
System.out.println(c);//托管态对象:有持久化标识OID,没有被session管理


}
}

c 对象由new关键字创建,此时还未与Session进行关联,它的状态称为瞬时态;在执行了session.save(c)操作后,c对象纳入了Session的管理范围,这时的 c 对象变成了持久态对象,此时Session的事务还没有被提交;程序执行完commit()操作并关闭了Session后,c对象与Session的关联被关闭,此时 c 对象就变成脱管态。

Hibernate持久化对象的三种状态转换

hibernate三种状态切换

从图中可以看出,当一个对象被执行new关键字创建后,该对象处于瞬时态;当对瞬时态对象执行Sessionsave()saveOrUpdate()方法后,该对象将被放入Session的以及缓存,对象进入持久态;当对持久态对象执行evict()close()clear()操作后,对象进入托管态;当直接执行Session的get()、load()、find()iterate()等方法从数据库里查询对象时,查询到的对象也处于持久态;当对数据库中的记录进行update()saveOrUpdate()以及lock()等操作后,此时脱管态的对象就过渡到持久态;由于瞬时态和脱管态的对象不在session的管理范围,所以会在一段时间后被JVM回收。

持久化对象的三种状态可以通过调用Session中的一系列方法实现状态间的转换,具体如下:

  1. 瞬时态转换到其他状态

    通过前面学习可知,瞬时态的对象由new关键字创建,瞬时态对象转换到其他状态总结如下:

    • 瞬间态转换为持久态:执行Session的save()或saveOrUpdate()方法。
    • 瞬时态转换为脱管态:为瞬时态对象设置持久化标识OID。

    由于持久化对象状态演化图中没有涉及到瞬时态转换到脱管态的情况,这里做下简要的说明,在前面学习中可知,脱管态对象窜OID,但是没有Session的关联,也就是说脱管态和瞬时态的区别就是OID有没有值,所以可以通过为瞬时态对象设置OID,使其变成脱管态对象。

    1
    2
    Customer customer = new Customer(); //瞬时态
    customer.setCust_id(1); //脱管态
  2. 持久态对象转换到其他状态

    持久化对象可以直接通过Hibernate中Sessionget()、load()方法,或者Query查询从数据库中获得,持久态对象转换到其他的状态总结如下:

    • 持久态转换为瞬时态:执行Sessiondelete()方法,需要注意的是被删除的持久化对象,不建议再次使用
    • 持久态转换为脱管态:执行Sessionevict()、close()clear()方法。evict()方法用于清除异己缓存的所有对象。
  3. 脱管态对象转换到其他状态

    脱管态对象无法直接获得,是由其他状态对象转换而来的,脱管态对象转换到其他状态总结如下:

    • 脱管态转换为持久态:执行Session的update()、saveOrUpdate()或lock()方法。

    • 脱管态转换为瞬时态:将脱管态对象的持久化标识OID设置为null。

      由于持久化对象状态演化图中没有涉及到脱管态换到瞬时态的情况,这里做下简要的说明,跟瞬时态转换到脱管态的情况相似,脱管态和瞬时态的区别就是OID有没有值,所以可以通过将脱管对象的OID设置为null,使其变成瞬时态对象。例如在session.close()操作后,加入代码customer.setCust_id(null);,customer对象将由脱管态转换为瞬时态。

持久态对象能够自动更新数据库

我们已经持久化对象的三种状态了,其实我们主要去研究持久态对象就够了,持久态对象其实有一个非常重要的特性:持久态对象可以自动更新数据库。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 持久化状态特点: 持久化状态对象的任何变化都会自动同步到数据库中.
public void fun3(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作

Customer c = session.get(Customer.class, 1l);//持久化状态对象

c.setCust_name("微软公司");

//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联


}

执行测试我们会发现,我们并没有手动调用update方法,Hibernate就可以将数据自动更新了。持久态对象就有这样的一个功能。持久态对象之所以有这样的功能其实都依赖了Hibernate的一级缓存。接下来我们就开始学习Hibernate缓存。

Hibernate的一级缓存

缓存是计算机领域非常通用的概念。它介于应用程序和永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写永久性数据存储源的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存。

Hibernate的缓存分为一级缓存和二级缓存,Hibernate的这两级缓存都位于持久化层,存储的都是数据的备份。其中第一级缓存为Hibernate的内置缓存,不能被卸载。接下来围绕Hibernate的一级缓存进行详细地讲解。

什么是Hibernate的一级缓存

Hibernate的一级缓存就是指Session缓存,Session缓存是一块内存空间,用来存放相互管理的java对象,在使用Hibernate查询对象的时候,首先会使用对象属性的OID值在Hibernate的一级缓存中进行查找,如果找到匹配OID值的对象,就知己将该对象从一级缓存中取出使用,不会再查询数据库;如果没有找到相同OID值的对象,则会去数据库中查找相应数据。当从数据库中查询到所需数据时,该数据信息也会放置到一级缓存中。Hibernate的一级缓存的作用就是减少对数据库的访问次数

在Session接口的实现中包含一系列的Java集合,这些Java集合构成了Session缓存。只要Session实例没有结束生命周期,存放在它缓存中的对象也不会结束生命周期。固一级缓存也被称为是Session基本的缓存。

Hibernate的一级缓存有如下特点:

  • 当应用程序调用Session接口的save()update()saveOrUpdate,如果Session缓存中没有相应的对象,Hibernate就会自动的把从数据库中查询到的相应对象信息加入到一级缓存中去。
  • 当调用Session接口的load()get()方法,一级Query接口的list()iterator()方法时,会判断缓存中是否存在该对象,有则返回,不会查询数据库,如果缓存中没有要查询对象,在去数据库中查询对应对象,并添加到一级缓存中。
  • 当调用Session的close()方法时,Session缓存会被清空。

测试一级缓存

我们已经大致了解什么是一级缓存,那么一级缓存具体是否存在呢,我们可以通过如下的程序来证明以及缓存是存在的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
//证明Hibernate的一级缓存的存在:
public void demo1(){
Sesion session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();

Customer customer1 = session.get(Customer.class,1l)//马上发送一条sql查询1号客服,并将数据存入了一级缓存
System.out.println(customer1);
Customer customer2 = session.get(Customer.class,1l)//没有发生SQL语句从一级缓存中获取数据
System.out.println(customer2);
System.out.println(customer1 == customer2);//true 一级缓存 缓存的是对象的地址。
tx.commit();
session.close();
}

在以上代码中,第一次执行Session的get()方法获取customer1对象时,由于一级缓存中没有数据,所以Hibernate会向数据库发送一条sql语句,查询id等于1的对象;当再次调用了session的get()方法获取customer2对象时,将不会再发送sql语句,这是因为customer2对象是从一级缓存中获取的。

一级缓存的内部结构:(快照区)

Hibernate向一级缓存放入数据时,同时复制一份数据放入到Hibernate快照中,当使用commit()方法提交事务时,同时会清理Session的一级缓存,这时会使用OID判断一级缓存中的对象和快照中的对象是否一致,如果两个对象中的属性发生变化,则执行update语句,将缓存的内容同步到数据库,并更新快照;如果一致,则不执行update语句。Hibernate快照的作用就是确保一级缓存中的数据和数据库中的数据一致。

Hibernate一级缓存的快照区

Hibernate 的事务控制

Hibernate是对JDBC的轻量级封装,其主要功能是操作数据库。在操作数据库过程中,经常会遇到事务处理的问题,那么我们接下来就来介绍Hibernate中的事务管理。

什么是事务

在数据库操作中,一项事务(Transaction)是由一条或多条操作数据库的SQL语句组成的一个不可分割的工作单元。当食物中的所有操作都正常完成时,整个事务才能被提交到数据库中,如果有一项操作没有完成,则整个事务都会被回滚。

事务的四个特性

事务有很严格的定义,需要同时满足四个特性,即原子性、一致性、隔离性、持久性。这四个特性通常称之为ACID特性,具体如下:

  • 原子性(Atomic): 表示将事务中所做的操作捆绑成一个不可分割的单元,即对事务所进行的数据修改等操作,要么全部执行,要么全都不执行。
  • 一致性(Consistency): 表示事务完成时,必须使所有的数据都保持一致状态。
  • 隔离性(Isolation): 指一个事物的执行不能被其它事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离性的,并发执行的各个事务之间不能相互干扰。
  • 持久性(Durability): 持久性也称为永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。提交后的其他操作或故障不会对其有任何影响。

事务的并发问题

在实际应用过程中,数据库是要被多个用户所共同访问的。在多个事务同时使用相同的数据时,可能会发生并发的问题,具体如下。

  1. 脏读:一个事务读取到另一个事务未提交的数据。
  2. 不可重复读:一个事务读到了另一个事务已经提交的update的数据,导致在同一个事务中的多次查询结果不一致。
  3. 虚读/幻读:一个事务读到了另一个事务已经提交的insert的数据,导致在同一个事务中的多次查询结果不一致

事务的隔离级别

为了避免事务并发问题的发生,在标准SQL规范中,定义了4个事务隔离级别,不同隔离级别对事务的处理不同。

  • 读未提交(ReadUncommitted,1级):一个事务在执行过程中,既可以访问其他事务未提交的新插入的数据,又可以访问未提交的修改数据。如果一个事务已经开始写数据,则另外一个事务则不允许同时进行写操作,但允许其他事务读此行数据。此隔离级别可防止丢失更新。
  • 已提交读(Read Committed,2级):一个事务在执行过程中,既可以访问其他事务成功提交的新插入的数据,又可以访问成功修改的数据。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。此隔离级别可有效防止脏读。
  • 可重复读(Repeatable Read,4级):一个事务在执行过程中,可以访问其他事务成功提交的新插入数据,但不可以访问成功修改的数据。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务,此隔离级别可有效的防止不可重复读和脏读。
  • 序列化/串行化(Serializable,8级):提供严格的事务隔离,它要求事务序列化执行,事务只能一个接着一个地执行,但不能并发执行。此隔离级别可有效的防止脏读、不可重复读和幻读。
隔离级别含义
READ_UNCOMMITTED允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读
READ_COMMITED允许在并发事务已经提交后读取。可防止脏读,但幻读和不可重复读仍可发生
REPEATABLE_READ对相同字段的多次读取是一致的,除非数据被事务本身改变。可以防止脏、不可重复读,但幻读仍可能发生。
SERIALIZABLE完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的

事务的隔离界别,是由数据库提供的,并不是所有数据库都支持四种隔离级别

  • MySQL:READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE(默认 REPEATABLE_READ)
  • Oracle:READ_UNCOMMITTED、READ_COMMITTED、SERIALIZABLE(默认 READ_COMMITTED)

在使用数据库时候,隔离级别越高,安全性越高,性能越低。

实际开发中,不会选择最高或者最低隔离级别,选择READ_COMMITTED(Oracle 默认)、REPEATABLE_READ(Mysql 默认)

Hibernate 中的事务管理

在 Hibernate 中,可以通过代码来操作管理实务,如通过 Transaction tx = session.beginTransaction(); 开启一个事务;持久化操作后,通过 tx.commit();提交事务;如果食物出现异常,又通过tx.rollback();操作来撤销事务(事务回滚)。

除了在代码块中队事务开启,提交和回滚操作外,还可以在 Hibernate 的配置文件中队事务进行配置。配置文件中,可以设置事务的隔离级别。其具体的配置方法是在 hibernate.cfg.xml 文件中的<session-factory>标签元素中进行的。配置方法如下所示。

1
2
3
4
5
6
7
8
9
10
11
<!-- 引入orm元数据
路径书写: 填写src下的路径
-->
<!-- 指定hibernate操作数据库时的隔离级别
#hibernate.connection.isolation 1|2|4|8
0001 1 读未提交
0010 2 读已提交
0100 4 可重复读
1000 8 串行化
-->
<property name="hibernate.connection.isolation">4</property>

到这我们已经设置了事务的隔离级别,那么我们在真正进行事务管理的时候,需要考虑事务的应用的场景,也就是说我们的事务控制不应该是在DAO层实现的,应该在Service层实现,并且在Service中调用多个Dao实现一个业务逻辑的操作。具体操作如下显示:

Hibernate的隔离级别事务显示

其实最主要的是如何保证在Service中开启的事务时使用的Session对象和DAO中多个操作使用的是同一个Session对象。

其实有两个方法可以实现:

  • 可以在业务层获取到Session,并将Session作为参数传递给DAO。
  • 可以使用ThreadLocal将业务层获取的Session绑定到当前线程中,然后再DAO中获取Session的时候,都从当前线程中获取。

其实使用第二种方式肯定是最优方案,那么具体的实现已经不用我们来完成了,Hibernate的内部已经将这个事情做完了。我们只需要完成一段配置即可。

Hibernate5中自身提供了三种管理Session对象的方法

  • Session对象的生命周期与本地线程绑定、

  • Session对象的生命周期与 JTA 事务绑定

  • Hibernate委托程序管理 Session 对象的生命周期

在 Hibernate 的配置文件中,hibernate.current_session_context_class 属性用于指定 Session 管理方式,可选值包括

  • Thread:Session对象的生命周期与本地线程绑定
  • jta: Session对象的生命周期与JTA事务绑定
  • managed: Hibernate 委托程序来管理 Session 对象的生命周期

在hibernate.cfg.xml中进行如下配置:

1
2
<!-- 指定session与当前线程绑定 -->
<property name="hibernate.current_session_context_class">thread</property>

hibernate提供sessionFactory.getCurrentSession()创建一个session和ThreadLocal绑定方法。

在HibernateUtil工具类中更改getCurrentSession方法:

1
2
3
4
//获取当前线程绑定的会话
public static Session getCurrentSession(){
return sessionFactory.getCurrentSession();
}

而且Hibernate中提供的这个与线程绑定的 session 可以不用关闭,当线程执行结束后,就会自动关闭了。

Hibernate的其他API

Query

Query代表面向对象的一个 Hibernate 查询操作。在 Hibernate 中,通常使用session.createQuery()方法接受一个HQL语句,然后调用 Query 的list()uniqueResult() 方法执行查询。所谓的 HQL 是Hibernate Query Language 缩写,其语法很像 SQL 语法,但它是完全面向对象的。

在 Hibernate中使用 Query对象的步骤,具体所示:

  1. 获得Hibernate的Session对象。
  2. 编写HQL语句。
  3. 调用session.createQuery 创建查询对象。
  4. 如果HQL语句包含参数,则调用Query的 setXxx 设置参数。
  5. 调用 Query 对象的 list()uniqueResult() 方法执行查询。

了解了使用 Query 对象的步骤后,接下来,通过具体示例来演示 Query 对象的查询操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//测试HQL语句
public class Demo {

@Test
//基本查询
public void fun1(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作
//-------------------------------------------
//1> 书写HQL语句
// String hql = " from cn.itheima.domain.Customer ";
String hql = " from Customer "; // 查询所有Customer对象
//2> 根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//3> 根据查询对象获得查询结果
List<Customer> list = query.list(); // 返回list结果
//query.uniqueResult();//接收唯一的查询结果

System.out.println(list);
//-------------------------------------------
//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联


}

@Test
//条件查询
//HQL语句中,不可能出现任何数据库相关的信息的
public void fun2(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作
//-------------------------------------------
//1> 书写HQL语句
String hql = " from Customer where cust_id = 1 "; // 查询所有Customer对象
//2> 根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//3> 根据查询对象获得查询结果
Customer c = (Customer) query.uniqueResult();

System.out.println(c);
//-------------------------------------------
//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联


}

@Test
//条件查询
//问号占位符
public void fun3(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作
//-------------------------------------------
//1> 书写HQL语句
String hql = " from Customer where cust_id = ? "; // 查询所有Customer对象
//2> 根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//设置参数
//query.setLong(0, 1l);
query.setParameter(0, 1l);
//3> 根据查询对象获得查询结果
Customer c = (Customer) query.uniqueResult();

System.out.println(c);
//-------------------------------------------
//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联


}

@Test
//条件查询
//命名占位符
public void fun4(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作
//-------------------------------------------
//1> 书写HQL语句
String hql = " from Customer where cust_id = :cust_id "; // 查询所有Customer对象
//2> 根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//设置参数
query.setParameter("cust_id", 1l);
//3> 根据查询对象获得查询结果
Customer c = (Customer) query.uniqueResult();

System.out.println(c);
//-------------------------------------------
//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联


}

@Test
//分页查询
public void fun5(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作
//-------------------------------------------
//1> 书写HQL语句
String hql = " from Customer "; // 查询所有Customer对象
//2> 根据HQL语句创建查询对象
Query query = session.createQuery(hql);
//设置分页信息 limit ?,?
query.setFirstResult(1);
query.setMaxResults(1);
//3> 根据查询对象获得查询结果
List<Customer> list = query.list();

System.out.println(list);
//-------------------------------------------
//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联


}
}

Query中出来使用list()方法查询全部数据外,还有其它一些常用方法,具体如下:

  • setter方法: Query接口中提供了一系列的 setter 方法用于设置查询语句的参数,针对不同的数据类型,需要用到不同的setter方法。
  • iterator()方法:该方法用于查询语句,返回的结果是一个Iterator对象,在读取时只能按照顺序方式读取,它仅把使用到的数据转换成Java实体对象。
  • uniqueResult()方法:该方法用于返回唯一的结果,在确保只有一条记录的查询时可以使用该方法。
  • executeUpdate()方法:该方法是 Hibernate 的新特性,它支持 HQL 语句的更新和删除操作。
  • setFirstResult()方法:该方法可以设置获取第一个记录的位置,也就是它表示从第几条记录开始查询,默认从0开始计算。
  • setMaxResult()方法:该方法用于设置结果集的最大记录数,通常与setFirstResult() 方法结合使用,用于限制结果集的范围,以实现分页功能。

Criteria

Criteria 是一个完全面向对象,可扩展的条件查询API,通过它完全不需要考虑数据库底层如何实现,以及SQL语句如何编写,它是Hibernate框架的核心查询对象。Criteria查询,又称为 QBC 查询(Query By Criteria),它是Hibernate的另一种对象检索方式。

org.hibernate.criterion.Criterion是 Hibernate 提供的一个面向对象查询条件接口,一个单独的查询就是 Criterion 接口的一个实例,用于限制 Criteria 对象的查询,在 Hibernate 中 Criterion 对象的创建通常是通过 Restrictions 工厂类完成的,它提供饿了条件查询方法。

通常,使用 Criteria 对象查询数据的主要步骤,具体如下:

  1. 获得Hibernate的 Session 对象。
  2. 通过 Session 获得 Criteria 对象。
  3. 使用 Restrictions 的静态方法创建 Criterion 条件对象。Restrictions 类中提供了一系列用于设定查询条件的静态方法,这些静态方法都返回Criterion 实例,每个 Criterion 实例代表一个查询条件。
  4. 向 Criteria 对象中添加 Criterion 查询条件。Criteria的 add() 方法用于查询条件。
  5. 执行 Criterita 的 list() 或 uniqueResult() 获得结果。

了解了 Criteria 对象的使用步骤后,接下来,通过具体示例来演示 Criterria 对象的查询操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
//测试Criteria查询
public class Demo {

@Test
//基本查询
public void fun1(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作
//-------------------------------------------

//查询所有的Customer对象
Criteria criteria = session.createCriteria(Customer.class);

List<Customer> list = criteria.list();

System.out.println(list);

// Customer c = (Customer) criteria.uniqueResult();

//-------------------------------------------
//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联


}

@Test
//条件查询
//HQL语句中,不可能出现任何数据库相关的信息的
// > gt
// >= ge
// < lt
// <= le
// == eq
// != ne
// in in
// between and between
// like like
// is not null isNotNull
// is null isNull
// or or
// and and
public void fun2(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作
//-------------------------------------------
//创建criteria查询对象
Criteria criteria = session.createCriteria(Customer.class);
//添加查询参数 => 查询cust_id为1的Customer对象
criteria.add(Restrictions.eq("cust_id", 1l));
//执行查询获得结果
Customer c = (Customer) criteria.uniqueResult();
System.out.println(c);
//-------------------------------------------
//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联


}



@Test
//分页查询
public void fun3(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作
//-------------------------------------------
//创建criteria查询对象
Criteria criteria = session.createCriteria(Customer.class);
//设置分页信息 limit ?,?
criteria.setFirstResult(1);
criteria.setMaxResults(2);
//执行查询
List<Customer> list = criteria.list();

System.out.println(list);
//-------------------------------------------
//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联


}

@Test
//查询总记录数
public void fun4(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作
//-------------------------------------------
//创建criteria查询对象
Criteria criteria = session.createCriteria(Customer.class);
//设置查询的聚合函数 => 总行数
criteria.setProjection(Projections.rowCount());
//执行查询
Long count = (Long) criteria.uniqueResult();

System.out.println(count);
//-------------------------------------------
//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联


}
}

SQLQuery

SQLQuery 这个就比较简单了,这个接口用于接收一个sql语句进行查询,然后调用 list()或者 uniqueResult() 方法进行查询。但是 sql 语句不会直接封装到实体对象中,需要我们手动写代码才可以封装到实体中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
//测试原生SQL查询
public class Demo {

@Test
//基本查询
public void fun1(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作
//-------------------------------------------
//1 书写sql语句
String sql = "select * from cst_customer";

//2 创建sql查询对象
SQLQuery query = session.createSQLQuery(sql);

//3 调用方法查询结果
List<Object[]> list = query.list();
//query.uniqueResult();

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

//-------------------------------------------
//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联


}

@Test
//基本查询
public void fun2(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作
//-------------------------------------------
//1 书写sql语句
String sql = "select * from cst_customer";

//2 创建sql查询对象
SQLQuery query = session.createSQLQuery(sql);
//指定将结果集封装到哪个对象中
query.addEntity(Customer.class);

//3 调用方法查询结果
List<Customer> list = query.list();

System.out.println(list);
//-------------------------------------------
//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联


}

@Test
//条件查询
public void fun3(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作
//-------------------------------------------
//1 书写sql语句
String sql = "select * from cst_customer where cust_id = ? ";

//2 创建sql查询对象
SQLQuery query = session.createSQLQuery(sql);

query.setParameter(0, 1l);
//指定将结果集封装到哪个对象中
query.addEntity(Customer.class);

//3 调用方法查询结果
List<Customer> list = query.list();

System.out.println(list);
//-------------------------------------------
//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联


}

@Test
//分页查询
public void fun4(){
//1 获得session
Session session = HibernateUtils.openSession();
//2 控制事务
Transaction tx = session.beginTransaction();
//3执行操作
//-------------------------------------------
//1 书写sql语句
String sql = "select * from cst_customer limit ?,? ";

//2 创建sql查询对象
SQLQuery query = session.createSQLQuery(sql);

query.setParameter(0, 0);
query.setParameter(1, 1);
//指定将结果集封装到哪个对象中
query.addEntity(Customer.class);

//3 调用方法查询结果
List<Customer> list = query.list();

System.out.println(list);
//-------------------------------------------
//4提交事务.关闭资源
tx.commit();
session.close();// 游离|托管 状态, 有id , 没有关联


}
}

Hibernate的进阶

表操作-多对多配置

数据库表与表之间的关系

一对多关系

  • 什么样关系属于一对多?

    • 一个部门对应多个员工,一个员工只能属于某一个部门。
    • 一个客户对应多个联系人,一个联系人只能属于某一个客服。

多对多关系

  • 什么关系属于多对多?
    • 一个学生可以选择多门课程,一门课程也可以被多个学生选择。
    • 一个用户可以选择多个角色,一个角色也可以被多个用户选择

一对一关系(了解)

  • 什么样关系属于一对一?
    • 一个公司只能有一个注册地址,一个注册地址只能被一个公司注册。

Hibernate 一对多的关系配置

创建一个项目,引入相应 jar 包

创建数据库和表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
CREATE TABLE `cst_customer` (
`cust_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '客户编号(主键)',
`cust_name` varchar(32) NOT NULL COMMENT '客户名称(公司名称)',
`cust_source` varchar(32) DEFAULT NULL COMMENT '客户信息来源',
`cust_industry` varchar(32) DEFAULT NULL COMMENT '客户所属行业',
`cust_level` varchar(32) DEFAULT NULL COMMENT '客户级别',
`cust_phone` varchar(64) DEFAULT NULL COMMENT '固定电话',
`cust_mobile` varchar(16) DEFAULT NULL COMMENT '移动电话',
PRIMARY KEY (`cust_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

CREATE TABLE `cst_linkman` (
`lkm_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '联系人编号(主键)
',
`lkm_name` varchar(16) DEFAULT NULL COMMENT '联系人姓名',
`lkm_cust_id` bigint(32) DEFAULT NULL COMMENT '客户id',
`lkm_gender` char(1) DEFAULT NULL COMMENT '联系人性别',
`lkm_phone` varchar(16) DEFAULT NULL COMMENT '联系人办公电话',
`lkm_mobile` varchar(16) DEFAULT NULL COMMENT '联系人手机',
`lkm_email` varchar(64) DEFAULT NULL COMMENT '联系人邮箱',
`lkm_qq` varchar(16) DEFAULT NULL COMMENT '联系人qq',
`lkm_position` varchar(16) DEFAULT NULL COMMENT '联系人职位',
`lkm_memo` varchar(512) DEFAULT NULL COMMENT '联系人备注',
PRIMARY KEY (`lkm_id`),
KEY `FK_cst_linkman_lkm_cust_id` (`lkm_cust_id`),
CONSTRAINT `FK_cst_linkman_lkm_cust_id` FOREIGN KEY (`lkm_cust_id`)
REFERENCES `cst_customer` (`cust_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

创建实体

  • 一的一方的实体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    public class Customer {
    private Long cust_id;
    private String cust_name;
    private String cust_source;
    private String cust_industry;
    private String cust_level;
    private String cust_phone;
    private String cust_mobile;
    // 通过ORM方式表示:一个客户对应多个联系人。
    // 放置的多的一方的集合。Hibernate默认使用的是Set集合。
    private Set<LinkMan> linkMans = new HashSet<LinkMan>();

    public Long getCust_id() {
    return cust_id;
    }
    public void setCust_id(Long cust_id) {
    this.cust_id = cust_id;
    }
    public String getCust_name() {
    return cust_name;
    }
    public void setCust_name(String cust_name) {
    this.cust_name = cust_name;
    }
    public String getCust_source() {
    return cust_source;
    }
    public void setCust_source(String cust_source) {
    this.cust_source = cust_source;
    }
    public String getCust_industry() {
    return cust_industry;
    }
    public void setCust_industry(String cust_industry) {
    this.cust_industry = cust_industry;
    }
    public String getCust_level() {
    return cust_level;
    }
    public void setCust_level(String cust_level) {
    this.cust_level = cust_level;
    }
    public String getCust_phone() {
    return cust_phone;
    }
    public void setCust_phone(String cust_phone) {
    this.cust_phone = cust_phone;
    }
    public String getCust_mobile() {
    return cust_mobile;
    }
    public void setCust_mobile(String cust_mobile) {
    this.cust_mobile = cust_mobile;
    }
    public Set<LinkMan> getLinkMans() {
    return linkMans;
    }
    public void setLinkMans(Set<LinkMan> linkMans) {
    this.linkMans = linkMans;
    }
    }
  • 多的一方的实体

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    public class LinkMan {
    private Long lkm_id;
    private String lkm_name;
    private String lkm_gender;
    private String lkm_phone;
    private String lkm_mobile;
    private String lkm_email;
    private String lkm_qq;
    private String lkm_position;
    private String lkm_memo;
    // 通过ORM方式表示:一个联系人只能属于某一个客户。
    // 放置的是一的一方的对象。
    private Customer customer;

    public Long getLkm_id() {
    return lkm_id;
    }
    public void setLkm_id(Long lkm_id) {
    this.lkm_id = lkm_id;
    }
    public String getLkm_name() {
    return lkm_name;
    }
    public void setLkm_name(String lkm_name) {
    this.lkm_name = lkm_name;
    }
    public String getLkm_gender() {
    return lkm_gender;
    }
    public void setLkm_gender(String lkm_gender) {
    this.lkm_gender = lkm_gender;
    }
    public String getLkm_phone() {
    return lkm_phone;
    }
    public void setLkm_phone(String lkm_phone) {
    this.lkm_phone = lkm_phone;
    }
    public String getLkm_mobile() {
    return lkm_mobile;
    }
    public void setLkm_mobile(String lkm_mobile) {
    this.lkm_mobile = lkm_mobile;
    }
    public String getLkm_email() {
    return lkm_email;
    }
    public void setLkm_email(String lkm_email) {
    this.lkm_email = lkm_email;
    }
    public String getLkm_qq() {
    return lkm_qq;
    }
    public void setLkm_qq(String lkm_qq) {
    this.lkm_qq = lkm_qq;
    }
    public String getLkm_position() {
    return lkm_position;
    }
    public void setLkm_position(String lkm_position) {
    this.lkm_position = lkm_position;
    }
    public String getLkm_memo() {
    return lkm_memo;
    }
    public void setLkm_memo(String lkm_memo) {
    this.lkm_memo = lkm_memo;
    }
    public Customer getCustomer() {
    return customer;
    }
    public void setCustomer(Customer customer) {
    this.customer = customer;
    }

    }

创建映射文件

  • 多的一方的映射的创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping>
    <class name="com.hibernate.domain.LinkMan" table="cst_linkman">
    <!-- 建立OID与主键映射 -->
    <id name="lkm_id" column="lkm_id">
    <generator class="native"/>
    </id>
    <!-- 建立普通属性与表字段映射 -->
    <property name="lkm_name"/>
    <property name="lkm_gender"/>
    <property name="lkm_phone"/>
    <property name="lkm_mobile"/>
    <property name="lkm_email"/>
    <property name="lkm_qq"/>
    <property name="lkm_position"/>
    <property name="lkm_memo"/>
    <!-- 配置多对一的关系:放置的是一的一方的对象 -->
    <!--
    many-to-one标签
    * name :一的一方的对象的属性名称。
    * class :一的一方的类的全路径。
    * column :在多的一方的表的外键的名称。
    -->
    <many-to-one name="customer" class="com.hibernate.domain.Customer" column="lkm_cust_id"/>
    </class>
    </hibernate-mapping>
  • 一的一方的映射的创建

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping>
    <class name="com.hibernate.domain.Customer" table="cst_customer">
    <!-- 建立OID与主键映射 -->
    <id name="cust_id" column="cust_id">
    <generator class="native"/>
    </id>
    <!-- 建立普通属性与数据库表字段映射 -->
    <property name="cust_name" column="cust_name" />
    <property name="cust_source" column="cust_source"/>
    <property name="cust_industry" column="cust_industry"/>
    <property name="cust_level" column="cust_level"/>
    <property name="cust_phone" column="cust_phone"/>
    <property name="cust_mobile" column="cust_mobile"/>
    <!-- 配置一对多的映射:放置的多的一方的集合 -->
    <!--
    set标签 :
    * name :多的一方的对象集合的属性名称。
    * cascade:级联
    * inverse:放弃外键维护权。
    -->
    <set name="linkMans" cascade="save-update" inverse="true">
    <!--
    key标签
    * column:多的一方的外键的名称。
    -->
    <key column="lkm_cust_id"/>
    <!--
    one-to-many标签
    * class :多的一方的类的全路径
    -->
    <one-to-many class="com.hibernate.domain.LinkMan"/>
    </set>
    </class>
    </hibernate-mapping>

创建核心配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- 连接数据库的基本参数 -->
<property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
<property name="hibernate.connection.url">jdbc:mysql:///hibernate</property>
<property name="hibernate.connection.username">root</property>
<property name="hibernate.connection.password">abc</property>
<!-- 配置Hibernate的方言 -->
<property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>

<!-- 可选配置================ -->
<!-- 打印SQL -->
<property name="hibernate.show_sql">true</property>
<!-- 格式化SQL -->
<property name="hibernate.format_sql">true</property>
<!-- 自动创建表 -->
<property name="hibernate.hbm2ddl.auto">update</property>

<!-- 配置C3P0连接池 -->
<property name="connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
<!--在连接池中可用的数据库连接的最少数目 -->
<property name="c3p0.min_size">5</property>
<!--在连接池中所有数据库连接的最大数目 -->
<property name="c3p0.max_size">20</property>
<!--设定数据库连接的过期时间,以秒为单位,
如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
<property name="c3p0.timeout">120</property>
<!--每3000秒检查所有连接池中的空闲连接 以秒为单位-->
<property name="c3p0.idle_test_period">3000</property>

<!-- 设置事务隔离级别 -->
<property name="hibernate.connection.isolation">4</property>
<!-- 配置当前线程绑定的Session -->
<property name="hibernate.current_session_context_class">thread</property>

<!-- 引入映射 -->
<!-- <mapping resource="com/hibernate/domain/Customer.hbm.xml"/>
<mapping resource="com/hibernate/domain/LinkMan.hbm.xml"/> -->
<mapping resource="com/hibernate/domain/User.hbm.xml"/>
<mapping resource="com/hibernate/domain/Role.hbm.xml"/>
</session-factory>
</hibernate-configuration>

引入工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class HibernateUtils {

public static final Configuration cfg;
public static final SessionFactory sf;

static{
cfg = new Configuration().configure();
sf = cfg.buildSessionFactory();
}

public static Session openSession(){
return sf.openSession();
}

public static Session getCurrentSession(){
return sf.getCurrentSession();
}
}

编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class HibernateDemo1 {

@Test
// 保存2个客户 和 3个联系人 并且建立好关系
public void demo1(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 创建两个客户
Customer customer1 = new Customer();
customer1.setCust_name("王东");
Customer customer2 = new Customer();
customer2.setCust_name("赵洪");

// 创建三个联系人
LinkMan linkMan1 = new LinkMan();
linkMan1.setLkm_name("凤姐");
LinkMan linkMan2 = new LinkMan();
linkMan2.setLkm_name("如花");
LinkMan linkMan3 = new LinkMan();
linkMan3.setLkm_name("旺财");

// 设置关系:
linkMan1.setCustomer(customer1);
linkMan2.setCustomer(customer1);
linkMan3.setCustomer(customer2);
customer1.getLinkMans().add(linkMan1);
customer1.getLinkMans().add(linkMan2);
customer2.getLinkMans().add(linkMan3);

// 保存数据:
session.save(linkMan1);
session.save(linkMan2);
session.save(linkMan3);
session.save(customer1);
session.save(customer2);

tx.commit();
}
}

Hibernate 的一对多相关操作

一对多关系只保存一边是否可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
// 一对多关系只保存一边是否可以
public void demo2(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

Customer customer = new Customer();
customer.setCust_name("赵洪");

LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("如花");
customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);

// 只保存一边是否可以:不可以,报一个瞬时对象异常:持久态对象关联了一个瞬时态对象。
// session.save(customer);
session.save(linkMan);

tx.commit();
}

一对多的级联操作

  • 什么叫级联
    • 级联指的是,操作一个对象的时候,是否会同时操作其关联的对象。
  • 级联是有方向性
    • 操作一的一方的时候,是否操作到多的一方
    • 操作多的一方的时候,是否操作到一的一方

级联保存或更新

  • 保存客户级联联系人

    1
    2
    3
    4
    5
    6
    7
    <!-- 
    set标签 :
    * name :多的一方的对象集合的属性名称。
    * cascade:级联
    * inverse:放弃外键维护权。
    -->
    <set name="linkMans" cascade="save-update" inverse="true">
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    @Test
    /**
    * 级联保存或更新操作:
    * * 保存客户级联联系人,操作的主体是客户对象,需要在Customer.hbm.xml中进行配置
    * * <set name="linkMans" cascade="save-update">
    */
    public void demo3(){
    Session session = HibernateUtils.getCurrentSession();
    Transaction tx = session.beginTransaction();

    Customer customer = new Customer();
    customer.setCust_name("赵洪");

    LinkMan linkMan = new LinkMan();
    linkMan.setLkm_name("如花");

    customer.getLinkMans().add(linkMan);
    linkMan.setCustomer(customer);

    session.save(customer);

    tx.commit();
    }
  • 保存联系人联系人级联客户
1
2
3
4
5
6
7
8
<!-- 配置多对一的关系:放置的是一的一方的对象 -->
<!--
many-to-one标签
* name :一的一方的对象的属性名称。
* class :一的一方的类的全路径。
* column :在多的一方的表的外键的名称。
-->
<many-to-one name="customer" class="com.itheima.hibernate.domain.Customer" column="lkm_cust_id"/>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Test
/**
* 级联保存或更新操作:
* * 保存联系人级联客户,操作的主体是联系人对象,需要在LinkMan.hbm.xml中进行配置
* * <many-to-one name="customer" cascade="save-update" class="com.itheima.hibernate.domain.Customer" column="lkm_cust_id"/>
*/
public void demo4(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

Customer customer = new Customer();
customer.setCust_name("李兵");

LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("凤姐");

customer.getLinkMans().add(linkMan);
linkMan.setCustomer(customer);

session.save(linkMan);

tx.commit();
}

测试对象的导航

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Test
/**
* 测试对象的导航
* * 前提:一对多的双方都设置cascade="save-update"
*/
public void demo5(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

Customer customer = new Customer();
customer.setCust_name("李兵");

LinkMan linkMan1 = new LinkMan();
linkMan1.setLkm_name("凤姐");
LinkMan linkMan2 = new LinkMan();
linkMan2.setLkm_name("如花");
LinkMan linkMan3 = new LinkMan();
linkMan3.setLkm_name("芙蓉");

linkMan1.setCustomer(customer);
customer.getLinkMans().add(linkMan2);
customer.getLinkMans().add(linkMan3);

// 双方都设置了cascade
// session.save(linkMan1); // 发送几条insert语句 4条
// session.save(customer); // 发送几条insert语句 3条
session.save(linkMan2); // 发送几条insert语句 1条

tx.commit();
}

级联删除

  • 级联删除:
    • 删除一边的时候,同时将另一方的数据也一并删除。
  • 删除客户级联删除联系人
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
/**
* 级联删除:
* * 删除客户级联删除联系人,删除的主体是客户,需要在Customer.hbm.xml中配置
* * <set name="linkMans" cascade="delete">
*/
public void demo6(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 没有设置级联删除,默认情况:修改了联系人的外键,删除客户
/*Customer customer = session.get(Customer.class, 1l);
session.delete(customer);*/

// 删除客户,同时删除联系人
Customer customer = session.get(Customer.class, 1l);
session.delete(customer);

tx.commit();
}
  • 删除联系人级联删除客户(基本不用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Test
/**
* 级联删除:
* * 删除联系人级联删除客户,删除的主体是联系人,需要在LinkMan.hbm.xml中配置
* * <many-to-one name="customer" cascade="delete">
*/
public void demo7(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 删除客户,同时删除联系人
LinkMan linkMan = session.get(LinkMan.class, 3l);
session.delete(linkMan);

tx.commit();
}

一对多设置了双向关联产生多余的SQL语句

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
/**
* 将2号联系人原来归1号客户,现在改为2号客户
*/
public void demo8(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 查询2号联系人
LinkMan linkMan = session.get(LinkMan.class, 2l);
// 查询2号客户
Customer customer = session.get(Customer.class, 2l);
// 双向的关联
linkMan.setCustomer(customer);
customer.getLinkMans().add(linkMan);

tx.commit();
}
  • 解决多余的SQL语句

    • 单向维护:

    • 使一方放弃外键维护权:

      • 一的一方放弃。在set上配置inverse=”true”
    • 一对多的关联查询的修改的时候。(CRM练习–)

区分cascade和inverse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
/**
* 区分cascade和inverse的区别
*/
public void demo9(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

Customer customer = new Customer();
customer.setCust_name("李兵");

LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("凤姐");

customer.getLinkMans().add(linkMan);

// 条件在Customer.hbm.xml上的set中配置了cascade="save-update" inverse="true"
session.save(customer); // 客户会插入到数据库,联系人也会插入到数据库,但是外键为null

tx.commit();
}
}

Hibernate多对多关系的配置

创建表

  • 用户表

    1
    2
    3
    4
    5
    6
    7
    8
    CREATE TABLE `sys_user` (
    `user_id` bigint(32) NOT NULL AUTO_INCREMENT COMMENT '用户id',
    `user_code` varchar(32) NOT NULL COMMENT '用户账号',
    `user_name` varchar(64) NOT NULL COMMENT '用户名称',
    `user_password` varchar(32) NOT NULL COMMENT '用户密码',
    `user_state` char(1) NOT NULL COMMENT '1:正常,0:暂停',
    PRIMARY KEY (`user_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
  • 角色表

    1
    2
    3
    4
    5
    6
    CREATE TABLE `sys_role` (
    `role_id` bigint(32) NOT NULL AUTO_INCREMENT,
    `role_name` varchar(32) NOT NULL COMMENT '角色名称',
    `role_memo` varchar(128) DEFAULT NULL COMMENT '备注',
    PRIMARY KEY (`role_id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
  • 中间表

    1
    2
    3
    4
    5
    6
    7
    8
    CREATE TABLE `sys_user_role` (
    `role_id` bigint(32) NOT NULL COMMENT '角色id',
    `user_id` bigint(32) NOT NULL COMMENT '用户id',
    PRIMARY KEY (`role_id`,`user_id`),
    KEY `FK_user_role_user_id` (`user_id`),
    CONSTRAINT `FK_user_role_role_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`role_id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
    CONSTRAINT `FK_user_role_user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`user_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建实体

  • 用户的实体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public class User {
private Long user_id;
private String user_code;
private String user_name;
private String user_password;
private String user_state;
// 设置多对多关系:表示一个用户选择多个角色?
// 放置的是角色的集合
private Set<Role> roles = new HashSet<Role>();
public Long getUser_id() {
return user_id;
}
public void setUser_id(Long user_id) {
this.user_id = user_id;
}
public String getUser_code() {
return user_code;
}
public void setUser_code(String user_code) {
this.user_code = user_code;
}
public String getUser_name() {
return user_name;
}
public void setUser_name(String user_name) {
this.user_name = user_name;
}
public String getUser_password() {
return user_password;
}
public void setUser_password(String user_password) {
this.user_password = user_password;
}
public String getUser_state() {
return user_state;
}
public void setUser_state(String user_state) {
this.user_state = user_state;
}
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}

}
  • 角色的实体
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Role {
private Long role_id;
private String role_name;
private String role_memo;
// 一个角色被多个用户选择:
// 放置的是用户的集合
private Set<User> users = new HashSet<User>();
public Long getRole_id() {
return role_id;
}
public void setRole_id(Long role_id) {
this.role_id = role_id;
}
public String getRole_name() {
return role_name;
}
public void setRole_name(String role_name) {
this.role_name = role_name;
}
public String getRole_memo() {
return role_memo;
}
public void setRole_memo(String role_memo) {
this.role_memo = role_memo;
}
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}

}

创建映射

  • 用户的映射

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping>
    <class name="com.hibernate.domain.User" table="sys_user">
    <!-- 建立OID与主键的映射 -->
    <id name="user_id" column="user_id">
    <generator class="native"/>
    </id>
    <!-- 建立普通属性与字段映射 -->
    <property name="user_code" column="user_code"/>
    <property name="user_name" column="user_name"/>
    <property name="user_password" column="user_password"/>
    <property name="user_state" column="user_state"/>
    <!-- 建立与角色的多对多的映射关系 -->
    <!--
    set标签
    * name :对方的集合的属性名称。
    * table :多对多的关系需要使用中间表,放的是中间表的名称。
    -->
    <set name="roles" table="sys_user_role" cascade="save-update,delete" >
    <!--
    key标签:
    * column :当前的对象对应中间表的外键的名称。
    -->
    <key column="user_id"/>
    <!--
    many-to-many标签:
    * class :对方的类的全路径
    * column :对方的对象在中间表中的外键的名称。
    -->
    <many-to-many class="com.hibernate.domain.Role" column="role_id"/>
    </set>
    </class>
    </hibernate-mapping>
  • 角色的映射

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
    <hibernate-mapping>
    <class name="com.hibernate.domain.Role" table="sys_role">
    <!-- 建立OID与主键的映射 -->
    <id name="role_id" column="role_id">
    <generator class="native"/>
    </id>
    <!-- 建立普通属性与字段的映射 -->
    <property name="role_name" column="role_name"/>
    <property name="role_memo" column="role_memo"/>
    <!-- 与用户的多对多的映射关系 -->
    <!--
    set标签
    * name :对方的集合的属性名称。
    * table :多对多的关系需要使用中间表,放的是中间表的名称。
    -->
    <set name="users" table="sys_user_role" cascade="save-update,delete" inverse="true">
    <!--
    key标签:
    * column :当前的对象对应中间表的外键的名称。
    -->
    <key column="role_id"/>
    <!--
    many-to-many标签:
    * class :对方的类的全路径
    * column :对方的对象在中间表中的外键的名称。
    -->
    <many-to-many class="com.hibernate.domain.User" column="user_id"/>
    </set>
    </class>
    </hibernate-mapping>

编写测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.itheima.hibernate.demo2;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import com.itheima.hibernate.domain.Role;
import com.itheima.hibernate.domain.User;
import com.itheima.hibernate.utils.HibernateUtils;

/**
* Hibernate的多对多的映射
* @author jt
*
*/
public class HibernateDemo2 {

@Test
/**
* 保存多条记录:保存多个用户和角色
*/
public void demo1(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 创建2个用户
User user1 = new User();
user1.setUser_name("赵洪");
User user2 = new User();
user2.setUser_name("李兵");

// 创建3个角色
Role role1 = new Role();
role1.setRole_name("研发部");
Role role2 = new Role();
role2.setRole_name("市场部");
Role role3 = new Role();
role3.setRole_name("公关部");

// 设置双向的关联关系:
user1.getRoles().add(role1);
user1.getRoles().add(role2);
user2.getRoles().add(role2);
user2.getRoles().add(role3);
role1.getUsers().add(user1);
role2.getUsers().add(user1);
role2.getUsers().add(user2);
role3.getUsers().add(user2);

// 保存操作:多对多建立了双向的关系必须有一方放弃外键维护。
// 一般是被动方放弃外键维护权。
session.save(user1);
session.save(user2);
session.save(role1);
session.save(role2);
session.save(role3);

tx.commit();
}
}

Hibernated 多对多的操作

只保存一边是否可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Test
/**
* 多对多的操作:
* * 只保存一边是否可以?不可以,瞬时对象异常
*/
public void demo2(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 创建2个用户
User user1 = new User();
user1.setUser_name("赵洪");

// 创建3个角色
Role role1 = new Role();
role1.setRole_name("研发部");

// 设置双向的关联关系:
user1.getRoles().add(role1);
role1.getUsers().add(user1);
// 只保存用户:
// session.save(user1);
session.save(role1);

tx.commit();
}

多对多的级联保存或更新

  • 保存用户级联保存角色
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Test
/**
* 多对多的级联保存:
* * 保存用户级联保存角色。在用户的映射文件中配置。
* * 在User.hbm.xml中的set上配置 cascade="save-update"
*/
public void demo3(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 创建2个用户
User user1 = new User();
user1.setUser_name("赵洪");

// 创建3个角色
Role role1 = new Role();
role1.setRole_name("研发部");

// 设置双向的关联关系:
user1.getRoles().add(role1);
role1.getUsers().add(user1);
// 只保存用户:
session.save(user1);

tx.commit();
}
  • 保存角色级联保存用户
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* 多对多的级联保存:
* * 保存角色级联保存用户。在角色的映射文件中配置。
* * 在Role.hbm.xml中的set上配置 cascade="save-update"
*/
@Test
public void demo4(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 创建2个用户
User user1 = new User();
user1.setUser_name("李兵");

// 创建3个角色
Role role1 = new Role();
role1.setRole_name("公关部");

// 设置双向的关联关系:
user1.getRoles().add(role1);
role1.getUsers().add(user1);
// 只保存用户:
session.save(role1);

tx.commit();
}

多对多的级联删除(基本用不上)

  • 删除用户级联删除角色

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /**
    * 多对多的级联删除:
    * * 删除用户级联删除角色
    * * 在User.hbm.xml中的set上配置 cascade="delete"
    */
    @Test
    public void demo5(){
    Session session = HibernateUtils.getCurrentSession();
    Transaction tx = session.beginTransaction();

    // 查询1号用户:
    User user = session.get(User.class, 1l);
    session.delete(user);

    tx.commit();
    }
  • 删除角色级联删除用户

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    /**
    * 多对多的级联删除:
    * * 删除角色级联删除用户
    * * 在Role.hbm.xml中的set上配置 cascade="delete"
    */
    @Test
    public void demo6(){
    Session session = HibernateUtils.getCurrentSession();
    Transaction tx = session.beginTransaction();

    // 查询2号角色:
    Role role = session.get(Role.class, 2l);
    session.delete(role);

    tx.commit();
    }

多对多的其他的操作

  • 给用户选择角色

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    @Test
    /**
    * 给用户选择角色
    */
    public void demo7(){
    Session session = HibernateUtils.getCurrentSession();
    Transaction tx = session.beginTransaction();

    // 给1号用户多选2号角色
    // 查询1号用户
    User user = session.get(User.class, 1l);
    // 查询2号角色
    Role role = session.get(Role.class, 2l);
    user.getRoles().add(role);

    tx.commit();
    }
  • 给用户改选角色

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    @Test
    /**
    * 给用户改选角色
    */
    public void demo8(){
    Session session = HibernateUtils.getCurrentSession();
    Transaction tx = session.beginTransaction();

    // 给2号用户将原有的2号角色改为3号角色
    // 查询2号用户
    User user = session.get(User.class, 2l);
    // 查询2号角色
    Role role2 = session.get(Role.class, 2l);
    Role role3 = session.get(Role.class, 3l);
    user.getRoles().remove(role2);
    user.getRoles().add(role3);

    tx.commit();
    }
  • 给用户删除角色

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @Test
    /**
    * 给用户改选角色
    */
    public void demo9(){
    Session session = HibernateUtils.getCurrentSession();
    Transaction tx = session.beginTransaction();

    // 给2号用户删除1号角色
    // 查询2号用户
    User user = session.get(User.class, 2l);
    // 查询2号角色
    Role role = session.get(Role.class, 1l);
    user.getRoles().remove(role);

    tx.commit();
    }
    }

Hibernate的查询的方式

在Hibernate中提供了很多种的查询的方式。Hibernate共提供了五种查询方式。

Hibernate的查询方式:OID查询

OID检索:Hibernate根据对象的OID(主键)进行检索

使用get方法

1
Customer customer = session.get(Customer.class,1l);

使用load方法

1
Customer customer = session.load(Customer.class,1l);

Hibernate的查询方式:对象导航检索

对象导航检索:Hibernate根据一个已经查询到的对象,获得其关联的对象的一种查询方式。

1
2
3
4
5
6
7
LinkMan linkMan = session.get(LinkMan.class,1l);

Customer customer =linkMan.getCustomer();

Customer customer = session.get(Customer.class,2l);

Set<LinkMan> linkMans = customer.getLinkMans();

Hibernate的查询方式:HQL检索

HQL查询:Hibernate Query Language,Hibernate的查询语言,是一种面向对象的方式的查询语言,语法类似SQL。通过session.createQuery(),用于接收一个HQL进行查询方式。

初始化一些数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@Test
/**
* 初始化数据
*/
public void demo1() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 创建一个客户
Customer customer = new Customer();
customer.setCust_name("李向文");

for (int i = 1; i <= 10; i++) {
LinkMan linkMan = new LinkMan();
linkMan.setLkm_name("王东" + i);
linkMan.setCustomer(customer);

customer.getLinkMans().add(linkMan);

session.save(linkMan);
}
session.save(customer);

tx.commit();
}

HQL的简单查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Test
/**
* HQL的简单查询
*/
public void demo2() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
// 简单的查询
Query query = session.createQuery("from Customer");
List<Customer> list = query.list();

// sql中支持*号的写法:select * from cst_customer; 但是在HQL中不支持*号的写法。
/*
* Query query = session.createQuery("select * from Customer");// 报错
* List<Customer> list = query.list();
*/

for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}

HQL的别名查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Test
/**
* 别名查询
*/
public void demo3() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
// 别名的查询
/*
* Query query = session.createQuery("from Customer c"); List<Customer>
* list = query.list();
*/

Query query = session.createQuery("select c from Customer c");
List<Customer> list = query.list();

for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}

HQL的排序查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
/**
* 排序查询
*/
public void demo4() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
// 排序的查询
// 默认情况
// List<Customer> list = session.createQuery("from Customer order by
// cust_id").list();
// 设置降序排序 升序使用asc 降序使用desc
List<Customer> list = session.createQuery("from Customer order by cust_id desc").list();

for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}

HQL的条件查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
@Test
/**
* 条件查询
*/
public void demo5() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
// 条件的查询
// 一、按位置绑定:根据参数的位置进行绑定。
// 一个条件
/*
* Query query = session.createQuery("from Customer where cust_name = ?"
* ); query.setParameter(0, "李兵"); List<Customer> list = query.list();
*/

// 多个条件
/*
* Query query = session.createQuery(
* "from Customer where cust_source = ? and cust_name like ?");
* query.setParameter(0, "小广告"); query.setParameter(1, "李%");
* List<Customer> list = query.list();
*/

// 二、按名称绑定
Query query = session.createQuery("from Customer where cust_source = :aaa and cust_name like :bbb");
// 设置参数:
query.setParameter("aaa", "朋友推荐");
query.setParameter("bbb", "李%");
List<Customer> list = query.list();

for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}

HQL的投影查询

投影查询:查询对象的某个或某些属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Test
/**
* 投影查询
*/
public void demo6() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 投影查询
// 单个属性
/*
* List<Object> list = session.createQuery(
* "select c.cust_name from Customer c").list(); for (Object object :
* list) { System.out.println(object); }
*/

// 多个属性:
/*
* List<Object[]> list = session.createQuery(
* "select c.cust_name,c.cust_source from Customer c").list(); for
* (Object[] objects : list) {
* System.out.println(Arrays.toString(objects)); }
*/

// 查询多个属性,但是我想封装到对象中。
List<Customer> list = session.createQuery("select new Customer(cust_name,cust_source) from Customer").list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}

HQL的分页查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
/**
* 分页查询
*/
public void demo7() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 分页查询
Query query = session.createQuery("from LinkMan");
query.setFirstResult(20);
query.setMaxResults(10);
List<LinkMan> list = query.list();

for (LinkMan linkMan : list) {
System.out.println(linkMan);
}
tx.commit();
}

HQL的分组统计查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
/**
* 分组统计查询
*/
public void demo8() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 聚合函数的使用:count(),max(),min(),avg(),sum()
Object object = session.createQuery("select count(*) from Customer").uniqueResult();
System.out.println(object);
// 分组统计:
List<Object[]> list = session.createQuery("select cust_source,count(*) from Customer group by cust_source")
.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}
tx.commit();
}

HQL的多表查询

  1. SQL的多表查询

    • 连接查询

      • 交叉连接:笛卡尔积

        1
        select * from A,B;
      • 内连接 : inner join (inner 可以省略)

        • 隐式内连接

          1
          select * from A,B where A.id = B.aid;
        • 显示内连接

          1
          select * from A inner join B on A.id = B.aid;
      • 外连接 :

        • 左外连接:left outer join(outer 可以省略)

          1
          select * from A left outer join B on A.id= B.aid;
        • 右外连接:right outer join(outer 可以省略)

          1
          select * from A right outer join B on A.id = B.aid;
      • 子查询
  2. HQL的多表查询

    • 连接查询
      • 交叉连接
      • 内连接
        • 显示内连接
        • 隐式内连接
        • 迫切内连接
      • 外连接
        • 左外连接
        • 右外连接
        • 迫切左外连接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Test
/**
* HQL的多表查询
*/
public void demo9() {
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();
// SQL:SELECT * FROM cst_customer c INNER JOIN cst_linkman l ON
// c.cust_id = l.lkm_cust_id;
// HQL:内连接 from Customer c inner join c.linkMans
/*
* List<Object[]> list = session.createQuery(
* "from Customer c inner join c.linkMans").list(); for (Object[]
* objects : list) { System.out.println(Arrays.toString(objects)); }
*/

// HQL:迫切内连接 其实就在普通的内连接inner join后添加一个关键字fetch. from Customer c inner
// join fetch c.linkMans
List<Customer> list = session.createQuery("select distinct c from Customer c inner join fetch c.linkMans")
.list();// 通知hibernate,将另一个对象的数据封装到该对象中

for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}

Hibernate的查询方式:QBC检索

QBC查询:Query By Criteria,条件查询。是一种更加面向对象化的查询的方式。

简单查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Test
/**
* 简单的查询
*/
public void demo1(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 获得Criteria的对象
Criteria criteria = session.createCriteria(Customer.class);
List<Customer> list = criteria.list();

for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}

排序查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Test
/**
* 排序查询
*/
public void demo2(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 排序查询
Criteria criteria = session.createCriteria(Customer.class);
// criteria.addOrder(Order.asc("cust_id")); // 升序
criteria.addOrder(Order.desc("cust_id")); // 降序
List<Customer> list = criteria.list();

for (Customer customer : list) {
System.out.println(customer);
}

tx.commit();
}

分页查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
/**
* 分页查询
*/
public void demo3(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 分页查询
Criteria criteria = session.createCriteria(LinkMan.class);
criteria.setFirstResult(10);
criteria.setMaxResults(10);
List<LinkMan> list = criteria.list();

for (LinkMan linkMan : list) {
System.out.println(linkMan);
}
tx.commit();
}

条件查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
@Test
/**
* 条件查询
*/
public void demo4(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 条件查询
Criteria criteria = session.createCriteria(Customer.class);
// 设置条件:
/**
* = eq
* > gt
* >= ge
* < lt
* <= le
* <> ne
* like
* in
* and
* or
*/
criteria.add(Restrictions.eq("cust_source", "小广告"));
// criteria.add(Restrictions.or(Restrictions.like("cust_name", "李%")));
criteria.add(Restrictions.like("cust_name", "李%"));
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}

统计查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
/**
* 统计查询
*/
public void demo5(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

Criteria criteria = session.createCriteria(Customer.class);
/**
* add :普通的条件。where后面条件
* addOrder :排序
* setProjection :聚合函数 和 group by having
*/
criteria.setProjection(Projections.rowCount());
Long num = (Long) criteria.uniqueResult();
System.out.println(num);
tx.commit();
}

离线条件查询(SSH)—DetachedCriteria

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
/**
* 离线条件查询
*/
public void demo6(){
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);
detachedCriteria.add(Restrictions.like("cust_name", "李%"));

Session session = HibernateUtils.getCurrentSession();
Transaction transaction = session.beginTransaction();

Criteria criteria = detachedCriteria.getExecutableCriteria(session);
List<Customer> list = criteria.list();
for (Customer customer : list) {
System.out.println(customer);
}
transaction.commit();
}
}

Hibernate的查询方式:SQL检索

SQL查询

SQL查询:通过使用sql语句进行查询

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class HibernateDemo3 {

@Test
public void demo1(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

/*SQLQuery sqlQuery = session.createSQLQuery("select * from cst_customer");
List<Object[]> list = sqlQuery.list();
for (Object[] objects : list) {
System.out.println(Arrays.toString(objects));
}*/

SQLQuery sqlQuery = session.createSQLQuery("select * from cst_customer");
sqlQuery.addEntity(Customer.class);
List<Customer> list = sqlQuery.list();
for (Customer customer : list) {
System.out.println(customer);
}
tx.commit();
}
}

Hibernate的抓取策略(优化)

延迟加载的概述

什么是延迟加载

延迟加载:lazy(懒加载)。执行到该行代码的时候,不会发送语句去进行查询,在真正使用这个对象的属性的时候才会发送SQL语句进行查询。

延迟加载的分类

  1. 类级别的延迟加载

    • 指的是通过load方法查询某个对象的时候,是否采用延迟。session.load(Customer.class,1l);

    • 类级别延迟加载通过上的lazy进行配置,如果让lazy失效

      • 将lazy设置为false

      • 将持久化类使用final修饰

      • Hibernate. Initialize()

  2. 关联级别的延迟加载

    • 指的是在查询到某个对象的时候,查询其关联的对象的时候,是否采用延迟加载。

      1
      Customer customer = session.get(Customer.class,1l);

      customer.getLinkMans();—-通过客户获得联系人的时候,联系人对象是否采用了延迟加载,称为是关联级别的延迟。

    • 抓取策略往往会和关联级别的延迟加载一起使用,优化语句。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class HibernateDemo1 {

@Test
/**
* 类级别的延迟加载
* * 在<class>的标签上配置的lazy
*/
public void demo1(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

Customer customer = session.load(Customer.class, 1l);
Hibernate.initialize(customer);
System.out.println(customer);

tx.commit();
}
}

抓取策略

抓取策略的概述

  • 通过一个对象抓取到关联对象需要发送SQL语句,SQL语句如何发送,发送成什么样格式通过策略进行配置。

    • 通过<set>或者<many-to-one>上通过fetch属性进行设置

    • fetch和这些标签上的lazy如何设置优化发送的SQL语句

<set>上的fetch和lazy

  • fetch:抓取策略,控制SQL语句格式

    • select :默认值,发送普通的select语句,查询关联对象

    • join :发送一条迫切左外连接查询关联对象

    • subselect :发送一条子查询查询其关联对象

  • lazy:延迟加载,控制查询关联对象的时候是否采用延迟

    • true :默认值,查询关联对象的时候,采用延迟加载

    • false :查询关联对象的时候,不采用延迟加载

    • extra :及其懒惰。

  • 在实际开发中,一般都采用默认值。如果有特殊的需求,可能需要配置join。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/**
* 在<set>上的fetch和lazy
* @author jt
*
*/
public class HibernateDemo2 {

@Test
/**
* 默认情况:
*/
public void demo1(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 查询1号客户
Customer customer = session.get(Customer.class, 1l);// 发送一条查询客户的SQL
System.out.println(customer.getCust_name());
// 查看1号客户的每个联系人的信息
for (LinkMan linkMan : customer.getLinkMans()) {// 发送一条根据客户ID查询联系人的SQL
System.out.println(linkMan.getLkm_name());
}
tx.commit();
}

@Test
/**
* 设置fetch="select" lazy="true"
*/
public void demo2(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 查询1号客户
Customer customer = session.get(Customer.class, 1l);// 发送一条查询客户的SQL
System.out.println(customer.getCust_name());
// 查看1号客户的每个联系人的信息
for (LinkMan linkMan : customer.getLinkMans()) {// 发送一条根据客户ID查询联系人的SQL
System.out.println(linkMan.getLkm_name());
}
tx.commit();
}

@Test
/**
* 设置 fetch="select" lazy="false"
*/
public void demo3(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 查询1号客户
Customer customer = session.get(Customer.class, 1l);// 发送两条SQL语句:查询客户的名称,查询客户关联联系人
System.out.println(customer.getCust_name());
/*// 查看1号客户的每个联系人的信息
for (LinkMan linkMan : customer.getLinkMans()) {//
System.out.println(linkMan.getLkm_name());
}*/

System.out.println(customer.getLinkMans().size());
tx.commit();
}

@Test
/**
* 设置fetch="select" lazy="extra"
*/
public void demo4(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 查询1号客户
Customer customer = session.get(Customer.class, 1l);// 发送一条查询1号客户的SQL语句
System.out.println(customer.getCust_name());

System.out.println(customer.getLinkMans().size());// 发送一条select count() from ...;
tx.commit();
}

@Test
/**
* 设置fetch="join" lazy=失效
*/
public void demo5(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

// 查询1号客户
Customer customer = session.get(Customer.class, 1l);// 发送一条迫切左外连接查询记录
System.out.println(customer.getCust_name());

System.out.println(customer.getLinkMans().size());// 不发送
tx.commit();
}

@SuppressWarnings("unchecked")
@Test
/**
* 设置fetch="subselect" lazy="true"
*/
public void demo6(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

List<Customer> list = session.createQuery("from Customer").list();// 发送查询所有客户的SQL
for (Customer customer : list) {
System.out.println(customer.getCust_name());
System.out.println(customer.getLinkMans().size());// 发送一条子查询
}

tx.commit();
}

@SuppressWarnings("unchecked")
@Test
/**
* 设置fetch="subselect" lazy="false"
*/
public void demo7(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

List<Customer> list = session.createQuery("from Customer").list();// 发送查询所有客户的SQL,发送一条子查询
for (Customer customer : list) {
System.out.println(customer.getCust_name());
System.out.println(customer.getLinkMans().size());//
}

tx.commit();
}
}

<many-to-one>上的fetch和lazy

  • fetch :抓取策略,控制SQL语句格式。

    • select :默认值,发送普通的select语句,查询关联对象。

    • join :发送一条迫切左外连接。

  • lazy :延迟加载,控制查询关联对象的时候是否采用延迟。

    • proxy :默认值,proxy具体的取值,取决于另一端的<class>上的lazy的值。

    • false :查询关联对象,不采用延迟。

    • no-proxy :(不会使用)

  • 在实际开发中,一般都采用默认值。如果有特殊的需求,可能需要配置join。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
* many-to-one上的fetch和lazy测试
* @author jt
*
*/
public class HibernateDemo3 {

@Test
/**
* 默认值
*/
public void demo1(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

LinkMan linkMan = session.get(LinkMan.class, 1l);// 发送一条查询联系人语句
System.out.println(linkMan.getLkm_name());
System.out.println(linkMan.getCustomer().getCust_name());// 发送一条select语句查询联系人所关联的客户

tx.commit();
}

@Test
/**
* fetch="select" lazy="proxy"
*/
public void demo2(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

LinkMan linkMan = session.get(LinkMan.class, 1l);// 发送一条查询联系人语句
System.out.println(linkMan.getLkm_name());
System.out.println(linkMan.getCustomer().getCust_name());// 发送一条select语句查询联系人所关联的客户

tx.commit();
}

@Test
/**
* fetch="select" lazy="false"
*/
public void demo3(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

LinkMan linkMan = session.get(LinkMan.class, 1l);// 发送一条查询联系人语句,发送一条select语句查询联系人所关联的客户
System.out.println(linkMan.getLkm_name());
System.out.println(linkMan.getCustomer().getCust_name());//

tx.commit();
}

@Test
/**
* fetch="join" lazy=失效
*/
public void demo4(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

LinkMan linkMan = session.get(LinkMan.class, 1l);// 发送一条迫切左外连接查询联系人所关联的客户。
System.out.println(linkMan.getLkm_name());
System.out.println(linkMan.getCustomer().getCust_name());//

tx.commit();
}
}

批量抓取

什么是批量抓取

一批关联对象一起抓取,batch-size

测试批量抓取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* 批量抓取
* @author jt
*
*/
public class HibernateDemo4 {

@SuppressWarnings("unchecked")
@Test
/**
* 获取客户的时候,批量抓取联系人
* 在Customer.hbm.xml中set上配置batch-size
*/
public void demo1(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

List<Customer> list = session.createQuery("from Customer").list();
for (Customer customer : list) {
System.out.println(customer.getCust_name());
for (LinkMan linkMan : customer.getLinkMans()) {
System.out.println(linkMan.getLkm_name());
}
}
tx.commit();
}

@SuppressWarnings("unchecked")
@Test
/**
* 获取联系人的时候,批量抓取客户
* * 在Customer.hbm.xml中<class>上配置
*/
public void demo2(){
Session session = HibernateUtils.getCurrentSession();
Transaction tx = session.beginTransaction();

List<LinkMan> list = session.createQuery("from LinkMan").list();
for (LinkMan linkMan : list) {
System.out.println(linkMan.getLkm_name());
System.out.println(linkMan.getCustomer().getCust_name());
}
tx.commit();
}
}
-------------本文结束 感谢您的阅读-------------
文章对我有益,我要小额赞赏...