超全面 hibernate 复习总结笔记

如有转载,请申明:
转载自 IT天宇www.jianshu.com/p/50964e92c…

前言

博客正式转到简书,实在忍受不了 CSDN 的模版,有广告或界面难看还可以忍,但有些模版还有 bug,作为程序员忍无可忍,修个 bug 真的有那么难吗,这么多年了。

接着上篇,先说个段子。
三四年前如果有人问我 Android/Ios 如何入门,我可能会推荐上百 G 的资料,但如果现在问我,我只会推荐一本书《app开发从上架到上吊》

《超全面 hibernate 复习总结笔记》

你可能觉得我危言耸听,但今年的移动开发行情真的很差,看到很多几年经验的一个月才收到几个面试通知,没有经验的就更绝望了。

好吧,今天不是来讨论行情的。
前段时间写了一篇 struts2 的笔记,有好心的老司机告诉我,struts2 已经被抛弃了。但话是这么说,面试的时候,难免碰到问 struts2,如果这个时候表现得一脸懵逼,那估计 offer 就没有了。所以虽然现在 hibernate 用得不多了,但还是得复习一下。

目录

  1. 环境搭建
  2. 实体类映射
  3. 核心配置详解
  4. 一级缓存
  5. 关系映射
  6. 抓取策略
  7. HQL
  8. QBC
  9. 其他配置
  10. 事务
  11. 二级缓存

正文

1.环境搭建

导包

根据需要选择手动导入 jar 包,或者用依赖管理工具。

此外,为了连接数据库,还需要数据库的驱动包。

配置

核心配置文件以 hibernate.cfg.xml 命名,放在类路径下(Idea 放在 resources 下)

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <!-- SessionFactory,相当于之前学习连接池配置 -->
    <session-factory>
        <!-- 1 基本4项 -->
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql:///db01</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">1234</property>

    </session-factory>

</hibernate-configuration>

2.实体类映射

数据库

create database db01;
use db01;

实体类

public class User {
    private Integer uid;
    private String username;
    private String password;
// 省略 get set 方法
}

映射实体类

可以用 xml 或者 注解来映射

xml

放在实体类同目录下,名字和实体类相同,扩展名为 .hbm.xml
例如: ser.hbm.xml

<!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.itheima.a_hello.User" table="t_user">
        <!-- 主键 -->
        <id name="uid">
            <generator class="native"></generator>
        </id>

        <!-- 普通属性 -->
        <property name="username"></property>
        <property name="password"></property>

    </class>
</hibernate-mapping>

核心配置文件中加上映射文件位置

<mapping resource="com/ittianyu/hibernate/helloworld/User.hbm.xml"/>

注解

// name 对应表名称
@Entity
@Table(name = "t_user")
public class User {
    // 主键
    @Id
    @GeneratedValue
    private Integer uid;
    private String username;
    private String password;
// 省略 get set 方法
}

在核心配置文件中加上映射文件位置

<mapping class="com.ittianyu.hibernate.helloworld.User" />

测试

public class HelloWorld {
    @Test
    public void hello() {
        // username, password
        User user = new User("123456", "123"); // 1.加载配置文件 Configuration configure = new Configuration().configure(); // 2.获得session factory对象 SessionFactory sessionFactory = configure.buildSessionFactory(); // 3.创建session Session session = sessionFactory.openSession(); // 4.开启事务 Transaction transaction = session.beginTransaction(); // 5.保存并提交事务 session.save(user); transaction.commit(); // 6.释放资源 session.close(); sessionFactory.close(); } }

3.核心配置详解

核心 api

Configuration

常用方法

  • 构造方法:默认加载 hibernate.properties
  • configure() 方法:默认加载 hibernate.cfg.xml
  • configure(String) 方法:加载指定配置文件

手动添加映射

// 手动加载指定的配置文件
config.addResource("com/ittianyu/a_hello/User.hbm.xml");

// 手动加载指定类,对应的映射文件 User--> User.hbm.xml
config.addClass(User.class);

SessionFactory

  • SessionFactory 相当于java web连接池,用于管理所有session
  • 获得方式:config.buildSessionFactory();
  • sessionFactory hibernate缓存配置信息 (数据库配置信息、映射文件,预定义HQL语句 等)
  • SessionFactory线程安全,可以是成员变量,多个线程同时访问时,不会出现线程并发访问问题
  • 开启一个 session:factory.openSession();
  • 获取和当前线程绑定的会话(需要配置):factory.getCurrentSession();
      <property name="hibernate.current_session_context_class">thread</property>

Session

  • Session 相当于 JDBC的 Connection — 会话
  • 通过session操作PO对象 –增删改查
  • session单线程,线程不安全,不能编写成成员变量。
  • Api:
      save 保存
      update 更新
      delete 删除
      get 通过id查询,如果没有 null
      load 通过id查询,如果没有抛异常
      createQuery("hql")  获得Query对象
      createCriteria(Class) 获得Criteria对象

Transaction

开启事务 beginTransaction()
获得事务 getTransaction()

提交事务:commit()
回滚事务:rollback()

和 spring 整合后,无需手动管理

Query

  • hibernate执行hql语句
  • hql语句:hibernate提供面向对象查询语句,使用对象(类)和属性进行查询。区分大小写。
  • 获得 session.createQuery("hql")
  • 方法:

      list()  查询所有
      uniqueResult() 获得一个结果。如果没有查询到返回null,如果查询多条抛异常。
    
      setFirstResult(int) 分页,开始索引数startIndex
      setMaxResults(int) 分页,每页显示个数 pageSize

Criteria

  • QBC(query by criteria),hibernate提供纯面向对象查询语言,提供直接使用PO对象进行操作。
  • 获得方式:Criteria criteria = session.createCriteria(User.class);
  • 条件
      criteria.add(Restrictions.eq("username", "tom"));
      Restrictions.gt(propertyName, value)        大于
      Restrictions.ge(propertyName, value)    大于等于
      Restrictions.lt(propertyName, value)    小于
      Restrictions.le(propertyName, value)    小于等于
      Restrictions.like(propertyName, value)    模糊查询,注意:模糊查询值需要使用 % _

工具类

public class HibernateUtils {
    private static SessionFactory sessionFactory;
    static {
        Configuration configuration = new Configuration().configure();
        sessionFactory = configuration.buildSessionFactory();

        Runtime.getRuntime().addShutdownHook(new Thread(){
            @Override
            public void run() {
                sessionFactory.close();
            }
        });
    }

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

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

    public static void main(String[] args) {
        Session session = openSession();
        System.out.println(session);
        session.close();

    }
}

核心配置

基本配置

<!-- SessionFactory,相当于之前学习连接池配置 -->
<session-factory>
    <!-- 1 基本4项 -->
    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="hibernate.connection.url">jdbc:mysql:///h_day01_db</property>
    <property name="hibernate.connection.username">root</property>
    <property name="hibernate.connection.password">1234</property>

    <!-- 2 与本地线程绑定 -->
    <property name="hibernate.current_session_context_class">thread</property>

        <!-- 3 方言:为不同的数据库,不同的版本,生成sql语句(DQL查询语句)提供依据 * mysql 字符串 varchar * orcale 字符串 varchar2 -->
        <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>

    <!-- 4 sql语句 -->
    <!-- 显示sql语句 -->
    <property name="hibernate.show_sql">true</property>
    <property name="hibernate.format_sql">true</property>

    <!-- 5 自动创建表(了解) ,学习中使用,开发不使用的。 * 开发中DBA 先创建表,之后根据表生产 PO类 * 取值: update:【】 如果表不存在,将创建表。 如果表已经存在,通过hbm映射文件更新表(添加)。(映射文件必须是数据库对应) 表中的列可以多,不负责删除。 create :如果表存在,先删除,再创建。程序结束时,之前创建的表不删除。【】 create-drop:与create几乎一样。如果factory.close()执行,将在JVM关闭同时,将创建的表删除了。(测试) validate:校验 hbm映射文件 和 表的列是否对应,如果对应正常执行,如果不对应抛出异常。(测试) -->
    <property name="hibernate.hbm2ddl.auto">create</property>

    <!-- 6 java web 6.0 存放一个问题 * BeanFactory 空指针异常 异常提示:org.hibernate.HibernateException: Unable to get the default Bean Validation factory * 解决方案:取消bean校验 -->
    <property name="javax.persistence.validation.mode">none</property>

    <!-- 添加映射文件 <mapping >添加映射文件 resource 设置 xml配置文件 (addResource(xml)) class 配置类 (addClass(User.class)) 配置的是全限定类名 -->
    <mapping resource="com/ittianyu/a_hello/User.hbm.xml"/>
</session-factory>

主键种类

  • 自然主键: 在业务中,某个属性符合主键的三个要求.那么该属性可以作为主键列.
  • 代理主键: 在业务中,不存符合以上3个条件的属性,那么就增加一个没有意义的列.作为主键.

类型对应

Java数据类型Hibernate数据类型标准SQL数据类型(不同DB有差异)
byte、java.lang.BytebyteTINYINT
short、java.lang.ShortshortSMALLINT
int、java.lang.IntegerintegerINGEGER
long、java.lang.LonglongBIGINT
float、java.lang.FloatfloatFLOAT
double、java.lang.DoubledoubleDOUBLE
java.math.BigDecimalbig_decimalNUMERIC
char、java.lang.CharactercharacterCHAR(1)
boolean、java.lang.BooleanbooleanBIT
java.lang.StringstringVARCHAR
boolean、java.lang.Booleanyes_noCHAR(1)(‘Y’或’N’)
boolean、java.lang.Booleantrue_falseCHAR(1)(‘Y’或’N’)
java.util.Date、java.sql.DatedateDATE
java.util.Date、java.sql.TimetimeTIME
java.util.Date、java.sql.TimestamptimestampTIMESTAMP
java.util.CalendarcalendarTIMESTAMP
java.util.Calendarcalendar_dateDATE
byte[]binaryVARBINARY、BLOB
java.lang.StringtextCLOB
java.io.SerializableserializableVARBINARY、BLOB
java.sql.ClobclobCLOB
java.sql.BlobblobBLOB
java.lang.ClassclassVARCHAR
java.util.LocalelocaleVARCHAR
java.util.TimeZonetimezoneVARCHAR
java.util.CurrencycurrencyVARCHAR

普通属性

<hibernate-mapping> 
            package 用于配置PO类所在包
                例如: package="com.itheima.d_hbm"
        <class> 配置 PO类 和 表 之间对应关系
            name:PO类全限定类名
                例如:name="com.itheima.d_hbm.Person"
                如果配置 package,name的取值可以是简单类名 name="Person"
            table : 数据库对应的表名
            dynamic-insert="false" 是否支持动态生成insert语句
            dynamic-update="false" 是否支持动态生成update语句
                如果设置true,hibernate底层将判断提供数据是否为null,如果为null,insert或update语句将没有此项。
        普通字段
            <property>
                name : PO类的属性
                column : 表中的列名,默认name的值相同
                type:表中列的类型。默认hibernate自己通过getter获得类型,一般情况不用设置
                    取值1: hibernate类型
                        string 字符串
                        integer 整形
                    取值2: java类型 (全限定类名)
                        java.lang.String 字符串
                    取值3:数据库类型
                        varchar(长度) 字符串
                        int 整形
                        <property name="birthday">
                             <column name="birthday" sql-type="datetime"></column>
                         </property>
                         javabean 一般使用类型 java.util.Date
                         jdbc规范提供3中
                             java类型                mysql类型
                             java.sql.Date        date
                             java.sql.time        time
                             java.sql.timestamp    timestamp
                             null                datetime

                             以上三个类型都是java.util.Date子类

                length : 列的长度。默认值:255
                not-null : 是否为null
                unique : 是否唯一
                access:设置映射使用PO类属性或字段
                    property : 使用PO类属性,必须提供setter、getter方法
                    field : 使用PO类字段,一般很少使用。
                insert 生成insert语句时,是否使用当前字段。
                update 生成update语句时,是否使用当前字段。
                    默认情况:hibernate生成insert或update语句,使用配置文件所有项
        注意:配置文件如果使用关键字,列名必须使用重音符

主键

<id>配置主键
name:属性名称
access="" 设置使用属性还是字段
column=""  表的列名
length=""  长度
type="" 类型
<generator> class属性用于设置主键生成策略
1.increment 由hibernate自己维护自动增长
    底层通过先查询max值,再+1策略
    不建议使用,存在线程并发问题
2.identity hibernate底层采用数据库本身自动增长列
    例如:mysql auto_increment
3.sequence hibernate底层采用数据库序列
    例如:oracle 提供序列
4.hilo 

    </generator>
5.native 根据底层数据库的能力选择 identity、sequence 或者 hilo 中的一个。【】
##以上策略使用整形,long, short 或者 int 类型
6.uuid 采用字符串唯一值【】
##以上策略 代理主键,有hibernate维护。
7.assigned 自然主键,由程序自己维护。【】

4.一级缓存

对象状态

三种状态

  • 瞬时态:transient,session没有缓存对象,数据库也没有对应记录。
    OID特点:没有值
  • 持久态:persistent,session缓存对象,数据库最终会有记录。(事务没有提交)
    OID特点:有值
  • 脱管态:detached,session没有缓存对象,数据库有记录。
    OID特点:有值

转换

《超全面 hibernate 复习总结笔记》

三种状态转换图

一级缓存

一级缓存:又称为session级别的缓存。当获得一次会话(session),hibernate在session中创建多个集合(map),用于存放操作数据(PO对象),为程序优化服务,如果之后需要相应的数据,hibernate优先从session缓存中获取,如果有就使用;如果没有再查询数据库。当session关闭时,一级缓存销毁。

@Test
public void demo02(){
    //证明一级缓存
    Session session = factory.openSession();
    session.beginTransaction();

    //1 查询 id = 1
    User user = (User) session.get(User.class, 1);
    System.out.println(user);
    //2 再查询 -- 不执行select语句,将从一级缓存获得
    User user2 = (User) session.get(User.class, 1);
    System.out.println(user2);

    session.getTransaction().commit();
    session.close();
}

可以调用方法清除一级缓存

//清除
//session.clear();
session.evict(user);

快照

与一级缓存一样的存放位置,对一级缓存数据备份。保证数据库的数据与 一级缓存的数据必须一致。如果一级缓存修改了,在执行commit提交时,将自动刷新一级缓存,执行update语句,将一级缓存的数据更新到数据库。

当缓存和数据库数据不一样且在提交之前,可以调用 refresh 强制刷新缓存。

5.关系映射

一对一

一对一关系一般是可以整合成一张表,也可以分成两张表。
维护两张表的关系可以选择外键也可以选择让主键同步。

实体类

Address.java

public class Address {
    private Integer id;
    private String name;

    private Company company;
    // 省略 get set
}

Company.java

public class Company {
    private Integer id;
    private String name;

    private Address address;
    // 省略 get set
}

外键维护关系

Address.hbm.xml

<!-- dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性 -->
<class name="Address" table="t_address_sync" dynamic-insert="true" dynamic-update="true">
    <id name="id" column="id">
        <!-- 主键与外键表的主键同步 -->
        <generator class="foreign">
            <param name="property">company</param>
        </generator>
    </id>
    <property name="name" column="name"/>

    <!-- 需要在同步主键的一方加上 constrained="true" 使用给主键加上外键约束 -->
    <one-to-one name="company" class="Company" constrained="true" />
</class>

Company.hbm.xml

<!-- dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性 -->
<class name="Company" table="t_company_ref" dynamic-insert="true" dynamic-update="true">
    <id name="id" column="id">
        <generator class="native"/>
    </id>
    <property name="name" column="name"/>

    <!-- one-to-one 中使用了 property-ref :当前类哪个属性是引用外键 放弃维护外键 -->
    <one-to-one name="address" class="Address" property-ref="company" />
</class>

主键同步关系

Address.hbm.xml

<!-- dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性 -->
<class name="Address" table="t_address_sync" dynamic-insert="true" dynamic-update="true">
    <id name="id" column="id">
        <!-- 主键与外键表的主键同步 -->
        <generator class="foreign">
            <param name="property">company</param>
        </generator>
    </id>
    <property name="name" column="name"/>

    <!-- 需要在同步主键的一方加上 constrained="true" 使用给主键加上外键约束 -->
    <one-to-one name="company" class="Company" constrained="true" />
</class>

Company.hbm.xml

<!-- dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性 -->
<class name="Company" table="t_company_sync" dynamic-insert="true" dynamic-update="true">
    <id name="id" column="id">
        <generator class="native"/>
    </id>
    <property name="name" column="name"/>

    <!-- 在另一个表需要修改主键生成策略为 外键 -->
    <one-to-one name="address" class="Address" />
</class>

一对多

实体类

Customer.java

public class Customer {
    private Integer id;
    private String name;

    private Set<Order> orders = new HashSet<>();
    // 省略 get set
}

Order.java

public class Order {
    private Integer id;
    private String name;

    private Customer customer;
    // 省略 get set
}

映射文件

Customer.hbm.xml

<?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 package="com.ittianyu.hibernate.onetomany">
    <!-- dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性 -->
    <class name="Customer" table="t_customer" dynamic-insert="true" dynamic-update="true">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name"/>
        <!-- inverse 为 true 表示放弃维护关系,留给对方来维护, 一般是一对多中 一的一方放弃,由多的一放维护, 这个时候删除对象时,需要手动将关联的对象外键引用移除 -->
        <set name="orders" inverse="true">
            <key column="cid"></key>
            <one-to-many class="Order" />
        </set>
    </class>
</hibernate-mapping>

Order.hbm.xml

<?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 package="com.ittianyu.hibernate.onetomany">
    <!-- dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性 -->
    <class name="Order" table="t_order" dynamic-insert="true" dynamic-update="true">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name"/>
        <many-to-one name="customer" column="cid" class="Customer" />
    </class>
</hibernate-mapping>

多对多

实体类

Course.java

public class Course {
    private Integer id;
    private String name;
    private Set<Student> students = new HashSet<>();
    // 省略 get set
}

Student.java

public class Student {
    private Integer id;
    private String name;
    private Set<Course> courses = new HashSet<>();
    // 省略 get set
}

映射文件

Course.hbm.xml

<?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 package="com.ittianyu.hibernate.manytomany">
    <!-- dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性 -->
    <class name="Course" table="t_course" dynamic-insert="true" dynamic-update="true">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name"/>

        <!-- many to many 中,需要给 set 加上 table 名 放弃维护外键 -->
        <set name="students" table="t_student_course" inverse="true">
            <key column="cid"></key>
            <many-to-many class="Student" column="sid" />
        </set>
    </class>
</hibernate-mapping>

Student.hbm.xml

<?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 package="com.ittianyu.hibernate.manytomany">
    <!-- dynamic-insert 和 dynamic-update 为 true 时,sql语句中只有值变化或者不为空的属性才会加上,用于更新部分属性 -->
    <class name="Student" table="t_studennt" dynamic-insert="true" dynamic-update="true">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name"/>
        <!-- many to many 中,需要给 set 加上 table 名 外键表由 student 维护,并启用级联,所以 course 中要放弃维护 -->
        <set name="courses" table="t_student_course" cascade="save-update">
            <key column="sid"></key>
            <many-to-many class="Course" column="cid" />
        </set>
    </class>
</hibernate-mapping>

级联

cascade 表示指定级联操作的类型。

  • save-update : 增加或更新 A 时,自动增加或更新 B。
  • delete : 删除 A 时,自动删除 B
  • all : 上面两项效果叠加
  • delete-orphan (孤儿删除) : 删除所有和当前对象解除关联关系的对象
  • all-delete-orphan : all + delete-orphan 效果叠加
<set name="courses" table="t_student_course" cascade="save-update">
    <key column="sid"></key>
    <many-to-many class="Course" column="cid" />
</set>

6.抓取策略

检索方式

  • 立即检索:立即查询,在执行查询语句时,立即查询所有的数据。
  • 延迟检索:延迟查询,在执行查询语句之后,在需要时在查询。(懒加载)

检索策略

  • 类级别检索:当前的类的属性获取是否需要延迟。
  • 关联级别的检索:当前类 关联 另一个类是否需要延迟。

类级别检索

  • get:立即检索。get方法一执行,立即查询所有字段的数据。
  • load:延迟检索。默认情况,load方法执行后,如果只使用OID的值不进行查询,如果要使用其他属性值将查询。可以配置是否延迟检索:
      <class  lazy="true | false">
      lazy 默认值true,表示延迟检索,如果设置false表示立即检索。

关联级别检索

容器<set> 提供两个属性:fetch、lazy,用于控制关联检索。

  • fetch:确定使用sql格式
    • join:底层使用迫切左外连接
    • select:使用多个select语句(默认值)
    • subselect:使用子查询
  • lazy:关联对象是否延迟。
    • false:立即
    • true:延迟(默认值)
    • extra:极其懒惰,调用 size 时,sql 查询 count。(用于只需要获取个数的时候)

批量查询

一次加载多行数据,用于减少 sql 语句数量
<set batch-size="5">

比如: 当客户关联查询订单时,默认给每一个客户生产一个select语句查询订单。开启批量查询后,使用in语句减少查询订单语句个数。

默认:select * from t_order where customer_id = ?
批量:select * from t_order where customer_id in (?,?,?,?)

检索总结

检索策略优点缺点优先考虑使用的场合
立即检索对应用程序完全透明,不管对象处于持久化状态还是游离状态,应用程序都可以从一个对象导航到关联的对象(1)select语句多<br/> (2)可能会加载应用程序不需要访问的对象,浪费许多内存空间。(1)类级别<br/> (2)应用程序需要立即访问的对象<br/> (3)使用了二级缓存
延迟检索由应用程序决定需要加载哪些对象,可以避免执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并节省内存空间。应用程序如果希望访问游离状态的代理类实例,必须保证她在持久化状态时已经被初始化。(1)一对多或者多对多关联<br/> (2)应用程序不需要立即访问或者根本不会访问的对象
表连接检索(1)对应用程序完全透明,不管对象处于持久化状态还是游离状态,都可从一个对象导航到另一个对象。<br/> (2)使用了外连接,select语句少(1)可能会加载应用程序不需要访问的对象,浪费内存。<br/> (2)复杂的数据库表连接也会影响检索性能。(1)多对一或一对一关联<br/> (2)需要立即访问的对象<br/> (3)数据库有良好的表连接性能。

7.HQL

查询所有

//1 使用简单类名 , 存在自动导包
// * Customer.hbm.xml <hibernate-mapping auto-import="true">
// Query query = session.createQuery("from Customer");
//2 使用全限定类名
Query query = session.createQuery("from com.ittianyu.bean.Customer");
// 获取结果
List<Customer> allCustomer = query.list();

条件查询

//1 指定数据,cid OID名称
//    Query query = session.createQuery("from Customer where cid = 1");
//2 如果使用id,也可以(了解)
//    Query query = session.createQuery("from Customer where id = 1");
//3 对象别名 ,格式: 类 [as] 别名
//    Query query = session.createQuery("from Customer as c where c.cid = 1");
//4 查询所有项,mysql--> select * from...
Query query = session.createQuery("select c from Customer as c where c.cid = 1");

Customer customer = (Customer) query.uniqueResult();

投影查询

//1 默认
//如果单列 ,select c.cname from,需要List<Object>
//如果多列,select c.cid,c.cname from ,需要List<Object[]> ,list存放每行,Object[]多列
// Query query = session.createQuery("select c.cid,c.cname from Customer c");
//2 将查询部分数据,设置Customer对象中
// * 格式:new Customer(c.cid,c.cname)
// * 注意:Customer必须提供相应的构造方法。
// * 如果投影使用oid,结果脱管态对象。
Query query = session.createQuery("select new Customer(c.cid,c.cname) from Customer c");

List<Customer> allCustomer = query.list();

排序

Query query = session.createQuery("from Customer order by cid desc");
List<Customer> allCustomer = query.list();

分页

Query query = session.createQuery("from Customer");
// * 开始索引 , startIndex 算法: startIndex = (pageNum - 1) * pageSize;
// *** pageNum 当前页(之前的 pageCode)
query.setFirstResult(0);
// * 每页显示个数 , pageSize
query.setMaxResults(2);

List<Customer> allCustomer = query.list();

绑定参数

Integer cid = 1;

//方式1 索引 从 0 开始
// Query query = session.createQuery("from Customer where cid = ?");
// query.setInteger(0, cid);
//方式2 别名引用 (:别名)
Query query = session.createQuery("from Customer where cid = :xxx");
// query.setInteger("xxx", cid);
query.setParameter("xxx", cid);

Customer customer = (Customer) query.uniqueResult();

聚合函数和分组

//1 
//    Query query = session.createQuery("select count(*) from Customer"); //2 别名 // Query query = session.createQuery("select count(c) from Customer c"); //3 oid Query query = session.createQuery("select count(cid) from Customer"); Long numLong = (Long) query.uniqueResult();

连接查询

//左外连接
//    List list = session.createQuery("from Customer c left outer join c.orderSet ").list();
//迫切左外链接 (默认数据重复)
//    List list = session.createQuery("from Customer c left outer join fetch c.orderSet ").list();
//迫切左外链接 (去重复)
List list = session.createQuery("select distinct c from Customer c left outer join fetch c.orderSet ").list();

命名查询

Custom.hbm.xml

...
    <!--局部 命名查询-->
    <query name="findAll"><![CDATA[from Customer ]]></query>
</class>
<!--全局 命名查询-->
<query name="findAll"><![CDATA[from Customer ]]></query>

测试

//全局
//List list = session.getNamedQuery("findAll").list();
//局部
List list = session.getNamedQuery("com.ittianyu.a_init.Customer.findAll").list();

8.QBC

查询所有

List<Customer> list = session.createCriteria(Customer.class).list();

分页查询

Criteria criteria = session.createCriteria(Order.class);
criteria.setFirstResult(10);
criteria.setMaxResults(10);
List<Order> list = criteria.list();

排序

Criteria criteria = session.createCriteria(Customer.class);
// criteria.addOrder(org.hibernate.criterion.Order.asc("age"));
criteria.addOrder(org.hibernate.criterion.Order.desc("age"));
List<Customer> list = criteria.list();

条件查询

// 按名称查询:
/*Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.eq("cname", "tom"));
List<Customer> list = criteria.list();*/

// 模糊查询;
/*Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.like("cname", "t%"));
List<Customer> list = criteria.list();*/

// 条件并列查询
Criteria criteria = session.createCriteria(Customer.class);
criteria.add(Restrictions.like("cname", "t%"));
criteria.add(Restrictions.ge("age", 35));
List<Customer> list = criteria.list();

离线查询

// service 层 封装与 session 无关的 criteria
DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);
detachedCriteria.add(Restrictions.eq("id", 4));

// dao 层
Session session = HibernateUtils.openSession();
Criteria criteria = detachedCriteria.getExecutableCriteria(session);
List list = criteria.list();

9.其他配置

c3p0(spring 整合后直接配 dataSource)

  1. 导入 c3p0 包
  2. hibernate.cfg.xml 配置
     <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>

log4j

  1. 导入包
    • log4j 核心包:log4j-1.2.17.jar
    • 过渡jar:slf4j-log4j12-1.7.5.jar
  2. 导入配置文件
    • log4j.properties ,此配置文件通知log4j 如何输出日志

10.事务

隔离级别

  • read uncommittd,读未提交。存在3个问题。
  • read committed,读已提交。解决:脏读。存在2个问题。
  • repeatable read ,可重复读。解决:脏读、不可重复读。存在1个问题。
  • serializable,串行化。单事务。没有问题。

hibernate 中配置

<property name="hibernate.connection.isolation">4</property>

对照上面的分别是 1 2 4 8,0表示没有事务级别

悲观锁

采用数据库锁机制。丢失更新肯定会发生。

  • 读锁:共享锁。
      select .... from  ... lock in share mode;
  • 写锁:排他锁。(独占)
      select ... from  ....  for update

Hibernate 中使用

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

乐观锁

在表中提供一个字段(版本字段),用于标识记录。如果版本不一致,不允许操作。丢失更新肯定不会发生

Hibernate 中使用

  1. 在PO对象(javabean)提供字段,表示版本字段。
     ...
     private Integer version;
     ...
  2. 在配置文件中增加 version
     <class ...>
         ...
         <version name="version" />
         ...

11.二级缓存

sessionFactory 级别缓存,整个应用程序共享一个会话工厂,共享一个二级缓存。

由4部分构成:

  • 类级别缓存
  • 集合级别缓存
  • 时间戳缓存
  • 查询缓存(二级缓存的第2大部分,三级缓存)

并发访问策略

transactional可以防止脏读和不可重复读,性能低
read-write可以防止脏读,更新缓存时锁定缓存数据
nonstrict-read-write不保证缓存和数据库一致,为缓存设置短暂的过期时间,减少脏读
read-only适用于不会被修改的数据,并发性能高

应用场景

  • 适合放入二级缓存中的数据:
    很少被修改
    不是很重要的数据, 允许出现偶尔的并发问题
  • 不适合放入二级缓存中的数据:
    经常被修改
    财务数据, 绝对不允许出现并发问题
    与其他应用数据共享的数据

二级缓存提供商

  • EHCache: 可作为进程(单机)范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 对 Hibernate 的查询缓存提供了支持。–支持集群。
  • OpenSymphony `:可作为进程范围内的缓存, 存放数据的物理介质可以是内存或硬盘, 提供了丰富的缓存数据过期策略, 对 Hibernate 的查询缓存提供了支持
  • SwarmCache: 可作为集群范围内的缓存, 但不支持 Hibernate 的查询缓存
  • JBossCache:可作为集群范围内的缓存, 支持 Hibernate 的查询缓存

开启二级缓存

  1. 导包 hibernate-ehcache-5.2.8.Final.jar
  2. 配置
     <!--二级缓存
     #hibernate.cache.region.factory_class org.hibernate.cache.internal.EhCacheRegionFactory
     -->
     <property name="hibernate.cache.use_second_level_cache">true</property>
     <property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

使用二级缓存

类缓存

<!-- 类缓存 -->
<class-cache class="com.ittianyu.hibernate.onetomany.Order" usage="read-only"/>
<class-cache class="com.ittianyu.hibernate.onetomany.Customer" usage="read-only"/>

集合缓存

<collection-cache collection="com.ittianyu.hibernate.onetomany.Customer.orders" usage="read-only" />

查询缓存

将HQL语句 与 查询结果进行绑定。通过HQL相同语句可以缓存内容。

  1. 配置

     #hibernate.cache.use_query_cache true 启用 HQL查询缓存
     <property name="hibernate.cache.use_query_cache">true</property>
  2. 使用

     Query query = session.createQuery("from Customer");
     query.setCacheable(true);// 标记为缓存
    原文作者:SQL
    原文地址: https://juejin.im/entry/58ef8822570c35005620201c
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞