(ãã®14)ãã©ã³ã¶ã¯ã·ã§ã³ç®¡ç
AOPç¨éã®73%ãå ãããã©ã³ã¶ã¯ã·ã§ã³ç®¡ç*1ããããåããã°ã¨ããããGuiceã使ã£ã¦ã¿ããã¨ãã人ãå¢ãããããããªãã®ã§ãã¡ãã£ã¨è©¦ãã¦ã¿ã¾ãã
ãã©ã³ã¶ã¯ã·ã§ã³æ©è½ã¯ä»ã©ã¤ãã©ãªã使ã
Guiceã¯ãã©ã³ã¶ã¯ã·ã§ã³ã«é¢ããæ©è½ãä¸åæã£ã¦ãã¾ãããããã¥ã¡ã³ããè¦ãã¨"@Transactional"ã¨ãæ¸ãã¦ãã£ã¦ç´ããããã®ã§ããããããããã¢ããã¼ã·ã§ã³ã使ãã°ï¼ãã¨ããç¨åº¦ã®è©±ã§ãããªã®ã§ãå®éã«ãã©ã³ã¶ã¯ã·ã§ã³ç®¡çãè¡ãå ´åã¯ãèªåã§å®è£ ããããä»ã®ã©ã¤ãã©ãªã®æ©è½ã使ãäºã«ãªãã¾ããä»åã¯Springã®TransactionInterceptorã使ã£ã¦ã¿ã¾ãããSesar2ã§ããã¶ãåããããªäºãåºæ¥ã¾ãããã¡ãã£ã¨ã¤ã³ã¿ã¼ã»ãã¿ãæ¸ãã°Hibernateã®ãã©ã³ã¶ã¯ã·ã§ã³æ©è½ã使ãäºãåºæ¥ãã¯ãã§ãã
ä»åã®è¦ä»¶
- ã¡ã½ããã«@Transactionalã¨æ¸ãã¨ããã®ã¡ã½ããã®å¦çããã©ã³ã¶ã¯ã·ã§ã³ã¨ãªãã
- éæ¤æ»ä¾å¤ï¼RuntimeExceptionã®ãµãã¯ã©ã¹ï¼ãèµ·ããããã¼ã«ããã¯ããã®ä»ã®å ´åã¯ã³ããããããã
- ãã©ã³ã¶ã¯ã·ã§ã³å±æ§ã¯Requiredï¼ã¨ãããããã©ã³ã¶ã¯ã·ã§ã³ãéå§ãããäºãç®çãªã®ã§ãRequiresNewã¨ãã§ãåãï¼ã
ä»ã«ãã¿ã¤ã ã¢ã¦ãã¨ãåé¢ã¬ãã«ãªã©è²ã ããã¨ã¯æãã¾ãããä»åã®ãµã³ãã«ã«å½±é¿ãç¡ãã¨æãããã®ã§çç¥ãã¾ãã
Springã®TransactionInterceptorã使ã£ã¦ã¿ã
ã¨ããäºã§è©¦ãã¦ã¿ã¾ããã¾ãDBã«ã¢ã¯ã»ã¹ããé©å½ãªDao
public class DaoImpl implements Dao { @Inject private SimpleJdbcTemplate jt; public void setupTable() { jt.update("drop table if exists ID_VALUE"); jt.update("create table ID_VALUE(ID int not null auto_increment,VALUE varchar(128) not null, primary key(ID)) engine=InnoDB"); } public int insert(String value) { return jt.update("insert into ID_VALUE(VALUE) values (?)", value); } }
Spring2.0ã§å ãã£ãSimpleJdbcTemplateã使ç¨ãã¦ãã¾ããããã®å¦çã§ã¯å
¨ç¶æé£ã¿ãç¡ãã§ãããDBã¯MySQLã®InnoDBã使ãã¾ãã
次ã«ãã®Daoãå¼ã³åºããµã¼ãã¹ã®å®è£
ãä¾å¤ã渡ãã¨æããã¨ããã¯koichikさんのSpring入門記ãçä¼¼ã¾ããã
import org.springframework.transaction.annotation.Transactional; public class ServiceImpl implements Service { @Inject private Dao dao; public void setupTable() { dao.setupTable(); } public void insert(String value, RuntimeException ex) { dao.insert(value); if(ex != null) throw ex; } @Transactional public void transactionalInsert(String value, RuntimeException ex) { dao.insert(value); if(ex != null) throw ex; } }
ãããã@Transactionalãåºã¦ãã¾ããããããã¯Springã®ã¢ããã¼ã·ã§ã³ããã®ã¾ã¾ä½¿ã£ã¦ãã¾ããã¢ããã¼ã·ã§ã³ã¯Guiceã®AOPæå®ã§annotateWith(Transactional.class)ã¨æå®ããçºã®åãªããã¼ã«ãªã®ã§ãèªä½ãã@Hogeã§ãä½ã§ãè¯ãã®ã§ããããã£ãããªã®ã§ä½¿ã£ã¦ãã¾ãã
ããã¦ãµã¼ãã¹ãå¼ã³åºãã³ã¼ãã§ããã¡ããã¨ãã©ã³ã¶ã¯ã·ã§ã³ã«ãªã£ã¦ããäºãåããããã«ããã©ã³ã¶ã¯ã·ã§ã³ç¡ãã®ä¾å¤æãç¡ãããã©ã³ã¶ã¯ã·ã§ã³æãã®ä¾å¤æãç¡ããã®4ãã¿ã¼ã³ã2åç¹°ãè¿ãã¦ãã¾ããIllegalAccessErrorã¯ã¡ããã¨ä¾å¤ãæãããã¦ããäºã®ç¢ºèªç®çã§ãã
public class Client { @Inject private Service service; public void execute() { service.setupTable(); for(int i = 0; i < 2; i++) { // ãã©ã³ã¶ã¯ã·ã§ã³ç¡ãinsert service.insert("non transactional insert", null); try { // ãã©ã³ã¶ã¯ã·ã§ã³ç¡ãinsertãä¾å¤ã§ããã¼ã«ããã¯ããªãã¯ã service.insert("non transactional insert with RuntimeException", new RuntimeException()); throw new IllegalAccessError(); } catch(RuntimeException ex) { } // ãã©ã³ã¶ã¯ã·ã§ã³æãinsert service.transactionalInsert("transactional insert", null); try { // ãã©ã³ã¶ã¯ã·ã§ã³æãinsertãä¾å¤ã«ãã£ã¦ãã¼ã«ããã¯ããã¯ã service.transactionalInsert("transactional insert with RuntimeException", new RuntimeException()); throw new IllegalAccessError(); } catch(RuntimeException ex) { } } } }
ããã¦æå¾ã«ãGuiceã®DIã³ã³ãããè¨å®ãã¦èµ·åããã¯ã©ã¹ã
public class Boot { public static void main(String[] args) { Injector injector = Guice.createInjector(new AbstractModule() { protected void configure() { // @Transcationalãã¤ããããã¡ã½ããã«ã¤ã³ã¿ã¼ã»ãã¿ãè¨å® TransactionInterceptor transactionInterceptor = createTransactionInterceptor(); bindInterceptor(any(), annotatedWith(Transactional.class), transactionInterceptor); bind(SimpleJdbcTemplate.class).toProvider(SimpleJdbcTemplateProvider.class); bind(Service.class).to(ServiceImpl.class); bind(Dao.class).to(DaoImpl.class); } // ã¤ã³ã¿ã¼ã»ãã¿ã®çæ private TransactionInterceptor createTransactionInterceptor() { // DataSource DataSource ds = new DriverManagerDataSource( "com.mysql.jdbc.Driver", "jdbc:mysql://127.0.0.1:3306/GUICE", "guice", "guice"); bind(DataSource.class).toInstance(ds); // TransactionManager PlatformTransactionManager txManager = new DataSourceTransactionManager(ds); // TransactionInterceptor Properties props = new Properties(); // TransactionInterceptorãã¡ã½ããåãå¤å®ããã®ã§ã*ãæå®ãã¦ããã props.setProperty("*", "PROPAGATION_REQUIRED"); TransactionInterceptor transactionInterceptor = new TransactionInterceptor(txManager, props); return transactionInterceptor; } }); // å®è¡ injector.getInstance(Client.class).execute(); } }
その3ã§è©¦ããéããAOPã¯bindInterceptor(é©ç¨ã¯ã©ã¹, é©ç¨ã¡ã½ãã, ã¤ã³ã¿ã¼ã»ãã¿)ã§weaveãã¾ãããã®ãµã³ãã«ã§ã¯ãé©ç¨ããã¡ã½ããã¨ãã¦Transactionalã¨ããã¢ããã¼ã·ã§ã³ãã¤ãããã®ãæå®ãã¦ãã¾ãã
ã¨ããã§ã覧ã®éãã¤ã³ã¿ã¼ã»ãã¿ãçæããé¨åãé常ã«ã¢ã¬ã§ããæ®éã«èããã¨DataSourceãTransactionManagerã®Providerãä½ã£ã¦InterceptorProviderã«injectãããã¨ãããªã®ã§ãããã¤ã³ã¿ã¼ã»ãã¿ãè¨å®ããbindInterceptor()ã®ç¬¬3å¼æ°ã¯MethodInterceptorããåããªãã®ã§ããã®åã«ã¤ã³ã¹ã¿ã³ã¹ãä½ã£ã¦ãããªããã°ãªããªãããã§ãããã1ã¤äºåã«injectorãä½ãã¨ãããã°å¤å°ã¯è¯ããªãããã§ãããã©ãããã¾ãã¡ãªæããç§ã®åéããªã®ãå®è£
ä¸ä¸å¯è½ãªã®ããä½ãªã®ã§ãããããããã¨ã¬ã¬ã³ãã«è¨å®ããæ¹æ³ããåç¥ã®æ¹ãããã£ããã£ãããæ¯éãæ示ä¸ããã
ã§ã¯å®è¡ãã¦ã¿ã¾ãã以ä¸ã«ãããã°ãã°ãæç²ãã¾ããã
// ã¤ã³ã¿ã¼ã»ãã¿ãä½ããã
DEBUG [org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource] - Adding transactional method [*] with attribute [PROPAGATION_REQUIRED,ISOLATION_DEFAULT]
// 1 : ãã©ã³ã¶ã¯ã·ã§ã³ç¡ãinsert
DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL statement [insert into ID_VALUE(VALUE) values (?)]
DEBUG [org.springframework.jdbc.core.JdbcTemplate] - SQL update affected 1 rows
// 2 : ãã©ã³ã¶ã¯ã·ã§ã³ç¡ãinsert
DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL statement [insert into ID_VALUE(VALUE) values (?)]
DEBUG [org.springframework.jdbc.core.JdbcTemplate] - SQL update affected 1 rows
// 3 : ãã©ã³ã¶ã¯ã·ã§ã³æãinsert
DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Creating new transaction with name [sample.guice.transaction.service.ServiceImpl.transactionalInsert]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL statement [insert into ID_VALUE(VALUE) values (?)]
DEBUG [org.springframework.jdbc.core.JdbcTemplate] - SQL update affected 1 rows
// ã³ãããããã¦ã
DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Initiating transaction commit
DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Committing JDBC transaction on Connection [com.mysql.jdbc.Connection@ef5502]
// 4 : ãã©ã³ã¶ã¯ã·ã§ã³æãinsert
DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Creating new transaction with name [sample.guice.transaction.service.ServiceImpl.transactionalInsert]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
DEBUG [org.springframework.jdbc.core.JdbcTemplate] - Executing prepared SQL statement [insert into ID_VALUE(VALUE) values (?)]
// ãã¼ã«ããã¯ããã¦ã
DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Initiating transaction rollback
DEBUG [org.springframework.jdbc.datasource.DataSourceTransactionManager] - Rolling back JDBC transaction on Connection [com.mysql.jdbc.Connection@111a775]
ä¸æããã£ã¦ãããã§ãï¼ã»ãã¨ã¯ä½åãã¯ã¾ã£ãã®ã§ãããæå¾ã«ã¯ä¸æããã£ãã¨ããäºã§ï¼ã
DBã®ä¸èº«ã¯ã
mysql> select * from ID_VALUE; +----+------------------------------------------------+ | ID | VALUE | +----+------------------------------------------------+ | 1 | non transactional insert | | 2 | non transactional insert with RuntimeException | | 3 | transactional insert | | 5 | non transactional insert | | 6 | non transactional insert with RuntimeException | | 7 | transactional insert | +----+------------------------------------------------+ 6 rows in set (0.00 sec)
ã¡ããã¨ããã©ã³ã¶ã¯ã·ã§ã³æãã§ä¾å¤ãçºçããã±ã¼ã¹ã ããã¼ã«ããã¯ããã¦ã¬ã³ã¼ããç¡ãã§ããã
ã¨ãã訳ã§ãGuiceã§ã·ã³ãã«ãªãã©ã³ã¶ã¯ã·ã§ã³ç®¡çãè¡ãã±ã¼ã¹ãè¦ã¦ã¿ã¾ãããã¤ã³ã¿ã¼ã»ãã¿çæé¨åã«èª²é¡ãæ®ã£ã¦ãã¾ãããããã¯å®åçãªã³ã¼ããªã®ã§æ°ã«ããªãã¨ããã°ãã¡ã½ããã«@Transactionalãã¤ãã¦bindInterceptor()ããã ãã§å¦çããã©ã³ã¶ã¯ã·ã§ã³åããäºãã§ããããã«ãªãäºãåããã¾ããã
追è¨(2008/3/5)
ç´1å¹´æ¯ãã«ã³ã¡ã³ããé ãã¾ãããtarouãããããã¨ããããã¾ãã
ã¨ããäºã§ãSimpleJdbcTemplateProviderã®ã½ã¼ã¹ãè²¼ãä»ãã¨ãã¾ãã
public class SimpleJdbcTemplateProvider implements Provider<SimpleJdbcTemplate> { private SimpleJdbcTemplate jt; @Inject public void setDataSource(DataSource dataSource) { jt = new SimpleJdbcTemplate(dataSource); } public SimpleJdbcTemplate get() { return jt; } }
*1:æ°å¤ã¯å