spring对hibernate的集成中的回调(CallBack)机制

简介: spring对hibernate的集成中的回调(CallBack)机制

回调函数对于我们来说并不陌生,之前我们在学习js的时候经常用到回调函数,在java基础中也接触到了回调函数,在这篇博客中我们将介绍spring和hibernate集成后的回调函数的使用。

 

为什么使用回调函数?

20170202191539482.png

我们都知道程序员在完成CRUD操作的时候,需要用到session来操作,但是在spirng和hibernate集成以后,session是有spring容器来产生并且管理,也就说如果我们在完成CRUD操作的时候,需要去spring容器中拿到session然后完成一系列的操作,但是spring并没有提供这样的API让我们在spring容器来获得session。那么我们拿不到这个session自然就不能完成CRUD操,那么到底是怎样的完成的,下面以save方法为例来说明。



20170202192054968.png


当我们执行hibernateTemplate.save(classes)代码的时候,这时候会利用回调机制将save方法回到spring容器中,然后在有spring容器完成保存一系列的操作


Session session = sessionFactory.getCurrentSession();
Transaction transaction = session.beginTransaction();
Session.save(classes)
session.close();


下面我们来跟踪一下源码:


20170202192601111.png

上面我们在执行save方法以后,跟踪源码的一个流程图,我们来简单的分析一下:

 

首先会将classes实参传递到entity这个形参上面,然后在save方法里面调用了executeWithNativeSession()而这个方法接收了一大坨参数,上面圈出来的地方,而这个参数的类型是一个接口HibernateCallback类型,下面给出HibernateCallback的代码,这是一个接口:


public interface HibernateCallback {
  Object doInHibernate(Session session) throws HibernateException, SQLException;
}

所以对于上面我们调用 executeWithNativeSession这个方法的时候,传递的参数是一个匿名类的写法,也就说我们不用单独第一个类文件来实现这个接口然后在调用,相当于在内存中直接产生了一个接口的实现并引用这个内存对象。动态的代码。

而这个接口中doInHibernate()就是spring 容器能做的事情,它能什么事情都能干,但是它却不知道干什么,这就需要我们在容器外面告诉容器需要干什么,也就是具体的实现是什么样的,spring容器就会做什么!


在上面的源代码中其实真正做事情的是doExecute这个方法,下面来看一下这个方法的完整的代码

/**
   * Execute the action specified by the given action object within a Session.
   * @param action callback object that specifies the Hibernate action
   * @param enforceNewSession whether to enforce a new Session for this template
   * even if there is a pre-bound transactional Session
   * @param enforceNativeSession whether to enforce exposure of the native
   * Hibernate Session to callback code
   * @return a result object returned by the action, or <code>null</code>
   * @throws org.springframework.dao.DataAccessException in case of Hibernate errors
   */
  protected Object doExecute(HibernateCallback action, boolean enforceNewSession, boolean enforceNativeSession)
      throws DataAccessException {
    Assert.notNull(action, "Callback object must not be null");
    Session session = (enforceNewSession ?
        SessionFactoryUtils.getNewSession(getSessionFactory(), getEntityInterceptor()) : getSession());
    boolean existingTransaction = (!enforceNewSession &&
        (!isAllowCreate() || SessionFactoryUtils.isSessionTransactional(session, getSessionFactory())));
    if (existingTransaction) {
      logger.debug("Found thread-bound Session for HibernateTemplate");
    }
    FlushMode previousFlushMode = null;
    try {
      previousFlushMode = applyFlushMode(session, existingTransaction);
      enableFilters(session);
      Session sessionToExpose =
          (enforceNativeSession || isExposeNativeSession() ? session : createSessionProxy(session));
      Object result = action.doInHibernate(sessionToExpose);
      flushIfNecessary(session, existingTransaction);
      return result;
    }
    catch (HibernateException ex) {
      throw convertHibernateAccessException(ex);
    }
    catch (SQLException ex) {
      throw convertJdbcAccessException(ex);
    }
    catch (RuntimeException ex) {
      // Callback code threw application exception...
      throw ex;
    }
    finally {
      if (existingTransaction) {
        logger.debug("Not closing pre-bound Hibernate Session after HibernateTemplate");
        disableFilters(session);
        if (previousFlushMode != null) {
          session.setFlushMode(previousFlushMode);
        }
      }
      else {
        // Never use deferred close for an explicitly new Session.
        if (isAlwaysUseNewSession()) {
          SessionFactoryUtils.closeSession(session);
        }
        else {
          SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
        }
      }
    }
  }


这个方法里面核心的代码就是:Object result = action.doInHibernate(sessionToExpose);这样我们相当于调用了

20170202195701156.png


上面对这个接口中方法的具体实现。这样就完成了整个回调机制。这个过程理解起来不是很好理解,但是回调机制是个很重要的知识,我们需要好好的理解一下。

 

其实整个回调机制就好比我们在一个黑箱子里面完成削苹果的动作,外面的没有削苹果的刀子,所以不能自己削苹果,这是外面的人需要做的事情就是,讲一个苹果传递给黑箱子并且告诉它要给苹果削皮。一会黑箱子就把削好皮的苹果送出来,具体黑箱子里面完成了什么样的动作,外面的人是不用关心的。其实这就是整个回调过程!


下面我们根据源码来写一个简单的例子模拟一下整个的回调过程!

先模拟一个接口:

package com.itheima11.spring.hibernate.callback;
import org.hibernate.Session;
public interface HibernateCallBack {
  //我能做的事情
  public Object doInHibernate(Session session);
}

在模拟一个模板类

package com.itheima11.spring.hibernate.callback;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.itheima11.spring.hibernate.domain.Classes;
public class Itheima11Template {
  private SessionFactory sessionFactory;
  public void setSessionFactory(SessionFactory sessionFactory) {
    this.sessionFactory = sessionFactory;
  }
  public void doExecute(HibernateCallBack action){
    Session session = this.sessionFactory.openSession();
    Transaction transaction = session.beginTransaction();
    action.doInHibernate(session);
    transaction.commit();
    session.close();
  }
}

然后编写DAO层代码

package com.itheima11.spring.hibernate.callback;
import org.hibernate.Session;
import com.itheima11.spring.hibernate.domain.Classes;
public class Itheima11ClassesDao extends Itheima11Template{
  public void save(){
    this.doExecute(new HibernateCallBack() {
      public Object doInHibernate(Session session) {
        Classes classes = new Classes();
        classes.setName("asfdasd");
        session.save(classes);
        return null;
      }
    });
  }
}

配置文件编写

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx 
           http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
  <!-- 引入prperties配置文件 -->
  <bean
    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
      <value>classpath:jdbc.properties</value>
    </property>
  </bean>
  <bean id="dataSource" destroy-method="close"
    class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
  </bean>
  <!-- 引入sessionFactory LocalSessionFactoryBean 既满足了hibernate的特点,也满足了spring容器的特点 -->
  <!-- <bean id="sessionFactory"
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="configLocation">
      <value>classpath:hibernate.cfg.xml</value>
    </property>
  </bean> -->
  <bean id="sessionFactory2"
    class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="mappingDirectoryLocations">
      <list>
        <!-- 
          spring容器会去该包及子包下搜索所有的映射文件
         -->
        <value>com/itheima11/spring/hibernate/domain</value>
      </list>
    </property>
    <property name="hibernateProperties">
      <props>
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</prop>
        <prop key="hibernate.show_sql">true</prop>
        <prop key="hibernate.hbm2ddl.auto">update</prop>
      </props>
    </property>
  </bean>
  <!-- 
    引入dao和service层
   -->
   <bean id="itheima11Template" class="com.itheima11.spring.hibernate.callback.Itheima11Template">
    <property name="sessionFactory">
      <ref bean="sessionFactory2"/>
    </property>
  </bean>
  <bean id="itheima11ClassesDao" 
    class="com.itheima11.spring.hibernate.callback.Itheima11ClassesDao"
    parent="itheima11Template"></bean>
</beans>

jdbc.properties文件编写

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://localhost\:3306/itheima11_hibernate
jdbc.username=sa
jdbc.password=123456


上面就是我们模拟spring集成hibernate的回调机制过程。通过对源码的分析对这个回调过程有了一定的理解,这个回调进行复杂的连表查询的时候会用到。上面就是小编的一些拙见,如果有错误请指出!

目录
相关文章
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
76 2
|
2月前
|
Java Maven Docker
gitlab-ci 集成 k3s 部署spring boot 应用
gitlab-ci 集成 k3s 部署spring boot 应用
|
8天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
52 14
|
4月前
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
定时任务在企业应用中至关重要,常用于异步数据处理、自动化运维等场景。在单体应用中,利用Java的`java.util.Timer`或Spring的`@Scheduled`即可轻松实现。然而,进入微服务架构后,任务可能因多节点并发执行而重复。Spring Cloud Alibaba为此发布了Scheduling模块,提供轻量级、高可用的分布式定时任务解决方案,支持防重复执行、分片运行等功能,并可通过`spring-cloud-starter-alibaba-schedulerx`快速集成。用户可选择基于阿里云SchedulerX托管服务或采用本地开源方案(如ShedLock)
144 1
|
23天前
|
XML Java API
Spring Boot集成MinIO
本文介绍了如何在Spring Boot项目中集成MinIO,一个高性能的分布式对象存储服务。主要步骤包括:引入MinIO依赖、配置MinIO属性、创建MinIO配置类和服务类、使用服务类实现文件上传和下载功能,以及运行应用进行测试。通过这些步骤,可以轻松地在项目中使用MinIO的对象存储功能。
|
24天前
|
缓存 Java 数据库连接
深入探讨:Spring与MyBatis中的连接池与缓存机制
Spring 与 MyBatis 提供了强大的连接池和缓存机制,通过合理配置和使用这些机制,可以显著提升应用的性能和可扩展性。连接池通过复用数据库连接减少了连接创建和销毁的开销,而 MyBatis 的一级缓存和二级缓存则通过缓存查询结果减少了数据库访问次数。在实际应用中,结合具体的业务需求和系统架构,优化连接池和缓存的配置,是提升系统性能的重要手段。
38 4
|
25天前
|
消息中间件 Java Kafka
什么是Apache Kafka?如何将其与Spring Boot集成?
什么是Apache Kafka?如何将其与Spring Boot集成?
62 5
|
29天前
|
Java 开发者 Spring
深入解析:Spring AOP的底层实现机制
在现代软件开发中,Spring框架的AOP(面向切面编程)功能因其能够有效分离横切关注点(如日志记录、事务管理等)而备受青睐。本文将深入探讨Spring AOP的底层原理,揭示其如何通过动态代理技术实现方法的增强。
58 8
|
27天前
|
消息中间件 Java Kafka
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
Spring Boot 与 Apache Kafka 集成详解:构建高效消息驱动应用
42 1
|
28天前
|
消息中间件 监控 Java
您是否已集成 Spring Boot 与 ActiveMQ?
您是否已集成 Spring Boot 与 ActiveMQ?
52 0