五、Hibernate相关理论知识

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mChenys/article/details/84636179

持久化对象的状态

Hibernate的持久化类

Java类与数据库的某个表建立了映射关系.这个类就称为是持久化类.
持久化类 = Java类 + hbm的配置文件

Hibernate的持久化类的状态

Hibernate为了管理持久化类:将持久化类分成了三个状态:

  • 瞬时态:Transient Object
    没有持久化标识OID, 没有被纳入到Session对象的管理.

  • 持久态:Persistent Object
    有持久化标识OID,已经被纳入到Session对象的管理.

  • 脱管态:Detached Object
    有持久化标识OID,没有被纳入到Session对象的管理.

Hibernate持久化对象的状态的转换

  1. 瞬时态 – 没有持久化标识OID, 没有被纳入到Session对象的管理
    获得瞬时态的对象
    User user = new User()

    瞬时态对象转换持久态
    save()/saveOrUpdate();

    瞬时态对象转换成脱管态
    user.setId(1); //此时虽然手动添加了oid,但是却没有纳入到Session对象的管理

  2. 持久态 – 有持久化标识OID,已经被纳入到Session对象的管理
    获得持久态的对象
    get()/load();

    持久态转换成瞬时态对象
    delete(); — 比较有争议的,进入特殊的状态(删除态:Hibernate中不建议使用的)

    持久态对象转成脱管态对象
    session的close()/evict()/clear();

    注意:持久态对象有自动更新数据库的能力!!!

  3. 脱管态 – 有持久化标识OID,没有被纳入到Session对象的管理
    获得托管态对象:不建议直接获得脱管态的对象.
    User user = new User(); user.setId(1);

    脱管态对象转换成持久态对象
    update();/saveOrUpdate()/lock();

    脱管态对象转换成瞬时态对象
    user.setId(null);

在这里插入图片描述


Hibernate的一级缓存

  1. 什么是缓存?
    其实就是一块内存空间,将数据源(数据库或者文件)中的数据存放到缓存中.再次获取的时候 ,直接从缓存中获取.可以提升程序的性能!

  2. Hibernate框架提供了两种缓存
    一级缓存 : 自带的不可卸载的.一级缓存的生命周期与session一致.一级缓存称为session级别的缓存.
    二级缓存 : 默认没有开启,需要手动配置才可以使用的.二级缓存可以在多个session中共享数据,二级缓存称为是sessionFactory级别的缓存.

  3. Session对象的缓存概述
    Session接口中,有一系列的java的集合,这些java集合构成了Session级别的缓存(一级缓存).将对象存入到一级缓存中,session没有结束生命周期,那么对象在session中存放着
    内存中包含Session实例 --> Session的缓存(一些集合) --> 集合中包含的是缓存对象!

  4. 证明一级缓存的存在,编写查询的代码即可证明
    在同一个Session对象中两次查询,可以证明使用了缓存,因为第二次查询的时候控制台没有输出sql语句(前提是要在核心配置文件中启用控制台打印sql语句的功能,如何开启看这里)

  5. Hibernate框架是如何做到数据发生变化时进行同步操作的呢?
    使用get方法查询User对象
    然后设置User对象的一个属性,注意:没有做update操作。发现,数据库中的记录也改变了。
    利用快照机制来完成的(SnapShot),hibernate在commit事务的时候会将缓存中的数据与快照的数据做对比,如果不一致,则说明需要更新数据库。


事务相关的概念

  1. 什么是事务
    事务就是逻辑上的一组操作,组成事务的各个执行单元,操作要么全都成功,要么全都失败.
    转账的例子:冠希给美美转钱,扣钱,加钱。两个操作组成了一个事情!

  2. 事务的特性
    原子性 : 事务不可分割.
    一致性 : 事务执行的前后数据的完整性保持一致.
    隔离性 : 一个事务执行的过程中,不应该受到其他的事务的干扰.
    持久性 : 事务一旦提交,数据就永久保持到数据库中.

  3. 如果不考虑隔离性:引发一些读的问题
    脏读 : 一个事务读到了另一个事务未提交的数据.
    不可重复读 : 一个事务读到了另一个事务已经提交的update数据,导致多次查询结果不一致.
    虚读 : 一个事务读到了另一个事务已经提交的insert数据,导致多次查询结构不一致.

  4. 通过设置数据库的隔离级别来解决上述读的问题
    未提交读:以上的读的问题都有可能发生.
    已提交读:避免脏读,但是不可重复读,虚读都有可能发生.
    可重复读:避免脏读,不可重复读.但是虚读是有可能发生.
    串行化:以上读的情况都可以避免.

  5. 如果想在Hibernate的框架中来设置隔离级别,需要在hibernate.cfg.xml的配置文件中通过标签来配置
    例如:<property name="hibernate.connection.isolation">4</property>
    取值:
    1—Read uncommitted isolation
    2—Read committed isolation(Oracle默认)
    4—Repeatable read isolation(Mysql默认)
    8—Serializable isolation


丢失更新的问题

如果不考虑隔离性,也会产生写入数据的问题,这一类的问题叫丢失更新的问题。
两个事务同时对某一条记录做修改,就会引发丢失更新的问题。例如:

  • A事务和B事务同时获取到一条数据,同时再做修改
  • 如果A事务修改完成后,提交了事务
  • B事务修改完成后,不管是提交还是回滚,如果不做处理,都会对A操作后的数据产生影响

解决方案有两种

  • 悲观锁
    采用的是数据库提供的一种锁机制,如果采用做了这种机制,在SQL语句的后面添加 for update 子句
    当A事务在操作该条记录时,会把该条记录锁起来,其他事务是不能操作这条记录的。
    只有当A事务提交后,锁释放了,其他事务才能操作该条记录
  • 乐观锁
    采用版本号的机制来解决的。会给表结构添加一个字段version,默认值是0
    当A事务在操作完该条记录,提交事务时,会先检查版本号,如果发生版本号的值相同时,才可以提交事务。同时会更新版本号version=1.
    当B事务操作完该条记录时,提交事务时,会先检查版本号,如果发现版本不同时,程序会出现错误。

使用Hibernate框架解决丢失更新的问题

  • 悲观锁(不建议使用,效率慢)
    使用session.get(Customer.class, 1,LockMode.UPGRADE);

  • 乐观锁
    1.在对应的JavaBean中添加一个属性,名称可以是任意的。
    例如:private Integer version;并 提供get和set方法;

    2.在映射的配置文件中,提供<version name="version"/>标签即可


绑定本地的Session

以前在用jdbc处理事务的时候,需要在业务层使用Connection来开启事务,一种是通过参数的方式传递下去,另一种是把Connection绑定到ThreadLocal对象中

现在的Hibernate框架中,使用session对象开启事务,框架也提供了ThreadLocal的方式,使用步骤有如下:

  1. 需要在hibernate.cfg.xml的配置文件中提供配置
    <property name="hibernate.current_session_context_class">thread</property>

  2. 使用SessionFactory的getCurrentSession()方法来获取当前的Session对象。并且该Session对象不用手动关闭,线程结束了,会自动关闭。

注意:想使用getCurrentSession()方法,步骤1不能少。
下面给个处理事务的service和dao层的代码片段示例:

UserService

public class UserService {
	
	public void save(User u1,User u2){
		UserDao dao = new UserDao();
		// 获取当前线程session
		Session session = HibernateUtils.getCurrentSession();
		// 开启事务
		Transaction tr = session.beginTransaction();
		try {
			dao.save1(u1); //添加第一条记录
			int a = 10/0;  //模拟异常操作
			dao.save2(u2); //添加第二条记录
			// 提交事务
			tr.commit();
		} catch (Exception e) {
			e.printStackTrace();
			// 出现问题:回滚事务
			tr.rollback();
		}finally{
			// Hibernate自己管理释放资源,不用手动关闭session了,线程结束后会自动关闭的!!
		}
	}

}

UserDao

public class UserDao {
	
	public void save1(User u1){
		Session session = HibernateUtils.getCurrentSession();
		session.save(u1);
	}
	
	public void save2(User u2){
		Session session = HibernateUtils.getCurrentSession();
		session.save(u2);
	}

}

HibernateUtils

public class HibernateUtils {
	
	private static final Configuration CONFIG;
	private static final SessionFactory FACTORY;
	
	// 编写静态代码块
	static{
		// 加载XML的配置文件
		CONFIG = new Configuration().configure();
		// 构造工厂
		FACTORY = CONFIG.buildSessionFactory();
	}
	
	/**
	 * 从工厂中新获取Session对象
	 * @return
	 */
	public static Session getSession(){
		return FACTORY.openSession();
	}
	
	/**
	 * 从ThreadLocal类中获取到当前线程的session对象
	 * @return
	 */
	public static Session getCurrentSession(){
		return FACTORY.getCurrentSession();
	}
	
}

猜你喜欢

转载自blog.csdn.net/mChenys/article/details/84636179
今日推荐