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月前
|
传感器 人工智能 算法
聚焦“以技术集成支撑单亩价值创造”与“增加值分配机制区块链存证确权”两大核心本质
“振兴链-技术集成科技小院”以技术集成与区块链为核心,推动农业现代化。通过多维度技术整合(如精准农业、物联网等),突破资源约束,最大化单亩产值;同时利用区块链确权存证,建立透明分配机制,解决传统农业中收益不均问题。技术赋能生产,制度重塑分配,实现效率与公平的平衡,助力乡村振兴与产业升级。典型场景显示,该模式可显著提升单亩价值并确保增值公平分配。
|
3月前
|
安全 Java Apache
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 身份和权限认证
本文介绍了 Apache Shiro 的身份认证与权限认证机制。在身份认证部分,分析了 Shiro 的认证流程,包括应用程序调用 `Subject.login(token)` 方法、SecurityManager 接管认证以及通过 Realm 进行具体的安全验证。权限认证部分阐述了权限(permission)、角色(role)和用户(user)三者的关系,其中用户可拥有多个角色,角色则对应不同的权限组合,例如普通用户仅能查看或添加信息,而管理员可执行所有操作。
140 0
|
3月前
|
安全 Java 数据安全/隐私保护
微服务——SpringBoot使用归纳——Spring Boot中集成 Shiro——Shiro 三大核心组件
本课程介绍如何在Spring Boot中集成Shiro框架,主要讲解Shiro的认证与授权功能。Shiro是一个简单易用的Java安全框架,用于认证、授权、加密和会话管理等。其核心组件包括Subject(认证主体)、SecurityManager(安全管理员)和Realm(域)。Subject负责身份认证,包含Principals(身份)和Credentials(凭证);SecurityManager是架构核心,协调内部组件运作;Realm则是连接Shiro与应用数据的桥梁,用于访问用户账户及权限信息。通过学习,您将掌握Shiro的基本原理及其在项目中的应用。
135 0
|
3月前
|
前端开发 Java 数据库
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——Thymeleaf 介绍
本课介绍Spring Boot集成Thymeleaf模板引擎。Thymeleaf是一款现代服务器端Java模板引擎,支持Web和独立环境,可实现自然模板开发,便于团队协作。与传统JSP不同,Thymeleaf模板可以直接在浏览器中打开,方便前端人员查看静态原型。通过在HTML标签中添加扩展属性(如`th:text`),Thymeleaf能够在服务运行时动态替换内容,展示数据库中的数据,同时兼容静态页面展示,为开发带来灵活性和便利性。
111 0
|
3月前
|
NoSQL Java 关系型数据库
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
本文介绍在 Spring Boot 中集成 Redis 的方法。Redis 是一种支持多种数据结构的非关系型数据库(NoSQL),具备高并发、高性能和灵活扩展的特点,适用于缓存、实时数据分析等场景。其数据以键值对形式存储,支持字符串、哈希、列表、集合等类型。通过将 Redis 与 Mysql 集群结合使用,可实现数据同步,提升系统稳定性。例如,在网站架构中优先从 Redis 获取数据,故障时回退至 Mysql,确保服务不中断。
139 0
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 介绍
|
3月前
|
JSON Java API
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的使用
本文详细介绍了Swagger2的使用方法,包括在Spring Boot项目中的配置与应用。重点讲解了Swagger2中常用的注解,如实体类上的`@ApiModel`和`@ApiModelProperty`,Controller类上的`@Api`、`@ApiOperation`以及参数上的`@ApiParam`等。通过示例代码展示了如何为实体类和接口添加注解,并在页面上生成在线接口文档,实现接口测试。最后总结了Swagger的优势及其在项目开发中的重要性,提供了课程源代码下载链接供学习参考。
111 0
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的使用
|
3月前
|
缓存 Java API
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
本文介绍了在Spring Boot中配置Swagger2的方法。通过创建一个配置类,添加`@Configuration`和`@EnableSwagger2`注解,使用Docket对象定义API文档的详细信息,包括标题、描述、版本和包路径等。配置完成后,访问`localhost:8080/swagger-ui.html`即可查看接口文档。文中还提示了可能因浏览器缓存导致的问题及解决方法。
127 0
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的配置
|
3月前
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于 xml 的整合
本教程介绍了基于XML的MyBatis整合方式。首先在`application.yml`中配置XML路径,如`classpath:mapper/*.xml`,然后创建`UserMapper.xml`文件定义SQL映射,包括`resultMap`和查询语句。通过设置`namespace`关联Mapper接口,实现如`getUserByName`的方法。Controller层调用Service完成测试,访问`/getUserByName/{name}`即可返回用户信息。为简化Mapper扫描,推荐在Spring Boot启动类用`@MapperScan`注解指定包路径避免逐个添加`@Mapper`
113 0
|
3月前
|
Java Maven 微服务
微服务——SpringBoot使用归纳——Spring Boot集成 Swagger2 展现在线接口文档——Swagger2 的 maven 依赖
在项目中使用Swagger2工具时,需导入Maven依赖。尽管官方最高版本为2.8.0,但其展示效果不够理想且稳定性欠佳。实际开发中常用2.2.2版本,因其稳定且界面友好。以下是围绕2.2.2版本的Maven依赖配置,包括`springfox-swagger2`和`springfox-swagger-ui`两个模块。
77 0
|
2月前
|
Java 开发工具 Spring
【Azure Application Insights】为Spring Boot应用集成Application Insight SDK
本文以Java Spring Boot项目为例,详细说明如何集成Azure Application Insights SDK以收集和展示日志。内容包括三步配置:1) 在`pom.xml`中添加依赖项`applicationinsights-runtime-attach`和`applicationinsights-core`;2) 在main函数中调用`ApplicationInsights.attach()`;3) 配置`applicationinsights.json`文件。同时提供问题排查建议及自定义日志方法示例,帮助用户顺利集成并使用Application Insights服务。