超全面 hibernate 复习总结笔记

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

前言

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

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


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

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

目录

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

正文

<a id="1"></a>

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>

<a id="2"> </a>

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

<a id="3"> </a>

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.Byte byte TINYINT
short、java.lang.Short short SMALLINT
int、java.lang.Integer integer INGEGER
long、java.lang.Long long BIGINT
float、java.lang.Float float FLOAT
double、java.lang.Double double DOUBLE
java.math.BigDecimal big_decimal NUMERIC
char、java.lang.Character character CHAR(1)
boolean、java.lang.Boolean boolean BIT
java.lang.String string VARCHAR
boolean、java.lang.Boolean yes_no CHAR(1)('Y'或'N')
boolean、java.lang.Boolean true_false CHAR(1)('Y'或'N')
java.util.Date、java.sql.Date date DATE
java.util.Date、java.sql.Time time TIME
java.util.Date、java.sql.Timestamp timestamp TIMESTAMP
java.util.Calendar calendar TIMESTAMP
java.util.Calendar calendar_date DATE
byte[] binary VARBINARY、BLOB
java.lang.String text CLOB
java.io.Serializable serializable VARBINARY、BLOB
java.sql.Clob clob CLOB
java.sql.Blob blob BLOB
java.lang.Class class VARCHAR
java.util.Locale locale VARCHAR
java.util.TimeZone timezone VARCHAR
java.util.Currency currency VARCHAR

普通属性

<hibernate-mapping> 
            package 用于配置PO类所在包
                例如: package="com.ittianyu.d_hbm"
        <class> 配置 PO类 和 表 之间对应关系
            name:PO类全限定类名
                例如:name="com.ittianyu.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 自然主键,由程序自己维护。【】

<a id="4"> </a>

4.一级缓存

对象状态

三种状态

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

转换

三种状态转换图

一级缓存

一级缓存:又称为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 强制刷新缓存。

<a id="5"> </a>

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>

<a id="6"> </a>

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

<a id="7"> </a>

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

<a id="8"> </a>

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

<a id="9"> </a>

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 如何输出日志

<a id="10"> </a>

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" />
        ...
    

<a id="11"> </a>

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);// 标记为缓存
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,511评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,495评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,595评论 0 225
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,558评论 0 190
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,715评论 3 270
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,672评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,112评论 2 291
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,837评论 0 181
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,417评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,928评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,316评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,773评论 2 234
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,253评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,827评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,440评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,523评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,583评论 2 249

推荐阅读更多精彩内容