Hibernate框架(二)缓存、事务、多表关系详解

本文深入讲解Hibernate框架的一级缓存机制、事务管理、表关系处理等核心内容,并通过实例演示了级联操作、inverse属性配置及多对多关系处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Hibernate框架(二)

一级缓存

一级缓存:一级缓存:也称为session缓存.属于线程级别缓存. 该缓存本质其实就是一个集合.该集合被放置到了session对象中.所以也叫session缓存.因为session是线程级别的.所以该缓存属于线程级别缓存.

缓存作用:为了提高效率

提高修改效率:如果修改了查询到的对象,那么会自动同步到数据库,如果修改但是值没有发生变化,hibernate会”聪明”的不执行任何操作

原因:ResultSet会封装成对象,一式两份,分别放到session缓存中,以及缓存快照中.事务在提交时,会比对session缓存中的以及快照中的对象属性是否发生变化,如果发生变化那么执行修改.

代码:

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.junit.Test;

import cn.it.domain.Customer;
import cn.it.utils.HibernateUtils;

/**
 * 演示一级缓存
 * @author Administrator
 *
 */
public class Demo01 {


    /**
     * 一级缓存在查询中的应用
     */
    @Test
    public void test1(){
        //1.获得session
        Session session = HibernateUtils.openSesseion();
        //2.开启事务
        Transaction tx = session.beginTransaction();
        //3.执行操作
        Customer c1 = session.get(Customer.class, 1l);
        c1.setCust_name("李四");
        Customer c2 = session.get(Customer.class, 1l);
        //验证c2是否修改
        System.out.println(c2.getCust_name());
        //验证c1和c2是否是同一个Customer对象
        System.out.println(c1 == c2);
        //4.提交事务
        tx.commit();
        //5.关闭连接
        session.close();
    }


    /**
     * 一级缓存在修改中的应用
     */
    @Test
    public void test2(){
        //1.获得session
        Session session = HibernateUtils.openSesseion();
        //2.开启事务
        Transaction tx = session.beginTransaction();
        //3.执行操作
        Customer c1 = session.get(Customer.class, 1l);
        c1.setCust_name("张三");
        //如果最后改的值与数据库中的一致,将不会执行update
        //如果最后改的值与数据库中的不一致,将会执行update
        c1.setCust_name("李四");

        //4.提交事务
        tx.commit();
        //5.关闭连接
        session.close();
    }
}

hibernate事务管理

事务

事务四大原则
A 原子性
C 一致性
I 隔离性
D 持久性

事务的并发问题
脏读:读取到别人修改但没有提交的数据
不可重复读:在两次连续读取过程中,读取到的数据不同,(修改)
幻|虚读:连续读取时,出现不同记录数(增删)

解决并发问题:设置隔离级别
(0001)1:读未提交:123
(0010)2:都已提交:23 (oracle默认)
(0100)4:可重复读:3 (mysql默认)
(1000)8:串行化

hibernate中修改事务隔离级别配置

在主配置文件中设置:

隔离级别(mysql默认为4)

hibernate事务的应用
  1. 事务在Service层管理
  2. 我们要确保Service层以及Dao层使用的是同一个session

使用hibernate框架也同样要确保Service层以及dao层曾使用的是同一个session。我们可以使用ThreadLoacl对象,将当前的session对象与线程绑定,从tl中获得的就是同一个。

hibernate中已经帮我们封装好了ThreadLocal对象,我们只需要调用getCurrentSession方法既是从ThreadLocal中获得与当前线程绑定的Session.

在主配置文件中设置:

<!--指定getCurrentSession获得session时与当前线程绑定>
<property name="hibernate.current_session_context_class">thread</property>

注意事项:

  1. getCurrentSession方法必须配置一行配置才能使用
  2. getCurrentSession方法获得的Session对象,会在事务结束后自动关闭,手动关闭会报错

代码:

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

import cn.it.utils.HibernateUtils;

/**
 * 演示getCurrentSession方法
 * @author Administrator
 *
 */
public class Demo01 {

    @Test
    public void test1(){
        //1.getCurrentSession方法获得session
        Session session1 = HibernateUtils.getCurrentSession();
        Session session2 = HibernateUtils.getCurrentSession();

        System.out.println(session1 == session2);
        //2.openSession方法获得session
        Session session3 = HibernateUtils.openSesseion();
        Session session4 = HibernateUtils.openSesseion();

        System.out.println(session3 == session4);
    }

    /**
     * getCurrentSession获得的session对象,会在事务提交时自动关闭,不要手动关闭
     */
    @Test
    public void test2(){
        //1.getCurrentSession方法获得session
        Session session = HibernateUtils.getCurrentSession();
        //2.开启事务
        Transaction tx = session.beginTransaction();
        //3.提交事务
        tx.commit();
        //4.关闭资源
        session.close();//会报错
    }

}

表关系

一对多|多对一
ORM

R关系数据库:

一对多|多对一:在表中表达,是在多的一方使用外键引用一的一方的主键

O对象(类):

一的一方使用集合来表达一对多关系,
多的一方直接引用一的属性,表达多对一关系

M映射文件:

<!-- 一对多关系映射
    name 属性:填写集合的属性名
    column属性:填写多的一方的外键名称
 -->
<set name="linkman">
    <key column="lkm_cust_id(外键名)"></key>
    <one-to-many class="LinkMan" />
</set>

<!-- 多对一关系映射 -->
<many-to-one name="外键名" column="表名" class="类名" />
一对多|多对一的应用
Cascade:级联操作

cascade:级联操作

save-update:级联保存或级联更新
级联保存:
保存客户是级联保存客户下的联系人,将于客户管理的瞬时状态对象转换为持久化
级联更新:
自动更新与持久化状态客户关联的联系人对象,将与客户关联的游离状态的联系人对象转换为持久化状态

delete:级联删除
级联删除:删除客户是自动删除客户关联的联系人,将持久化状态的客户删除时,会级联把客户下的联系人删除(转换为瞬时状态)

all:save-update+delete

结论:cascade属性的性质属于减少开发时书写代码量的配置,属性如果不用完全可以靠写代码来代替,而且级联操作中尤其级联删除配置使用风险极大,不建议使用级联属性.

代码

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

import cn.it.domain.Customer;
import cn.it.domain.LinkMan;
import cn.it.utils.HibernateUtils;

/**
 * 级联操作
 * @author Administrator
 *
 */
public class Demo01 {

    /**
     * 添加一个客户,并添加客户下的两个联系人,级联保存
     */
    @Test
    public void test1(){
        //1.获取session
        Session session = HibernateUtils.openSesseion();
        //2.开启事务
        Transaction tx = session.beginTransaction();
        //3.执行操作
        //创建客户
        Customer customer = new Customer();
        customer.setCust_name("张三");
        //创建联系人
        LinkMan linkMan1 = new LinkMan();
        linkMan1.setLkm_name("张三丰");
        LinkMan linkMan2 = new LinkMan();
        linkMan2.setLkm_name("张无忌");
        //表达一对多或多对一关系
        customer.getLinkMen().add(linkMan1);
        customer.getLinkMen().add(linkMan2);

        linkMan1.setCustomer(customer);
        linkMan2.setCustomer(customer);
        //将客户和联系人转换为持久化状态
        session.save(customer);
//      session.save(linkMan1);
//      session.save(linkMan2);
        //4.提交事务
        tx.commit();
        //5.关闭资源
        session.close();
    }

    /**
     * 级联更新
     */
    @Test
    public void test2(){
        //1.获取session
        Session session = HibernateUtils.openSesseion();
        //2.开启事务
        Transaction tx = session.beginTransaction();
        //3.执行操作
        //创建客户
        Customer customer = new Customer();
        customer.setCust_name("李四");
        //创建联系人
        LinkMan linkMan1 = new LinkMan();
        linkMan1.setLkm_name("李世民");
        linkMan1.setLkm_id(3l);
        LinkMan linkMan2 = new LinkMan();
        linkMan2.setLkm_name("李隆基");
        linkMan2.setLkm_id(4l);
        //表达一对多或多对一关系
        customer.getLinkMen().add(linkMan1);
        customer.getLinkMen().add(linkMan2);

        linkMan1.setCustomer(customer);
        linkMan2.setCustomer(customer);
        //将客户和联系人转换为持久化状态
        session.save(customer);
        //4.提交事务
        tx.commit();
        //5.关闭资源
        session.close();
    }

    /**
     * 级联删除
     */
    @Test
    public void test3(){
        //1.获取session
        Session session = HibernateUtils.openSesseion();
        //2.开启事务
        Transaction tx = session.beginTransaction();
        //3.执行操作
        //获取客户
        Customer customer = session.get(Customer.class, 4l);
        //将客户和联系人转换为瞬时状态
        session.delete(customer);
        //4.提交事务
        tx.commit();
        //5.关闭资源
        session.close();
    }
}
inverse属性

问题:sql语句打印冗余

分析:
关系配置了两次.
1对多 为客户添加联系人
多对1 为联系人添加所属客户

解决方案:
1. 只有多的一方维护关系,这一方一定是多的一方(外键所在方维护关系)
2. 配置inverse属性,让一的一方(客户)不再维护关系.
inverse属性,是否放弃维护关系,ture:放弃,false(默认值):维护.
一对多中可以使用inverse属性放弃维护关系,多对一中不能放弃维护关系.

结论:为了避免sql语句打印冗余,在开发时往往选择放弃一的一方的关系维护,配置后要注意,表达关系要由多的一方进行维护.

代码:

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

import cn.it.domain.Customer;
import cn.it.domain.LinkMan;
import cn.it.utils.HibernateUtils;

/**
 * 表的关系:一对多|多对一
 * 表之间的关系维护
 * @author Administrator
 *
 */
public class Demo02 {

    /**
     * 维护关系重复导致冗余sql语句
     * 分析:关系配置了两次
     * 为客户添加联系人
     * 为联系人添加所属客户
     * 解决方案:只由外键所在方维护关系,一的一方不进行维护
     */
    @Test
    public void test1(){
        //1.获取session
        Session session = HibernateUtils.openSesseion();
        //2.开启事务
        Transaction tx = session.beginTransaction();
        //3.执行操作
        //创建客户
        Customer customer = new Customer();
        customer.setCust_name("张三");
        //创建联系人
        LinkMan linkMan1 = new LinkMan();
        linkMan1.setLkm_name("张三丰");
        LinkMan linkMan2 = new LinkMan();
        linkMan2.setLkm_name("张无忌");
        //表达一对多或多对一关系
//      customer.getLinkMen().add(linkMan1);
//      customer.getLinkMen().add(linkMan2);

        linkMan1.setCustomer(customer);
        linkMan2.setCustomer(customer);
        //将客户和联系人转换为持久化状态
        session.save(customer);
        session.save(linkMan1);
        session.save(linkMan2);
        //4.提交事务
        tx.commit();
        //5.关闭资源
        session.close();
    }

    /**
     * 维护关系重复导致冗余sql语句
     * 分析:关系配置了两次
     * 为客户添加联系人
     * 为联系人添加所属客户
     * 解决方案:配置inverse属性,让一的一方不再维护关系
     */
    @Test
    public void test2(){
        //1.获取session
        Session session = HibernateUtils.openSesseion();
        //2.开启事务
        Transaction tx = session.beginTransaction();
        //3.执行操作
        //创建客户
        Customer customer = new Customer();
        customer.setCust_name("张三");
        //创建联系人
        LinkMan linkMan1 = new LinkMan();
        linkMan1.setLkm_name("张三丰");
        LinkMan linkMan2 = new LinkMan();
        linkMan2.setLkm_name("张无忌");
        //表达一对多或多对一关系
        customer.getLinkMen().add(linkMan1);
        customer.getLinkMen().add(linkMan2);

        linkMan1.setCustomer(customer);
        linkMan2.setCustomer(customer);
        //将客户和联系人转换为持久化状态
        session.save(customer);
        session.save(linkMan1);
        session.save(linkMan2);
        //4.提交事务
        tx.commit();
        //5.关闭资源
        session.close();
    }

}
事务操作
import java.util.Set;

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

import cn.it.domain.Customer;
import cn.it.domain.LinkMan;
import cn.it.utils.HibernateUtils;

/**
 * 表的关系:一对多|多对一
 * @author Administrator
 *
 */
public class Demo03 {

    /**
     * 外键值置为null,联系人与客户解除关系
     */
    @Test
    public void test1(){
        //1.获取session
        Session session = HibernateUtils.openSesseion();
        //2.开启事务
        Transaction tx = session.beginTransaction();
        //3.执行操作
        //获得id为5的联系人
        LinkMan linkMan = session.get(LinkMan.class, 5l);
        //设置联系人不属于任何客户
        linkMan.setCustomer(null);
        //4.提交事务
        tx.commit();
        //5.关闭资源
        session.close();
    }

    /**
     * 取得客户所属联系人
     */
    @Test
    public void test2(){
        //1.获取session
        Session session = HibernateUtils.openSesseion();
        //2.开启事务
        Transaction tx = session.beginTransaction();
        //3.执行操作
        //获得id为5的客户
        Customer customer = session.get(Customer.class, 2l);
        //取出id为5的客户的联系人
        Set<LinkMan> linkMen = customer.getLinkMen();
        for (LinkMan linkMan : linkMen) {
            System.out.println(linkMan.getLkm_name());
        }
        //4.提交事务
        tx.commit();
        //5.关闭资源
        session.close();
    }


    /**
     *修改联系人所属的客户
     */
    @Test
    public void test3(){
        //1.获取session
        Session session = HibernateUtils.openSesseion();
        //2.开启事务
        Transaction tx = session.beginTransaction();
        //3.执行操作
        //获得id为5的联系人
        LinkMan linkMan = session.get(LinkMan.class, 5l);
        //新建id为3的客户
        Customer customer = new Customer();
        customer.setCust_id(3l);
        //修改联系人客户
        linkMan.setCustomer(customer);
        //4.提交事务
        tx.commit();
        //5.关闭资源
        session.close();
    }

}
多对多
ORM

R 关系数据库

新建一张第三方表(中间表),关联有关系的两张表

O 对象
分别用集合引用另一个对象

M 映射文件
两个配置文件格式相同,值进行更换

<!-- 多对多文件配置 -->
    <set name="集合属性名" table="中间表名">
        <key column="引用'我'的外键名"></key>
        <many-to-many class="与谁是多对多的类名" column="引用'他'的外键名"></many-to-many>
    </set>
操作

问题:如果员工以及员工下的角色,导致向中间表插入重复记录而报错.
解决:由一方放弃维护关系
1.不写其中一方维护关系的代码.
2.配置inverse=true,强制让一方放弃维护关系.

结论:在开发中为了避免多对多两方多维护关系造成报错,一定需要选择一方来放弃维护关系,哪一方放弃要看业务方向,先存着的一方放弃维护关系.

cascade属性:级联操作
save-update:级联保存或更新
delete:级联删除
all:二合一

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值