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

目录
相关文章
|
SQL 数据可视化 关系型数据库
MCP与PolarDB集成技术分析:降低SQL门槛与简化数据可视化流程的机制解析
阿里云PolarDB与MCP协议融合,打造“自然语言即分析”的新范式。通过云原生数据库与标准化AI接口协同,实现零代码、分钟级从数据到可视化洞察,打破技术壁垒,提升分析效率99%,推动企业数据能力普惠化。
948 3
|
人工智能 JSON 安全
Spring Boot实现无感刷新Token机制
本文深入解析在Spring Boot项目中实现JWT无感刷新Token的机制,涵盖双Token策略、Refresh Token安全性及具体示例代码,帮助开发者提升用户体验与系统安全性。
1404 4
|
传感器 人工智能 算法
聚焦“以技术集成支撑单亩价值创造”与“增加值分配机制区块链存证确权”两大核心本质
“振兴链-技术集成科技小院”以技术集成与区块链为核心,推动农业现代化。通过多维度技术整合(如精准农业、物联网等),突破资源约束,最大化单亩产值;同时利用区块链确权存证,建立透明分配机制,解决传统农业中收益不均问题。技术赋能生产,制度重塑分配,实现效率与公平的平衡,助力乡村振兴与产业升级。典型场景显示,该模式可显著提升单亩价值并确保增值公平分配。
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
612 2
|
Java Maven Docker
gitlab-ci 集成 k3s 部署spring boot 应用
gitlab-ci 集成 k3s 部署spring boot 应用
|
Java 数据库连接 API
Java 8 + 特性及 Spring Boot 与 Hibernate 等最新技术的实操内容详解
本内容涵盖Java 8+核心语法、Spring Boot与Hibernate实操,按考试考点分类整理,含技术详解与代码示例,助力掌握最新Java技术与应用。
409 2
|
JSON 前端开发 Java
Spring MVC 核心组件与请求处理机制详解
本文解析了 Spring MVC 的核心组件及请求流程,核心组件包括 DispatcherServlet(中央调度)、HandlerMapping(URL 匹配处理器)、HandlerAdapter(执行处理器)、Handler(业务方法)、ViewResolver(视图解析),其中仅 Handler 需开发者实现。 详细描述了请求执行的 7 步流程:请求到达 DispatcherServlet 后,经映射器、适配器找到并执行处理器,再通过视图解析器渲染视图(前后端分离下视图解析可省略)。 介绍了拦截器的使用(实现 HandlerInterceptor 接口 + 配置类)及与过滤器的区别
1335 0
|
XML Java Maven
Spring 手动实现Spring底层机制
Spring 第六节 手动实现Spring底层机制 万字详解!
647 32
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
1361 8
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
2613 15