Showing posts with label iBATIS. Show all posts
Showing posts with label iBATIS. Show all posts

2011-05-20

Tips on MyBatis

Thread-safety of SqlSession

Two main implementation of SqlSession interface have opposite semantics in thread-safety.
org.apache.ibatis.session.defaults.DefaultSqlSession is not thread-safe and so should not be shared among threads. But, org.mybatis.spring.SqlSessionTemplate provided to used with Spring framework is thread-safe.

Two implementations can not replace each other. So, the POJO into which SqlSessionTemplate is expected to be injected by Spring IoC container will not work correctly with other implementation of SqlSession in non-Spring environment. Can this be called POJO ?

I think this is somewhat confusing feature. I'm not sure thead-safety is dealt with interface scope of respective implmentation scope. SqlSession is in the heart of MyBatis, and so it seems desirable to have more clear semantics.

2009-09-30

Performance of iBATIS

Lock occurs at com.ibatis.common.beans.ClassInfo#getInstance in case of cacheEnabled = true

In heavy traffic , lock occurs at com.ibatis.common.beans.ClassInfo#getInstance in case of cacheEnabled = true. It seems to be happend because of "double synchronized"

The following is interesting article related to the above issue.

The above article says that we'd better use java.util.concurrent.ConcurrentHashMap (in JDK 5) rather than Collections.synchronizedMap, Hashtable or synchronized block.

Here is another article worth reading.


Race conditions in Throttle lead to thread blockage popping items from ThrottledPools under stress.

com.ibatis.common.util.Throttle seems to be a mechanism for iBATIS to control concurrent load, but causes few problems. Throttle is removed entirely in 2.3.1.



The test I involved showed 20% enhancement of throughput under high congestion after removing wait and notify from com.ibatis.common.util.Throttle and applying ConcurrentHashMap to com.ibatis.common.beans.ClassInfo.getInstance(). The tested application was stock trading application which had about 12,000 lines in all and consisted of 8 stateless local session beans using iBATIS 2.3 for data access. The application had about 12,000 lines. The application was on a IBM machine which had 10 PowerPC POWER6 CPUs and 16GB RAM. The stress test showed 2,400TPS before and 2,900TPS after the fixing.

Oracle BLOB/CLOB Handling of iBATIS

The followings are tested and applied with iBATIS 2.3 and Oracle 10g. In case of iBATIS 3, is there anyone who tested the same thing ?


Oracle CLOB columns can be mapped string fields like VARCHAR2 columns without additional type handler or explicit Java/JDBC type description. In fact this feature is completely the benefit of enhancement of Oracle JDBC driver. Oracle JDBC driver higher than 10.2 provides functionality to handle CLOB column like varchar cloumn and it will work with Oracle database 9i as well as Oracle database 10g or higher. About this you can refer the following article.


Oracle BLOB column can be mapped byte[] field without custom handler using JDBC type and Java type description in result map definition or parameter map definition. This feature needs iBATIS 2.0.9 or higher and recent Oracle JDBC driver. Unlike the above article, the javaType="[B" should be described explicitly in my case.

2009-08-13

about SqlMapClientFactoryBean of Spring Framework

SqlMapClientBean of springframework creates SqlMapClient with EXTERNAL transaction management by default and internally wrap the given data source to TransactionAwareDataSourceProxy.

So, the typical setup for iBATIS SqlMapClient bean in spring configuration would be like the following.

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
 <property name="driverClassName" value="oracle.jdbc.OracleDriver" />
 <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:XE" />
 <property name="username" value="_user_" />
 <property name="password" value="_passwd_" />
 <property name="defaultAutoCommit" value="true" />
 <property name="maxActive" value="10" />
 <property name="maxIdle" value="10" />
 <property name="minIdle" value="0" />
 <property name="initialSize" value="10" />
</bean>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
 <property name="dataSource" ref="dataSource" />
 <property name="defaultTimeout" value="120" />
</bean>
 
<tx:annotation-driven 
 transaction-manager="transactionManager"
 mode="proxy"
 proxy-target-class="true"
/>

<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">
 <description>default SqlMapClient bean for DAOs</description>
 <property name="configLocation" value="classpath:_pathto_/sql-map.xml" />
  
 <property name="dataSource" ref="dataSource" />
 <!-- Default setting is specified for clarity. -->
 <property name="useTransactionAwareDataSource" value="true"/>
 <!-- Default setting is specified for clarity. -->
 <property name="transactionConfigClass">
  <value type="java.lang.Class">com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig
 </property>

</bean>

At the above sample, the default setup for useTransactionAwareDataSource and transactionConfigClass properties is specified for clarity.

ExternalTransactionConfig would set the "SetAutoCommitAllowed" property of SqlMapClient to false, in contrast to the iBATIS default, to always keep the original autoCommit value as provided by the connection pool.

For more information, refer API documentation of SqlMapClientFactoryBean, specially setDataSource, setUseTransactionAwareDataSource, and setTransactionConfigClass methods.

2008-12-19

Using Stored Procedure/Function with iBATIS

This is simple example for using stored function with iBATIS.

Recently I implemented number generating module using oracle stored function and iBATIS with lots of trial/error more than I expected. It's because I couldn't find a complete working example. The example in official manual from iBATIS is not sufficient , the example from the book 'iBATIS in Action' is not correct(I didn't check the corrections from the Manning site), and the search results from Goole didn't include excellent examples for me.

The following exmaple is using number table to generate number for construct new primary key for specified table.

The number table has following structure
CREATE TABLE test.PS_NUMBER (
  table_name VARCHAR2(50) NOT NULL,
  next_no NUMBER(20) NOT NULL,
  CONSTRAINT pk_number PRIMARY KEY (table_name)
);
To prevent same number is given in highly concurrent situation with Oracle database, SELECT ~ FOR UPDATE should be used.
But iBATIS dosen't provide ways to execute both select statement and update statement at the same time(as I know), I decided to use stored procedure. Because it is possible to lock the table row in case of server failure right after the first select(for update) statement is executed but before second update statement is excuted.

The stored procedure is like following
CREATE OR REPLACE FUNCTION gen_num_for_table(table_name_in IN VARCHAR2)
  RETURN NUMBER
  IS
  next_no_out NUMBER;
BEGIN
  SELECT next_no INTO next_no_out
  FROM ps_number
  WHERE table_name = table_name_in
  FOR UPDATE WAIT 3;

  UPDATE ps_number SET next_no = next_no + 1
  WHERE table_name = table_name_in;

  RETURN next_no_out;
END gen_num_for_table;
Now, for server applications the SQL Map is like following The statement part should be enclosed with curly brackets({}) and the statement is proceeded by '? = call' in case of function. In case of procedure, just start with 'call'. These are same syntax with java.sql.CallableStatement. Acually, iBATIS will execute callable statement using specified statement and parameterMap. The parameterMap should be specified surely. The order of parameter is important. In case of function, the first parameter is for return value and so should have 'OUT' mode attribute. For each parameter, jdbcType should be specified explicitly, so as to be interpreted to CallableStatment.registerOutParameter method with correct parameter and setXXX of CallableStatement.

Finally the application using above sql map is like following
public int generateNumberForTable(String tableName) throws Exception{

  java.util.Map map = new java.util.HashMap();
  map.put("table_name", tableName);

  this.sqlMapClient.queryForObject("Common.generateNumberForTable", map);

  return (Integer)map.get("next_no");
}
The input is given using map object as is expected from SQL Map definition. The important thing is that the result is not given as a return value of queryForObject method but is put in the map with the key specified in SQL Map definition.

For your information, the equivalent codes using plain JDBC would be like following. It could help you understand how the above codes would work.
public int generateNumberForTable(String tableName) throws Exception{
   Connection conn = null;
   CallableStatement cstmt = null;

   try{
      DataSource ds = this.sqlMapClient.getDataSource();
      conn = ds.getConnection();
      cstmt = conn.prepareCall("{? = call gen_num_for_table(?)}");
      cstmt.registerOutParameter(1, java.sql.Types.INTEGER);
      cstmt.setString(2, tableName);

      cstmt.executeQuery();

      return cstmt.getInt(1);
   }catch(Exception ex){
      ex.printStackTrace(System.err);
      throw ex;
   }finally{
      if(cstmt != null){ try{ cstmt.close();}catch(Exception ex2){} }
      if(conn != null){ try{ conn.close();}catch(Exception ex2){} }
   }
}
For more readings.
  • iBatis and Stored Oracle procedures/functions This is the article that I found after this writing and I think it gives you more information about iBATIS and Oracle stored procedure/function. I didn't execute personally the code in that article and I didn't understand what is 'ORACLECURSOR' type in the example code, but as a whole you can find hints or guide from the article.