博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
hibernate_get和load方法的区别、持久化类、一级缓存和二级缓存、事务控制
阅读量:3710 次
发布时间:2019-05-21

本文共 9591 字,大约阅读时间需要 31 分钟。

1.get和load方法的区别

1.1 获取Session的两种方式

Session是Hibernate的核心,我们在创建了SessionFactory对象后,有两种方式可以获取Session实例

  • Session session = sessionFactory.openSession();
    采用openSession方法获取Session实例时,SessionFactory直接创建一个新的Session实例,并且在使用完成后需要调用close方法进行手动关闭。
  • Session session = sessionFactory.getCurrentSession();
    而getCurrentSession方法创建的Session实例会被绑定到当前线程中,它在提交或回滚操作时会自动关闭。

1.2 Session的事务管理—Transaction对象

我们通过Session开启事务,获取Transaction对象

//开启事务Transaction transaction = session.beginTransaction();// 提交事务transaction.commit();// 回滚事务transaction.rollback();

Session执行完数据库操作后,要使用Transaction接口的commit()方法进行事务提交,才能真正的将数据操作同步到数据库中。发生异常时,需要使用rollback()方法进行事务回滚,以避免数据发生错误。

由于Session对象属于线程不安全,当一个线程有多个Session时,无法保证事务。所以一个线程要保障只有一个Session对象。

为了保证一个线程中只有一个Session,所以在实际开发中,我们将Session绑定到当前线程中

1.3 get方法和load方法

在hibernate.cfg.xml中开启显示SQL语句

true

(1)实体表和映射文件

Customer.java

public class Customer {
private Long custId;// 客户编号 private String custName;// 客户名称 private String custSource;// 客户信息来源 private String custIndustry;// 客户所属行业 private String custLevel;// 客户级别 private String custAddress;// 客户联系地址 private String custPhone;// 客户联系方式 //一对多关系映射:一个客户可以对应多个联系人 private Set
linkmans = new HashSet
(); ......}

Customer.hbm.xml

LinkMan.java

public class LinkMan implements Serializable {
private Long lkmId;//联系人编号(主键) private String lkmName;//联系人姓名 private String lkmGender;//联系人性别 private String lkmPhone;//联系人办公电话 private String lkmMobile;//联系人手机 private String lkmEmail;//联系人邮箱 private String lkmPosition;//联系人职位 private String lkmMemo;//联系人备注 private Customer customer;//对应联系人表中的外键 ......}

LinkMan.hbm.xml

(2)dao类

public class CustomerDao {
/** * 根据id查询客户信息 get立即加载 */ public static Customer findOneById(long id){
Session session = null;//Session对象 try{
// 查询不需要开启事务,只有对数据库内容进行多次增删改操作时 session = HibernateUtil.openSession(); Customer newCustomer = session.get(Customer.class, id); return newCustomer; }catch(Exception e){
e.printStackTrace(); }finally{
session.close();//释放资源 } return null; } /** * 根据id查询客户信息 load延迟加载 */ public static Customer loadOneById(long id){
Session session = null;//Session对象 try{
// 查询不需要开启事务,只有对数据库内容进行多次增删改操作时 session = HibernateUtil.openSession(); Customer newCustomer = session.load(Customer.class, id); return newCustomer; }catch(Exception e){
e.printStackTrace(); }finally{
session.close();//释放资源 } return null; }}

(3)工具类获取session

HibernateUtil

public class HibernateUtil {
private static SessionFactory factory; // 使用静态代码块获取SessionFactory static {
//细节:Hibernate把所有可预见的异常,都转成了运行时异常(工具中不会提示要添加异常块) try {
// 加载hibernate.cfg.xml配置文件 Configuration config = new Configuration().configure(); factory = config.buildSessionFactory(); } catch (Exception e) {
e.printStackTrace(); } } /** * 获取一个新的Session对象(每次都是一个新对象) * @return */ public static Session openSession(){
return factory.openSession(); } /** * 获取一个新的Session对象(每次都是一个新对象) * @return */ public static Session getCurrentSession(){
return factory.getCurrentSession(); }}

(4)测试get方法

@Testpublic void testGet(){
Customer oneById = CustomerDao.findOneById(1L); System.out.println("-------------------------"); System.out.println(oneById);}

测试结果如下:

在这里插入图片描述
我们发现确实有执行sql,但最后还是报错了,报错的位置在Customer的toString方法中,在获取Customer中的linkmans属性时报错,这是因为在执行get方法时,查询的sql,只针对于cst_customer表(等于select * from cst_customer where cust_id = ?),所以不会填充linkmans属性,而在CustomerDao方法的最后,我们手动关闭了session,所以在test方法中,再想通过session去懒加载就加载不了了

解决方式:

  • 1.删除finally中的session.close();
    不可取,使用openSession时,必须要手动关闭session,如果此次不关闭,session就会一直存在,消耗性能
  • 2.在session没关闭时,使用linkmans对象,这样就会去执行sql,初始化linkmans对象了
public static Customer findOneById(long id){
Session session = null;//Session对象 try{
session = HibernateUtil.openSession(); Customer newCustomer = session.get(Customer.class, id); System.out.println("---------------------------------"); System.out.println(newCustomer); return newCustomer; }catch(Exception e){
e.printStackTrace(); }finally{
session.close();//释放资源 } return null;}

在这里插入图片描述

(5)测试load方法

@Testpublic void tesLoad(){
Customer customer = CustomerDao.loadOneById(1L); System.out.println(customer);}

执行结果:我们发现这次连sql语句都没有,报错的情况也是no session,所以load方法获取的对象,也是在使用时才去执行sql

在这里插入图片描述
当调用load()方法的时候会返回一个目标对象的代理对象,在这个代理对象中只存储了目标对象的ID值,只有当调用除ID值以外的属性值的时候才会发出SQL查询的。

public static Customer loadOneById(long id){
Session session = null;//Session对象 try{
// 查询不需要开启事务,只有对数据库内容进行多次增删改操作时 session = HibernateUtil.openSession(); Customer newCustomer = session.load(Customer.class, id); System.out.println(newCustomer.getCustId()); return newCustomer; }catch(Exception e){
e.printStackTrace(); }finally{
session.close();//释放资源 } return null;}

这个查询结果也证明了刚刚说的话

在这里插入图片描述
解决方式:和get方法一样,在session未关闭前,使用他们
可以使用 Hibernate.initialize(xx);方法,不过这个方法需要对类中的属性对象也要使用

public static Customer loadOneById(long id){
Session session = null;//Session对象 try{
// 查询不需要开启事务,只有对数据库内容进行多次增删改操作时 session = HibernateUtil.openSession(); Customer newCustomer = session.load(Customer.class, id); Hibernate.initialize(newCustomer); Hibernate.initialize(newCustomer.getLinkmans()); return newCustomer; }catch(Exception e){
e.printStackTrace(); }finally{
session.close();//释放资源 } return null;}

总结:

  • 返回值:
    get()返回的是查询出来的实体对象
    而load()查询出来的是一个目标实体的代理对象。
  • 查询时机:
    get()在调用的时候就立即发出SQL语句查询
    而load()在访问非ID属性的时候才会发出查询语句并且将被代理对象target填充上,但是如果这个动作发生在Session被关闭后的话就会抛出LazyInitializationException。
  • 查询结果为空时:
    get()抛出NullPointerException
    load()抛出ObjectNotFoundException

2.持久化类和对象标识符

2.1 持久化类的编写规范

  • 持久化类需要提供无参数的构造方法。因为在Hibernate的底层需要使用反射生成类的实例。
  • 持久化类的属性需要私有,对私有的属性提供公有的get和set方法。因为在Hibernate底层会将查询到的数据进行封装。
  • 持久化类的属性要尽量使用包装类的类型。
  • 持久化类要有一个唯一标识OID与表的主键对应。
  • 持久化类尽量不要使用final进行修饰。

3.一级缓存和二级缓存

3.1 一级缓存:session

Hibernate的一级缓存就是指Session缓存

在使用Hibernate查询对象的时候,首先会使用对象属性的OID值在Hibernate的一级缓存中进行查找,如果找到匹配OID值的对象,就直接将该对象从一级缓存中取出使用,不会再查询数据库;如果没有找到相同OID值的对象,则会去数据库中查找相应数据。(session关闭,缓存消失)

Hibernate的一级缓存的作用就是减少对数据库的访问次数(降低IO读写次数)。

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

  • 一级缓存默认是打开状态
  • 一级缓存的使用范围是session范围(从session创建到session关闭)
  • 一级缓存中存储的数据必须都是持久态数据
@Testpublic void testCache(){
//从Hibernate封装的工具类中获取Session对象 Session session=HibernateUtil.openSession(); //开启事务 Transaction tx=session.beginTransaction(); //第一次执行get方法:一级缓存中无数据,会去数据库中查询 Customer c1=session.get(Customer.class, 100L); System.out.println("One : "+c1); //第二次执行get方法:一级缓存中有数据,直接获取缓存中的数据Customer c2=session.get(Customer.class, 100L); System.out.println("Two : "+c2); System.out.println(c1==c2);//结果为true tx.commit(); session.close();}

在这里插入图片描述

一级缓存执行过程
在这里插入图片描述
一级缓存的特性:快照机制

public void testUpdateName(){
//从Hibernate封装的工具类中获取Session对象 Session session=HibernateUtil.openSession(); //开启事务 Transaction tx=session.beginTransaction(); //第一步:先查询出客户信息(根据ID查询) Customer c=session.get(Customer.class, 95L); //第二步:对查询出的客户实体进行修改(修改名称) c.setCustName("传智.黑马程序员"); //第三步:调用Hibernate方法实现更新操作 //session.update(c); //常规方式是要调用update方法,但此处省略看看执行结果 tx.commit(); session.close();}

在这里插入图片描述

解释:以上java程序中没有直接调用update方法,同样也对数据修改成功。主要是借助了Hibernate的快照功能

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

结论:Hibernate快照的作用就是确保一级缓存中的数据和数据库中的数据一致。

4.Hibernate的事务控制

在这里插入图片描述

为了实现一个线程中session是同一个,可以使用ThreadLocal将业务层获取的Session绑定到当前线程中,然后在DAO中获取Session的时候,都从当前线程中获取。

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

thread

获取当前线程的session

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

5.Session、EntityManager、HibernateTemplate的区别

  • Session是Hibernate操作数据库的对象
  • EntityManager是JPA操作数据库的对象
  • HibernateTemplate是Spring整合Hibernate后操作数据库的对象(底层是session)

6.数据库中字段类型和映射文件中不匹配时

cst_customer表:表中cust_id并非主键,只是不能为null

在这里插入图片描述
Customer.hbm.xml,配置中custId为主键,且生成策略为native(使用本地数据库的策略)

此时当我们在保存对象时:

public static void findOne(Customer c){
Session session = null;//Session对象 Transaction tx = null ;//事务对象 try{
//使用Hibernate工具类获取Session对象 session = HibernateUtil.openSession(); tx = session.beginTransaction();//开启事务 //保存客户 session.save(c);//save方法返回实体保存后的主键值 //提交事务 tx.commit(); }catch(Exception e){
tx.rollback();//事务回滚 e.printStackTrace(); }finally{
session.close();//释放资源 }}

执行报错,我们看执行的sql,sql中并没有cust_id的值,而表中cust_id的值不能为空,所以报了这个错

在这里插入图片描述
那么将表中cust_id改为可以为空,能成功吗
还是报错,
在这里插入图片描述
所以我们在开发中,一定要保证表中字段的生成策略和映射中一致

转载地址:http://zwnjn.baihongyu.com/

你可能感兴趣的文章
K210官网云端目标检测模型训练-使用vott进行图像标注三
查看>>
网站记录
查看>>
缺陷检测思路(未完成)
查看>>
英语后缀词性
查看>>
win10下PyCharm安装openCV
查看>>
JavaScript学习 (一)js简介,初步了解js
查看>>
开始学习js 基本语法
查看>>
js中栈和堆详解
查看>>
js中的运算符(大量例子来说明)
查看>>
条件语句之 if 语句
查看>>
了解for循环
查看>>
快速了解git日常操作
查看>>
一、jq简介
查看>>
二、jq强大的选择器
查看>>
jq之DOM对象操作
查看>>
ajax中type与method的区别
查看>>
HTML状态码(200、404、301)
查看>>
各种查看距离的方法和元素大小
查看>>
cmd基本操作(自己开发需要的小笔记,日常添加)
查看>>
8.7立秋
查看>>