Dict.CN 在線詞典, 英語(yǔ)學(xué)習(xí), 在線翻譯

          都市淘沙者

          荔枝FM Everyone can be host

          統(tǒng)計(jì)

          留言簿(23)

          積分與排名

          優(yōu)秀學(xué)習(xí)網(wǎng)站

          友情連接

          閱讀排行榜

          評(píng)論排行榜

          Unit tests with H2 and iBATIS

          source : http://tech.shopzilla.com/2010/05/unit-tests-with-h2-and-ibatis/

          Unit tests with H2 and iBATIS
          Posted by Steve Corwin Friday, May 7th 
          2010 

          Since my previous post on Using HSQLDB 
          for unit testing database access code I began work on a project that needed to use iBATIS instead of JPA or Hibernate for database access. Based on a recommendation from my previous post, I decided to try H2 instead of HSQLDB as the in-memory database. This project still uses Oracle and Spring.

          H2 proved much easier when the production database is Oracle. I was able to copy DDL extracted from the Oracle database and use it (almost) unmodified in H2. The DDL ran without error in all cases I’ve tried, including “created_date date 
          default sysdate not null”. There were a couple of issues:
          1. there was a Java Boolean being stored in a 1-character varchar column. The Oracle JDBC driver translates to “0″ or “1″, which fits. The H2 JDBC driver translates to “true” or “false”. I just changed the column to “varchar2(5 char)” in the test code.
          2. date columns in H2 don’t store the time, only the date (no hours, minutes, seconds). I just changed the column to a timestamp in the test code.
          (I did 
          try H2’s Oracle compatibility mode, but it didn’t make a difference.)

          iBATIS was not so pleasant 
          for me. Because of some complexities in this service, which needs to effectively combine operations on two different database connections into one transaction, I decided to use iBATIS manual transaction demarcation. This looks simple, but wasn’t so easy to get working. The iBATIS javadocs show manual transactions as a straightforward pattern:

          view sourceprint
          ?1 try {  

          2   sqlMap.startTransaction();  

          3   // do work  

          4   sqlMap.commitTransaction();  

          5 } finally {  

          6   sqlMap.endTransaction();  

          7 } 

          However there is a subtlety: depending on the SqlMapClient configuration, the commitTransaction() and endTransaction() methods may not 
          do anything at all. This was the configuration we were getting from our existing Spring config files, and the result was that none of my changes were committed to the database. Apparently something was defaulting to having the transactions handled by com.ibatis.sqlmap.engine.transaction.external.ExternalTransaction. Looking at that source you can see that the commit() method has an empty body.

          In order to have my transactions commit, I needed to use the 
          class com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransactionConfig instead. In this code the commit() method calls commit() on the underlying JDBC connection.

          Using the correct 
          class is just a matter of setting the fully-qualified class name as the value of the “transactionConfigClass” property for the SqlMapClient instance. Here is an example from applicationContext.xml:

          view sourceprint
          ?01 <bean id="sqlMapClient" 

          02 class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">  

          03    <qualifier value="account"/>  

          04    <property name="configLocation">  

          05      <value>classpath:P13NSqlMapConfig.xml</value>  

          06    </property>  

          07    <property name="dataSource" ref="masterDataSource" />  

          08    <!-- without this setting the commitTransaction() method does not  

          09 commit --> 

          10    <property name="transactionConfigClass" value=  

          11 "com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransactionConfig" />  

          12    

          13 </bean> 

          Note that you also need to have the connections set to not autocommit. For Oracle 
          this seems to be the default.

          Using 
          this unit-testing technique requires completing three steps:
          1. Create a class that will create the tables and (optionally) populate with test data.
          2. Create a unit test that will use DbTestUtils to test a DAO implementation.
          3. Supply a DataSource to the DAO/class under test.

          Most of what follows is the same as it was 
          for HSQLDB & Hibernate; the only real difference is the config file, which now supplies iBATIS objects instead of Hibernate/JPA. After some thought I decided to include the sections that haven’t changed so that this post could (hopefully) stand on its own.

          1. Create a class that will create the tables and (optionally) populate with test data.

          view sourceprint
          ?01 public class DbTestUtils {  

          02     public static void setupTables(Connection connection) throws SQLException {  

          03    

          04         dropTable(connection, "activation_code_mapping");  

          05    

          06         executeSql(connection, "create table activation_code_mapping (" 

          07                 + "activation_code varchar2(80 char) not null, " 

          08                 + "email_address varchar2(500 char) not null, " 

          09                 + "source varchar2(25 char) not null, " + "merchant_id number(38), " 

          10                 + "historical_merchant_name varchar2(1020 char), " 

          11                 // date columns in H2 don't store the time, only the date, so I changed it to  

          12                 // timestamp  

          13                 + "created_date timestamp default sysdate not null," 

          14                 + "constraint pk_activation_code_mapping primary key (activation_code)" + ")");  

          15     }  

          16    

          17     public static void dropSequence(Connection connection, String sequence) {  

          18         try {  

          19    

          20             executeSql(connection, "drop sequence " + sequence);  

          21             connection.commit();  

          22         } catch (SQLException se) {  

          23             // ignore exception  

          24             // System.out.println(se);  

          25         }  

          26     }  

          27    

          28     public static void dropTable(Connection connection, String table) {  

          29         try {  

          30    

          31             executeSql(connection, "drop table " + table);  

          32             connection.commit();  

          33         } catch (SQLException se) {  

          34             // ignore exception  

          35             // System.out.println(se);  

          36         }  

          37    

          38     }  

          39    

          40     public static void executeSql(Connection connection, String sql) throws SQLException {  

          41    

          42         Statement statement = connection.createStatement();  

          43         statement.execute(sql);  

          44     }  

          45 } 

          2. Create a unit test that will use DbTestUtils to test a DAO implementation.

          view sourceprint
          ?001 public class ActivationCodeMappingCacheStoreTest extends AbstractDependencyInjectionSpringContextTests {  

          002    

          003     private DataSource dataSource;  

          004     private ActivationCodeMappingCacheStore activationCodeMappingCacheStore;  

          005    

          006     @Override 

          007     protected String getConfigPath() {  

          008         return "unit-test-config.xml";  

          009     }  

          010    

          011     @Required 

          012     public void setDataSource(DataSource dataSource) {  

          013         this.dataSource = dataSource;  

          014     }  

          015    

          016     @Required 

          017     public void setActivationCodeMappingCacheStore(  

          018             ActivationCodeMappingCacheStore activationCodeMappingCacheStore) {  

          019         this.activationCodeMappingCacheStore = activationCodeMappingCacheStore;  

          020     }  

          021    

          022     @Override 

          023     protected void onSetUp() {  

          024    

          025         try {  

          026             Connection connection = dataSource.getConnection();  

          027             connection.setAutoCommit(false);  

          028    

          029             DbTestUtils.setupTables(connection);  

          030    

          031             insertTestData(connection);  

          032    

          033             // simple test to verify that the correct data was inserted into the database  

          034             Statement statement = connection.createStatement();  

          035             ResultSet resultSet = statement.executeQuery("select * from activation_code_mapping");  

          036    

          037             assertTrue(resultSet.next());  

          038             assertEquals("4fe801", resultSet.getString("activation_code"));  

          039    

          040             connection.commit();  

          041    

          042             connection.close();  

          043    

          044         } catch (SQLException se) {  

          045             throw new RuntimeException(se);  

          046         }  

          047     }  

          048    

          049     private void insertTestData(Connection connection) throws SQLException {  

          050    

          051         String sql = "insert into activation_code_mapping(activation_code, email_address, source, merchant_id, historical_merchant_name)" 

          052                 + "values ('4fe801', 'arthur@hgttg.com', '0001', 1234567, 'Slartibartfast')";  

          053         DbTestUtils.executeSql(connection, sql);  

          054     }  

          055    

          056     public void testLoad() {  

          057    

          058         // test load  

          059         String key = "4fe801";  

          060         Object foundObject = activationCodeMappingCacheStore.load(key);  

          061         assertNotNull(foundObject);  

          062    

          063         ActivationCodeMapping activationCodeMapping = (ActivationCodeMapping) foundObject;  

          064    

          065         // verify columns including date  

          066         assertEquals("4fe801", activationCodeMapping.getActivationCode());  

          067         assertEquals("arthur@hgttg.com", activationCodeMapping.getEmailAddress());  

          068         assertEquals("0001", activationCodeMapping.getSource());  

          069         assertEquals(1234567L, activationCodeMapping.getMerchantId().longValue());  

          070         assertEquals("Slartibartfast", activationCodeMapping.getHistoricalMerchantName());  

          071    

          072         assertNotNull(activationCodeMapping.getCreatedDate());  

          073         assertEquals(1, activationCodeMapping.getImplVersion());  

          074     }  

          075    

          076     public void testStore() {  

          077    

          078         final String activationCode = "1234";  

          079    

          080         // test insert  

          081         ActivationCodeMapping activationCodeMappingForInsert = new ActivationCodeMapping();  

          082         activationCodeMappingForInsert.setActivationCode(activationCode);  

          083         activationCodeMappingForInsert.setEmailAddress("ford@hgttg.com");  

          084         activationCodeMappingForInsert.setSource("000A");  

          085         activationCodeMappingForInsert.setMerchantId(7890L);  

          086         activationCodeMappingForInsert.setHistoricalMerchantName("Milliways");  

          087    

          088         activationCodeMappingCacheStore.store(activationCode, activationCodeMappingForInsert);  

          089    

          090         // verify insert  

          091         Object foundObject = activationCodeMappingCacheStore.load(activationCode);  

          092         assertNotNull(foundObject);  

          093    

          094         ActivationCodeMapping foundActivationCodeMapping = (ActivationCodeMapping) foundObject;  

          095         assertEquals(activationCode, foundActivationCodeMapping.getActivationCode());  

          096         assertEquals("ford@hgttg.com", foundActivationCodeMapping.getEmailAddress());  

          097         assertEquals("000A", foundActivationCodeMapping.getSource());  

          098         assertEquals(7890L, foundActivationCodeMapping.getMerchantId().longValue());  

          099         assertEquals("Milliways", foundActivationCodeMapping.getHistoricalMerchantName());  

          100         assertNotNull(foundActivationCodeMapping.getCreatedDate());  

          101    

          102         //   

          103     } 

          3. Supply a DataSource to the DAO/class under test.

          Once again we’re using Spring to inject the needed instances. However the config file is now specifying an instance of an iBATIS SqlMapClient.

          view sourceprint
          ?01 <?xml version="1.0" encoding="UTF-8"?>  

          02 <beans xmlns="http://www.springframework.org/schema/beans" 

          03                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 

          04                 xmlns:tx="http://www.springframework.org/schema/tx" 

          05                 xsi:schemaLocation="http://www.springframework.org/schema/beans  

          06    

          07 http://www.springframework.org/schema/beans/spring-beans-2.0.xsd  

          08    

          09 http://www.springframework.org/schema/tx  

          10    

          11 http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">  

          12    

          13   <!-- create data sources connected to an in-memory H2 (not HSQLDB) database. --> 

          14   <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  

          15     <property name="driverClass" value="org.h2.Driver"/>  

          16     <property name="jdbcUrl" value="jdbc:h2:mem:test;AUTOCOMMIT=OFF"/>  

          17     <property name="user" value="sa" />  

          18     <property name="password" value="" />  

          19   </bean>  

          20    

          21   <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">  

          22     <property name="configLocation">  

          23       <value>classpath:ActivationCodeMappingSqlMapConfig.xml</value>  

          24     </property>  

          25     <property name="dataSource" ref="dataSource" />  

          26     <!-- without this setting the commitTransaction() method does not commit --> 

          27     <property name="transactionConfigClass" value="com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransactionConfig" />  

          28   </bean>  

          29    

          30   <bean id="activationCodeMappingCacheStore" class="com.shopzilla.site.service.activationcode.cachestore.ActivationCodeMappingCacheStore" >  

          31     <property name="sqlMapClient" ref="sqlMapClient" />  

          32   </bean>  

          33 </beans> 

          After using both H2 
          & HSQLDB, I definitely prefer H2 and expect to use it going forward.


          posted on 2010-10-09 15:10 都市淘沙者 閱讀(738) 評(píng)論(0)  編輯  收藏 所屬分類: Hibernate/ORM

          主站蜘蛛池模板: 静乐县| 布拖县| 大名县| 嘉祥县| 蚌埠市| 沅江市| 福海县| 神池县| 阿拉善左旗| 牙克石市| 泗洪县| 瑞金市| 济阳县| 章丘市| 奉贤区| 梨树县| 太谷县| 阿合奇县| 芜湖县| 神池县| 绿春县| 邵武市| SHOW| 东乡族自治县| 岐山县| 乐清市| 张家界市| 濮阳县| 福泉市| 巴彦淖尔市| 从江县| 阿巴嘎旗| 湖南省| 体育| 东城区| 康平县| 左权县| 土默特右旗| 永新县| 茶陵县| 邵武市|