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的回调机制过程。通过对源码的分析对这个回调过程有了一定的理解,这个回调进行复杂的连表查询的时候会用到。上面就是小编的一些拙见,如果有错误请指出!

目录
相关文章
|
10月前
|
数据可视化 Java BI
将 Spring 微服务与 BI 工具集成:最佳实践
本文探讨了 Spring 微服务与商业智能(BI)工具集成的潜力与实践。随着微服务架构和数据分析需求的增长,Spring Boot 和 Spring Cloud 提供了构建可扩展、弹性服务的框架,而 BI 工具则增强了数据可视化与实时分析能力。文章介绍了 Spring 微服务的核心概念、BI 工具在企业中的作用,并深入分析了两者集成带来的优势,如实时数据处理、个性化报告、数据聚合与安全保障。同时,文中还总结了集成过程中的最佳实践,包括事件驱动架构、集中配置管理、数据安全控制、模块化设计与持续优化策略,旨在帮助企业构建高效、智能的数据驱动系统。
467 1
将 Spring 微服务与 BI 工具集成:最佳实践
|
10月前
|
SQL 数据可视化 关系型数据库
MCP与PolarDB集成技术分析:降低SQL门槛与简化数据可视化流程的机制解析
阿里云PolarDB与MCP协议融合,打造“自然语言即分析”的新范式。通过云原生数据库与标准化AI接口协同,实现零代码、分钟级从数据到可视化洞察,打破技术壁垒,提升分析效率99%,推动企业数据能力普惠化。
824 3
|
人工智能 JSON 安全
Spring Boot实现无感刷新Token机制
本文深入解析在Spring Boot项目中实现JWT无感刷新Token的机制,涵盖双Token策略、Refresh Token安全性及具体示例代码,帮助开发者提升用户体验与系统安全性。
1285 4
|
12月前
|
XML 人工智能 Java
Spring Boot集成Aviator实现参数校验
Aviator是一个高性能、轻量级的Java表达式求值引擎,适用于动态表达式计算。其特点包括支持多种运算符、函数调用、正则匹配、自动类型转换及嵌套变量访问,性能优异且依赖小。适用于规则引擎、公式计算和动态脚本控制等场景。本文介绍了如何结合Aviator与AOP实现参数校验,并附有代码示例和仓库链接。
714 0
|
安全 Java 数据库
第16课:Spring Boot中集成 Shiro
第16课:Spring Boot中集成 Shiro
1189 0
|
消息中间件 存储 Java
第15课: Spring Boot中集成ActiveMQ
第15课: Spring Boot中集成ActiveMQ
714 0
|
传感器 人工智能 算法
聚焦“以技术集成支撑单亩价值创造”与“增加值分配机制区块链存证确权”两大核心本质
“振兴链-技术集成科技小院”以技术集成与区块链为核心,推动农业现代化。通过多维度技术整合(如精准农业、物联网等),突破资源约束,最大化单亩产值;同时利用区块链确权存证,建立透明分配机制,解决传统农业中收益不均问题。技术赋能生产,制度重塑分配,实现效率与公平的平衡,助力乡村振兴与产业升级。典型场景显示,该模式可显著提升单亩价值并确保增值公平分配。
|
人工智能 Java 测试技术
Spring Boot 集成 JUnit 单元测试
本文介绍了在Spring Boot中使用JUnit 5进行单元测试的常用方法与技巧,包括添加依赖、编写测试类、使用@SpringBootTest参数、自动装配测试模块(如JSON、MVC、WebFlux、JDBC等),以及@MockBean和@SpyBean的应用。内容实用,适合Java开发者参考学习。
1344 0
|
安全 Java Apache
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 身份和权限认证
本文介绍了 Apache Shiro 的身份认证与权限认证机制。在身份认证部分,分析了 Shiro 的认证流程,包括应用程序调用 `Subject.login(token)` 方法、SecurityManager 接管认证以及通过 Realm 进行具体的安全验证。权限认证部分阐述了权限(permission)、角色(role)和用户(user)三者的关系,其中用户可拥有多个角色,角色则对应不同的权限组合,例如普通用户仅能查看或添加信息,而管理员可执行所有操作。
696 0