目錄 搜尋
前言 简介 概览 使用场景 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 异步的JMS JDBC Web层 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 OJB iBATIS Hibernate JDO UrlFilenameViewController 更新的样例应用 改进的文档 核心技术 IoC(控制反转)容器 简介 基本原理 - 容器和bean 容器 配置元数据 实例化容器 XML配置元数据的结构 多种bean bean的命名 bean的别名 实例化bean 用构造器来实例化 使用静态工厂方法实例化 使用实例工厂方法实例化 使用容器 依赖 注入依赖 构造器注入 构造器参数解析 构造器参数类型匹配 构造参数索引 Setter注入 一些例子 依赖配置详解 直接变量(基本类型、Strings类型等。) idref元素 引用其它的bean(协作者) 内部bean 集合 集合的合并 强类型集合(仅适用于Java5+) Nulls XML配置文件的简写及其他 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容器 了解自己 BeanFactoryAware BeanNameAware bean定义的继承 容器扩展点 用BeanPostProcessor定制bean 使用BeanPostProcessor的Hello World示例 RequiredAnnotationBeanPostProcessor示例 用BeanFactoryPostProcessor定制配置元数据 PropertyPlaceholderConfigurer示例 PropertyOverrideConfigurer示例 使用FactoryBean定制实例化逻辑 The ApplicationContext BeanFactory 还是 ApplicationContext? 利用MessageSource实现国际化 事件 底层资源的访问 ApplicationContext在WEB应用中的实例化 粘合代码和可怕的singleton 以J2EE RAR文件的形式部署Spring ApplicationContext 基于注解(Annotation-based)的配置 @Autowired 基于注解的自动连接微调 CustomAutowireConfigurer @Resource @PostConstruct 与 @PreDestroy 对受管组件的Classpath扫描 @Component和更多典型化注解 自动检测组件 使用过滤器自定义扫描 自动检测组件的命名 为自动检测的组件提供一个作用域 用注解提供限定符元数据 注册一个LoadTimeWeaver 资源 简介 Resource接口 内置 Resource 实现 UrlResource ClassPathResource FileSystemResource ServletContextResource InputStreamResource ByteArrayResource ResourceLoader接口 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 contexts Spring中其他的AspectJ切面 使用Spring IoC来配置AspectJ的切面 在Spring应用中使用AspectJ加载时织入(LTW) 第一个例子 切面 'META-INF/aop.xml' 相关类库(JARS) Spring配置 特定环境的配置 通用Java应用 Tomcat WebLogic 更多资源 Spring AOP APIs 简介 Spring中的切入点API 概念 切入点运算 AspectJ切入点表达式 便利的切入点实现 静态切入点 正则表达式切入点 属性驱动的切入点 动态切入点 控制流切入点 切入点的超类 自定义切入点 Spring的通知API 通知的生命周期 Spring里的通知类型 拦截环绕通知 前置通知 异常通知 后置通知 引入通知 Spring里的Advisor API 使用ProxyFactoryBean创建AOP代理 基础 JavaBean属性 基于JDK和CGLIB的代理 对接口进行代理 对类进行代理 使用“全局”通知器 简化代理定义 使用ProxyFactory通过编程创建AOP代理 操作被通知对象 使用“自动代理(autoproxy)”功能 自动代理bean定义 BeanNameAutoProxyCreator DefaultAdvisorAutoProxyCreator AbstractAdvisorAutoProxyCreator 使用元数据驱动的自动代理 使用TargetSource 热交换目标源 池化目标源 原型目标源 ThreadLocal目标源 定义新的Advice类型 更多资源 测试 简介 单元测试 Mock对象 JNDI Servlet API Portlet 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 有关的设置 事务传播 required RequiresNew Nested 通知事务操作 结合AspectJ使用 @Transactional 编程式事务管理 使用TransactionTemplate 指定事务设置 使用PlatformTransactionManager 选择编程式事务管理还是声明式事务管理 与特定应用服务器集成 IBM WebSphere BEA WebLogic Oracle 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类 NativeJdbcExtractor JDBC批量操作 使用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容器中创建 SessionFactory The HibernateTemplate 不使用回调的基于Spring的DAO实现 基于Hibernate3的原生API实现DAO 编程式的事务划分 声明式的事务划分 事务管理策略 容器资源 vs 本地资源 在应用服务器中使用Hibernate的注意事项 JDO 建立PersistenceManagerFactory JdoTemplate和JdoDaoSupport 基于原生的JDO API实现DAO 事务管理 JdoDialect Oracle TopLink SessionFactory 抽象层 TopLinkTemplate and TopLinkDaoSupport 基于原生的TopLink API的DAO实现 事务管理 iBATIS SQL Maps 创建SqlMapClient 使用 SqlMapClientTemplate 和 SqlMapClientDaoSupport 基于原生的iBATIS API的DAO实现 JPA 在Spring环境中建立JPA LocalEntityManagerFactoryBean 从JNDI中获取 EntityManagerFactory LocalContainerEntityManagerFactoryBean Tomcat(5.0以上)加载时的织入配置 使用VM代理的全局加载时织入 上下文范围内的加载时织入配置 处理多持久化单元 JpaTemplate 和 JpaDaoSupport 基于原生的JPA实现DAO 异常转化 事务管理 JpaDialect The Web Web MVC framework Web框架 概述 与其他MVC实现框架的集成 Spring Web MVC框架的特点 DispatcherServlet 控制器 AbstractController 和 WebContentGenerator 其它的简单控制器 MultiActionController 命令控制器 处理器映射(handler mapping) BeanNameUrlHandlerMapping SimpleUrlHandlerMapping 拦截器(HandlerInterceptor) 视图与视图解析 视图解析器(ViewResolver) 视图解析链 重定向(Rediret)到另一个视图 RedirectView redirect:前缀 forward:前缀 本地化解析器 AcceptHeaderLocaleResolver CookieLocaleResolver SessionLocaleResolver LocaleChangeInterceptor 使用主题 简介 如何定义主题 主题解析器 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 需要的资源 如何集成Tiles UrlBasedViewResolver类 ResourceBundleViewResolver类 SimpleSpringPreparerFactory 和 SpringBeanPreparerFactory Velocity和FreeMarker 需要的资源 Context 配置 创建模板 高级配置 velocity.properties FreeMarker 绑定支持和表单处理 用于绑定的宏 简单绑定 表单输入生成宏 输入域 选择输入域 重载HTML转码行为并使你的标签符合XHTML XSLT 写在段首 Bean 定义 标准MVC控制器代码 把模型数据转化为XML 定义视图属性 文档转换 小结 文档视图(PDF/Excel) 简介 配置和安装 文档视图定义 Controller 代码 Excel视图子类 PDF视图子类 JasperReports 依赖的资源 配置 配置ViewResolver 配置View 关于报表文件 使用 JasperReportsMultiFormatView 构造ModelAndView 使用子报表 配置子报表文件 配置子报表数据源 配置Exporter的参数 集成其它Web框架 简介 通用配置 JavaServer Faces DelegatingVariableResolver FacesContextUtils Struts ContextLoaderPlugin DelegatingRequestProcessor DelegatingActionProxy ActionSupport Classes Tapestry 注入 Spring 托管的 beans 将 Spring Beans 注入到 Tapestry 页面中 组件定义文件 添加抽象访问方法 将 Spring Beans 注入到 Tapestry 页面中 - Tapestry 4.0+ 风格 WebWork 更多资源 Portlet MVC框架 介绍 控制器 - MVC中的C 视图 - MVC中的V Web作用范围的Bean DispatcherPortlet ViewRendererServlet 控制器 AbstractController 和 PortletContentGenerator 其它简单的控制器 Command控制器 PortletWrappingController 处理器映射 PortletModeHandlerMapping ParameterHandlerMapping PortletModeParameterHandlerMapping 增加 HandlerInterceptors HandlerInterceptorAdapter ParameterMappingInterceptor 视图和它们的解析 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) 访问远程SLSB Accessing EJB 2.x SLSBs versus EJB 3 SLSBs 使用Spring提供的辅助类实现EJB组件 EJB 2.x base classes EJB 3 注入拦截 JMS (Java Message Service) 简介 使用Spring JMS JmsTemplate 连接工厂 目的地管理 消息侦听容器 SimpleMessageListenerContainer DefaultMessageListenerContainer ServerSessionMessageListenerContainer 事务管理 发送消息 使用消息转换器 SessionCallback 和 ProducerCallback 接收消息 同步接收 异步接收 - 消息驱动的POJO SessionAwareMessageListener接口 MessageListenerAdapter 事务中的消息处理 JCA消息端点的支持 JMS命名空间支持 JMX 介绍 将Bean暴露为JMX 创建MBeanServer 重用原有的MBeanServer 延迟初始化的MBean MBean的自动注册 控制注册行为 控制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访问为操作对象 MappingRecordOperation MappingCommAreaOperation 自动生成输出记录 总结 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 beans Groovy beans 通过回调定制Groovy对象 BeanShell beans 场景 Spring MVC控制器的脚本化 Validator的脚本化 Bits and bobs AOP - 通知脚本化bean 作用域 更多的资源 注解和源代码级的元数据支持 简介 Spring的元数据支持 注解 @Required Spring中的其它@Annotations Jakarta Commons Attributes集成 元数据和Spring AOP自动代理 基本原理 声明式事务管理 示例程序 演示案例 介绍 使用动态语言实现的Spring MVC控制器 构建与部署 使用SimpleJdbcTemplate和@Repository实现DAO 域对象 Data Access Object 构建 XML Schema-based configuration Introduction XML Schema-based configuration Referencing the schemas The 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 schema The jms schema The tx (transaction) schema The aop schema The context schema <property-placeholder/> <annotation-config/> <component-scan/> <load-time-weaver/> <spring-configured/> <mbean-export/> The tool schema The beans schema Setting up your IDE Setting up Eclipse Setting up IntelliJ IDEA Integration issues XML parsing errors in the Resin v.3 application server Extensible XML authoring Introduction Authoring the schema Coding a NamespaceHandler Coding a BeanDefinitionParser Registering the handler and the schema 'META-INF/spring.handlers' 'META-INF/spring.schemas' Using a custom extension in your Spring XML configuration Meatier examples Nesting custom tags within custom tags Custom attributes on 'normal' elements Further Resources spring-beans-2.0.dtd spring.tld Introduction The bind tag The escapeBody tag The hasBindErrors tag The htmlEscape tag The message tag The nestedPath tag The theme tag The transform tag spring-form.tld Introduction The checkbox tag The checkboxes tag The errors tag The form tag The hidden tag The input tag The label tag The option tag The options tag The password tag The radiobutton tag The radiobuttons tag The select tag The textarea tag Spring 2.5开发手册中文化项目 声明 致谢 参与人员 项目历程
文字

12.6. JPA

org.springframework.orm.jpa 包下的)Spring JPA以类似整合Hibernate或者JDO的方式, 提供了对 Java Persistence API 的全面支持,同时为提供附加的特性,必须了解底层的实现。

12.6.1. 在Spring环境中建立JPA

Spring JPA 提供了三种方法创建JPA EntityManagerFactory

12.6.1.1. LocalEntityManagerFactoryBean

LocalEntityManagerFactoryBean负责创建一个适合于仅使用JPA进行数据访问的环境的 EntityManager。 Factory bean将使用JPA PersistenceProvider 类的自动检测机制(根据JPA的 Java SE启动),而在绝大多数情况下,只需要指定persistence unit名称:

<beans>

   <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">
      <property name="persistenceUnitName" value="myPersistenceUnit"/>
   </bean>

</beans>

这种JPA部署方式最为简单,但却最受限制。例如,不能连接到现有的JDBCDataSource, 并且不支持全局事务。甚至,持久化类的织入(字节码转换)也是特定于提供者的,经常需要在启动时指定一个特定的JVM代理。 总之,这种方法实际上只适用于独立的应用程序和测试环境(这正是JPA规范设计它的原因)。

仅在简单部署环境中只使用这种方式,比如独立的应用程序和集成测试。

12.6.1.2. 从JNDI中获取 EntityManagerFactory

从JNDI获取 EntityManagerFactory (例如在Java EE 5环境中),仅通过修改XML配置即可实现:

<beans>

    <jee:jndi-lookup id="entityManagerFactory" jndi-name="persistence/myPersistenceUnit"/>

</beans>

在标准的Java EE 5启动过程中,Java EE服务器自动检测持久化单元(例如应用程序文件包中的META-INF/persistence.xml) ,以及Java EE部署描述符中定义给那些持久化单元命名上下文位置的环境的persistence-unit-ref项(例如web.xml)。

在这种情况下,整个持久化单元部署,包括持久化类的织入(字码码转换)都取决于Java EE服务器。 JDBC DataSource 通过在META-INF/persistence.xml 文件中的JNDI位置进行定义;EntityManager事务与服务器的JTA子系统整合。Spring仅仅用获得的EntityManagerFactory, 通过依赖注入将它传递给应用程序对象,并为它管理事务(一般通过JtaTransactionManager)。

注意,如果在同一个应用程序中使用了多个持久化单元,JNDI获取的这种持久化单元的bean名称 应该与应用程序用来引用它们的持久化单元名称相符(例如@PersistenceUnit@PersistenceContext注解)。

在部署到Java EE 5服务器时使用该方法。关于如何将自定义JPA提供者部署到服务器,以及允许使用服务器提供的缺省提供者之外的JPA提供者,请查看服务器文档的相关说明。

12.6.1.3. LocalContainerEntityManagerFactoryBean

LocalContainerEntityManagerFactoryBean 提供了对JPA EntityManagerFactory 的全面控制,非常适合那种需要细粒度定制的环境。LocalContainerEntityManagerFactoryBean 将基于 persistence.xml 文件创建 PersistenceUnitInfo 类,并提供 dataSourceLookup 策略和 loadTimeWeaver。 因此它可以在JNDI之外的用户定义的数据源之上工作,并控制织入流程。

<beans>
        
 <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="dataSource" ref="someDataSource"/>
  <property name="loadTimeWeaver">
    <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
  </property>
 </bean>
 
</beans>

这是最为强大的JPA配置方式,允许在应用程序中灵活进行本地配置。它支持连接现有JDBC DataSource , 支持本地事务和全局事务等等。然而,它也将需求强加到了运行时环境中,例如,如果持久化提供者需要字节码转换,则必须有织入ClassLoader的能力。

注意,这个选项可能与Java EE 5服务器内建的JPA功能相冲突。因此,当运行在完全Java EE 5环境中时, 要考虑从JNDI获取EntityManagerFactory。另一种可以替代的方法是,在 LocalContainerEntityManagerFactoryBean定义中通过“persistenceXmlLocation”指定相关位置, 例如“META-INF/my-persistence.xml”,并且只将包含该名称的描述符放在应用程序包文件中。因为Java EE 5服务器将只 查找默认的META-INF/persistence.xml文件,它会忽略这种定制的持久化单元,因而避免与前面Spring 驱动的JPA配置冲突。(例如,适用于Rdsin 3.1)。

在基于Spring的应用程序环境中使用该方式可获得全部JPA功能。这包括web容器,如Tomcat, 以及独立的应用程序和包含复杂持久化需求的集成测试。

LoadTimeWeaver 接口由Spring提供,允许JPA ClassTransformer 实例 能够根据环境(web容器/应用服务器)以特定的方式插入。 通过Java 5 代理挂钩 ClassTransformers经常是无效的 ―― 代理通常在 整个虚拟机 环境下工作,并且监控 每一个 被加载的类 ―― 这在生产环境下一般是不提倡的。

Spring提供了大量用于不同环境的 LoadTimeWeaver 实现类, 允许 ClassTransformer 实例能够仅用于每个classloader ,而不是每个虚拟机。

接下来的一节将讨论在Tomcat以及使用Spring的VM代理情况下的典型JPA织入配置。关于设置常用加载时织入的详细内容, 请参见AOP一章中的第 6.8.4.5 节 “Spring配置”一节,它涵盖了Tomcat、VM代理以及WebLogic、OC4J、GlassFish和Resin。

12.6.1.3.1. Tomcat(5.0以上)加载时的织入配置

Apache Tomcat 缺省的ClassLoader(类装载器)并不支持类的切换, 但是它允许使用用户自定义的类装载器。Spring提供了 TomcatInstrumentableClassLoader 类 (在org.springframework.instrument.classloading.tomcat 包中),这个类继承自Tomcat的类装载器 (WebappClassLoader)并且允许JPA ClassTransformer 的实例来“增强”所有由它加载的类。 简单说,JPA转化器(JPA transformer)仅仅在(使用 TomcatInstrumentableClassLoader 的)特定web应用程序中才能被使用。

为使用用户自定义的类装载器:

  1. spring-tomcat-weaver.jar 复制到 $CATALINA_HOME/server/lib 下 (其中$CATALINA_HOME 表示Tomcat的安装路径)。

  2. 通过修改web application context使Tomcat使用用户自定义的类装载器(而不是默认的类装载器):

    <Context path="/myWebApp" docBase="/my/webApp/location">
        <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
    </Context>

    Tomcat 5.0.x 和 5.5.x 系列支持多个上下文路径(context locations): 服务器配置文件($CATALINA_HOME/conf/server.xml), 默认的上下文配置($CATALINA_HOME/conf/context.xml)会影响所有被部署的web应用程序、 单独部署在Server端的web应用程序的配置($CATALINA_HOME/conf/[enginename]/[hostname]/my-webapp-context.xml) 或者与web应用程序一起(your-webapp.war/META-INF/context.xml)。从效率的角度说, 我们推荐在web-app的内部配置的方式,因为仅仅使用JPA的应用程序会使用用户自定义的类装载器。 更多具体有关可用的上下文路径的内容请参见Tomcat 5.x的文档。

    注意,5.5.20之前的版本有一个XML配置解析的bug,造成 server.xml 中无法使用Loader标签,无论是否指定了classloader,也不管这个classloader是官方的还是自定义的。

            

    如果你正在使用的是Tomcat 5.5.20以上的版本,就可以将useSystemClassLoaderAsParent设置成         false来解决这个问题:

    <Context path="/myWebApp" docBase="/my/webApp/location">
        <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"
                useSystemClassLoaderAsParent="false"/>
    </Context>
  1. spring-tomcat-weaver.jar复制到$CATALINA_HOME/lib (where $CATALINA_HOME表示Tomcat安装根目录的位置)。

  2. 通过编辑web应用程序上下文文件,使Tomcat使用自定义的ClassLoader(而不是默认的ClassLoader):

    <Context path="/myWebApp" docBase="/my/webApp/location">
        <Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader"/>
    </Context>

    Tomcat 6.0.x (类似于5.0.x/5.5.x)系列支持几种上下文路径:(context locations): 服务器配置文件($CATALINA_HOME/conf/server.xml), 默认的上下文配置($CATALINA_HOME/conf/context.xml)会影响所有被部署的web应用程序、 单独部署在Server端的web应用程序的配置($CATALINA_HOME/conf/[enginename]/[hostname]/my-webapp-context.xml) 或者与web应用程序一起(your-webapp.war/META-INF/context.xml)。从效率的角度说, 我们推荐在web-app的内部配置的方式,因为仅仅使用JPA的应用程序会使用用户自定义的类装载器。 更多具体有关可用的上下文路径的内容请参见Tomcat 5.x documentation。

  • Tomcat 5.0.x/5.5.x

  • Tomcat 6.0.x

所有Tomcat版本所需的最后一步,是在配置LocalContainerEntityManagerFactoryBean的时,使用 相应的LoadTimeWeaver

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="loadTimeWeaver">
    <bean class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
  </property>
</bean>

利用这种方法,依赖工具的JPA应用程序无需代理就可以在Tomcat上运行。这在宿主应用程序依赖不同的JPA实现时尤为重要, 因为JPA转化器只适用于ClassLoader级别,它们之间是彼此隔离的。

注意

如果Tomcat使用TopLink作为JPA提供者,请将核心的toplink jar包放在$CATALINA_HOME/shared/lib文件夹中,而不再放到war中。

12.6.1.3.2. 使用VM代理的全局加载时织入

对于需要类工具,同时现有的LoadTimeWeaver实现不提供这种支持的环境,JDK代理是唯一的解决方案。对于这种情况,Spring提供了 需要特定于Spring(但非常常用)的VM代理(spring-agent.jar)的InstrumentationLoadTimeWeaver

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="loadTimeWeaver">
    <bean class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
  </property>
</bean>

请注意在启动虚拟机时,同时启动Spring代理,方法是提供下列JVM选项:

-javaagent:/path/to/spring-agent.jar
12.6.1.3.3. 上下文范围内的加载时织入配置

自Spring 2.5,可以使用context:load-time-weaver元素来配置 上下文范围的LoadTimeWeaver了。这种“全局”织入器由所有JPA LocalContainerEntityManagerFactoryBeans自动拣选。

这是配置加载时织入器的推荐方法,提供平台(ebLogic, OC4J, GlassFish, Tomcat, Resin, VM agent)的自动检测,以及织入器到所有织入器知道的bean的自动传播。

<context:load-time-weaver/>

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    ...
</bean>

关于如何建立常用加载时织入的详细内容,请参见第 6.8.4.5 节 “Spring配置”一节,它涵盖了 Tomcat、VM代理,以及WebLogic, OC4J, GlassFish和Resin。

12.6.1.4. 处理多持久化单元

对于那些依靠多个持久化单元位置(例如存放在classpath中的多个jar中)的应用程序, Spring提供了作为中央仓库的PersistenceUnitManager, 避免了持久化单元查找过程(的潜在开销)。缺省实现允许指定多个位置 (默认情况下classpath会搜索META-INF/persistence.xml文件),它们会被解析然后通过持久化单元名称被获取:

<bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager">
  <property name="persistenceXmlLocation">
    <list>
     <value>org/springframework/orm/jpa/domain/persistence-multi.xml</value>
     <value>classpath:/my/packagecustom-persistence.xml</value>
     <value>classpath*:META-INF/persistence.xml</value>
    </list>
  </property>
  <property name="dataSources">
   <map>
    <entry key="localDataSource" value-ref="local-db"/>
    <entry key="remoteDataSource" value-ref="remote-db"/>
   </map>
  </property>
  <!-- if no datasource is specified, use this one -->
  <property name="defaultDataSource" ref="remoteDataSource"/>
</bean>

<bean id="emf" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="persistenceUnitManager" ref="pum"/>
</bean>

要注意的是,缺省实现允许在将持久化单元信息传入JPA provider之前用 PersistenceUnitPostProcessor(它允许选择持久化单元)修改它们, 传入的过程可以是通过属性声明式地传入(影响其中所有的单元)或编程式地传入。 如果没有指定persistenceUnitManager,LocalContainerEntityManagerFactoryBean 会创建一个并在内部使用它。

12.6.2. JpaTemplateJpaDaoSupport

每个基于JPA的DAO都将通过依赖注入接收一个 EntityManagerFactory 实例。 这样的DAO可以通过指定的 EntityManagerFactory 来操作原生JPA的API进行数据访问, 也可以直接使用Spring的 JpaTemplate

<beans>

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

</beans>
public class JpaProductDao implements ProductDao {
  
    private JpaTemplate jpaTemplate;

    public void setEntityManagerFactory(EntityManagerFactory emf) {
        this.jpaTemplate = new JpaTemplate(emf);
    }

    public Collection loadProductsByCategory(final String category) throws DataAccessException {
        return (Collection) this.jpaTemplate.execute(new JpaCallback() {
            public Object doInJpa(EntityManager em) throws PersistenceException {
                Query query = em.createQuery("from Product as p where p.category = :category");
                query.setParameter("category", category);
                List result = query.getResultList(); 
                // do some further processing with the result list
                return result;
            }
        });
    }
}

JpaCallback 实现允许任意类型的JPA数据访问。 JpaTemplate 将确保 EntityManager 正确地打开和关闭,并且能够自动地参与到事务中去。 除此之外,JpaTemplate 能够恰当地处理异常,确保资源的及时清理以及必要时的事务回滚。 Template实例不仅是线程安全的,而且它是可重用的,因而它能够作为实例变量被一个类持有。 注意, JpaTemplate 提供了简单的诸如find、load、merge等操作的快捷函数来替代默认的回调实现。

不仅如此,Spring还提供了一个方便的 JpaDaoSupport 基类,提供了 get/setEntityManagerFactory 方法以及 getJpaTemplate() 方法供子类调用:

public class ProductDaoImpl extends JpaDaoSupport implements ProductDao {
  
    public Collection loadProductsByCategory(String category) throws DataAccessException {
        Map<String, String> params = new HashMap<String, String>();
        params.put("category", category);
        return getJpaTemplate().findByNamedParams("from Product as p where p.category = :category", params);
    }
}

除了直接使用Spring的 JpaTemplate之外,也可以使用原生JPA的API来实现基于Spring的DAO, 此时你需要自行明确地处理EntityManager。正如在相应的Hibernate章节描述的一样,这种做法的主要优 点在于你的数据访问代码可以在整个过程中抛出checked exceptions。JpaDaoSupport 为这种情况提 供了多种函数支持,包括获取和释放一个具备事务管理功能的 EntityManager 和相关的异常转化。

JpaTemplate主要以JdoTemplate和HibernateTemplate的同胞而存在,为使用它的人们提供相同的风格。 对于新启动的项目,要考虑采用原生的JPA风格编写数据访问对象的代码,基于通过JPA@PersistenceContext注解 获取的一个“共享EntityManager”引用(利用Spring的PersistenceAnnotationBeanPostProcessor;详情如下。)

12.6.3. 基于原生的JPA实现DAO

注意

虽然EntityManagerFactory实例是线程安全的, 但EntityManager实例确实不是这样。被注入的JPA EntityManager的行为和从应用服务器JNDI环境中获取的没有区别, 和JPA规范定义的一样。如果存在一个被注入的JPA EntityManager, 它会代理对当前事务化的EntityManager的所有调用; 否则每个操作都会创建一个EntityManager,确保线程安全。

你完全可以使用原生的JPA的API进行编程,而无需对Spring产生任何依赖,这可以通过一个被注入的 EntityManagerFactoryEntityManager 来完成。 注意,如果 PersistenceAnnotationBeanPostProcessor 被启用,Spring就能够识别字段或者方法级别的 @PersistenceUnit@PersistenceContext 注解。相应的DAO实现看起来像这样:

public class ProductDaoImpl implements ProductDao {

    private EntityManagerFactory emf;

    @PersistenceUnit
    public void setEntityManagerFactory(EntityManagerFactory emf) {
        this.emf = emf;
    }

    public Collection loadProductsByCategory(String category) {
        EntityManager em = this.emf.createEntityManager();
        try {
             Query query = em.createQuery("from Product as p where p.category = ?1");
             query.setParameter(1, category);
             return query.getResultList();
        }
        finally {
            if (em != null) {
                em.close();
            }
        }
    }
}

上述的DAO不对Spring产生任何依赖,而它就如同使用Spring的 JpaTemplate 那样,仍然非常适合 在Spring的application context中进行配置。此外,这样的DAO可以利用注解来要求缺省EntityManagerFactory 的注入:

<beans>

  <!-- bean post-processor for JPA annotations -->
  <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

  <bean id="myProductDao" class="product.ProductDaoImpl"/>

</beans>

注意:作为显式定义PersistenceAnnotationBeanPostProcessor的另一种方法, 可以考虑在应用程序上下文配置中使用Spring 2.5的context:annotation-config XML元素。 它会自动为基于注解的配置注册Spring所有标准的后置处理器(包括CommonAnnotationBeanPostProcessor等等)。

<beans>

  <!-- post-processors for all standard config annotations -->
  <context:annotation-config/>

  <bean id="myProductDao" class="product.ProductDaoImpl"/>

</beans>

这类DAO的主要问题在于每次总是从工厂中获取一个新的 EntityManager 实例。 这一点可以通过对 EntityManager 而不是factory进行注入来解决:

public class ProductDaoImpl implements ProductDao {

    @PersistenceContext
    private EntityManager em;

    public Collection loadProductsByCategory(String category) {
       Query query = em.createQuery("from Product as p where p.category = :category");
       query.setParameter("category", category);
       return query.getResultList(); 
    }
}

注意:@PersistenceContext注解有一个可选的属性type,它的默认值是 PersistenceContextType.TRANSACTION。该默认需要你接收一个“共享EntityManager”代理。 另一种方式,PersistenceContextType.EXTENDED则完全不同:它会产生一个所谓的“扩展EntityManager”, 这个不是线程安全的,因此不应该用在当前被访问的组件中,例如Spring管理的singleton bean。 扩展的EntityManager只能用在有状态的组件中,例如,在会话中,EntityManager的生命周期与当前事务无关,完全取决于应用程序。

被注入的 EntityManager 由Spring管理(能够感知当前的事务)。 值得注意的是,即使这种新的实现更倾向于方法级别的注入(使用 EntityManager 而不是 EntityManagerFactory), 对于注解的使用,application context的XML配置则无需任何改变。

这种DAO风格的主要优点在于它仅仅依赖JPA,而无需依赖任何的Spring的类。除此之外,由于JPA的注解可被理解,注入能够被Spring容器自动应用。 从无入侵性的角度来说,这一点非常有吸引力,它对于JPA开发者来说也更自然。

12.6.4. 异常转化

然而,DAO不仅会抛出普通的 PersistenceException 异常(这是一个无需声明和捕获的unchecked exception), 还会抛出诸如 IllegalArgumentExceptionIllegalStateException 之类的异常。 这意味着,DAO的调用者只能以普通的错误来处理这些异常,除非完全依赖JPA自身的异常体系。因而,除非你将DAO的调用者绑定到具体的实现策略上去, 否则将无法捕获特定的异常原因(诸如乐观锁异常)。这种折中平衡或许可以被接受,如果你的应用完全基于JPA或者无需进行特殊的异常处理。不过, Spring提供了一个允许你进行透明的异常转化的解决方案:通过使用 @Repository 注解:

@Repository
public class ProductDaoImpl implements ProductDao {

    // class body here...

}
<beans>

  <!-- Exception translation bean post processor -->
  <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor"/>

  <bean id="myProductDao" class="product.ProductDaoImpl"/>

</beans>

后置处理器将自动地寻找所有的异常转化器(PersistenceExceptionTranslator 接口的实现), 并通知所有标有 @Repository 注解的bean,从而能够使得被找到的异常转化器能够在抛出异常时进行相应的异常转化工作。

总之:DAO能够基于普通的Java持久层API和注解来实现,但同样也能享受到由Spring管理事务、IoC和透明的异常转化 (转化成为Spring的异常体系)等好处。

上一篇: 下一篇: