內容相關的配置
/* 【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相關文章