Hi,

I have come across an error when using camel-mybatis with Postgres that I
think merits a code change. 

I am using Camel 2.8 in Servicemix 4.4.1, though the issue is reproducible
in Camel 2.10. I have set up the MyBatisComponent to use a mybatis-spring
SqlSessionFactoryBean, which in turn uses an OSGi-configured DataSource:

    <bean id="mybatis"
class="org.apache.camel.component.mybatis.MyBatisComponent">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

    <bean id="sqlSessionFactory"
class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="/META-INF/SqlMapConfig.xml"/>
        <property name="mapperLocations"
value="classpath*:META-INF/mappers/**/*.xml"/>
    </bean>

    <bean id="dataSource" class="org.postgresql.ds.PGSimpleDataSource">
        <property name="serverName" value="${pg.server}"/>
        <property name="databaseName" value="${pg.database}"/>
        <property name="portNumber" value="${pg.port}"/>
        <property name="user" value="${pg.username}"/>
        <property name="password" value="${pg.password}"/>
    </bean>

This setup allows me to externalise the database connection config outside
of MyBatis' Environments construct, so it plays nicer with how ServiceMix
apps are typically structured.

The issue pops up when I attempt to insert an object into the database
through a simple route:

        <from uri="direct:insert"/>
        <to uri="mybatis:myModel.insert?statementType=Insert"/>

When I send a model object in to the route, I get the following exception
(abridged):
org.apache.camel.CamelExecutionException: Exception occurred during
execution on the exchange: Exchange[Message: <snip>]
        <snip>
Caused by: org.apache.ibatis.exceptions.PersistenceException: 
### Error committing transaction.  Cause: org.postgresql.util.PSQLException:
Cannot commit when autoCommit is enabled.
### Cause: org.postgresql.util.PSQLException: Cannot commit when autoCommit
is enabled.
        at
org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:8)
        at
org.apache.ibatis.session.defaults.DefaultSqlSession.commit(DefaultSqlSession.java:143)
        at
org.apache.ibatis.session.defaults.DefaultSqlSession.commit(DefaultSqlSession.java:135)
        at
org.apache.camel.component.mybatis.MyBatisProducer.doInsert(MyBatisProducer.java:125)
        at
org.apache.camel.component.mybatis.MyBatisProducer.process(MyBatisProducer.java:53)
        at
org.apache.camel.impl.converter.AsyncProcessorTypeConverter$ProcessorToAsyncProcessorBridge.process(AsyncProcessorTypeConverter.java:50)
        at
org.apache.camel.util.AsyncProcessorHelper.process(AsyncProcessorHelper.java:78)
        <snip>
        ... 33 more
Caused by: org.postgresql.util.PSQLException: Cannot commit when autoCommit
is enabled.
        at
org.postgresql.jdbc2.AbstractJdbc2Connection.commit(AbstractJdbc2Connection.java:705)
        at
org.mybatis.spring.transaction.SpringManagedTransaction.commit(SpringManagedTransaction.java:97)
        at org.apache.ibatis.executor.BaseExecutor.commit(BaseExecutor.java:152)
        at
org.apache.ibatis.executor.CachingExecutor.commit(CachingExecutor.java:80)
        at
org.apache.ibatis.session.defaults.DefaultSqlSession.commit(DefaultSqlSession.java:140)
        ... 80 more

I have confirmed that it is not a mere DataSource config thing, as I get the
same results using a more typical:

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="org.postgresql.Driver"/>
        <property name="jdbcUrl"
value="jdbc:postgresql://${pg.server}:${pg.port}/${pg.database}"/>
        <property name="user" value="${pg.username}"/>
        <property name="password" value="${pg.password}"/>
    </bean>

Having taken a look at MyBatisProducer, there are SqlSession.commit() calls
in doInsert(), doInsertList(), doUpdate() and doDelete() - rollback() is
never used. These appear to be the cause of the behaviour I'm seeing. From
what I have read about the autoCommit functionality, the JDBC spec doesn't
say whether it should be turned on or off by default
(http://www.mchange.com/projects/c3p0/index.html#autoCommitOnClose). In
Postgres, it is, whereas in Derby (used in the unit tests) it's not. It's
also not in H2, which I was using for my (successful) integration tests. 

To confirm whether the behaviour would work correctly without the commits, I
wrote up a pair of MyBatis delegate classes for SqlSessionFactory and
SqlSession, that trapped all commits. Inserts were successful both in
Postgres and H2. I would like to propose that the commit() calls be removed
from the MyBatisProducer (happy to provide patch), as whether the
transaction is committed or rolled back should be up to the underlying layer
in which the user might configure a transaction manager to make that
decision. Is there anything that I have missed around the reasoning behind
the commits?

Thanks,

Jakub

--
View this message in context: 
http://camel.465427.n5.nabble.com/camel-mybatis-insert-commits-failing-with-Postgres-tp5716081.html
Sent from the Camel Development mailing list archive at Nabble.com.

Reply via email to