Java for Web學習筆記(一零二):Spring架構中使用JPA(2)Transaction(下)__Java

來源:互聯網
上載者:User
內容相關的配置

/* 【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相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.