目录搜索
前言简介概览使用场景Spring 2.0和 2.5的新特性简介控制反转(IoC)容器新的bean作用域更简单的XML配置可扩展的XML编写Annotation(注解)驱动配置在classpath中自动搜索组件面向切面编程(AOP)更加简单的AOP XML配置对@AspectJ 切面的支持对bean命名pointcut( bean name pointcut element)的支持对AspectJ装载时织入(AspectJ load-time weaving)的支持中间层在XML里更为简单的声明性事务配置对Websphere 事务管理的完整支持JPA异步的JMSJDBCWeb层Spring MVC合理的默认值Portlet 框架基于Annotation的控制器Spring MVC的表单标签库对Tiles 2 支持对JSF 1.2支持JAX-WS支持其他动态语言支持增强的测试支持JMX 支持将Spring 应用程序上下文部署为JCA adapter计划任务对Java 5 (Tiger) 支持移植到Spring 2.5改变支持的JDK版本Spring 2.5的Jar打包XML配置Deprecated(淘汰)的类和方法Apache OJBiBATISHibernateJDOUrlFilenameViewController更新的样例应用改进的文档核心技术IoC(控制反转)容器简介基本原理 - 容器和bean容器配置元数据实例化容器XML配置元数据的结构多种beanbean的命名bean的别名实例化bean用构造器来实例化使用静态工厂方法实例化使用实例工厂方法实例化使用容器依赖注入依赖构造器注入构造器参数解析构造器参数类型匹配构造参数索引Setter注入一些例子依赖配置详解直接变量(基本类型、Strings类型等。)idref元素引用其它的bean(协作者)内部bean集合集合的合并强类型集合(仅适用于Java5+)NullsXML配置文件的简写及其他XML-based configuration metadata shortcuts使用p名称空间配置属性组合属性名称使用depends-on延迟初始化bean自动装配(autowire)协作者将bean排除在自动装配之外依赖检查方法注入Lookup方法注入自定义方法的替代方案Bean的作用域Singleton作用域Prototype作用域Singleton beans和prototype-bean的依赖其他作用域初始化web配置Request作用域Session作用域global session作用域作用域bean与依赖选择创建代理的类型自定义作用域创建自定义作用域使用自定义作用域定制bean特性生命周期回调初始化回调析构回调缺省的初始化和析构方法组合生命周期机制在非web应用中优雅地关闭Spring IoC容器了解自己BeanFactoryAwareBeanNameAwarebean定义的继承容器扩展点用BeanPostProcessor定制bean使用BeanPostProcessor的Hello World示例RequiredAnnotationBeanPostProcessor示例用BeanFactoryPostProcessor定制配置元数据PropertyPlaceholderConfigurer示例PropertyOverrideConfigurer示例使用FactoryBean定制实例化逻辑The ApplicationContextBeanFactory 还是 ApplicationContext?利用MessageSource实现国际化事件底层资源的访问ApplicationContext在WEB应用中的实例化粘合代码和可怕的singleton以J2EE RAR文件的形式部署Spring ApplicationContext基于注解(Annotation-based)的配置@Autowired基于注解的自动连接微调CustomAutowireConfigurer@Resource@PostConstruct 与 @PreDestroy对受管组件的Classpath扫描@Component和更多典型化注解自动检测组件使用过滤器自定义扫描自动检测组件的命名为自动检测的组件提供一个作用域用注解提供限定符元数据注册一个LoadTimeWeaver资源简介Resource接口内置 Resource 实现UrlResourceClassPathResourceFileSystemResourceServletContextResourceInputStreamResourceByteArrayResourceResourceLoader接口ResourceLoaderAware 接口把Resource作为属性来配置Application context 和Resource 路径构造application context创建 ClassPathXmlApplicationContext 实例 - 简介Application context构造器中资源路径的通配符Ant风格的pattern潜在的可移植性classpath*: 前缀其他关于通配符的说明FileSystemResource 说明校验,数据绑定,BeanWrapper,与属性编辑器简介使用Spring的Validator接口进行校验从错误代码到错误信息Bean处理和BeanWrapper设置和获取属性值以及嵌套属性内建的PropertyEditor实现注册用户自定义的PropertyEditor使用PropertyEditorRegistrars使用Spring进行面向切面编程(AOP)简介AOP概念Spring AOP的功能和目标AOP代理@AspectJ支持启用@AspectJ支持声明一个切面声明一个切入点(pointcut)切入点指示符(PCD)的支持组合切入点表达式共享通用切入点定义示例声明通知前置通知后置通知(After returning advice)异常通知(After throwing advice)最终通知(After (finally) advice)环绕通知通知参数(Advice parameters)访问当前的连接点传递参数给通知确定参数名处理参数通知顺序引入(Introduction)切面实例化模型例子基于Schema的AOP支持声明一个切面声明一个切入点声明通知前置通知后置通知异常通知最终通知环绕通知通知参数通知顺序引入切面实例化模型Advisor例子AOP声明风格的选择Spring AOP还是完全用AspectJ?Spring AOP中使用@AspectJ还是XML?混合切面类型代理机制理解AOP代理以编程方式创建@AspectJ代理在Spring应用中使用AspectJ在Spring中使用AspectJ进行domain object的依赖注入@Configurable对象的单元测试Working with multiple application contextsSpring中其他的AspectJ切面使用Spring IoC来配置AspectJ的切面在Spring应用中使用AspectJ加载时织入(LTW)第一个例子切面'META-INF/aop.xml'相关类库(JARS)Spring配置特定环境的配置通用Java应用TomcatWebLogic更多资源Spring AOP APIs简介Spring中的切入点API概念切入点运算AspectJ切入点表达式便利的切入点实现静态切入点正则表达式切入点属性驱动的切入点动态切入点控制流切入点切入点的超类自定义切入点Spring的通知API通知的生命周期Spring里的通知类型拦截环绕通知前置通知异常通知后置通知引入通知Spring里的Advisor API使用ProxyFactoryBean创建AOP代理基础JavaBean属性基于JDK和CGLIB的代理对接口进行代理对类进行代理使用“全局”通知器简化代理定义使用ProxyFactory通过编程创建AOP代理操作被通知对象使用“自动代理(autoproxy)”功能自动代理bean定义BeanNameAutoProxyCreatorDefaultAdvisorAutoProxyCreatorAbstractAdvisorAutoProxyCreator使用元数据驱动的自动代理使用TargetSource热交换目标源池化目标源原型目标源ThreadLocal目标源定义新的Advice类型更多资源测试简介单元测试Mock对象JNDIServlet APIPortlet API单元测试支持类通用工具类Spring MVC集成测试概览使用哪个支持框架通用目标上下文管理及缓存测试fixtures依赖注入事务管理集成测试支持类JDBC测试支持常用注解JUnit 3.8遗留支持上下文管理及缓存测试fixture依赖注入字段级别(Field Level)注入事务管理JUnit 3.8 遗留支持类Java 5+ 专有支持使用注解的事务相关测试JPA支持类Spring TestContext Framework主要的抽象上下文管理和缓存测试fixture的依赖注入事务管理TestContext支持类JUnit 3.8支持类JUnit 4.4支持类定制JUnit 4.4运行器TestNG支持类TestContext框架注解支持PetClinic示例更多资源中间层数据访问事务管理简介动机关键抽象使用资源同步的事务高层次方案低层次方案TransactionAwareDataSourceProxy声明式事务管理理解Spring的声明式事务管理实现第一个例子回滚为不同的bean配置不同的事务语义<tx:advice/> 有关的设置使用 @Transactional@Transactional 有关的设置事务传播requiredRequiresNewNested通知事务操作结合AspectJ使用 @Transactional编程式事务管理使用TransactionTemplate指定事务设置使用PlatformTransactionManager选择编程式事务管理还是声明式事务管理与特定应用服务器集成IBM WebSphereBEA WebLogicOracle OC4J常见问题的解决方法对一个特定的 DataSource 使用了错误的事务管理器更多的资源DAO支持简介一致的异常层次一致的DAO支持抽象类使用JDBC进行数据访问简介选择一种工作模式Spring JDBC包结构利用JDBC核心类控制JDBC的基本操作和错误处理JdbcTemplate类一些示例查询(SELECT)更新(INSERT/UPDATE/DELETE)其他操作JdbcTemplate 的最佳实践NamedParameterJdbcTemplate类SimpleJdbcTemplate类DataSource接口SQLExceptionTranslator接口执行SQL语句执行查询更新数据库获取自动生成的主键控制数据库连接DataSourceUtils类SmartDataSource接口AbstractDataSource类SingleConnectionDataSource类DriverManagerDataSource类TransactionAwareDataSourceProxy类DataSourceTransactionManager类NativeJdbcExtractorJDBC批量操作使用JdbcTemplate进行批量操作使用SimpleJdbcTemplate进行批量操作通过使用SimpleJdbc类简化JDBC操作使用SimpleJdbcInsert插入数据使用SimpleJdbcInsert来获取自动生成的主键指定SimpleJdbcInsert所使用的字段使用SqlParameterSource提供参数值使用SimpleJdbcCall调用存储过程声明SimpleJdbcCall使用的参数如何定义SqlParameters使用SimpleJdbcCall调用内置函数使用SimpleJdbcCall返回的ResultSet/REF Cursor用Java对象来表达JDBC操作SqlQuery类MappingSqlQuery类SqlUpdate类StoredProcedure类SqlFunction类参数和数据处理的基本原则为参数设置SQL类型信息处理BLOB 和 CLOB对象在IN语句中传入一组参数值处理复杂类型的存储过程调用使用ORM工具进行数据访问简介Hibernate资源管理在Spring容器中创建 SessionFactoryThe HibernateTemplate不使用回调的基于Spring的DAO实现基于Hibernate3的原生API实现DAO编程式的事务划分声明式的事务划分事务管理策略容器资源 vs 本地资源在应用服务器中使用Hibernate的注意事项JDO建立PersistenceManagerFactoryJdoTemplate和JdoDaoSupport基于原生的JDO API实现DAO事务管理JdoDialectOracle TopLinkSessionFactory 抽象层TopLinkTemplate and TopLinkDaoSupport基于原生的TopLink API的DAO实现事务管理iBATIS SQL Maps创建SqlMapClient使用 SqlMapClientTemplate 和 SqlMapClientDaoSupport基于原生的iBATIS API的DAO实现JPA在Spring环境中建立JPALocalEntityManagerFactoryBean从JNDI中获取 EntityManagerFactoryLocalContainerEntityManagerFactoryBeanTomcat(5.0以上)加载时的织入配置使用VM代理的全局加载时织入上下文范围内的加载时织入配置处理多持久化单元JpaTemplate 和 JpaDaoSupport基于原生的JPA实现DAO异常转化事务管理JpaDialectThe WebWeb MVC framework Web框架概述与其他MVC实现框架的集成Spring Web MVC框架的特点DispatcherServlet控制器AbstractController 和 WebContentGenerator其它的简单控制器MultiActionController命令控制器处理器映射(handler mapping)BeanNameUrlHandlerMappingSimpleUrlHandlerMapping拦截器(HandlerInterceptor)视图与视图解析视图解析器(ViewResolver)视图解析链重定向(Rediret)到另一个视图RedirectViewredirect:前缀forward:前缀本地化解析器AcceptHeaderLocaleResolverCookieLocaleResolverSessionLocaleResolverLocaleChangeInterceptor使用主题简介如何定义主题主题解析器Spring对分段文件上传(multipart file upload)的支持介绍使用MultipartResolver在表单中处理分段文件上传使用Spring的表单标签库配置form标签input标签checkbox标签checkboxes标签radiobutton标签radiobuttons标签password标签select标签option标签options标签textarea标签hidden标签errors标签处理异常惯例优先原则(convention over configuration)对控制器的支持:ControllerClassNameHandlerMapping对模型的支持:ModelMap(ModelAndView)对视图的支持:RequestToViewNameTranslator基于注解的控制器配置建立dispatcher实现注解支持使用@Controller定义一个控制器使用@RequestMapping映射请求使用@RequestParam绑定请求参数到方法参数使用@ModelAttribute提供一个从模型到数据的链接使用@SessionAttributes指定存储在会话中的属性自定义WebDataBinder初始化使用@InitBinder自定义数据绑定配置一个定制的WebBindingInitializer更多资源集成视图技术简介JSP和JSTL视图解析器'Plain-old' JSPs versus JSTL 'Plain-old' JSP与JSTL帮助简化开发的额外的标签Tiles需要的资源如何集成TilesUrlBasedViewResolver类ResourceBundleViewResolver类SimpleSpringPreparerFactory 和 SpringBeanPreparerFactoryVelocity和FreeMarker需要的资源Context 配置创建模板高级配置velocity.propertiesFreeMarker绑定支持和表单处理用于绑定的宏简单绑定表单输入生成宏输入域选择输入域重载HTML转码行为并使你的标签符合XHTMLXSLT写在段首Bean 定义标准MVC控制器代码把模型数据转化为XML定义视图属性文档转换小结文档视图(PDF/Excel)简介配置和安装文档视图定义Controller 代码Excel视图子类PDF视图子类JasperReports依赖的资源配置配置ViewResolver配置View关于报表文件使用 JasperReportsMultiFormatView构造ModelAndView使用子报表配置子报表文件配置子报表数据源配置Exporter的参数集成其它Web框架简介通用配置JavaServer FacesDelegatingVariableResolverFacesContextUtilsStrutsContextLoaderPluginDelegatingRequestProcessorDelegatingActionProxyActionSupport ClassesTapestry注入 Spring 托管的 beans将 Spring Beans 注入到 Tapestry 页面中组件定义文件添加抽象访问方法将 Spring Beans 注入到 Tapestry 页面中 - Tapestry 4.0+ 风格WebWork更多资源Portlet MVC框架介绍控制器 - MVC中的C视图 - MVC中的VWeb作用范围的BeanDispatcherPortletViewRendererServlet控制器AbstractController 和 PortletContentGenerator其它简单的控制器Command控制器PortletWrappingController处理器映射PortletModeHandlerMappingParameterHandlerMappingPortletModeParameterHandlerMapping增加 HandlerInterceptorsHandlerInterceptorAdapterParameterMappingInterceptor视图和它们的解析Multipart文件上传支持使用 PortletMultipartResolver处理表单里的文件上传异常处理Portlet应用的部署整合使用Spring进行远程访问与Web服务简介使用RMI暴露服务使用RmiServiceExporter暴露服务在客户端链接服务使用Hessian或者Burlap通过HTTP远程调用服务为Hessian和co.配置DispatcherServlet使用HessianServiceExporter暴露你的bean在客户端连接服务使用Burlap对通过Hessian或Burlap暴露的服务使用HTTP Basic认证使用HTTP调用器暴露服务Exposing the service object在客户端连接服务Web Services使用JAX-RPC暴露基于servlet的web服务使用JAX-RPC访问web服务注册JAX-RPC Bean映射注册自己的JAX-RPC 处理器使用JAX-WS暴露基于servlet的web服务使用JAX-WS暴露单独web服务使用Spring支持的JAX-WS RI来暴露服务使用JAX-WS访问web服务使用XFire来暴露Web服务JMS服务端配置客户端配置对远程接口不提供自动探测实现在选择这些技术时的一些考虑Enterprise Java Beans (EJB) 集成简介访问EJB概念访问本地的无状态Session Bean(SLSB)访问远程SLSBAccessing EJB 2.x SLSBs versus EJB 3 SLSBs使用Spring提供的辅助类实现EJB组件EJB 2.x base classesEJB 3 注入拦截JMS (Java Message Service)简介使用Spring JMSJmsTemplate连接工厂目的地管理消息侦听容器SimpleMessageListenerContainerDefaultMessageListenerContainerServerSessionMessageListenerContainer事务管理发送消息使用消息转换器SessionCallback 和 ProducerCallback接收消息同步接收异步接收 - 消息驱动的POJOSessionAwareMessageListener接口MessageListenerAdapter事务中的消息处理JCA消息端点的支持JMS命名空间支持JMX介绍将Bean暴露为JMX创建MBeanServer重用原有的MBeanServer延迟初始化的MBeanMBean的自动注册控制注册行为控制Bean的管理接口MBeanInfoAssembler接口使用源码级元数据使用JDK 5.0的注解源代码级的元数据类型AutodetectCapableMBeanInfoAssembler接口用Java接口定义管理接口使用MethodNameBasedMBeanInfoAssembler控制Bean的ObjectName从Properties读取Properties使用MetadataNamingStrategy<context:mbean-export/>元素JSR-160连接器服务器端连接器客户端连接器基于Burlap/Hessian/SOAP的JMX通过代理访问MBean通知为通知注册监听器发布通知更多资源JCA CCI简介配置CCI连接器配置在Spring中配置ConnectionFactory配置CCI连接使用一个 CCI 单连接使用Spring的 CCI访问支持记录转换CciTemplate类DAO支持自动输出记录生成总结直接使用一个CCI Connection接口和Interaction接口CciTemplate 使用示例建模CCI访问为操作对象MappingRecordOperationMappingCommAreaOperation自动生成输出记录总结MappingRecordOperation 使用示例MappingCommAreaOperation 使用示例事务Spring邮件抽象层简介使用Spring邮件抽象MailSender 和 SimpleMailMessage 的基本用法使用 JavaMailSender 和 MimeMessagePreparator使用MimeMessageHelper发送附件和嵌入式资源(inline resources)附件内嵌资源使用模板来创建邮件内容一个基于Velocity的示例Spring中的定时调度(Scheduling)和线程池(Thread Pooling)简介使用OpenSymphony Quartz 调度器使用JobDetailBean使用 MethodInvokingJobDetailFactoryBean使用triggers和SchedulerFactoryBean来包装任务使用JDK Timer支持类创建定制的timers使用 MethodInvokingTimerTaskFactoryBean类最后:使用TimerFactoryBean来设置任务SpringTaskExecutor抽象TaskExecutor接口TaskExecutor类型使用TaskExecutor动态语言支持介绍第一个示例定义动态语言支持的bean公共概念<lang:language/> 元素Refreshable bean内置动态语言源文件理解dynamic-language-backed bean上下文中的构造器注入JRuby beansGroovy beans通过回调定制Groovy对象BeanShell beans场景Spring MVC控制器的脚本化Validator的脚本化Bits and bobsAOP - 通知脚本化bean作用域更多的资源注解和源代码级的元数据支持简介Spring的元数据支持注解@RequiredSpring中的其它@AnnotationsJakarta Commons Attributes集成元数据和Spring AOP自动代理基本原理声明式事务管理示例程序演示案例介绍使用动态语言实现的Spring MVC控制器构建与部署使用SimpleJdbcTemplate和@Repository实现DAO域对象Data Access Object构建XML Schema-based configurationIntroductionXML Schema-based configurationReferencing the schemasThe util schema<util:constant/>Setting a bean property or constructor arg from a field value<util:property-path/>Using <util:property-path/> to set a bean property or constructor-argument<util:properties/><util:list/><util:map/><util:set/>The jee schema<jee:jndi-lookup/> (simple)<jee:jndi-lookup/> (with single JNDI environment setting)<jee:jndi-lookup/> (with multiple JNDI environment settings)<jee:jndi-lookup/> (complex)<jee:local-slsb/> (simple)<jee:local-slsb/> (complex)<jee:remote-slsb/>The lang schemaThe jms schemaThe tx (transaction) schemaThe aop schemaThe context schema<property-placeholder/><annotation-config/><component-scan/><load-time-weaver/><spring-configured/><mbean-export/>The tool schemaThe beans schemaSetting up your IDESetting up EclipseSetting up IntelliJ IDEAIntegration issuesXML parsing errors in the Resin v.3 application serverExtensible XML authoringIntroductionAuthoring the schemaCoding a NamespaceHandlerCoding a BeanDefinitionParserRegistering the handler and the schema'META-INF/spring.handlers''META-INF/spring.schemas'Using a custom extension in your Spring XML configurationMeatier examplesNesting custom tags within custom tagsCustom attributes on 'normal' elementsFurther Resourcesspring-beans-2.0.dtdspring.tldIntroductionThe bind tagThe escapeBody tagThe hasBindErrors tagThe htmlEscape tagThe message tagThe nestedPath tagThe theme tagThe transform tagspring-form.tldIntroductionThe checkbox tagThe checkboxes tagThe errors tagThe form tagThe hidden tagThe input tagThe label tagThe option tagThe options tagThe password tagThe radiobutton tagThe radiobuttons tagThe select tagThe textarea tagSpring 2.5开发手册中文化项目声明致谢参与人员项目历程
文字

12.2. Hibernate

我们将首先从Hibernate3 (http://www.hibernate.org/)开始,通过讲解Hibernate在Spring环境中的使用来阐述Spring框架对于O/R Mapping工具的整合方式。 本章节将涉及到许多细节问题,并向你展示各种不同的DAO实现方式和事务划分。这其中的绝大多数模式能够被Spring支持的其他O/R Mapping工具所使用。 这一章节的其他部分将为你讲述其他的O/R Mapping技术,并给出一些简短的例子。

注意:Spring2.5版本需要Hibernate3.1或更高版本,不再提供对Hibernate2.1与Hibernate3.0版本的支持。

12.2.1. 资源管理

典型的业务程序经常会被重复的资源管理代码搞得混乱。很多项目都试图创建自己的方案来解决这个问题,有时会为了编程方便而牺牲恰当的错误处理。 对于恰当的资源管理,Spring提倡一种瞩目而又简洁的解决方案:使用模板化的IoC,诸如基础构建类、回调接口以及使用AOP拦截器。 基础构建类负责恰当的资源处理,以及将特定的异常代码转换为unchecked exception体系。Spring引进了DAO异常体系,可适用于任何数据访问策略。 对于直接使用JDBC的情况,前面章节提到的 JdbcTemplate 类负责处理connection,并正确地把 SQLException 转换为 DataAccessException 体系,包括将与数据库相关的SQL错误代码变成有意义的异常类。 Spring同时通过它们各自的事务管理器支持JTA和JDBC事务。

Spring同样也提供了对Hibernate和JDO的支持,包括 HibernateTemplate / JdoTemplate 类似于 JdbcTemplateHibernateInterceptor / JdoInterceptor 以及一个 Hibernate / JDO 事务管理器。 这样做的主要目的是为了能够清晰地划分应用程序层次而不管使用何种数据访问和事务管理技术,从而降低各个应用程序对象之间的耦合。 业务逻辑不再依赖于特定的数据访问与事务策略;不再有硬编码的资源查找、不再有难以替换的singletons、不再有用户自定义的服务注册。 Spring提供了一个简单且稳固的方案使得各种应用逻辑对象连接在一起,使这些对象可重用,并尽可能不依赖容器。 所有的数据访问技术都能独立使用,但是他们在Spring提供的基于XML配置且无需依赖Spring的普通JavaBean下会与application Context整合的更好。 在典型的Spring应用程序中,很多重要的对象都是JavaBeans:数据访问template、数据访问对象(使用template)、事务管理器、业务逻辑对象(使用数据访问对象和事务管理器)、web视图解析器、web控制器(使用业务服务)等等。

12.2.2. 在Spring容器中创建 SessionFactory

为了避免硬编码的资源查找与应用程序对象紧密耦合,Spring允许你在Spring容器中以bean的方式定义诸如JDBC DataSource或者Hibernate SessionFactory 的数据访问资源。 任何需要进行资源访问的应用程序对象只需要持有这些事先定义好的实例的引用(DAO定义在下一章节介绍),下面的代码演示如何创建一个JDBC DataSource 和Hibernate SessionFactory

<beans>

  <bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="org.hsqldb.jdbcDriver"/>
    <property name="url" value="jdbc:hsqldb:hsql://localhost:9001"/>
    <property name="username" value="sa"/>
    <property name="password" value=""/>
  </bean>

  <bean id="mySessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="myDataSource"/>
    <property name="mappingResources">
      <list>
        <value>product.hbm.xml</value>
      </list>
    </property>
    <property name="hibernateProperties">
      <value>
        hibernate.dialect=org.hibernate.dialect.HSQLDialect
      </value>
    </property>
  </bean>

</beans>

将一个本地定义的,如Jakarta Commons DBCP的 BasicDataSource 切换为一个JNDI定位的DataSource(通常由应用程序服务器管理),仅仅需要改变配置:

<beans>

  <bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/myds"/>
  </bean>

</beans>

通过使用Spring的 JndiObjectFactoryBean 来暴露和获,你也可以访问一个JNDI定位的Hibernate SessionFactory。 当然,如果在EJB上下文之外,这是不必要的。

12.2.3. The HibernateTemplate

对于特定的数据访问对象或业务对象的方法,基本的模板编程模型看起来像下面所示的代码那样。 对于这些外部对象来说,没有任何实现特定接口的约束,仅仅要求提供一个Hibernate SessionFactory。 它可以从任何地方得到,不过比较适宜的方法是从Spring的IoC容器中取得bean的引用:通过简单的 setSessionFactory(..) 这个bean的setter方法。 下面的代码片段展示了在Spring容器中一个DAO的定义,它引用了上面定义的 SessionFactory,同时展示了一个DAO方法的具体实现。

<beans>

  <bean id="myProductDao" class="product.ProductDaoImpl">
    <property name="sessionFactory" ref="mySessionFactory"/>
  </bean>

</beans>
public class ProductDaoImpl implements ProductDao {

    private HibernateTemplate hibernateTemplate;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.hibernateTemplate = new HibernateTemplate(sessionFactory);
    }

    public Collection loadProductsByCategory(String category) throws DataAccessException {
    	return this.hibernateTemplate.find("from test.Product product where product.category=?", category);
    }
}

除了很多类似上例中易用的方法外,HibernateTemplate类提供了大量方法对应Hibernate Session接口中暴露的方法。当你需要使用的Session方法没有在HibernateTemplate中提供时,可以通过下面提供的基于回调的方案来实现。

public class ProductDaoImpl implements ProductDao {

    private HibernateTemplate hibernateTemplate;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.hibernateTemplate = new HibernateTemplate(sessionFactory);
    }

    public Collection loadProductsByCategory(final String category) throws DataAccessException {
        return this.hibernateTemplate.execute(new HibernateCallback() {

            public Object doInHibernate(Session session) {
                Criteria criteria = session.createCriteria(Product.class);
                criteria.add(Expression.eq("category", category));
                criteria.setMaxResults(6);
                return criteria.list();
            }
        };
    }
}

一个回调实现能够有效地在任何Hibernate数据访问中使用。HibernateTemplate 会确保当前Hibernate的 Session 实例的正确打开和关闭,并直接参与到事务管理中去。 Template实例不仅是线程安全的,同时它也是可重用的。因而他们可以作为外部对象的实例变量而被持有。对于那些简单的诸如find、load、saveOrUpdate或者delete操作的调用,HibernateTemplate 提供可选择的快捷函数来替换这种回调的实现。 不仅如此,Spring还提供了一个简便的 HibernateDaoSupport 基类,这个类提供了 setSessionFactory(..) 方法来接受一个 SessionFactory 对象,同时提供了 getSessionFactory()getHibernateTemplate() 方法给子类使用。 综合了这些,对于那些典型的业务需求,就有了一个非常简单的DAO实现:

public class ProductDaoImpl extends HibernateDaoSupport implements ProductDao {

    public Collection loadProductsByCategory(String category) throws DataAccessException {
        return this.getHibernateTemplate().find(
            "from test.Product product where product.category=?", category);
    }
}

12.2.4. 不使用回调的基于Spring的DAO实现

作为不使用Spring的 HibernateTemplate 来实现DAO的替代解决方案,你依然可以用传统的编程风格来编写你的数据访问代码。 无需将你的Hibernate访问代码包装在一个回调中,只需符合Spring的通用的 DataAccessException 异常体系。 HibernateDaoSupport 基类提供了访问与当前事务绑定的 Session 对象的函数,因而能保证在这种情况下异常的正确转化。 类似的函数同样可以在 SessionFactoryUtils 类中找到,但他们以静态方法的形式出现。 值得注意的是,通常将 'false' 作为参数值(表示是否允许创建)传递给 getSession(..) 方法进行调用。 此时,整个调用将在同一个事务内完成(它的整个生命周期由事务控制,避免了关闭返回的 Session 的需要)。

public class HibernateProductDao extends HibernateDaoSupport implements ProductDao {

    public Collection loadProductsByCategory(String category) throws DataAccessException, MyException {
        Session session = getSession(false);
        try {
            Query query = session.createQuery("from test.Product product where product.category=?");
            query.setString(0, category);
            List result = query.list();
            if (result == null) {
                throw new MyException("No search results.");
            }
            return result;
        }
        catch (HibernateException ex) {
            throw convertHibernateAccessException(ex);
        }
    }
}

这种直接使用Hibernate访问代码的好处在于它允许你在数据访问代码中抛出 任何 checked exception,而 HibernateTemplate 却受限于回调中的unchecked exception。 注意,你通常可以将这些应用程序的异常处理推迟到回调函数之后,这样,你依然可以正常使用 HibernateTemplate。 一般来说,HibernateTemplate 类所提供的许多方法在许多情况下看上去更简单和便捷。

12.2.5. 基于Hibernate3的原生API实现DAO

Hibernate3 引入了一个新的特性:“带上下文环境的Session”。 这一特性使得Hibernate自身具备了每个事务绑定当前 Session 对象的功能。 这与Spring中每个Hibernate的 Session 与事务同步的功能大致相同。一个相应的基于原生的Hibernate API的DAO实现正如下例所示:

public class ProductDaoImpl implements ProductDao {

    private SessionFactory sessionFactory;

    public void setSessionFactory(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
    }

    public Collection loadProductsByCategory(String category) {
        return this.sessionFactory.getCurrentSession()
                .createQuery("from test.Product product where product.category=?")
                .setParameter(0, category)
                .list();
    }
}

这种风格与你在Hibernate文档和示例中见到的非常类似,除了DAO实现类中持有了一个 SessionFactory 的实例变量。 我们强烈推荐这种基于实例变量的DAO构建方式,而不是使用那种过去由Hibernate的示例程序中提到的 静态的 HibernateUtil 类。 (通常来说,不要在静态变量中保存任何资源信息除非 确实 有这个必要)。

上述DAO遵循依赖注入模式:它如同使用Spring的 HibernateTemplate 进行编程那样,适合在Spring IoC容器中进行配置。 当然,这样的DAO还可以建立在一个普通的Java类中(诸如在Unit Test中): 仅仅需要初始化这个类, 调用 setSessionFactory(..) 方法设置你所期望的工厂资源。 以Spring的bean的定义方式,它看上去就像这样:

<beans>

  <bean id="myProductDao" class="product.ProductDaoImpl">
    <property name="sessionFactory" ref="mySessionFactory"/>
  </bean>

</beans>

这种DAO访问方式的主要优势在于它仅仅依赖于Hibernate API本身而无需引入任何Spring的类。 从无入侵性的角度来看,这一点非常吸引人。对于Hibernate开发人员来说也无疑更加自然。

然而,这样的DAO访问方式会抛出原生 HibernateException (这是一个无需声明或捕获的unchecked exception), 这意味着,DAO的调用者只能以致命的错误来处理这些异常,除非完全依赖Hibernate自身的异常体系。 因而,除非你将DAO的调用者绑定到具体的实现策略上去,否则你将无法捕获特定的异常原因,诸如乐观锁异常。 这种折中平衡或许可以被接受,如果你的应用完全基于Hibernate或者无需进行特殊的异常处理。

幸运的是,Spring的 LocalSessionFactoryBean 可以在任何Spring的事务管理策略下, 提供对Hibernate的 SessionFactory.getCurrentSession() 方法的支持,它将返回当前受Spring事务管理(甚至是 HibernateTransactionManager 管理的)的 Session 对象。 当然,该函数的标准行为依然有效:返回当前与正在进行的JTA事务(无论是Spring的 JtaTransactionManager、EJB CMT或者普通的JTA)绑定的 Session 对象。

总体来说,DAO可以基于Hibernate3的原生API实现,同时,它依旧需要能够参与到Spring的事务管理中。

12.2.6. 编程式的事务划分

我们可以在这些低层次的数据访问服务之上的应用程序进行更高层次的事务划分,从而让事务能够横跨多个操作。 这里对于相关的业务逻辑对象同样没有实现接口的限制,它只需要一个Spring的 PlatformTransactionManager。 同SessionFactory一样,它可以来自任何地方,但是最好是一个经由 setTransactionManager(..) 方法注入的bean的引用,正如使用 setProductDao 方法来设置 productDAO 一样。 下面演示了在Spring的application context中定义一个事务管理器和一个业务对象,以及具体的业务方法实现:

<beans>

  <bean id="myTxManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="mySessionFactory"/>
  </bean>

  <bean id="myProductService" class="product.ProductServiceImpl">
    <property name="transactionManager" ref="myTxManager"/>
    <property name="productDao" ref="myProductDao"/>
  </bean>

</beans>
public class ProductServiceImpl implements ProductService {

    private TransactionTemplate transactionTemplate;
    private ProductDao productDao;

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionTemplate = new TransactionTemplate(transactionManager);
    }

    public void setProductDao(ProductDao productDao) {
        this.productDao = productDao;
    }

    public void increasePriceOfAllProductsInCategory(final String category) {
        this.transactionTemplate.execute(new TransactionCallbackWithoutResult() {

                public void doInTransactionWithoutResult(TransactionStatus status) {
                    List productsToChange = this.productDao.loadProductsByCategory(category);
                    // do the price increase...
                }
            }
        );
    }
}

12.2.7. 声明式的事务划分

作为可选方案,我们可以使用Spring声明式的事务支持。声明式的事务支持通过配置Spring容器中的AOP Transaction Interceptor来替换事务划分的硬编码。 这将使你可以从每个业务方法中重复的事务划分代码中解放出来,真正专注于为你的应用添加有价值的业务逻辑代码。此外,类似传播行为和隔离级别等事务语义能够在配置文件中改变,而不会影响具体的业务对象实现。

<beans>

  <bean id="myTxManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="mySessionFactory"/>
  </bean>

  <bean id="myProductService" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces" value="product.ProductService"/>
    <property name="target">
        <bean class="product.DefaultProductService">
            <property name="productDao" ref="myProductDao"/>
        </bean>
    </property>
    <property name="interceptorNames">
      <list>
        <value>myTxInterceptor</value> <!-- the transaction interceptor (configured elsewhere) -->
      </list>
    </property>
  </bean>

</beans>
public class ProductServiceImpl implements ProductService {

    private ProductDao productDao;

    public void setProductDao(ProductDao productDao) {
        this.productDao = productDao;
    }

    // notice the absence of transaction demarcation code in this method
    // Spring's declarative transaction infrastructure will be demarcating transactions on your behalf 
    public void increasePriceOfAllProductsInCategory(final String category) {
        List productsToChange = this.productDao.loadProductsByCategory(category);
        // ...
    }
}

Spring的 TransactionInterceptor 允许任何应用程序的checked exception在回调代码中抛出,而 TransactionTemplate 在回调中则严格要求被封装成unchecked exception。 TransactionTemplate 会在一个unchecked exception抛出时,或者当事务被应用程序通过 TransactionStatus 标记为rollback-only时触发一个数据库回滚操作。 TransactionInterceptor 默认进行同样的操作,但是它允许对每个方法配置自己的rollback策略。

下面列出的高级别的声明式的事务管理方案并没有使用 ProxyFactoryBean,它将使那些大量的业务对象需要被纳入到事务管理中时的配置变得非常简单。

注意

在你打算继续阅读下一部分之前,我们 强烈 推荐你想去阅读 第 9.5 节 “声明式事务管理” 这一章节。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd
       http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

  <!-- SessionFactory, DataSource, etc. omitted -->

  <bean id="myTxManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
    <property name="sessionFactory" ref="mySessionFactory"/>
  </bean>
  
  <aop:config>
    <aop:pointcut id="productServiceMethods" expression="execution(* product.ProductService.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="productServiceMethods"/>
  </aop:config>

  <tx:advice id="txAdvice" transaction-manager="myTxManager">
    <tx:attributes>
      <tx:method name="increasePrice*" propagation="REQUIRED"/>
      <tx:method name="someOtherBusinessMethod" propagation="REQUIRES_NEW"/>
      <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
    </tx:attributes>
  </tx:advice>

  <bean id="myProductService" class="product.SimpleProductService">
    <property name="productDao" ref="myProductDao"/>
  </bean>

</beans>

12.2.8. 事务管理策略

TransactionTemplateTransactionInterceptor 都将真正的事务处理委托给一个 PlatformTransactionManager 实例来处理。 在Hibernate应用中,它可以是一个 HibernateTransactionManager(对于单独一个的Hibernate SessionFactory 使用一个 ThreadLocalSession)或一个 JtaTransactionManager(代理给容器的JTA子系统)。 你甚至可以使用自定义的 PlatformTransactionManager 的实现。因而,在你的应用碰到了特定的分布式事务的部署需求时(类似将原来的Hibernate事务管理转变为JTA),仅仅需要改变配置而已:简单将Hibernate的事务管理器替换成JTA事务实现。 任何的事务划分和数据访问的代码都无需因此而改变,因为他们仅仅使用了通用的事务管理API。

对于横跨多个Hibernate SessionFacotry的分布式事务,只需简单地将 JtaTransactionManager 同多个 LocalSessionFactoryBean 的定义结合起来作为事务策略。 你的每一个DAO通过bean属性得到各自的 SessionFactory 引用。如果所有的底层JDBC数据源都是支持事务的容器,那么只要业务对象使用了 JtaTransactionManager 作为事务策略,它就可以横跨多个DAO和多个session factories来划分事务,而不需要做任何特殊处理。

<beans>

  <bean id="myDataSource1" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName value="java:comp/env/jdbc/myds1"/>
  </bean>

  <bean id="myDataSource2" class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiName" value="java:comp/env/jdbc/myds2"/>
  </bean>

  <bean id="mySessionFactory1" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="myDataSource1"/>
    <property name="mappingResources">
      <list>
        <value>product.hbm.xml</value>
      </list>
    </property>
    <property name="hibernateProperties">
      <value>
        hibernate.dialect=org.hibernate.dialect.MySQLDialect
        hibernate.show_sql=true
      </value>
    </property>
  </bean>

  <bean id="mySessionFactory2" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
    <property name="dataSource" ref="myDataSource2"/>
    <property name="mappingResources">
      <list>
        <value>inventory.hbm.xml</value>
      </list>
    </property>
    <property name="hibernateProperties">
      <value>
        hibernate.dialect=org.hibernate.dialect.OracleDialect
      </value>
    </property>
  </bean>

  <bean id="myTxManager" class="org.springframework.transaction.jta.JtaTransactionManager"/>

  <bean id="myProductDao" class="product.ProductDaoImpl">
    <property name="sessionFactory" ref="mySessionFactory1"/>
  </bean>

  <bean id="myInventoryDao" class="product.InventoryDaoImpl">
    <property name="sessionFactory" ref="mySessionFactory2"/>
  </bean>

  <!-- this shows the Spring 1.x style of declarative transaction configuration -->
  <!-- it is totally supported, 100% legal in Spring 2.x, but see also above for the sleeker, Spring 2.0 style -->
  <bean id="myProductService"
      class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
    <property name="transactionManager" ref="myTxManager"/>
    <property name="target">
      <bean class="product.ProductServiceImpl">
        <property name="productDao" ref="myProductDao"/>
        <property name="inventoryDao" ref="myInventoryDao"/>
      </bean>
    </property>
    <property name="transactionAttributes">
      <props>
        <prop key="increasePrice*">PROPAGATION_REQUIRED</prop>
        <prop key="someOtherBusinessMethod">PROPAGATION_REQUIRES_NEW</prop>
        <prop key="*">PROPAGATION_SUPPORTS,readOnly</prop>
      </props>
    </property>
  </bean>

</beans>

HibernateTransactionManagerJtaTransactionManager 都允许恰当的JVM级别的Hibernate缓存处理,而无需使用与容器相关的事务管理器或JCA连接器。(只要不是由EJB发起的事务)

对于特定的 DataSourceHibernateTransactionManager 能够为普通的JDBC访问代码提供Hibernate所使用的 Connection。 这一功能允许你可以完全混和使用Hibernate/JDBC进行数据访问而无需依赖JTA在高层次代码中进行事务划分,只要你依然访问的是同一个数据库! HibernateTransactionManager 能够自动地将Hibernate事务暴露为JDBC事务,如果你通过设置 DataSource来建立SessionFactory 对象(通过设置 LocalSessionFactoryBean 中的“dataSource”属性)。 另外一种配置方法是通过设置 HibernateTransactionManager 的“dataSource”属性,明确指定事务需要暴露的 DataSource

12.2.9. 容器资源 vs 本地资源

Spring的资源管理允许你简单地在一个JNDI的 SessionFactory 和一个本地的 SessionFactory 之间切换而无需更改任何一行应用程序代码。 把资源定义放在容器中还是放在应用程序本地中主要是由使用的事务策略决定的。与Spring定义的本地 SessionFactory 相比,一个手工注册的JNDI SessionFactory 并没有体现出多大的优势。 通过Hibernate的JCA连接器来部署一个 SessionFactory 提供了能使之参与到J2EE服务器管理架构的增值服务,不过除此之外也并没有增加实际的价值。

Spring对事务管理的支持有一个非常重要的好处:它不依赖于任何容器。使用非JTA的任何其他事务策略的配置,程序也能在独立的测试环境下正常工作。 尤其对于那些典型的单数据库事务情况下,这将是一个非常轻量级而又强大的JTA替代方案。当你使用一个本地的EJB SLSB来驱动事务时,即时你可能只访问一个数据库而且仅仅通过CMT使用SLSB的声明性事务,你仍然要依赖于EJB容器和JTA。 使用编程式JTA替换方案依然需要J2EE环境,JTA不仅对于JTA本身,还对于JNDI的 DataSource 实例引入了容器依赖。 对于非Spring环境的JTA驱动的Hibernate事务,你必须使用Hibernate JCA连接器或者额外的Hibernate事务代码及 TransactionManagerLookup 配置,来恰当地处理JVM级别的缓存。

Spring驱动的事务管理与本地定义的 SessionFactory 能够工作得非常好,就像使用本地的JDBC DataSource 一样,当然前提必须是访问单个数据库。 因此当你真正面对分布式事务的需求时,可以马上回到Spring的JTA事务。必须要注意,一个JCA连接器需要特定容器的部署步骤,而且首先容器要支持JCA。 这要比使用本地资源定义和Spring驱动事务来部署一个简单的Web应用麻烦得多。而且你通常需要特定企业版本的容器,但是像类似WebLogic的Express版本并不提供JCA。 一个仅使用本地资源并且针对一个数据库的事务操作的Spring应用将可以在任何一种J2EE的Web容器中工作(不需要JTA、JCA或者EJB),比如Tomcat、Resin甚至普通的Jetty。 除此之外,这样的中间层可以在桌面应用或测试用例中被简单地重用。

考虑一下所有的情况:如果你不使用EJB,坚持使用本地 SessionFactory 以及Spring的 HibernateTransactionManager 或者 JtaTransactionManager, 你将获得包括适当的JVM级别上的缓存以及分布式事务在内的所有好处,而不会有任何容器部署的麻烦。通过JCA连接器的Hibernate的 SessionFactory 的JNDI注册仅仅在EJB中使用会带来好处。

12.2.10. 在应用服务器中使用Hibernate的注意事项

在一些具有非常严格的 XADataSource 实现的JTA环境中(目前来说只是WebLogic和WebSphere的某些版本)当你将Hibernate配置成不感知JTA PlatformTransactionManager 对象时,容器可能会在应用服务器日志中报出一些警告和异常。 这些警告和异常通常会说:“正在被访问的连接不再有效,或者JDBC连接不再有效,可能由于事务不再处于激活状态”。下面是一个从WebLogic上抛出的异常:

java.sql.SQLException: The transaction is no longer active - status: 'Committed'.
   No further JDBC access is allowed within this transaction.

这样的错误警告很容易解决:只要简单的把Hibernate配置成感知到JTA PlatformTransactionManager 实例的存在即可,同时将他们进行同步(与Spring同步)。可以有两种方法达到这种效果:

  • 如果你已经在application context中定义并获取了JTA PlatformTransactionManager 对象(或许来自通过 JndiObjectFactoryBean 得到的JNDI)并已经将它注入到类似Spring的 JtaTransactionManager 中, 那么最简单的方式就是指定这个bean的引用作为 LocalSessionFactoryBeanjtaTransactionManager 属性。Spring将使这个对象被Hibernate所感知。

  • 多数情况下,你还没有得到JTA的 PlatformTransactionManager 实例(由于Spring的 JtaTransactionManager 能够自己找到它),所以你需要自行配置Hibernate并直接寻找资源。 正如Hibernate文档中提到的,这可以通过在Hibernate配置一个应用服务器特定的 TransactionManagerLookup 类来完成。

对于恰当的使用方法,你无需了解更多的东西。但是我们在这里将描述一下,对于将Hibernate配置为感知或者不感知JTA的 PlatformTransactionManager 对象这两种情况下,整个事务的执行顺序。

Hibernate被配置成不感知JTA PlatformTransactionManager 的情况下,当一个JTA事务提交时,整个事件的执行顺序如下:

  • JTA 事务提交

  • Spring的 JtaTransactionManager 同步到JTA事务,它被JTA事务管理器通过调用 afterCompletion 执行回调。

  • 在所有其他活动中,这将由Spring向Hibernate触发一个回调,通过Hibernate的 afterTransactionCompletion 回调(用于清除Hibernate缓存),然后紧跟着一个明确的Hibernate Session的 close() 调用。 这将使Hibernate试图去关闭JDBC的Connection。

  • 在某些环境中,Connection.close() 的调用将会触发一个警告或者错误信息。 这是由于特定的应用服务器将不再考虑 Connection 的可用性,因为此时事务已经被提交了。

在Hibernate被配置成感知JTA的 PlatformTransactionManager 的情况下,当一个JTA事务提交时,整个事件的执行顺序如下:

  • JTA 事务准备提交

  • Spring的 JtaTransactionManager 同步到JTA事务,因而它被JTA事务管理器通过调用 beforeCompletion 执行回调。

  • Spring能感知到Hibernate自身被同步到JTA Transaction,因而表现得与先前那种情况有所不同。 假设Hibernate的 Session 需要被关闭,Spring将负责关闭它。

  • JTA 事务提交

  • Hibernate将与JTA transaction进行同步,因而它被JTA事务管理器通过调用 afterCompletion 执行回调,并且适时地清除缓存。

上一篇:下一篇: