上下文的配置
/* 【3】设置事务管理
* 3.1】允许@Transactional标记有效
* 使得@Transactional有效,可以被动态截获bean中被标识的方法,对其进行Spring framework method Advice,类似@EnableAsync,需要设置@EnableTransactionManagement。但需要注意:对于方法增强,我们应使用同一方式,如果配置成不同,spring将选择其中的一个,会导致不确定性。因此,@EnableAsync和@EnableTransactionManagement需使用相同的AdviceMode(PROXY or ASPECTJ),以及同样的proxyTargetClass的值,简单地可以分别设置为PROXY和flase。
* ➤ AdviceMode.PROXY表示通过proxy的类将wrap被标记的方法。动态proxy(proxyTargetClass = false)只在调用标识方法时才对方法进行封装,而CGLIB(proxyTargetClass = true)则会override所有的方法,即调用非标记方法时也会进行方法增强(感觉应该是在创建实例是进行,也因此CGLIB不能用final class)。
* 1、通常建议proxyTargetClass = false,也就是说这个方法必须是在接口中的,且本类内方法调用时标记是无效的,必须在外部调用才有效。
* 2、如果我们非要在一个非接口的其他public方法中使用,就需要使用CGLIB proxy,即proxyTargetClass = true,CGLIB proxy的缺点是构造函数被调用了两次。
* ➤ AdviceMode.ASPECTJ是load-time weaving enabled,也就是说在load的时候,修改编译的bytecode,将相关的方法增强加入,可以用于final类和方法,可以用于类内方法的调用,甚至可以用于非spring bean(适用于传统代码改造)。使用ASPECTJ,还需要加上
* @EnableLoadTimeWeaving(aspectjWeaving=EnableLoadTimeWeaving.AspectJWeaving.ENABLED)
* 并在pom.xml中加入:
* <dependency>
* <groupId>org.springframework</groupId>
* <artifactId>spring-aspects</artifactId>
* <version>4.0.2.RELEASE</version>
* <scope>runtime</scope>
* </dependency>
*
* 当多个proxy存在的时候,我们还需考虑proxy的顺序。如果transaction proxy在异步proxy之前,可能会导致无法真正异步执行,因此我们设置了合适的顺序。显然异步执行应更优先,否则在@Transactional内调用标记了@Async的方法,异步将不生效。
* */
@Configuration
@EnableScheduling
@EnableAsync(mode = AdviceMode.PROXY, proxyTargetClass = false, order = Ordered.HIGHEST_PRECEDENCE)
@EnableTransactionManagement(mode = AdviceMode.PROXY, proxyTargetClass = false, order = Ordered.LOWEST_PRECEDENCE)
@ComponentScan(basePackages = "cn.wei.flowingflying.chapter21.site",
excludeFilters = @ComponentScan.Filter({Controller.class, ControllerAdvice.class}) )
public class RootContextConfiguration implements AsyncConfigurer, SchedulingConfigurer{
private static final Logger log = LogManager.getLogger();
private static final Logger schedulingLogger = LogManager.getLogger(log.getName() + ".[scheduling]");
/*【1】设置DataSource。
* 方式一:如果我们简单地只是使用一个jdbc的连接,这种方式只是在临时项目中使用,可能是测试代码,也可能是原型代码
* @Bean
* public DataSource springJpaDataSource(){
* DriverManagerDataSource dataSource = new DriverManagerDataSource();
* dataSource.setUrl("jdbc:mysql://localhost/SpringJpa");
* dataSource.setUsername("tomcatUser");
* dataSource.setPassword("password1234");
* return dataSource;
* }
*
* 方式二:利用C3P0提供的连接地址池。我们需要在pom.xml中加入
* <dependency>
* <groupId>mysql</groupId>
* <artifactId>mysql-connector-java</artifactId>
* <version>5.1.44</version>
* </dependency>
* <dependency>
* <groupId>com.mchange</groupId>
* <artifactId>c3p0</artifactId>
* <version>0.9.5.2</version>
* </dependency>
* 由于我们没有使用tomcat的连接池,因此在war卸装的时候,需要小心地进行关闭。
* 关闭的代码可以参见http://blog.csdn.net/flowingflying/article/details/51932956。
* @Bean
* public DataSource springJpaDataSource() throws PropertyVetoException {
* ComboPooledDataSource dataSource = new ComboPooledDataSource();
* dataSource.setDriverClass("com.mysql.jdbc.Driver");
* dataSource.setJdbcUrl("jdbc:mysql://localhost/SpringJpa?useSSL=true&useUnicode=true&characterEncoding=utf-8&autoReconnect=true");
* dataSource.setUser("tomcatUser");
* dataSource.setPassword("password1234");
* dataSource.setMinPoolSize(2);
* dataSource.setAcquireIncrement(1);
* dataSource.setMaxPoolSize(20);
* dataSource.setMaxIdleTime(2);
* dataSource.setInitialPoolSize(2);
* dataSource.setIdleConnectionTestPeriod(300);
* return dataSource;
* }
* 除了C3P0外,我们还可以使用Apache Commons DBCP,Apache CommonsPool来串接连接池的DataSource。
*
* 方式三:延用之前tomcat context配置的方式。即例子给出的方式。
*/
@Bean
public DataSource springJpaDataSource(){
JndiDataSourceLookup lookup = new JndiDataSourceLookup();
return lookup.getDataSource("jdbc/learnTest");
}
/*【2】创建persistence unit。在Spring中,我们需要一个bean来实现org.springframework.orm.jpa.AbstractEntityManagerFactoryBean,它将创建SharedEntityManagerBean来为我们的repository提供线程安全的事务。
* 方式一:通过LocalEntityManagerFactoryBean实现,使用/META-INF/persistence.xml配置文件,通过设置persistence unit Name来设置
* @Bean
* public LocalEntityManagerFactoryBean entityManagerFactoryBean(){
* LocalEntityManagerFactoryBean factory = new LocalEntityManagerFactoryBean();
* factory.setPersistenceUnitName("SpringJpa");
* factory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
* factory.setDataSource(this.springJpaDataSource());
* return factory;
* }
* 方式二:通过LocalContainerEntityManagerFactoryBean实现,来指定xml配置文件
* @Bean
* public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){
* LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
* factory.setPersistenceXmlLocation("classpath:com/wrox/config/persistence.xml");
* factory.setPersistenceUnitName("SpringJpa");
* factory.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
* factory.setDataSource(this.springJpaDataSource());
* return factory;
* }
* 方式三:通过LocalContainerEntityManagerFactoryBean实现,在代码中进行配置,不适用xml配置文件。
* 即小例子给出的代码。在之前学习中,我们通过persistence.xml来进行设置,可以进行对照,实际是相同的。*/
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactoryBean(){
//2.1) 创建map用于存放JPA的properties配置
Map<String, Object> properties = new Hashtable<>();
properties.put("javax.persistence.schema-generation.database.action","none");
//2.2) 创建hibernate adapter作为factory的adaptor,提供了很多信息
// ➤ 相当于persistence.xml中的<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
// ➤ 告知SharedEntityManagerBean其所需要proxy的EntityManagerFactory(扩展为是HibernateEntityManagerFactory)和EntityManager(扩展为HibernateEntityManager)。
// ➤ 告知spring需要翻译Hibernate相关的ORM异常为DataAccessException们。
// ➤ 可为Hibernate配置正确的是数据库语言。传统的dialect是MySQL 4.x,为了避免版本上的歧义,最好明确进行设定
// - MySQL 4.x Generic : org.hibernate.dialect.MySQLDialect(对MySQL的缺省选择)
// - MySQL 4.x MyISAM Engine :org.hibernate.dialect.MySQLMyISAMDialect
// - MySQL 4.x InnoDB Engine:org.hibernate.dialect.MySQLInnoDBDialect
// - MySQL 5.x+ Generic + MyISAM : org.hibernate.dialect.MySQL5Dialect
// - MySQL 5.x+ InnoDB Engine :org.hibernate.dialect.MySQL5InnoDBDialect
HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
adapter.setDatabasePlatform("org.hibernate.dialect.MySQL5InnoDBDialect");
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(adapter);
//2.3) 设置DataSource,相当于xml配置中的<non-jta-data-source>和transaction-type为RESOURCE_LOCAL。如果使用setJtaDataSource()则相当于<jta-data-source>和transaction-type为JTA,这一般用于多数据源的情况。
factory.setDataSource(this.springJpaDataSource());
//2.4)设置扫描路径。相当于<exclude-unlisted-classes>true</exclude-unlisted-classes>,并在<class>中列出entity class
factory.setPackagesToScan("cn.wei.flowingflying.chapter21.site.entity");
factory.setSharedCacheMode(SharedCacheMode.ENABLE_SELECTIVE);
factory.setValidationMode(ValidationMode.NONE);
factory.setJpaPropertyMap(properties);
return factory;
}
/** 3.2】设置PlatformTransactionManager Bean
* 当我们使用@EnableTransactionManagement,就必须设置PlatformTransactionManager bean。对于JPA来讲,使用JpaTransactionManager,需要在构造函数中关联了EntityManagerFactory。
* 本例子给出了单一PlatformTransactionManager Bean的代码。如果存在多个PlatformTransactionManager Bean,则jpaTransactionManager是缺省的事务管理器,其例子如下
* public SomeService{
* @Transactional public void actionOne(); //使用缺省的TransactionManager
* @Transactional("jpaTransactionManager") public void actionTwo();
* @Transactional("dataSourceTransactionManager") public void actionThree();
* }
* 设置其他PlatformTransactionManager的方式如下:
* 1、设置其他PlatformTransactionManager的例子:
* @Bean
* public PlatformTransactionManager dataSourceTransactionManager(){
* return new DataSourceTransactionManager(this.springJpaDataSource()); //用于简单的数据源操作。
* }
* 2、设置缺省值。对于JTA@Transactional标记,spring使用缺省的PlatformTransactionManager,这是由TransactionManagementConfigurer 返回的,如果由多个事务管理器,则是TransactionManagementConfigurer里面名为txManager,如果我们使用xml作为配置(可以在网上search一下),就比较清晰了。如果我们使用的spring的@Transactional标记,则可以在标记中指定使用哪个PlatformTransactionManager,如果没有指明,则使用缺省的,即事务管理会返回一个(第一个)实现PlatformTransactionManager的名为txManager的bean。但是如果存放多个PlatformTransactionManagers,我们最好明确指明哪个是缺省的,这在spring中的TransactionManagementConfigurer 接口通过annotationDrivenTransactionManager()返回,因此需要继承TransactionManagementConfigurer,即
* public class RootContextConfiguration implements AsyncConfigurer, SchedulingConfigurer, TransactionManagementConfigurer{
* @Override
* public PlatformTransactionManager annotationDrivenTransactionManager() {
* return this.dataSourceTransactionManager();
* }
* 這里有一个问题:这里不能返回this.jpaTransactionManager()。在entityManagerFactoryBean()中的factory.setDataSource(this.springJpaDataSource())会调用annotationDrivenTransactionManager()方法,如果annotationDrivenTransactionManager()方法调用jpaTransactionManager(),则jpaTransactionManager()就会调用entityManagerFactoryBean,这是个循环,会报错的。
* 如果只有一个数据源,如本例子所示,我们不需要这样进行操作,但是如果有多种数据源,我们就需要考虑了。*/
@Bean
public PlatformTransactionManager jpaTransactionManager(){
return new JpaTransactionManager(this.entityManagerFactoryBean().getObject());
}
.....
}
相关链接: 我的Professional Java for Web Applications相关文章