回调函数对于我们来说并不陌生,之前我们在学习js的时候经常用到回调函数,在java基础中也接触到了回调函数,在这篇博客中我们将介绍spring和hibernate集成后的回调函数的使用。
为什么使用回调函数?
我们都知道程序员在完成CRUD操作的时候,需要用到session来操作,但是在spirng和hibernate集成以后,session是有spring容器来产生并且管理,也就说如果我们在完成CRUD操作的时候,需要去spring容器中拿到session然后完成一系列的操作,但是spring并没有提供这样的API让我们在spring容器来获得session。那么我们拿不到这个session自然就不能完成CRUD操,那么到底是怎样的完成的,下面以save方法为例来说明。
当我们执行hibernateTemplate.save(classes)代码的时候,这时候会利用回调机制将save方法回到spring容器中,然后在有spring容器完成保存一系列的操作
Session session = sessionFactory.getCurrentSession(); Transaction transaction = session.beginTransaction(); Session.save(classes) session.close();
下面我们来跟踪一下源码:
上面我们在执行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);这样我们相当于调用了
上面对这个接口中方法的具体实现。这样就完成了整个回调机制。这个过程理解起来不是很好理解,但是回调机制是个很重要的知识,我们需要好好的理解一下。
其实整个回调机制就好比我们在一个黑箱子里面完成削苹果的动作,外面的没有削苹果的刀子,所以不能自己削苹果,这是外面的人需要做的事情就是,讲一个苹果传递给黑箱子并且告诉它要给苹果削皮。一会黑箱子就把削好皮的苹果送出来,具体黑箱子里面完成了什么样的动作,外面的人是不用关心的。其实这就是整个回调过程!
下面我们根据源码来写一个简单的例子模拟一下整个的回调过程!
先模拟一个接口:
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的回调机制过程。通过对源码的分析对这个回调过程有了一定的理解,这个回调进行复杂的连表查询的时候会用到。上面就是小编的一些拙见,如果有错误请指出!