2.Hibernate进阶

本文详细介绍了Hibernate进阶知识,包括持久化类的编写规则,如需提供无参构造、私有属性及getter/setter方法,以及避免使用final。讨论了主键生成策略,如increment、identity、sequence等,并通过实例展示了不同策略的使用。此外,还讲解了持久化类的三种状态(瞬时态、持久态、脱管态)及其转换,以及Hibernate的一级缓存机制和事务管理,包括事务隔离级别和线程绑定的Session。最后,简要提及了Query和Criteria查询API的使用。

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

一.持久化类的编写规则

什么是持久化类?
持久化:将内存中的数据永久存储到关系型数据库中的过程,Hibernate框架就是用来进行持久化的框架;
持久化类:一个Java对象与数据库中的表建立了映射关系,那么这类在Hibernate中称为是持久化类,可以通俗认为,持久化类 = Java类 + 映射文件
持久化类的编写规则:
对持久化类提供一个无参的构造方法:因为Hibernate底层需要使用反射生成实例;
属性需要私有,对私有属性提供public的get和set方法:因为Hibernate中需要获取或者设置对象的值;
对持久化类提供一个唯一标识OID与数据库主键对应:Java中通过对象的地址区分是否是同一个对象,数据库中通过主键确定是否是同一个记录,在Hibernate中通过持久化类的OID属性区分是否是同一个对象。
持久化类中属性尽量使用包装类类型:因为基本数据类型默认是0,0会有许多歧义,包装类类型默认为null,与0有着明显的区别。
持久化类不要使用final修饰:延迟加载(也称懒加载)本身是Hibernate的一个优化手段,返回的是一个代理的对象(javassist可以对没有实现接口的类产生代理,其使用了非常底层的字节码增强技术,继承这个类进行代理)。这里如果用final进行修饰,则不能被继承了,也即不能产生代理对象,延迟加载也就失效了,则load方法和get方法也就一样了。

二.主键生成策略

主键分类:
自然主键:主键的本身就是数据库表中的一个字段(即为实体中的一个具体的属性)
举例:创建一个人员表,人员都会有一个身份证号(唯一不可重复的),使用了身份证号作为主键,这种主键称为是自然主键;
代理主键:主键的本身不是表中必须的一个字段(即不是实体中的某个具体的属性)
举例:创建一个人员表,没有使用人员中的身份证号,用了一个与这个表不相关的字段,如ID,这种主键称为是代理主键;

:在实际开发中,尽量使用代理主键,因为一旦自然主键参与到业务逻辑中,后期有可能需要修改源代码;好的程序一般满足OCP原则,即对程序的扩展是Open的,对源码的修改是Close的。

Hibernate主键生成策略:
  在实际开发中,一般不允许用户手动设置主键,一般将主键交给数据库,手动编写程序进行设置。在Hibernate中,为了减少程序编写,提供了很多种的主键生成策略:
increment:hibernate中提供的自动增长机制(没有采用数据库底层的自动增长),适用于short、int、long类型的主键,在单线程程序中使用;
说明:实现过程为,首先发送一条语句“select max(id) from 表名”,然后让id+1作为下一条记录的主键,这就可能产生线程安全问题。
identity:使用short、int、long类型的主键,使用的是数据库底层的自动增长机制,适用于有自动增长机制功能的数据库(MySQL、MSSQL),但是Oracle是没有自动增长机制的;
sequence:使用short、int、long类型的主键,采用的是序列的方式。(Oracle支持序列,MySQL不支持序列);
uuid:适用于字符串类型的主键,使用hibernate中的随机方式生成字符串主键;
native:本地策略,可以在identity和sequence之间进行自动切换;
assigned:hibernate放弃外键的管理,需要手动编写程序或者用户自己设置;
foreign:外部的,在一对一的一种关联映射的情况下使用(了解即可)。

Hibernate主键生成策略实例:
创建项目:
在这里插入图片描述
引入jar包,创建两个配置文件(hibernate.cfg.xml,Customer.hbm.xml),持久化类(Customer.java),工具类(HibernateUtils.java),测试类(hibernate_demo1.java)
代码:
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>
    <!-- 建立类与表的映射 -->
    <class name = "hibernate_start1.Customer" table = "cst_customer">
        <!-- 建立类中的属性与表中的主键对应 -->
        <id name = "cust_id" column = "cust_id">
            <generator class="identity"/>
        </id>
        
        <!-- 建立类中的普通的属性和表的字段的对应 -->
        <property name="cust_name" column = "cust_name"/>
        <property name="cust_source" column = "cust_source"/>
        <property name="cust_industry" column = "cust_industry"/>
        <property name="cust_level" column = "cust_level"/>
        <property name="cust_phone" column = "cust_phone"/>
        <property name="cust_mobile" column = "cust_mobile"/>
    </class>
</hibernate-mapping>

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-configuration>
    <session-factory>
        <!-- 连接数据库的基本参数 -->
        <property name = "hibernate.connection.driver_class" >com.mysql.jdbc.Driver</property>
        <property name = "hibernate.connection.url" >jdbc:mysql:///hibernate_start1</property>
        <property name = "hibernate.connection.username" >root</property>
        <property name = "hibernate.connection.password" >2e5y8hxf</property>
        
        <!-- hibernate方言设置 -->
        <property name = "hibernate.dialect" >org.hibernate.dialect.MySQLDialect</property>

        
        <!-- 可选配置 -->
        <!-- 打印SQL -->
        <property name = "hibernate.show_sql">true</property>
        <!-- 格式化SQL -->
        <property name = "hibernate.format_sql">true</property>
        <!-- 自动创建表 -->
        <property name = "hibernate.hbm2ddl.auto">update</property>
        
        <mapping resource = "hibernate_start1/Customer.hbm.xml"/>
    </session-factory>    

</hibernate-configuration>

Customer.java

package hibernate_start1;

public class Customer {
    private Long cust_id;
    private String cust_name;
    private String cust_source;
    private String cust_industry;
    private String cust_level;
    private String cust_phone;
    private String cust_mobile;
	public Long getCust_id() {
		return cust_id;
	}
	public void setCust_id(Long cust_id) {
		this.cust_id = cust_id;
	}
	public String getCust_name() {
		return cust_name;
	}
	public void setCust_name(String cust_name) {
		this.cust_name = cust_name;
	}
	public String getCust_source() {
		return cust_source;
	}
	@Override
	public String toString() {
		return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + ", cust_source=" + cust_source
				+ ", cust_industry=" + cust_industry + ", cust_level=" + cust_level + ", cust_phone=" + cust_phone
				+ ", cust_mobile=" + cust_mobile + "]";
	}
	public void setCust_source(String cust_source) {
		this.cust_source = cust_source;
	}
	public String getCust_industry() {
		return cust_industry;
	}
	public void setCust_industry(String cust_industry) {
		this.cust_industry = cust_industry;
	}
	public String getCust_level() {
		return cust_level;
	}
	public void setCust_level(String cust_level) {
		this.cust_level = cust_level;
	}
	public String getCust_phone() {
		return cust_phone;
	}
	public void setCust_phone(String cust_phone) {
		this.cust_phone = cust_phone;
	}
	public String getCust_mobile() {
		return cust_mobile;
	}
	public void setCust_mobile(String cust_mobile) {
		this.cust_mobile = cust_mobile;
	}
    
}

HibernateUtils.java:

package hibernate_utils1;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtils {

	public static final Configuration cfg;
	public static final SessionFactory sf;
	
	static{
		cfg = new Configuration().configure();
		sf = cfg.buildSessionFactory();
	}
	
	public static Session openSession(){
		return sf.openSession();
	}
}

hibernate_demo1.java:

package hibernate_start1;

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

import hibernate_utils1.HibernateUtils;

public class hibernate_demo1 {
    @Test
    public void demo1() {
    	Session session = HibernateUtils.openSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//编写代码
    	Customer customer = new Customer();
    	customer.setCust_name("张无忌");
    	session.save(customer);
    	
    	transaction.commit();
    	session.close();
    	
    }
    
    @Test
    public void demo2() {
    	Session session = HibernateUtils.openSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//编写代码
    	Customer customer = new Customer();
    	customer.setCust_name("赵敏");
    	session.save(customer);
    	
    	transaction.commit();
    	session.close();
    	
    }
}

测试结果:
分析:将Customer.hbm.xml文件中的主键生成策略参数<generator = “native”>改成increment,再将hibernate.cfg.xml文件中的自动建表方式改成create,这时创建的表是非自动增长的,此时再将create改成update,在测试类hibernate_demo1中同时测试demo1()和demo2(),当demo1()执行保存操作后,会查询表中当前的最大的主键id:
在这里插入图片描述
提交操作前执行demo2(),到demo2()执行到提交操作前暂停,此时demo2()也会查询表中的最大主键id:
在这里插入图片描述
也就是说demo1()和demo2()查询到的主键id一样。之后回去继续执行demo1()中的提交,此时表中按照自动增长的方式添加了一条数据A(主键为a+1),之后再执行demo2()中的提交操作,这时程序报错,此时demo2()也将添加一条数据B(主键也为a+1),所以两个线程发生冲突。
解决方法:将increment换成identity,使用的是数据库底层的自动增长机制,不受Hibernate约束。

三.持久化类的三种状态

三种状态概述:
1.瞬时态(transient):这种对象没有持久化标识OID(相当于主键),没有被session管理;
2.持久态(persistent):这种对象有持久化标识OID,并且被session管理;
3.脱管态(detached):这种对象有持久化标识OID,没有别session管理;

    @Test
    public void demo1() {
    	Session session = HibernateUtils.openSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//编写代码
    	Customer customer = new Customer();   //这里customer为瞬时态对象,没有持久化标识OID,没有被session管理
    	customer.setCust_name("张无忌");
    	session.save(customer);     //这行代码执行过后,customer为持久态对象,有持久化标识OID,并且被session管理
    	
    	transaction.commit();
    	session.close();
    	
    	System.out.println(customer.getCust_name());   //这里的customer为脱管态对象,有持久化标识OID,但是没有被session管理
    	
    }

分析
Customer customer = new Customer(); //这里customer为瞬时态对象,没有持久化标OID,没有被session管理

session.save(customer); //这行代码执行过后,customer为持久态对象,有持久化标识OID,并且被session管理

System.out.println(customer.getCust_name()); //这里的customer为脱管态对象,有持久化标识OID,但是没有被session管理

三种状态转换:
状态转换图

在这里插入图片描述
瞬时态对象:
获取瞬时态对象
  Customer customer = new Customer();
状态转换
瞬时态 → \to 持久态:
  save(Object obj)、saveOrUpdate(Object obj)
瞬时态 → \to 脱管态:
  customer.setCust_id(1l);

持久态对象:
获取持久态对象
通过get()、load()、find()、iterate()对象获取,比如:Customer customer = new Customer();
状态转换
持久态 → \to 瞬时态:
  delete();
持久态 → \to 脱管态:
close()、clear()、evict(Object obj);

脱管态对象:
获取脱管态对象
  Customer customer = new Customer();
  customer.setCust_id(1l);
状态转换
脱管态 → \to 持久态:update()、saveOrUpdate();
脱管态 → \to 瞬时态:customer.setCust_id(null);

持久态对象特性:
主要特性:持久化类的持久化对象能够自动地更新数据库
实例:在获取持久化对象后,再调用save等方法对数据库进行更新

    @Test
    public void demo3() {
    	Session session = HibernateUtils.openSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//编写代码
    	Customer customer = session.get(Customer.class, 1l);  //这里不再需要调用save方法
    	customer.setCust_name("赵敏");  
	
    	transaction.commit();
    	session.close();
    	
    }

四.Hibernate一级缓存

一级缓存概述:
什么是Hibernate一级缓存
Hibernate一级缓存就是指Session缓存,Session缓存是一块内存空间,用来存放java对象。在使用Hibernate查询对象的时候,首先会使用对象属性的OID的值在Hibernate一级缓存中查找,如果找到匹配OID的值,则将该对象从一级缓存中取出(这里可以认为是复制)使用,不会再查询数据库;如果没有找到相同OID值的对象,则会去数据库中查找相应数据。当从数据库中查询到所需的数据时,该数据信息也会放置到一级缓存中。Hibernate一级缓存的作用就是减少对数据库的访问次数

Hibernate一级缓存的特点
  当程序调用Session接口的save()、update()、saveOrUpdate()时,如果一级缓存中没有相应java对象,则将该对象信息加入到一级缓存中;
  当程序调用Session接口的load()、get(),以及Query接口的list()、iterator()方法时,会判断一级缓存中是否存在该对象,有则从一级缓存中返回该对象,不会再查询数据库;如果一级缓存中没有要查询的对象,再到数据库中查询相应的对象,并将其添加到一级缓存中。
  当调用Session的close()方法时,Session缓存会被清空。

一级缓存实例

    @Test
    public void demo3() {
    	Session session = HibernateUtils.openSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//编写代码
    	Customer customer1 = session.get(Customer.class, 1l);
    	System.out.println(customer1);
    	Customer customer2 = session.get(Customer.class, 1l);
    	System.out.println(customer2);   	
    	System.out.println(customer1 == customer2);
    	
    	transaction.commit();
    	session.close();
    	
    }

运行结果:
在这里插入图片描述
结果分析:
第一次查询(customer1),一级缓存中没有这个对象,所以需要去数据库中查询,所以打印了select…查询语句,之后将这个对象存到一级缓存中;第二次查询(customer2),一级缓存中已经有了这个对象,所以直接返回就行,不需要再去数据库中查找,所以没有打印select…查询语句;而且最后两次查询得到的持续化对象地址比较为true,说明说明这两个对象存在同一个地方(即一级缓存中)。

快照区:
一级缓存包含:缓存区和快照区,底层实现一般是用map,缓存区和快照区分别对应于,map的键和值。
举例
当执行代码Customer customer = session.get(Customer.class, 1l); 时,会将缓存区中id为1的数据拷贝一份到快照区,当执行customer.setCust_name(“赵敏”);时,会改变缓存区中的值(比如改为“赵敏”),当进行事务提交时,会比较缓存区与快照区中id为1的值是否一致,若一致,不更新数据库,若不一致,则更新数据库。

    	Customer customer = session.get(Customer.class, 1l);  
    	customer.setCust_name("赵敏");  
    	
        transaction.commit();

五.Hibernate中的事务管理

事务的隔离级别:
在这里插入图片描述
Hibernate中设置事务隔离级别:在核心配置文件中设置,在Spring中默认为4。

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

与线程绑定的session:
分析:在DAO层中执行的是对单个数据源的操作,在Service层中执行封装的业务逻辑操作,在这里s1()方法中的DAO1.aaa()和DAO2.bbb()必须保证连接对象是同一个Session对象,有两种实现方式:
1.向下传递 DBUtils
2.使用ThreadLocal对象,即线程绑定对象(实现过程:如首先将DAO1.aaa()这个连接绑定到当前线程中,在调用DAO2.bbb()时,通过当前线程获取Session连接,就能保住DAO1和DAO2连接的是同一个Session了)
在这里插入图片描述
Hibernate中:Hibernate框架内部已经绑定好了ThreadLocal,在SessionFactory提供了一个方法getCurrentSession()用于获取当前线程中的Session。
实例
在工具类HibernateUtils中加入getCurrentSession()函数:
在这里插入图片描述
在测试类中,以Session session = HibernateUtils.getCurrentSession();方式获取Session对象:
:由于这里的Session是线程绑定的,随着线程的结束而销毁,所以不需要关闭。

    @Test
    public void demo4() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//编写代码
    	Customer customer1 = session.get(Customer.class, 1l);
    	System.out.println(customer1);
    	Customer customer2 = session.get(Customer.class, 1l);
    	System.out.println(customer2);   	
    	System.out.println(customer1 == customer2);
    	
    	transaction.commit();
    	//session.close();
    	
    }

核心配置文件代码:
在这里插入图片描述

五.Hibernate的其他API

1.Query
概述:Query代表Hibernate中面向对象的查询操作。在Hibernate中,通常使用session.createQuery()方法接收一个HQL语句,然后调用Query的list()或uniqueResult()方法执行查询。
HQL:Hibernate Query Language,其语法很想SQL,但HQL是完全面向对象的。

在Hibernate中使用Query对象的步骤:
(1)获得Hibernate的Session对象;
(2)编写HQL语句;
(3)调用session.createQuery创建查询对象;
(4)如果HQL语句包含参数,则调用Query的setXxx语句来设置参数;
(5)调用Query的list()或者uniqueResult()方法来执行查询;

举例
查询所有数据:

    @Test
    public void demo5() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//编写代码
        String hql = "from Customer";
        Query query = session.createQuery(hql);
        List<Customer> list = query.list();
        System.out.println(list);
        for(Customer customer : list) {
        	System.out.println(customer);
        }    	
    	transaction.commit();
    	//session.close();  因为这里使用的是线程绑定的session,随着线程的结束而关闭,不需要手动关闭   	
    }

查询结果:
在这里插入图片描述
条件查询:

    @Test
    public void demo5() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//编写代码
        String hql = "from Customer where cust_name like ?";
        Query query = session.createQuery(hql);
        query.setParameter(0, "赵%");
        List<Customer> list = query.list();
        for(Customer customer : list) {
        	System.out.println(customer);
        }    	
    	transaction.commit();	
    }

查询结果:
在这里插入图片描述
分页查询:这里分页查询查询1页,参数setFirstResult(0)代表从id为0(不包括0)开始查询,setMaxResults(2)这里的2代表每页有两个记录。

    @Test
    public void demo5() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//编写代码
        String hql = "from Customer";
        Query query = session.createQuery(hql);
        query.setFirstResult(0);
        query.setMaxResults(2);
        List<Customer> list = query.list();
        for(Customer customer : list) {
        	System.out.println(customer);
        }    	
    	transaction.commit();	
    }

在这里插入图片描述

2.Query
概述:Criteria是一个完全面向对象,可扩展的条件查询API,通过它完全不需要考虑数据库底层是怎么实现的,以及SQL语句是如何编写的。Criteria查询又称QBC(Query by Criteria)查询,是Hibernate中另一种对象检索方式。

举例:
查询所有:

    @Test
    public void demo6() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//查询所有
        Criteria criteria = session.createCriteria(Customer.class);
        List<Customer> list = criteria.list();
        for(Customer customer : list) {
        	System.out.println(customer);
        }    	
        
    	transaction.commit();	
    }

条件查询:

    @Test
    public void demo6() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//条件查询
        Criteria criteria = session.createCriteria(Customer.class);
        criteria.add(Restrictions.like("cust_name", "赵%"));
        List<Customer> list = criteria.list();
        for(Customer customer : list) {
        	System.out.println(customer);
        }    	    
    	transaction.commit();	
    }

分页查询:

    @Test
    public void demo6() {
    	Session session = HibernateUtils.getCurrentSession();
    	Transaction transaction = session.beginTransaction();
    	
    	//分页查询
        Criteria criteria = session.createCriteria(Customer.class);
        criteria.setFirstResult(0);
        criteria.setMaxResults(2);
        List<Customer> list = criteria.list();
        for(Customer customer : list) {
        	System.out.println(customer);
        }    	    
    	transaction.commit();	
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值