Hibernate 5.3 (九)

本文介绍了Hibernate中的事务概念及其ACID特性,强调了Session与事务的关系,包括线程安全性和长事务处理策略。此外,还详细阐述了Hibernate的上下文相关Session管理和事件机制,包括拦截器和事件监听器的使用,帮助读者深入理解Hibernate的事务管理和扩展性。

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

事务概念

事务是一步或几步基本操作组成的逻辑执行单元,这些基本操作作为一个整体执行单元,它们要么全部执行,要么全部取消,绝不能仅仅执行一部分。一般而言,每次用户请求,对应一个业务逻辑方法,一个业务逻辑方法往往具有逻辑上的原子性,应该使用事务。例如一个转账操作,对应修改两个账户的余额,这两个账户的修改要么同时生效,要么同时取消一同时生效是转账成功,同时取消是转账失败,但不可只修改其中一个账户,那将破坏数据库的完整性。

事务特性(ACID)

  1. 原子性(atomicity): 事务中不可分割最小逻辑执行体
  2. 一致性(consistency):如果系统运行发生中断,某个事务尚未完成而被迫中断, 而该未完成的事务对数据库所做的修改已被写入数据库, 此时,数据库就处于一种不正确的状态。比如银行在两个账户之间转账:从A账户向B账户转入1000元。系统先减少A账户的1000元, 然后再为B账户增加1000元。如果全部执行成功,数据库处于一致性状态。如果仅执行完A账户金额的修改, 而没有增加B账户的金额,则数据库就处于不一致性状态。 因此,一致性是通过原子性来保证的。
  3. 隔离性(isolation):事务之间互相不影响。
  4. 持续性(durability):也称之为持久性,事务一旦提交,就保存到数据库。

Session与事务

Tip

线程安全:在多线程使用该对象的时候,底层是通过锁机制去保证,每次只有一个对象执行。
线程不安全:在多线程使用该对象的时候,多个线程对同时执行,会出现逻辑错乱。

SessionFactory 对象的创建代价很高,它是线程安全的对象,被设计成可以被所有线程所共享。通常,SessionFactory 会在应用程序启动时创建,一旦创建 了SessionFactory 将不会轻易关闭, 只有当应用退出时才关闭SessionFactory。

Session对象是轻量级的,它也是线程不安全的。下面是比较的方法去获取session:

public static final ThreadLocal<Session> session
		= new ThreadLocal<Session>();

	public static Session currentSession()
		throws HibernateException
	{
		Session s = session.get();
		// 如果该线程还没有Session,则创建一个新的Session
		if (s == null)
		{
			s = sessionFactory.openSession();
			// 将获得的Session变量存储在ThreadLocal变量session里
           // 相当于每一个线程都绑定一个session,你每次线程来拿的时候,都用自己的线程的session,怎么会冲突,就不需要线程同步
			session.set(s);
		}
		return s;
	}

对于单个业务进程、单个的工作单元而言,Session只被使用一次。创建Session 时,并不会立即打开与数据库之间的连接,只有需要进行数据库操作时,Session才会获取JDBC连接。因此,打开和关闭Session,并不会对性能造成很大的影响。甚至即使无法确定一个请求是否需要数据访问,也可以打开Session对象,因为如果不进行数据库访问,Session不会获取JDBC连接。

Hibernate 建议一次事务请求,对应一个Session。

长事务

事务持续时间很长,在事务中有多个用户操作。

对于长事务而言:一个比较差的做法就是在用户与服务器会话(session, 通常就是一次HTTP Session)期间,应用程序保持 Session与数据库事务打开,并保持数据库锁定,以阻止并发修改,从而保证数据库事务隔离级别和原子操作。这种数据库锁定会导致应用程序无法扩展并发用户的数量。每次HTTP Session对应一次Hibernate Session的模式会导致应用程序无法扩展并发用户的数量,因此不推荐使用。

Hibernate 处理长事务的三种方式:

  • 自动版本化: Hibernate能够自动进行乐观并发控制,如果在用户处理的过程中持久化实体发生并发修改,
    Hibernate能够自动检测到。
  • 脱管对象:如果采用每次用户请求对应一次Session的模式,那么,前面载入的实例在用户处理的过程中,始终与Session脱离,处于脱管状态。Hibernate允许把脱管对象重新关联到Session上,并且对修改进行持久化。在这种模式下,自动版本化被用来隔离并发修改。这种模式也被称为使用脱管对象的每次请求对应一个Hibernate Session。
  • 长生命周期Session: Session可以在数据库事务提交之后,断开和底层的JDBC连接。当新的客户端请求到来时,它又重新连接上底层的JDBC连接。这种模式被称为每个应用程序事务对应一个Session。因为应用程序事务是相当长(跨越多个用户请求)的,所以也被称为长生命周期Session。

在session 关闭之前,如果集合属性,,由于懒加载的原因,一定要装载所有的数据,不然你在后面使用的时候,会报异常。

上下文相关的Session

Hibernate 应用使用Session时都用到了HibermateUtil 工具类,因为该工具类可以保证将线程不安全的Session绑定限制在当前线程内,也就是实现一种“上下文相关的”Session。

Hibernate 管理上下文的三种策略
  • org.hibernate.context.JTASessionContext:根据JTA来跟踪和界定上下文相关Session,这和最早的仅支持JTA的方法是完全一样的。
  • org.hibernate.context.ThreadLocalSessionContext:通过当前正在执行的线程来跟踪和界定上下文相关Session,也就是和前面的HibernateUtil的Session维护模式相似。
  • org.hibernate.context.ManagedSessionContext:通过当前执行的线程来跟踪和界定上下文相关Session。但是程序需要使用这个类的静态方法将Session实例绑定、取消绑定,它并不会自动打开、fush 或者关闭任何Session。

如果使用ThreadLocalSessionContext 策略,Hibermate 的Session会随着getCurrentSession(方法自动打开,并随着事务提交自动关闭,非常方便。对于在容器中使用Hibermate 的场景而言,通常会采用第种方式,对于独立的Hibernate应用而言,通常会采用第二种方式。

我们通常在Hibernate 配置文件中,可以通过hibernate.current_session_context_class去配置该属性。

事件机制

Hibermate执行持久化过程中,应用程序无法参与其中。

通过事件框架,Hibermate 允许应用程序能响应特定的内部事件,从而允许实现某些通用的功能,或者对Hibernate功能进行扩展。

Hibernate的事件框架组成
  • 拦截器机制:对于特定动作拦截,回调应用中的特定动作。
  • 事件系统:重写Hibernate的事件监听器。
拦截器机制

通过实现Interceptor接口,可以从Session中回调应用程序的特定方法,这种回调机制可让应用程序在持久化对象被保存、更新、删除或加载之前,检查并修改其属性。

使用拦截器按如下步骤进行:

  1. 定义实现Interceptor接口的拦截器类。
  2. 通过Session启用拦截器,或者通过Configuration 启用全局拦截器。

在Hibernate 下有Interceptor 接口和EmptyInterceptor 类,EmptyInterceptor 就像是一个适配器类,因为Interceptor 要实现的方法比较多,没有必要全写,所以可以使用EmptyInterceptor 。

public class MyHibernateTestInterceptor extends EmptyInterceptor {
	private int count;
//注意这里实现的是Hibernate 下 的拦截器
	@Override
	public boolean onSave(Object entity, Serializable id, Object[] state,
			String[] propertyNames, Type[] types) {
		// TODO Auto-generated method stub
		count++;
		return super.onSave(entity, id, state, propertyNames, types);
	}
	@Override
	public void afterTransactionCompletion(Transaction tx) {
		// TODO Auto-generated method stub
		System.out.println(count);
	}
	

}

该拦截器实现简单,在save 实例的时候,统计save 次数。

拦截器种类
  • 打开一个带局部拦截器的Session,这个拦截器只是针对该session有效
		   Session ss = sf.withOptions().interceptor(new MyHibernateTestInterceptor()).openSession();//这里必须要这样写,用链式调用创建session,否则拦截器无效。而且这种,每次都要创建一个sessionfactory,用同一个也无效。
  • 通过Configuration的setInterceptor(Interceptor in)方法设置全局拦截器。
事件系统

事件系统可以替代拦截器

基本上,Session 接口的每个方法都有对应的事件,比如LoadEvent、 FlushEvent 等。当Session调用某个方法时,Hibernate Session会生成对应的事件,并激活对应的事件监听器。

使用事件系统按如下步骤进行:

  1. 实现自己的事件监听器类。
  2. 注册自定义事件监听器,代替系统默认的事件监听器。
public class MyHibernateTestListener extends DefaultSaveEventListener {
	@Override
	public void onSaveOrUpdate(SaveOrUpdateEvent arg0) {
		// TODO Auto-generated method stub
		System.out.println("在save 触发的事件");
		super.onSaveOrUpdate(arg0);
	}

}

注意:扩展用户自定义监听器时,别忘了在方法中调用父类的对应方法, 否则Hibernate 默认的持久化行为都会失效。 因为这些持久化行为本身就是通过事件来完成的。

注册事件监听器,(所有的事件监听器全在这注册):


    public class RegisterService implements Integrator {

	@Override
	public void disintegrate(SessionFactoryImplementor arg0,
			SessionFactoryServiceRegistry arg1) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void integrate(Metadata arg0, SessionFactoryImplementor arg1,
			SessionFactoryServiceRegistry arg2) {
		// TODO Auto-generated method stub
	    EventListenerRegistry eventListenerRegistry = 
	            arg2.getService(EventListenerRegistry.class);

	      eventListenerRegistry.getEventListenerGroup(EventType.SAVE)
	                     .appendListener(new MyHibernateTestListener());
	                     //EventType 事件枚举类型,包含所有的事件类型
	      
	}

}

在代码中应用注册事件类

BootstrapServiceRegistry bootstrapRegistry =
                 new BootstrapServiceRegistryBuilder()
                 .applyIntegrator(new RegisterService())
                 .build();
		StandardServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder(bootstrapRegistry)
        .configure().build();
		Metadata metadata = new MetadataSources( serviceRegistry ).getMetadataBuilder().build();
		SessionFactory sf = metadata.getSessionFactoryBuilder().build();

还有一种配置事件的方法,在Hibernate配置文件中:

  1. 拓展系统默认xx事件监听器。
  2. 在hibernate 配置文件中配置。
	    <listener  type="save" class="com.example.test.listener.MyHibernateTestListener"/>//这里type 自己可以查看hibernate 事件类型,实际上是保存在一个map 中,key 就是这里type ,value 就是hibernate自己事件类,而我们之间拓展默认的事件,一旦注册,就会使用我们默认的,所以上面注意的那句话,就是保证原来hibernate 不会丢了,知道不?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值