文章目录

Hibernate笔记

Hibernate的入门(Hibernate的环境搭建,Hibernate的API,Hibernate的CRUD)

什么是框架

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

EE的三层架构

客户端层JavaAppplet->Html,css,JS
web层Servlet,JSP
业务逻辑层JavaBean
持久层JDBC

Servlet+JSP+JavaBean+JDBC使用这套架构可以开发市面上的所有有用。但在企业中不会使用(过于底层,效率低)。企业中开发一般使用SSH(Struts+Spring+Hibernate),SSM(SpringMVC+Spring+Mybatis)

Hibernate的概述

开放源代码的对象关系映射框架,简单来说就是使用对象编程思维来操纵数据库,一个__持久层__的__ORM__框架

什么是ORM

ORM:Object Relational Mapping(对象关系映射)
传统的JDBC的开发:

UserDao{
    public void save(User user){
        Connection conn=...;
        PreParedStatement stmt=...;
        conn=...;
        String sql="insert into...";
        stmt=conn...;
        stmt=setXXX();
        stmt.executeUpate();
        }
}

ORM框架开发
Java:Object

User{
    String name;
    String password;    
}

Mysql:table

create  table user(
    name varchar(20) primary key,
    password varchar(20) not null 
)

dao层

UserDao{
    public void save(User user){
        session.save(user);
    }
}

java对象和数据库表建立一种映射关系,从而操作对象就可以操作数据库中的表

为什么要学习Hibernate框架呢?

1,简化了数据访问层频繁的重复性代码,并且减少了内存消耗,加快了运行效率
2,简化了DAO层编码工作
3,性能非常好
4,可拓展性强

Hibernate的开发环境

Hiberante3.x
Hibernate4.x
Hibernate5.x

jar包

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

建表

CREATE TABLE user(
    id INT PRIMARY KEY  AUTO_INCREMENT,
    username VARCHAR(20) NOT NULL,
    password VARCHAR(20) NOT NULL,
    cellphone VARCHAR(20) NOT NULL
);

domain

package domain;

public class User {
    private int id;
    private String username;
    private String password;
    private String cellphone;


    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getCellphone() {
        return cellphone;
    }

    public void setCellphone(String cellphone) {
        this.cellphone = cellphone;
    }
    
}

创建映射

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

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<!--package[要映射的对象所在包](在domain包下)-->
<hibernate-mapping package="domain">
    <!--一个节点一般来说对应一个映射文件-->
    <!--   name[映射对象的类型],table[对象对应的表]-->
    <class name="User" table="user">

        <!--主键映射,属性名为id,列名也为id-->
        <id name="id" column="id">
            <generator class="native"/>
        </id>

        <!--非主键映射,属性和列名一一对应-->
        <property name="username" column="username"></property>
        <property name="cellphone" column="cellphone"></property>
        <property name="password" column="password"></property>

    </class>
</hibernate-mapping>

配置详解

  • class标签的配置
    • 标签用来建立类与表的映射关系
    • 属性:
      • name:类的全路径
      • table:表名
      • catalog:数据库名
  • id标签的配置
    • 标签用来建立类中的属性与表中的主键的对应关系
    • 属性:
      • name:类中属性名
      • column:表中的字段名
      • length:长度
      • type:类型
  • property标签的配置
    • 用来用来建立类中普通属性与表的字段的对应关系
    • 属性:
      • name:表中的属性名
      • column:表中的字段名
      • length:长度
      • type:类型
      • not-null:非空约束
      • unique:唯一约束

Hibernate的核心配置文件

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

<?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的主配置文件 -->
<hibernate-configuration>
    <!-- 通常,一个session-factory节点代表一个数据库 -->
    <session-factory>

        <!-- 1. 数据库连接配置 -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql:///test?characterEncoding=UTF-8</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">761020zy</property>
        <!--
            数据库方法配置, hibernate在运行的时候,会根据不同的方言生成符合当前数据库语法的sql
         -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>

        <!-- 2.1 显示hibernate在运行时候执行的sql语句 -->
        <property name="hibernate.show_sql">true</property>
        <!-- 2.2 格式化sql -->
        <property name="hibernate.format_sql">true</property>
        <!-- 2.3 自动建表  -->
<!--        <property name="hibernate.hbm2ddl.auto">create</property>-->

        <!--3. 加载所有映射-->
        <mapping resource="User.hbm.xml"/>

    </session-factory>

配置详解

  • 必须的配置
    • 连接数据库的基本的参数
      • 驱动类
      • url路径
      • 用户名
      • 密码
      • 方言
  • 可选的配置
    • 显示SQL:hibernate.show_sql
    • 格式化SQL:hibernate_formate_sql
    • 自动建表:hibernate.hbm2dd1.auto
      • none:不使用hiberate的自动建表
      • create:如果数据库中已经有表,删除原有表,如果没有表,新建表
      • create_drop:如果数据库中已经有表,删除原有表,执行操作,删除这个表。如果没有表,新建一个,使用完了删除该表
      • update:如果数据库中有表,使用原有表,如果没有表,创建新表(更新表结构)
      • validate:如果没有表,不会创建表。只会使用数据库中原有的表(校验映射和表的结构)
  • 映射文件的引入
    • 引入映射文件的配置
      • mapping resource

测试

package test;

import domain.User;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;

import org.hibernate.cfg.Configuration;

public class test {
    public static void main(String[] args) {

        //创建对象
        User user = new User();
        user.setPassword("857857");
        user.setCellphone("123321");
        user.setUsername("张三");

        //获取加载配置管理类
        Configuration configuration = new Configuration();

        //不给参数就默认加载hibernate.cfg.xml文件,
        configuration.configure();

        //创建Session工厂对象
        SessionFactory factory = configuration.buildSessionFactory();

        //得到Session对象
        Session session = factory.openSession();

        //使用Hibernate操作数据库,都要开启事务,得到事务对象
        Transaction transaction = session.getTransaction();

        //开启事务
        transaction.begin();

        //把对象添加到数据库中
//        session.save(user);
        session.save(user);

        //提交事务
        transaction.commit();

        //关闭Session
        session.close();
    }
}

Hibernatede API

  • Configuration
    • 加载核心配置文件
      • hibernate.properties
        Configuration cfg=new Configuration();
      • hibernate.cfg.xml
        Configuration cfg=new Configuration().configure();
    • 加载映射文件
      • 手动加载:configuration.addResource(“xxx.hbm.xml的全路径”)
  • SessionFactory:session工厂
    • 维护了Hibernate的连接池和Hibernate的二级缓存,是线程安全的对象,一个项目创建一个对象即可,可以创建一个工具类
  • Session
    • Session代表的是Hibernate与数据库的连接对象。不是线程安全的。与数据库交互桥梁
    • 保存方法:
      • Serializable save(Object obj)
    • 查询方法:
      • T get(Class c,Serializable id);
      • T load(Class c,Serializable id);
      • get和load的区别
        • get方法:
          • 采用的是立即加载,执行到这行代码的时候,就会马上发送SQL语句去查询
          • 查询后返回是真实对象本身
          • 查询一个找不到的对象的时候,返回null
        • load方法:采用的是懒加载,执行到这行代码的时候,不会发送SQL语句,当真正使用这个对象的时候才会发送SQL语句
          • 查询后返回的是代理对象,利用javassist技术产生的代理
          • 查询一个找不到的对象的时候,返回ObjectNotFoundException
    • 修改方法:
      • void update(Object obj)
        • 直接创建对象,进行修改
        • 先查询,再修改(推荐)
    • 删除方法
      • void delete(Object obj)
        • 直接创建对象,删除
        • 先查询再删除(推荐)
    • 保存或更新
      • void saveOrUpdate(Object obj)
      • 有主键就更新,没有就保存

接收HQL:hibernate Query Language面向对象的查询语言
createQuery(“from User”)
接收SQL:
createSQKQuery(“select * from User”)

  • Transaction:事务对象
    • commit();
    • rollback();

Hibernate的持久化,主键生成策略以及缓存,其他API

什么是持久化类

持久化:将内存中的一个对象持久化到数据库中过程。Hibernate框架就是用来进行持久化的框架。
持久化类:一个java对象与数据库的表建立了映射关系,那么这个类在Hibernate中称为是持久化类
持久化类=java类+映射关系

持久化类的编写规则

对持久化类提供一个__无参数的构造方法__Hibernate底层需要使用反射生成实例
对持久化类提供一个__唯一标识OID与数据库主键对应__Hibernate中获取,设置对象的值
对持久化类提供一个__唯一标识OID与数据库主键对应__java中通过对象的地址区分是否是同一个对象,数据库中通过主键确定是否是同一个记录,在Hibernate中通过持久化类的OID的属性区分是否是同一个对象
持久化类的属性尽量使用包装类(Integer,Long,Double,String而不是int,long,doule)的类型因为基本数据类型默认为0,那么0就会有很多的歧义,包装类的默认值是null
持久化类不要使用final进行修饰延迟加载本身是hibernate一个优化的手段,返回的是一个代理对象(javassist)可以对没有实现接口的类产生代理–使用了非常底层字节码增强技术,需要__继承__这个类进行代理

主键的分类

自然主键

自然主键:主键的本身就是表中的一个字段(实体中的一个具体的属性)
创建一个人员表,人员都会有一个身份证号(唯一的不可重复的),使用了身份证号作为主键,这种主键称为自然主键

代理主键

代理主键:主键的本身不是表中必须的一个字段(不是实体中的某个具体的属性)
创建一个人员表,没有使用人员中的身份证号,用了一个与这个表不相关的字段ID,这种主键称为代理主键
在实际开发中,尽量使用代理主键
一旦自然逐渐参与到业务逻辑中,后期有可能需要修改源代码
好的程序设计满足OCP原则:对程序的扩展是开发的,对修改源码是封闭的

主键的生成策略

在实际开发中一般不允许用户手动设置主键,一般将主键交给数据库,手动编写程序进行设置,在Hibernate中为了减少程序编写,提供了很多种的主键的生成策略

incrementhibernate中提供的自动增长机制,适用short,int,long类型的主键。在单线程程序中使用。首先发送一条语句,select max(id) from table然后id+1作为下一条记录的主键
identity适用于short,int,long类型的主键,使用的是数据库底层的自动增长机制。使用于有自动增长机制数据库,但是Oracle是没有自动增长
sequence适用short,int,long类型的主键,采用的是序列的方式(Oracle支持序列),像MySQL就不能使用sequence
uuid适用于字符串类型主键。使用hibernate中的随机方式生成字符串主键
native本地策略,可以在identity和sequence之间进行自动切换
assignedhibernate放弃外键的管理,需要通过手动编写程序或者用户自己设置
foreign外部的。一对一的一种关联映射的情况下使用

持久化类的三种状态

Hibernate是持久层框架,通过持久化类完成ORM操作。Hibernate为了更好的管理持久化类,将持久化类分成三种状态。

瞬时态

这种对象没有唯一的表示OID,没有被session管理,称为是瞬时态对象

持久态

这种对象有唯一表示OID,被session管理,称为是持久态对象,可以自动更新数据库

脱管态

这种对象有唯一表示OID,没有被session管理,称为脱管态对象

@Test
public void demo(){
    Customer customer=new Customer();
    customer.setName("zhangsan");
    
    Serializable id=session.save(customer);//瞬时态对象:没有唯一表示OID,没有被session管理
    
    session.get(Customer.class,id);//持久态对象:有唯一标识OID,被session管理
    
    transaction.commit();
    session.close();
    
    System.out.println("name:"+customer.getName());//脱管态对象:有唯一标识OID,没有被session管理
    }

持久化类的状态转换

  • 瞬时态对象
    • 获得
      • Customer customer=new Customer();
    • 状态转换
      • 瞬时->持久save(Object obj),saveOrUpdate(Object obj)
      • 瞬时->脱管customer.setId(1)
  • 持久态对象
    • 获取
      • get(),load(),find(),iterate()
      • Cunstomer customer=session.get(Customer.class,1l)
    • 状态转换
      • 持久->瞬时delete()
      • 持久->脱管close(),clear(),evict(Object,obj)
  • 脱管态对象
    • 获得
      • Customer customer=new Customer();
      • customer.setId(1l)
    • 状态转换
      • 脱管->持久update(),saveOrUpdate();
      • 脱管->瞬时customer.setId(null)

什么是缓存

缓存:是一种优化的方式,将数据存入到内存中,使用的时候直接从缓存中获取,不用通过存储源

Hibernate的一级缓存

Hibernate框架中提供了优化手段:缓存,抓取策略。Hibnerante中提供了两种缓存机制:一级缓存,二级缓存。
Hibernate的一级缓存:称为是Session级别的缓存,一级缓存生命周期与session一致(一级缓存是由session中的一系列的java集合构成)。一级缓存是自带的不可卸载的。
Hibernate的二级缓存是SessionFactory级别的缓存,需要配置的缓存,可以被其他缓存技术代替,比如:redis

Hibernate的一级缓存的结构

一级缓存中特殊区域:快照区

比较缓存区和快照区的数据:一致就不更新数据,不一致就更新数据库

session.evict(Object obj)//清空obj对象
session.clear()//清空一级缓存中所有数据

事务回顾

什么是事务

事务:事务指的是逻辑上的一组操作,组成这组操作的各个逻辑三元要么全部成功,要么全部失败

事务特性

原子性:事务不可分割
一致性:事务执行的前后,数据的完整性保持一致
隔离性:一个事物执行的过程中,不应该受到其他事务的干扰
持久性:事务执行完成之后,数据就持久到数据库中

如果不考虑隔离性,引发安全性问题
  • 读问题:
    • 脏读:一个事务读到另一个事务未提交的数据
    • 不可重复读:一个事务读到另一个事务已经提交的update数据,导致在前一个事务多次查询结果不一致
    • 虚读:一个事务读到另一个事务已经提交的insert数据,导致在前一个事务多次查询结果不一致
  • 写问题:
    • 引发两类丢失更新
读问题的解决
  • 设置事务的隔离级别
    • Read uncommitted:以上读问题都会发生
    • Read committed:解决脏读
    • Repeatable read:解决脏读和不可重复读
    • Serializable:解决所有读问题
<!--        设置事务隔离级别-->
<property name="hibernate.connection.isolation">4</property>//1,2,4,8

Hibernate解决Service层事务管理

  • 必须保证连接对象是同一个
    • 向下传递:DBUtils
    • 使用ThreadLocal对象
      • 将这个连接绑定到当前线程中
      • 在DAO的方法中,通过当前线程获取连接对象
  • Hibernate框架内部已经绑定好了ThreadLocal
    • 在SessionFactory中提供了一个方法getCurrentSession()
    • 通过一个配置完成
      Hibernate工具类
public class hibernateUtils {
    public static final Configuration cfg;
    public static final SessionFactory sf;

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

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

}

改写

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

配置(Session绑定当前线程)

   <property name="hibernate.current_session_context_class">thread</property>

Hibernate的其他API

Query

Query接口用于接收HQL,查询多个对象

基础查询

 Query query = session.createQuery("from User");
        query.setFirstResult(0);
        query.setMaxResults(3);
        List<User> list=query.list();
        for(User u:list){
            System.out.println(u.getUsername());
        }

条件查询

  Query query = session.createQuery("from User where username like ?");
        query.setParameter(0,"张%");
        List<User> list=query.list();
        for(User u:list){
            System.out.println(u.getUsername());
        }

分页查询

   Query query= session.createQuery("from User");
        query.setMaxResults(1);
        query.setFirstResult(0);
        List<User> list = query.list();
        for (User u : list) {
            System.out.println(u.getUsername());
        }
Criteria

条件查询,更加面向对象的查询方式
基础查询

  Criteria criteria=session.createCriteria(User.class);
        List<User> list = criteria.list();
        for (User u : list) {
            System.out.println(u.getUsername());
        }

条件查询

        Criteria criteria=session.createCriteria(User.class);
        criteria.add(Restrictions.like("username","张%"));
        List<User> list = criteria.list();
        for (User u : list) {
            System.out.println(u.getUsername());
        }

分页查询

  Criteria criteria=session.createCriteria(User.class);
        criteria.setFirstResult(0);
        criteria.setMaxResults(1);
        List<User> list = criteria.list();
        for (User u : list) {
            System.out.println(u.getUsername());
        }
SQLQuery

接收SQL,特别复杂情况下使用

Hibernate的一对多,多对多,一对一配置

数据库表与表之间的关系

一对多关系
  • 什么样关系属于一对多?
    • 一个部门对于多个员工,一个员工只能属于某一个部门
    • 一个客户对应多个联系人,一个联系人只能属于某一个客户
      例:
      客户表(1)
客户ID客户名称
1王东
2赵洪

联系人(n)

联系人ID联系人名称客户ID
1凤姐1
2如花1
3李兵2

一对多建表原则:在多的一方创建外键指向一的一方的主键

多对多关系
  • 什么样关系属于多对多?
    • 一个学生可以选择多门课程,一门课程也可以被多个学生选择
    • 一个用户可以选择多个角色,一个角色也可以被多个用户选择
      例:
      学生表
sidsname
1张三
2李四

课程表

cidcname
1Java
2C++
3UI

学生选课表

sidcid
11
12
22

多对多建表原则:创建一个中间表,中间表至少有两个字段分别作为外键指向多对多双方的主键

一对一关系
  • 什么样关系属于一对一?
    • 一个公司只有一个注册地址,一个注册地址只能被一个公司注册
      公司表
cidcname
1黑马
2百度

地址表(唯一外键(保证外键唯一unique,否则是一对多了)指向主键)

aidanamecid
1北京西三旗1
2北京西二旗2

地址表(主键关联)

aidaname
1北京西三旗
2北京西二旗

创建实体和映射

一对多
代码部分

例:user和address,一个user只有一个address,一个address有很多user
实体类
User(多的一方)

package domain;


public class User {
    //    表中字段:user_id
    private int user_id;
    //    表中字段:user_name
    private String user_name;
    //    一个user只有一个address
    //   存放一的一方数据
    private Address address;

    public int getUser_id() {
        return user_id;
    }

    public void setUser_id(int user_id) {
        this.user_id = user_id;
    }

    public String getUser_name() {
        return user_name;
    }

    public void setUser_name(String user_name) {
        this.user_name = user_name;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }
}

Address(一的一方)

package domain;

import java.util.LinkedHashSet;
import java.util.Set;

public class Address {
//    表中字段:city_user_id
    private int city_id;
//    表中字段:city_name
    private String city_name;
//    表中字段:city_description
    private String city_description;
//    存放多的一方数据
    private Set<User> userSet=new LinkedHashSet<>();

    public int getCity_id() {
        return city_id;
    }

    public void setCity_id(int city_id) {
        this.city_id = city_id;
    }

    public String getCity_name() {
        return city_name;
    }

    public void setCity_name(String city_name) {
        this.city_name = city_name;
    }

    public String getCity_description() {
        return city_description;
    }

    public void setCity_description(String city_description) {
        this.city_description = city_description;
    }

    public Set<User> getUserSet() {
        return userSet;
    }

    public void setUserSet(Set<User> userSet) {
        this.userSet = userSet;
    }
}

User的配置文件(多的一方)

<?xml version="1.0"?>
<!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="domain.User" table="user">
        <!--建立OID与主键映射-->
        <id name="user_id" column="user_id">
            <generator class="native"/>
        </id>
        <!--建立普通属性与数据表字段映射-->
        <property name="user_name" column="user_name"/>
        <!--配置多对一的关系:放置的是一的一方的对象-->
        <!--name:一的一方的对象的属性名称-->
        <!--class:一的一方的类的全路径-->
        <!--column:多的一方的表的外键名称-->
        <many-to-one name="address" class="domain.Address" column="city_id"/>
    </class>
</hibernate-mapping>

Address的配置文件(一的一方)

<?xml version="1.0"?>
<!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="domain.Address" table="address">
        <!--建立OID与主键映射-->
        <id name="city_id" column="city_user_id">
            <generator class="native"/>
        </id>
        <!--建立普通属性和数据库表字段映射-->
        <property name="city_name" column="city_name"/>
        <property name="city_description" column="city_description"/>
        <!--配置多对一的映射:放置的多的一方的集合-->
        <!--name:多的一方的对象集合的属性名称-->
        <!--column:多的一方的外键名称-->
        <!--class:多的一方的类的全路径-->
        <set name="userSet" table="user">
            <key column="city_id"/>
            <one-to-many class="domain.User"/>
        </set>
    </class>
</hibernate-mapping>

核心配置文件

<?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的主配置文件 -->
<hibernate-configuration>
    <!-- 通常,一个session-factory节点代表一个数据库 -->
    <session-factory>

        <!--数据库连接配置 -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql:///test?characterEncoding=UTF-8</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">761020zy</property>
        <!--
            数据库方法配置, hibernate在运行的时候,会根据不同的方言生成符合当前数据库语法的sql
         -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>

        <!--其他相关配置 -->
        <!--显示hibernate在运行时候执行的sql语句 -->
        <property name="hibernate.show_sql">true</property>
        <!--格式化sql -->
        <property name="hibernate.format_sql">true</property>
        <!--自动建表  -->
        <property name="hibernate.hbm2ddl.auto">create</property>
        <!--Session绑定当前线程-->
        <property name="hibernate.current_session_context_class">thread</property>
        <!--设置事务隔离级别-->
        <property name="hibernate.connection.isolation">4</property>

        <!--加载所有映射-->
        <mapping resource="User.hbm.xml"/>
        <mapping resource="Address.hbm.xml"/>

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

测试部分

public class test {
    public static void main(String[] args) {

        //获取加载配置管理类
        Configuration configuration = new Configuration();

        //不给参数就默认加载hibernate.cfg.xml文件,
        configuration.configure();

        //创建Session工厂对象
        SessionFactory factory = configuration.buildSessionFactory();

        //得到Session对象
        Session session = factory.getCurrentSession();

        //使用Hibernate操作数据库,都要开启事务,得到事务对象
        Transaction transaction = session.getTransaction();

        //开启事务
        transaction.begin();
        //把对象添加到数据库中
        User user01 = new User();
        user01.setUser_name("朱总");
        User user02 = new User();
        user02.setUser_name("郭总");
        User user03 = new User();
        user03.setUser_name("聪老板");

        Address address01 = new Address();
        address01.setCity_name("四川");
        address01.setCity_description("四川人民爱吃辣");
        Address address02 = new Address();
        address02.setCity_name("广东");
        address02.setCity_description("马化腾牛逼");

        address01.getUserSet().add(user01);
        address01.getUserSet().add(user02);
        address02.getUserSet().add(user03);
        
        user01.setAddress(address01);
        user02.setAddress(address01);
        user03.setAddress(address02);

        session.save(address02);
        session.save(address01);
        session.save(user01);
        session.save(user02);
        session.save(user03);
        //提交事务
        transaction.commit();

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

测试部分

    //保存数据
    session.save(address02);
    session.save(address01);

不行,会报错

org.hibernate.TransientObjectException

原因:持久态对象关联了一个瞬时态对象
解决:级联操作

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

保存地址级联用户(只save地址,也能保存用户)

<set  name="userSet" table="user" cascade="save-update">

保存用户级联地址(只save用户,也能保存地址)

 <many-to-one name="address" class="domain.Address" column="city_id" cascade="save-update"/>
  • 级联删除
    • 删除一方的时候,同时将另一方的数据也一并删除
      没有设置级联删除,默认情况:修改了user的外键为null,再删除address
        Address address=(Address) session.get(Address.class,2);
        session.delete(address);

设置了级联删除:删除address,同时删除user记录
删除address级联删除user,删除的主体是address,需要在address.hbm.xml中配置

<set  name="userSet" table="user" cascade="delete">
测试对象的导航
        //把User对象添加到Address对象的set集合中
        address01.getUserSet().add(user01);
        address01.getUserSet().add(user02);
        user03.setAddress(address01);
        //保存数据
        //双方都设置了cascade
       // session.save(address01);//三条insert数据
       // session.save(user03);//四条insert数据
       // session.save(user02);//一条insert数据
        //提交事务
        transaction.commit();
一对多设置了双向关联产生多余的SQL语句

问题:将一位user的address进行更改
双向维护关系产多余的SQL(两条update语句)

		User user = (User) session.get(User.class, 1);
        Address address = (Address) session.get(Address.class, 2);
        
        user.setAddress(address);
        address.getUserSet().add(user);

在这里插入图片描述
解决多余的SQL语句

  • 单项维护
  • 使一方放弃外键维护权(一条update语句)
    • 一的一方放弃," inverse=“true”
  • 一对多的关联查询的修改的时候也会用到
    区分cascade和inverse
//set配置了cascade="save-update" inverse="true"
session.save(address)//address会插入到数据库,user也会插入到数据库,但是外键为null
多对多关联映射

特权和角色:一个角色有很多的特权,权利可以被很多的角色拥有,例如:总经理可以开除员工,和巡视公司,保安可以巡视公司,和看门。
在这里插入图片描述

创建表
CREATE TABLE role (
  id          int  PRIMARY KEY AUTO_INCREMENT,
  name        VARCHAR(20) NOT NULL,
  description VARCHAR(255)
    )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    
CREATE TABLE privilege (
      id          int PRIMARY KEY AUTO_INCREMENT,
      name        VARCHAR(20) NOT NULL,
      description VARCHAR(255)
    )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
    
CREATE TABLE role_privilege (
    role_id      int,
    privilege_id int,
    PRIMARY KEY (role_id, privilege_id),
    CONSTRAINT role_id_FK1 FOREIGN KEY (role_id) REFERENCES role (id),
    CONSTRAINT privilege_id_FK FOREIGN KEY (privilege_id) REFERENCES privilege (id)
  )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

特权类

package domain;

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

public class Privilege {
    private int privilege_id;
    private String privilege_name;
    private String privilege_description;
    //    一个特权可以被多个角色拥有
    private Set<Role> roles = new HashSet<Role>();

    public int getPrivilege_id() {
        return privilege_id;
    }

    public void setPrivilege_id(int privilege_id) {
        this.privilege_id = privilege_id;
    }

    public String getPrivilege_name() {
        return privilege_name;
    }

    public void setPrivilege_name(String privilege_name) {
        this.privilege_name = privilege_name;
    }

    public String getPrivilege_description() {
        return privilege_description;
    }

    public void setPrivilege_description(String privilege_description) {
        this.privilege_description = privilege_description;
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }
}

角色类

package domain;

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

public class Role {
    private int role_id;
    private String role_name;
    private String role_description;
//    表示一个角色拥有的特权
    private Set<Privilege> privileges = new HashSet<Privilege>();

    public int getRole_id() {
        return role_id;
    }

    public void setRole_id(int 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_description() {
        return role_description;
    }

    public void setRole_description(String role_description) {
        this.role_description = role_description;
    }

    public Set<Privilege> getPrivileges() {
        return privileges;
    }

    public void setPrivileges(Set<Privilege> privileges) {
        this.privileges = privileges;
    }
}

Role的映射配置

<?xml version="1.0"?>
<!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="domain.Role" table="role">
        <!--        OID和主键的映射-->
        <id name="role_id" column="id">
            <generator class="native"/>
        </id>
        <!--        建立普通属性与字段的映射-->
        <property name="role_name" column="name"/>
        <property name="role_description" column="description"/>
        <!--建立角色和特权的多对多的映射关系-->
        <!--        set-->
        <!--        * name:对方的集合的属性名称-->
        <!--        * table:多对多的关系需要使用中间表,放的是中间表的名称-->
        <!--        key-->
        <!--        * column:当前的对象对应中间表的外键的名称-->
        <!--        many-to-many-->
        <!--        * class:对方类的全路径-->
        <!--        * column:对方的对象在中间表中的外键的名称-->
        <set name="privileges" table="role_privilege">
            <key column="role_id"/>
            <many-to-many class="domain.Privilege" column="privilege_id"/>
        </set>
    </class>
</hibernate-mapping>

Privilege的映射配置

<?xml version="1.0"?>
<!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="domain.Privilege" table="privilege">
        <!--        OID和主键的映射-->
        <id name="privilege_id" column="id">
            <generator class="native"/>
        </id>
        <!--        普通属性和字段的映射-->
        <property name="privilege_name" column="name"/>
        <property name="privilege_description" column="description"/>

        <set name="roles" table="role_privilege" inverse="true">
            <key column="privilege_id"/>
            <many-to-many class="domain.Role" column="role_id"/>
        </set>
    </class>
</hibernate-mapping>

hibernate的核心配置文件

<?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的主配置文件 -->
<hibernate-configuration>
    <!-- 通常,一个session-factory节点代表一个数据库 -->
    <session-factory>

        <!--数据库连接配置 -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql:///test?characterEncoding=UTF-8</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">761020zy</property>
        <!--
            数据库方法配置, hibernate在运行的时候,会根据不同的方言生成符合当前数据库语法的sql
         -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>

        <!--其他相关配置 -->
        <!--显示hibernate在运行时候执行的sql语句 -->
        <property name="hibernate.show_sql">true</property>
        <!--格式化sql -->
        <property name="hibernate.format_sql">true</property>
        <!--自动建表  -->
        <property name="hibernate.hbm2ddl.auto">create</property>
        <!--Session绑定当前线程-->
        <property name="hibernate.current_session_context_class">thread</property>
        <!--设置事务隔离级别-->
        <property name="hibernate.connection.isolation">4</property>

        <!--加载所有映射-->
<!--        <mapping resource="User.hbm.xml"/>-->
<!--        <mapping resource="Address.hbm.xml"/>-->
        <mapping resource="Privilege.hbm.xml"/>
        <mapping resource="Role.hbm.xml"/>

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

测试代码

package test;

import Utils.hibernateUtils;

import domain.Privilege;
import domain.Role;
import org.hibernate.*;


public class test {
    public static void main(String[] args) {
        Session session = hibernateUtils.gerCurrentSession();
        Transaction tx = session.beginTransaction();
//        两个角色
        Role role01 = new Role();
        Role role02 = new Role();
        role01.setRole_name("角色1");
        role01.setRole_description("这是角色1");
        role02.setRole_name("角色2");
        role02.setRole_description("这是角色2");

//        三个特权
        Privilege privilege01 = new Privilege();
        Privilege privilege02 = new Privilege();
        Privilege privilege03 = new Privilege();
        privilege01.setPrivilege_description("这是特权1");
        privilege01.setPrivilege_name("特权1");
        privilege02.setPrivilege_description("这是特权2");
        privilege02.setPrivilege_name("特权2");
        privilege03.setPrivilege_description("这是特权3");
        privilege03.setPrivilege_name("特权3");
//多对多建立了双向的关系必须有一方放弃对主键的维护,一般是被动方(特权)放弃外键维护(inverse="true")
        role01.getPrivileges().add(privilege01);
        role01.getPrivileges().add(privilege02);
        role01.getPrivileges().add(privilege03);
        role02.getPrivileges().add(privilege01);
        role02.getPrivileges().add(privilege02);
        privilege01.getRoles().add(role01);
        privilege01.getRoles().add(role02);
        privilege02.getRoles().add(role01);
        privilege02.getRoles().add(role02);
        privilege03.getRoles().add(role01);
        
        session.save(role01);
        session.save(role02);
        session.save(privilege01);
        session.save(privilege02);
        session.save(privilege03);
        
        tx.commit();
    }
}
多对多的单向级联保存
 		role01.getPrivileges().add(privilege01);
        role01.getPrivileges().add(privilege02);
        role01.getPrivileges().add(privilege03);
        role02.getPrivileges().add(privilege01);
        role02.getPrivileges().add(privilege02);
        session.save(role01);
        session.save(role02);

配置映射

   <set name="privileges" table="role_privilege" cascade="save-update">
多对多的级联删除
 		Role role = (Role) session.get(Role.class, 1);
        session.delete(role);

配置映射

   <set name="privileges" table="role_privilege" cascade="delete">
多对多的其他操作

给角色选择特权

 Role role = (Role) session.get(Role.class, 2);
        Privilege privilege = (Privilege) session.get(Privilege.class, 1);
        role.getPrivileges().add(privilege);

给角色改选特权

Role role = (Role) session.get(Role.class, 1);
        Privilege privilege1 = (Privilege) session.get(Privilege.class, 1);
        Privilege privilege2 = (Privilege) session.get(Privilege.class, 2);
        role.getPrivileges().remove(privilege2);
        role.getPrivileges().add(privilege1);

给角色删除特权

Role role = (Role) session.get(Role.class, 1);
        Privilege privilege1 = (Privilege) session.get(Privilege.class, 1);
        Privilege privilege2 = (Privilege) session.get(Privilege.class, 2);
        role.getPrivileges().remove(privilege2);

Hibernate的查询方式,抓取策略

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

Hibernate的查询方式:OID查询

OID检索:Hibernate根据对象的OID(主键)进行检索
使用get方法

User user = (User) session.get(User.class, 1);

使用load方法

 User user = (User) session.load(User.class, 1);

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

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

  ``	User user = (User) session.get(User.class, 1);
        Address address = user.getAddress();

Hibernate的查询方式:HQL检索

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

HQL的简单查询
        Query query = session.createQuery("from User");//from 类名
        List<User> list = query.list();
        for (User user : list) {
            System.out.println(user.toString());
        }
HQL的排序查询

默认升序排序

        List<User> list = session.createQuery("from User order by user_id").list();
 for (User user : list) {
            System.out.println(user.toString());
        }

降序排序 升序asc,降序desc

        List<User> list = session.createQuery("from User order by user_id desc").list();
        for (User user : list) {
            System.out.println(user.toString());
        }
HQL的条件查询

条件的查询
1.按位置绑定

 //一个条件
 Query query = session.createQuery("from User where user_name like ? ");
query.setParameter(0, "赵%");
List<User> list = query.list();
//多个条件
Query query = session.createQuery("from User where user_name like ? and user_id=?");
query.setParameter(0, "赵%");
query.setParameter(1, 3);
List<User> list = query.list();

2.按名称绑定

        Query query = session.createQuery("from User where user_name=:aaa and user_id=:bbb");
        query.setParameter("aaa", "赵小老弟");
        query.setParameter("bbb", 3);
        List<User> list=query.list();
        for (User user : list) {
            System.out.println(user.toString());
        }
HQL的投影查询

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

  List<Object> list=session.createQuery("select  u.user_name from User u").list();
        for(Object obj:list){
          System.out.println(obj);
        }

多个属性

   List<Object[]> list=session.createQuery("select  u.user_id,u.user_name from User u").list();
        for(Object[] obj:list){
          System.out.println(Arrays.toString(obj));
        }

多个属性,并且封装到对象中

 List<User> list = session.createQuery("select  new User(user_id,user_name) from User").list();
        for (User user : list) {
            System.out.println(user.getUser_id() + ":" + user.getUser_name());
        }
HQL的分页查询
   Query query = session.createQuery("from User ");
        query.setFirstResult(0);
        query.setMaxResults(3);
        List<User> list = query.list();
        for (User user : list) {
            System.out.println(user.toString());
        }
HQL的分组统计查询

聚合函数的使用:count(),max(),min(),avg(),sum()

        Query query = session.createQuery("select count(*) from User ");
        List<Object> list = query.list();
       System.out.println(list.get(0));

分组统计

   Query query = session.createQuery("select user_name,count(*) from User group by  user_name having count(*)=1");
        List<Object[]> list = query.list();
        for (Object[] obj : list) {
           System.out.println(Arrays.toString(obj));
        }
HQL的多表查询
  • 连接查询
    • 交叉连接:笛卡尔积
    • 内连接:inner join
      • 隐式内连接
        select * from a,b where a.id=b.id
      • 显示内连接
        select * from a inner join b where a.id=b.id
    • 外连接
      • 左外连接:left outer join
        select * from a left outer join b on a.id=b.id
      • 右外连接:right outer join
        select * from a right outer join b on a.id=b.id
    • 子查询
      HQL的多表查询
  • 连接查询
    • 交叉连接
    • 内连接
      • 显示内连接
      • 隐式内连接
      • 迫切内连接
    • 外连接
      • 左外连接
      • 右外连接
      • 迫切左外连接
        内连接
 List<Object[]> list = session.createQuery("from Address a inner join a.userSet").list();
        for (Object[] objects: list) {
            System.out.println(Arrays.toString(objects));
        }

迫切内连接

        //通知hibernate,将另一个对象的数据封装到该对象中
       List<Address> list=session.createQuery("select  distinct a from Address a inner join fetch a.userSet").list();
       for(Address address:list){
           System.out.println(address);
       }

可能会报错

java.lang.StackOverflowError(堆栈溢出)

解决方案:
取消任意一方或双方,关系表重写的toString。

Hibernate的查询方式:QBC检索

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

简单查询
     Criteria criteria=session.createCriteria(User.class);
        List<User> list=criteria.list();
        for(User user:list){
            System.out.println(user);
        }
排序查询
Criteria criteria = session.createCriteria(User.class);
        criteria.addOrder(Order.desc("user_id"));
        List<User> list = criteria.list();
        for (User user : list) {
            System.out.println(user);
        }
分页查询
  Criteria criteria = session.createCriteria(User.class);
        criteria.setFirstResult(0);
        criteria.setMaxResults(3);
        List<User> list = criteria.list();
        for (User user : list) {
            System.out.println(user);
        }
条件查询
 		Criteria criteria = session.createCriteria(User.class);
//        =     eq
//        >     gt
//        >=    ge
//        <     lt
//        <=    le
//        <>    ne
//        like
//        in
//        and
//        or
        criteria.add(Restrictions.eq("user_name", "赵小老弟"));
        criteria.add(Restrictions.like("user_id", 3));
        List<User> list = criteria.list();
        for (User user : list) {
            System.out.println(user);
        }
统计查询
//        add   :普通的条件,where后面的
//        addOrder:排序
//        setProjection:聚合函数和group by
        Criteria criteria = session.createCriteria(User.class);
        criteria.setProjection(Projections.rowCount());
        Long num = (Long) criteria.uniqueResult();
        System.out.println(num);
离线条件查询(SSH)–DetachedCriteria

使用DetachedCriteria–离线(脱离session使用)

Hibernate的查询方式:SQL检索

  SQLQuery sqlQuery = session.createSQLQuery("select * from Address");
        sqlQuery.addEntity(Address.class);
        List<Object> list=sqlQuery.list();
        for(Object obj:list){
            System.out.println(obj);
        }

Hibernate的抓取策略(优化)

什么是延迟加载

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

延迟加载的分类
  • 类级别的延迟加载
    • 指的是通过load方法查询某个对象的时候,是否采用延迟。
      session.load(Address.class,1);
      • 将lazy设置为false
      • 将持久化类使用final修饰
      • Hibernate.initialize(对象)
    <class name="domain.Address" table="address" lazy="true">
  • 关联级别的延迟加载
    • 指的是在查询到某个对象的时候,查询其关联的对象的时候,是否采用延迟加载
      Address address=session.get(Address.class,1)
      address.getUserSet();
 <set  name="userSet" table="user" lazy="true">
 <many-to-one name="address" class="domain.Address" column="city_id" lazy="false"/>

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

抓取策略

抓取策略的概述

通过一个对象抓取到关联对象需要发送SQL语句,SQL语句如何发送,发送成什么样格式通过策略进行配置。
通过或者上通过fetch属性进行设置
fetch和这些标签上的lazy如何设置优化方发送的SQL语句

上的fetch和lazy

  • fetch:抓取策略,控制SQL语句格式
    • select:默认值,发送普通的select语句,查询关联对象
    • join:发送一条迫切左外连接查询关联对象
    • subselect:发送一条子查询查询其关联对象
  • lazy:延迟加载,控制查询关联对象的时候是否采用延迟
    • true:默认值,查询关联对象的时候,采用延迟加载
    • false:查询关联对象的时候,不采用延迟对象
    • extra:极其懒惰
      在实际开发中,一般采取默认值
  <set name="userSet" table="user" lazy="true" fetch="select">
      Address address = (Address) session.get(Address.class, 1);//发送查询address的SQL语句
//    select
//        address0_.city_user_id as city1_1_0_,
//        address0_.city_name as city2_1_0_,
//        address0_.city_description as city3_1_0_ 
//    from
//        address address0_ 
//    where
//        address0_.city_user_id=?
        System.out.println(address.getCity_name());
        System.out.println(address.getUserSet().size());//发送查询size的SQL语句
    //   select
    //   userset0_.city_id as city3_1_1_,
    //   userset0_.user_id as user1_1_,
    //   userset0_.user_id as user1_0_0_,
    //   userset0_.user_name as user2_0_0_,
    //   userset0_.city_id as city3_0_0_ 
    //   from
    //   user userset0_ 
    //   where
    //   userset0_.city_id=?

 <set name="userSet" table="user" lazy="false" fetch="select">
     Address address = (Address) session.get(Address.class, 1);//发送两条SQL语句:查询Address,查询User

        System.out.println(address.getCity_name());
        System.out.println(address.getUserSet().size());
// select
//        address0_.city_user_id as city1_1_0_,
//        address0_.city_name as city2_1_0_,
//        address0_.city_description as city3_1_0_ 
//    from
//        address address0_ 
//    where
//        address0_.city_user_id=?

//             select
//        userset0_.city_id as city3_1_1_,
//        userset0_.user_id as user1_1_,
//        userset0_.user_id as user1_0_0_,
//        userset0_.user_name as user2_0_0_,
//        userset0_.city_id as city3_0_0_ 
//    from
//        user userset0_ 
//    where
//        userset0_.city_id=?

 <set name="userSet" table="user" lazy="extra" fetch="select">
        Address address = (Address) session.get(Address.class, 1);//发送查询客户的SQL语句
//  select
//        address0_.city_user_id as city1_1_0_,
//        address0_.city_name as city2_1_0_,
//        address0_.city_description as city3_1_0_ 
//    from
//        address address0_ 
//    where
//        address0_.city_user_id=?        
		System.out.println(address.getCity_name());
        System.out.println(address.getUserSet().size());//发送一条select count() from...;
// select
//        count(user_id) 
//    from
//        user 
//    where
//        city_id =?

        <set name="userSet" table="user" lazy="true" fetch="join">//lazy就失效了,设置成什么都一样
 Address address = (Address) session.get(Address.class, 1);//发送一条迫切左外连接查询记录
        System.out.println(address.getCity_name());
        System.out.println(address.getUserSet().size());

  <set name="userSet" table="user" lazy="subselect" fetch="join">
​```java
 List<Address> list = session.createQuery("from Address ").list();//发送查询所有客户的SQL
        for (Address address : list) {
            System.out.println(address.getCity_name());
            System.out.println(address.getUserSet().size());//发送一条子查询
        }

上的fetch和lazy

  • fetch:抓取策略,控制SQL语句格式
    • select:默认值,发送普通的select语句,查询关联对象
    • join:发送一条迫切左外连接
  • lazy:延迟加载,控制查询关联对象的时候是否采用延迟
    • proxy:默认值,proxy的具体取值,取决于另一端上的lazy的值
    • false:查询关联对象,不采用延迟
    • no-proxy:(不会使用)
  • 批量抓取
    • 什么是批量抓取
    • 一批关联对象一起抓取,batch-size
  <set name="userSet" table="user" batch-size="4">
 <class name="domain.Address" table="address" batch-size="3">

y_user_id as city1_1_0_,
// address0_.city_name as city2_1_0_,
// address0_.city_description as city3_1_0_
// from
// address address0_
// where
// address0_.city_user_id=?
System.out.println(address.getCity_name());
System.out.println(address.getUserSet().size());//发送一条select count() from…;
// select
// count(user_id)
// from
// user
// where
// city_id =?

----------------------------
```xml
        <set name="userSet" table="user" lazy="true" fetch="join">//lazy就失效了,设置成什么都一样
 Address address = (Address) session.get(Address.class, 1);//发送一条迫切左外连接查询记录
        System.out.println(address.getCity_name());
        System.out.println(address.getUserSet().size());

  <set name="userSet" table="user" lazy="subselect" fetch="join">
​```java
 List<Address> list = session.createQuery("from Address ").list();//发送查询所有客户的SQL
        for (Address address : list) {
            System.out.println(address.getCity_name());
            System.out.println(address.getUserSet().size());//发送一条子查询
        }

上的fetch和lazy

  • fetch:抓取策略,控制SQL语句格式
    • select:默认值,发送普通的select语句,查询关联对象
    • join:发送一条迫切左外连接
  • lazy:延迟加载,控制查询关联对象的时候是否采用延迟
    • proxy:默认值,proxy的具体取值,取决于另一端上的lazy的值
    • false:查询关联对象,不采用延迟
    • no-proxy:(不会使用)
  • 批量抓取
    • 什么是批量抓取
    • 一批关联对象一起抓取,batch-size
  <set name="userSet" table="user" batch-size="4">
 <class name="domain.Address" table="address" batch-size="3">
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐