MyBatis: BATCH operations
authorGustavo Martin Morcuende <gu.martinm@gmail.com>
Fri, 20 Mar 2015 02:50:28 +0000 (03:50 +0100)
committerGustavo Martin Morcuende <gu.martinm@gmail.com>
Fri, 20 Mar 2015 02:50:28 +0000 (03:50 +0100)
MyBatis/MyBatis-Spring/src/main/java/de/example/mybatis/batch/repository/mapper/AdSpringBatchMapper.java [new file with mode: 0644]
MyBatis/MyBatis-Spring/src/main/java/de/example/mybatis/spring/TestMain.java
MyBatis/MyBatis-Spring/src/main/java/de/example/mybatis/spring/service/BatchAndSimpleSameTrx.java [new file with mode: 0644]
MyBatis/MyBatis-Spring/src/main/java/de/example/mybatis/spring/service/ExampleBatchService.java [new file with mode: 0644]
MyBatis/MyBatis-Spring/src/main/java/de/example/mybatis/spring/service/ExampleService.java
MyBatis/MyBatis-Spring/src/main/resources/de/example/mybatis/batch/repository/mapper/AdSpringBatchMapper.xml [new file with mode: 0644]
MyBatis/MyBatis-Spring/src/main/resources/spring-config.xml
MyBatis/MyBatis/src/main/java/de/example/mybatis/TestMainBatch.java [new file with mode: 0644]

diff --git a/MyBatis/MyBatis-Spring/src/main/java/de/example/mybatis/batch/repository/mapper/AdSpringBatchMapper.java b/MyBatis/MyBatis-Spring/src/main/java/de/example/mybatis/batch/repository/mapper/AdSpringBatchMapper.java
new file mode 100644 (file)
index 0000000..7204c19
--- /dev/null
@@ -0,0 +1,9 @@
+package de.example.mybatis.batch.repository.mapper;
+
+import de.example.mybatis.model.Ad;
+
+
+public interface AdSpringBatchMapper /** extends MyBatisScanFilter NO SCAN BY MyBatis!! **/ {
+
+       int insert(Ad record);
+}
index 11e3677..2f894af 100644 (file)
@@ -1,7 +1,12 @@
 package de.example.mybatis.spring;
 
+import java.sql.SQLException;
+
 import org.apache.log4j.Logger;
+import org.springframework.jdbc.CannotGetJdbcConnectionException;
 
+import de.example.mybatis.spring.service.BatchAndSimpleSameTrx;
+import de.example.mybatis.spring.service.ExampleBatchService;
 import de.example.mybatis.spring.service.ExampleCustomService;
 import de.example.mybatis.spring.service.ExampleService;
 
@@ -18,14 +23,35 @@ public class TestMain {
 //        exampleService.insertNewAd();
 //
 //        exampleService.getAdsByCriteria();
-
-
-        final ExampleCustomService exampleCustomService = (ExampleCustomService) SpringContextLocator
-                .getInstance().getBean("exampleCustomService");
-
-        exampleCustomService.getAds();
+//
+//
+//        final ExampleCustomService exampleCustomService = (ExampleCustomService) SpringContextLocator
+//                .getInstance().getBean("exampleCustomService");
+//
+//        exampleCustomService.getAds();
+//        
+//        exampleCustomService.updateAds();
+        
+        
+//        final ExampleBatchService exampleBatchService = (ExampleBatchService) SpringContextLocator
+//                .getInstance().getBean("exampleBatchService");
+//        
+//        exampleBatchService.insertNewAd();
+//
+//        exampleBatchService.insertBatchNewAd();
+        
+        
+        final BatchAndSimpleSameTrx batchAndSimpleSameTrx = (BatchAndSimpleSameTrx) SpringContextLocator
+                .getInstance().getBean("batchAndSimpleSameTrx");
         
-        exampleCustomService.updateAds();
+        try {
+               batchAndSimpleSameTrx.insertNewAd();
+        } catch (CannotGetJdbcConnectionException e) {
+               logger.error("Error exception: ", e);
+        } catch (SQLException e) {
+               logger.error("Error exception: ", e);
+        }
+  
     }
 
 }
diff --git a/MyBatis/MyBatis-Spring/src/main/java/de/example/mybatis/spring/service/BatchAndSimpleSameTrx.java b/MyBatis/MyBatis-Spring/src/main/java/de/example/mybatis/spring/service/BatchAndSimpleSameTrx.java
new file mode 100644 (file)
index 0000000..7a15a89
--- /dev/null
@@ -0,0 +1,105 @@
+package de.example.mybatis.spring.service;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.Date;
+
+import javax.sql.DataSource;
+
+import org.apache.ibatis.jdbc.SQL;
+import org.apache.log4j.Logger;
+import org.springframework.jdbc.CannotGetJdbcConnectionException;
+import org.springframework.jdbc.datasource.DataSourceUtils;
+import org.springframework.transaction.annotation.Transactional;
+
+import de.example.mybatis.model.Ad;
+import de.example.mybatis.repository.mapper.AdMapper;
+
+
+public class BatchAndSimpleSameTrx {
+       private static final Logger logger = Logger.getLogger(BatchAndSimpleSameTrx.class);
+       
+    private AdMapper adMapper;
+    private DataSource dataSource;
+       
+       @Transactional
+       /** 
+        * JUST IN THIS VERY MOMENT Spring sends "set autocommit = 0" EVEN IF THERE ARE NO OPERATIONS ON THE DATABASE!!!
+        * So, Spring is picking up some thread from the connections pool and giving it to the current Java thread.
+        */
+       public void insertNewAd() throws CannotGetJdbcConnectionException, SQLException {
+               logger.info("Insert new Ad");
+
+        final Ad adTest = new Ad();
+        adTest.setAdMobileImage("bild.jpg");
+        adTest.setCompanyCategId(200L);
+        adTest.setCreatedAt(new Date());
+        adTest.setCompanyId(2L);
+        adTest.setUpdatedAt(new Date());
+        
+        /**
+         * No batched inserts will be sent to data base just in this very moment.
+         */
+        this.adMapper.insert(adTest); 
+        
+        /**
+         * We want to use SIMPLE and BATCH operations but MyBatis complains with this exception:
+         * "Cannot change the ExecutorType when there is an existing transaction".
+         * 
+         * So, we get the connection for the current transaction by means of the Spring DataSourceUtils
+         * and using JDBC we implement the BATCH operation in the current open transaction
+         * (it was open because of the @Transactional annotation)
+         */
+        this.doBatch(DataSourceUtils.getConnection(this.dataSource));
+        
+        /**
+         * No batched inserts will be sent to data base just in this very moment.
+         */
+        this.adMapper.insert(adTest);
+        
+        /**
+         * WHEN RETURNING FROM THIS METHOD Spring SENDS "set autocommit = 1" AND TRANSACTION ENDS
+         * (OR IF THERE IS ERROR Spring SENDS "rollback" AS EXPECTED)
+         */
+       }
+       
+       private void doBatch(final Connection connection) throws SQLException {
+
+        final PreparedStatement preparedStatement = connection.prepareStatement(doStatement());
+        try {
+               for (int i = 0; i < 10; i++) {
+                       /**
+                        * Batched statements are not yet sent to data base.
+                        */
+                       preparedStatement.addBatch();
+               }
+               
+               /**
+                * RIGHT HERE THE BATCH STATEMENTS WILL BE SENT TO THE DATA BASE.
+                */
+               preparedStatement.executeBatch();
+               
+        } finally {
+               preparedStatement.close();
+        }
+       }
+       
+       private String doStatement() {
+               return new SQL() {
+                       {
+                               INSERT_INTO("ad");
+                               VALUES("company_categ_id, ad_mobile_image, created_at, updated_at",
+                                               "'200', 'batch.jpg', '2015-03-20 02:54:50.0', '2015-03-20 02:54:50.0'");
+                       }
+               }.toString();
+       }
+    
+    public void setAdMapper(final AdMapper adMapper) {
+        this.adMapper = adMapper;
+    }
+    
+    public void setDataSource(final DataSource dataSource) {
+       this.dataSource = dataSource;
+    }
+}
diff --git a/MyBatis/MyBatis-Spring/src/main/java/de/example/mybatis/spring/service/ExampleBatchService.java b/MyBatis/MyBatis-Spring/src/main/java/de/example/mybatis/spring/service/ExampleBatchService.java
new file mode 100644 (file)
index 0000000..6412ec6
--- /dev/null
@@ -0,0 +1,117 @@
+package de.example.mybatis.spring.service;
+
+import java.util.Date;
+
+import org.apache.log4j.Logger;
+import org.springframework.transaction.annotation.Transactional;
+
+import de.example.mybatis.batch.repository.mapper.AdSpringBatchMapper;
+import de.example.mybatis.model.Ad;
+import de.example.mybatis.repository.mapper.AdMapper;
+
+
+public class ExampleBatchService {
+       private static final Logger logger = Logger.getLogger(ExampleBatchService.class);
+       
+       private AdSpringBatchMapper adSpringBatchMapper;
+    private AdMapper adMapper;
+
+       @Transactional 
+       /** 
+        * JUST IN THIS VERY MOMENT Spring sends "set autocommit = 0" EVEN IF THERE ARE NO OPERATIONS ON THE DATABASE!!!
+        * So, Spring is picking up some thread from the connections pool and giving it to the current Java thread.
+        */
+    public void insertBatchNewAd() {
+        logger.info("Insert new Batch Ad");
+
+        final Ad adTest = new Ad();
+        adTest.setAdMobileImage("bild.jpg");
+        adTest.setCompanyCategId(200L);
+        adTest.setCreatedAt(new Date());
+        adTest.setCompanyId(2L);
+        adTest.setUpdatedAt(new Date());
+        
+        /**
+         * BECAUSE THIS IS A BATCH MAPPER, OPERATIONS WILL NOT BE SENT TO THE DATABASE UNTIL COMMAND commit IS SENT TO DATABASE.
+         * BECAUSE THIS METHOD IS ANNOTATED WITH @Transactional, THE COMMAND commit WILL BE SENT WHEN RETURNING FROM THIS METHOD
+         * (WHEN TRANSACTION ENDS UP)
+         * 
+         * WORK AROUND: using SqlSession (MyBatis) we have access to the flushStatements method!!!
+         * SO, WE MUST INJECT THE SqlSession BEAN AND USE IT HERE IF WE DON'T WANT TO WAIT UNTIL TRANSACTION IS FINISHED.
+         */
+        this.adSpringBatchMapper.insert(adTest);
+        
+        /**
+         * There will not be new "set autocommit = 0" because Spring remembers that this thread already started some transaction
+         * (it must be using ThreadLocals)
+         */
+        this.insertPrivateNewAd();
+    }
+       
+       private void insertPrivateNewAd() {
+               logger.info("Insert new private Ad");
+
+        final Ad adTest = new Ad();
+        adTest.setAdMobileImage("bild.jpg");
+        adTest.setCompanyCategId(200L);
+        adTest.setCreatedAt(new Date());
+        adTest.setCompanyId(2L);
+        adTest.setUpdatedAt(new Date());
+        
+        // MyBatis complains: "Cannot change the ExecutorType when there is an existing transaction".
+        // Work around in BatchAndSimpleSameTrx.
+        this.adMapper.insert(adTest); 
+       }
+       
+       @Transactional
+       /** 
+        * JUST IN THIS VERY MOMENT Spring sends "set autocommit = 0" EVEN IF THERE ARE NO OPERATIONS ON THE DATABASE!!!
+        * So, Spring is picking up some thread from the connections pool and giving it to the current Java thread.
+        */
+       public void insertNewAd() {
+               logger.info("Insert new Ad");
+
+        final Ad adTest = new Ad();
+        adTest.setAdMobileImage("bild.jpg");
+        adTest.setCompanyCategId(200L);
+        adTest.setCreatedAt(new Date());
+        adTest.setCompanyId(2L);
+        adTest.setUpdatedAt(new Date());
+        this.adMapper.insert(adTest); 
+        
+        //DataSourceUtils.getConnection();
+        
+        this.insertPrivateBatchNewAd();
+       }
+       
+    private void insertPrivateBatchNewAd() {
+        logger.info("Insert new private Batch Ad");
+
+        final Ad adTest = new Ad();
+        adTest.setAdMobileImage("bild.jpg");
+        adTest.setCompanyCategId(200L);
+        adTest.setCreatedAt(new Date());
+        adTest.setCompanyId(2L);
+        adTest.setUpdatedAt(new Date());
+        
+        /**
+         * BECAUSE THIS IS A BATCH MAPPER, OPERATIONS WILL NOT BE SENT TO THE DATABASE UNTIL COMMAND commit IS SENT TO DATABASE.
+         * BECAUSE THIS METHOD IS ANNOTATED WITH @Transactional, THE COMMAND commit WILL BE SENT WHEN RETURNING FROM THIS METHOD
+         * (WHEN TRANSACTION ENDS UP)
+         * 
+         * WORK AROUND: using SqlSession (MyBatis) we have access to the flushStatements method!!!
+         * SO, WE MUST INJECT THE SqlSession BEAN AND USE IT HERE IF WE DON'T WANT TO WAIT UNTIL TRANSACTION IS FINISHED.
+         */
+        // MyBatis complains: "Cannot change the ExecutorType when there is an existing transaction".
+        // Workaround in BatchAndSimpleSameTrx.
+        this.adSpringBatchMapper.insert(adTest);
+    }
+       
+    public void setAdSpringBatchMapper(final AdSpringBatchMapper adSpringBatchMapper) {
+       this.adSpringBatchMapper = adSpringBatchMapper;
+    }
+    
+    public void setAdMapper(final AdMapper adMapper) {
+        this.adMapper = adMapper;
+    }
+}
index 8d868ca..a08f2b0 100644 (file)
@@ -64,5 +64,7 @@ public class ExampleService {
         adTest.setCompanyId(2L);
         adTest.setUpdatedAt(new Date());
         this.adMapper.insert(adTest);
+        
+        this.adMapper.insert(adTest);
     }
 }
\ No newline at end of file
diff --git a/MyBatis/MyBatis-Spring/src/main/resources/de/example/mybatis/batch/repository/mapper/AdSpringBatchMapper.xml b/MyBatis/MyBatis-Spring/src/main/resources/de/example/mybatis/batch/repository/mapper/AdSpringBatchMapper.xml
new file mode 100644 (file)
index 0000000..68d0a0a
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
+<mapper namespace="de.example.mybatis.batch.repository.mapper.AdSpringBatchMapper" >
+  
+    <insert id="insert" parameterType="de.example.mybatis.model.Ad" >
+    <selectKey resultType="java.lang.Long" keyProperty="id" order="AFTER" >
+      SELECT LAST_INSERT_ID()
+    </selectKey>
+    insert into ad (company_id, company_categ_id, ad_mobile_image, 
+      created_at, updated_at, ad_gps
+      )
+    values (#{companyId,jdbcType=BIGINT}, #{companyCategId,jdbcType=BIGINT}, #{adMobileImage,jdbcType=VARCHAR}, 
+      #{createdAt,jdbcType=TIMESTAMP}, #{updatedAt,jdbcType=TIMESTAMP}, #{adGps,jdbcType=LONGVARBINARY}
+      )
+  </insert>
+
+</mapper>
\ No newline at end of file
index 2f91317..4670947 100644 (file)
@@ -25,7 +25,8 @@
     See http://mybatis.org/schema/mybatis-spring.xsd for further information.
     -->
     <mybatis:scan base-package="de.example.mybatis.repository.mapper"
-                  marker-interface="de.example.mybatis.mapper.filter.MyBatisScanFilter" />
+                  marker-interface="de.example.mybatis.mapper.filter.MyBatisScanFilter"
+                  template-ref="sqlSimpleSession" /> <!-- Required because I am using SIMPLE and BATCH MyBatis Executors -->
 
     <!-- enable the configuration of transactional behavior based on annotations -->
     <tx:annotation-driven transaction-manager="transactionManager"/>
         <property name="dataSource" ref="dataSource"/>
     </bean>
  
+       <!-- It will be shared by SIMPLE and BATCH operations. -->
     <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
         <property name="dataSource" ref="dataSource" />
         <property name="configLocation" value="classpath:config/mybatis-config.xml" />
     </bean>
     
+    <!-- We need to declare in this way the SqlSessionTemplate bean because otherwise 
+         mybatis:scan will retrieve the first instantiated Spring bean of this type.
+         Because we want to create a BATCH SqlSessionTemplate we also have to create
+         a SIMPLE SqlSessionTemplate (it is SIMPLE because if we do not declare the Executor type
+         SqlSessionFactoryBean will retrieve its value from mybatis-config.xml and in that file
+         I am using defaultExecutorType=SIMPLE)-->
+    <bean id="sqlSimpleSession" class="org.mybatis.spring.SqlSessionTemplate">
+       <constructor-arg index="0" ref="sqlSessionFactory" />
+       </bean>
+    
     <!-- 
     The <mybatis:scan/> XML element will search for mappers in a very similar way than the
     Spring built-in element <context:component-scan/>  searches for beans.
 
     Notice that there is no need to specify a SqlSessionFactory or SqlSessionTemplate as an
     attribute in the <mybatis:scan/> element because it will create MapperFactoryBeans that can
-    be autowired. But if you are using more than one DataSource autowire may not work for you.
+    be autowired. But if you are using more than one DataSource autowire may not work for you
+    (in my case I am using SIMPLE and BATCH operations, and autowire did not work for me because
+    once you declare SqlSessionTemplate bean for BATCH operations you have also to declare
+    the SqlSessionTemplate for SIMPLE operations)
     In this case you can use the factory-ref or template-ref attributes to set the right bean
     name to use.
 
         <property name="dataSource" ref="dataSource" />
         <property name="changeLog" value="classpath:/liquibase/liquibaseChangeLogs.xml" />
     </bean>
+    
+    
+    <!-- 
+    MyBatis batch implementation
+    IT DOESN'T ALLOW YOU TO USE DIFFERENT TYPE OF EXECUTORS IN THE SAME TRANSACTION!!! Work around to this
+    limitation in BatchAndSimpleSameTrx.java class (using JDBC)
+        
+       MyBatis batch mappers will not be scanned because they have different base package and different interface.
+       Session will be shared by batched and simple MyBatis mappers (so they can share the same transaction)
+       -->
+    
+    <bean id="sqlBatchSession" class="org.mybatis.spring.SqlSessionTemplate">
+       <constructor-arg index="0" ref="sqlSessionFactory" />
+       <constructor-arg index="1" value="BATCH" />
+       </bean>
+    
+    <bean id="adSpringBatchMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
+        <property name="mapperInterface" value="de.example.mybatis.batch.repository.mapper.AdSpringBatchMapper" />
+        <property name="sqlSessionTemplate" ref="sqlBatchSession" />
+    </bean>
+    
+    <bean id="exampleBatchService" class="de.example.mybatis.spring.service.ExampleBatchService">
+        <property name="adSpringBatchMapper" ref="adSpringBatchMapper" />
+        <property name="adMapper" ref="adMapper" />
+    </bean>
+    
+    <bean id="batchAndSimpleSameTrx" class="de.example.mybatis.spring.service.BatchAndSimpleSameTrx">
+        <property name="adMapper" ref="adMapper" />
+        <property name="dataSource" ref="dataSource" />
+    </bean>
+        
 </beans>
diff --git a/MyBatis/MyBatis/src/main/java/de/example/mybatis/TestMainBatch.java b/MyBatis/MyBatis/src/main/java/de/example/mybatis/TestMainBatch.java
new file mode 100644 (file)
index 0000000..2e2c93e
--- /dev/null
@@ -0,0 +1,247 @@
+package de.example.mybatis;
+
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+
+import org.apache.ibatis.io.Resources;
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.apache.ibatis.session.SqlSessionFactoryBuilder;
+import org.apache.log4j.Logger;
+
+import de.example.mybatis.model.Ad;
+import de.example.mybatis.model.AdCriteria;
+import de.example.mybatis.repository.mapper.AdMapper;
+
+
+public class TestMainBatch {
+    private static final Logger logger = Logger.getLogger(TestMainBatch.class);
+
+    public static void main(final String[] args) throws IOException {
+
+        // From org.xml.sax.InputSource Javadoc:
+        // The SAX parser will use the InputSource object to determine how to
+        // read XML input. If there is a character stream available, the parser
+        // will read that stream directly, disregarding any text encoding
+        // declaration found in that stream. If there is no character stream,
+        // but there is a byte stream, the parser will use that byte stream,
+        // using the encoding specified in the InputSource or else (if no
+        // encoding is specified) autodetecting the character encoding using an
+        // algorithm such as the one in the XML specification. If neither a
+        // character stream nor a byte stream is available, the parser will
+        // attempt to open a URI connection to the resource identified by the
+        // system identifier.
+
+        // Then if we use an InputStream (it is not a character stream) and
+        // we do not specify the encoding, the encoding should be autodetected
+        // reading the XML header. :) That is what I want. :)
+
+
+
+        // Scope and Lifecycle
+        //
+        // 1. SqlSessionFactoryBuilder:
+        // This class can be instantiated, used and thrown away. There is no
+        // need to keep it around once you've created your SqlSessionFactory.
+        //
+        // 2. SqlSessionFactory:
+        // Once created, the SqlSessionFactory should exist for the duration of
+        // your application execution.
+        //
+        // 3. SqlSession:
+        // Each thread should have its own instance of SqlSession. Instances of
+        // SqlSession are not to be shared and are not thread safe. Therefore
+        // the best scope is request or method scope. You should always ensure
+        // that it's closed within a finally block.
+        //
+        // 4. Mapper Instances:
+        // Mappers are interfaces that you create to bind to your mapped
+        // statements. Instances of the mapper interfaces are acquired from the
+        // SqlSession. They do not need to be closed explicitly.
+
+        final SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
+        .build(/**TestMain.class.getResourceAsStream("sql-maps-config.xml")**/
+                Resources.getResourceAsStream("mybatis-sql-maps-config.xml"), "mybatisexample");
+
+        // The default openSession() method that takes no parameters will create
+        // a SqlSession with the following characteristics:
+        //
+        // * A transaction scope will be started (i.e. NOT auto-commit).
+        // * A Connection object will be acquired from the DataSource instance
+        // configured by the active environment.
+        // * The transaction isolation level will be the default used by the
+        // driver or data source.
+        // * No PreparedStatements will be reused, and no updates will be
+        // batched.
+
+        // MyBatis uses two caches: a local cache and a second level cache.
+        //
+        // Each time a new session is created MyBatis creates a local cache and
+        // attaches it to the session. Any query executed within the session
+        // will be stored in the local cache so further executions of the same
+        // query with the same input parameters will not hit the database. The
+        // local cache is cleared upon update, commit, rollback and close.
+        //
+        // By default local cache data is used for the whole session duration.
+        // This cache is needed to resolve circular references and to speed up
+        // repeated nested queries, so it can never be completely disabled but
+        // you can configure the local cache to be used just for the duration of
+        // an statement execution by setting localCacheScope=STATEMENT.
+        //
+        // Note that when the localCacheScope is set to SESSION, MyBatis returns
+        // references to the same objects which are stored in the local cache.
+        // Any modification of returned object (lists etc.) influences the local
+        // cache contents and subsequently the values which are returned from
+        // the cache in the lifetime of the session. Therefore, as best
+        // practice, do not to modify the objects returned by MyBatis.
+        /** 
+         * OVERRIDE THE DECLARED EXECUTOR IN mybatis-sql-maps-config.xml
+         *  BY DEFAULT autocommit = false (what means autocommit=0 until session.close is called)
+         */
+        SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH);
+
+        try {
+            final AdMapper adMapper = session.getMapper(AdMapper.class);
+            final Ad adTest = new Ad();
+            adTest.setAdMobileImage("mobileImage.jpg");
+            adTest.setCompanyCategId(200L);
+            adTest.setCreatedAt(new Date());
+            adTest.setCompanyId(2L);
+            adTest.setUpdatedAt(new Date());
+
+            // This first insert took ages because I was dropping IPV6 connections.
+            // That is because during the first socket connection, the JVM
+            // tries to find out if IPV6 is available by means of opening a random
+            // AF_INET6 POSIX socket.
+            /** 
+             * WITH BATCH OPERATIONS, INSERT WILL NOT BE PEFORMED UNTIL CALLING COMMIT OR FLUSHSTATEMENTS
+             * BUT set autocommit=0 WILL BE PEFORMED JUST IN THIS VERY MOMENT EVEN IF WE ARE NOT
+             * YET SENDING DATA!!!!! So, it is as always but without sending data.
+             */
+            adMapper.insert(adTest);
+            adMapper.insert(adTest);
+            /**
+             * BY MEANS OF THIS METHOD WE CAN SEND THE BATCHED DATA TO DATA BASE AND
+             * RETRIEVE THE RESULTS OF THE BATCH OPERATIONS. OTHERWISE WE HAVE TO WAIT
+             * UNTIL session.commit AND WE LOOSE THE RESULTS.
+             */
+            session.flushStatements();
+            
+            /** 
+             *  IF WE DIDN'T USE THE flushStatements METHOD, IN THIS VERY MOMENT BATCH DATA WOULD BE SENT TO SERVER
+             *  (BUT BECAUSE WE USED THE flushStatements METHOD NO DATA WILL BE SENT)
+             *  JUST THE commit COMMAND IS SENT RIGHT NOW!!!
+             */
+            session.commit();
+
+            final List<Ad> adLists = adMapper.selectByExample(null);
+            for (final Ad ad : adLists) {
+                logger.info("Ad id: " + ad.getId());
+                if (ad.getAdGps() != null) {
+                    logger.info("Ad GPS: " + new String(ad.getAdGps(), "UTF-8"));
+                }
+                logger.info("Ad mobileImage: " + ad.getAdMobileImage());
+                logger.info("Ad companyCategId: " + ad.getCompanyCategId());
+                logger.info("Ad companyId: " + ad.getCompanyId());
+                logger.info("Ad createdAt: " + ad.getCreatedAt());
+                logger.info("Ad updatedAt: " + ad.getUpdatedAt());
+                logger.info("\n");
+            }
+        } finally {
+            // Besides this will restore the auto-commit value.
+               /** 
+             *  JUST IN THIS VERY MOMENT set autocommit=1 IS SENT!!!!!
+             *  
+             *  This is what @Transactional (Spring annotation) do when
+             *  returning from an annotated transactional method.
+             */
+            session.close();
+        }
+
+        /** 
+         * OVERRIDE THE DECLARED EXECUTOR IN mybatis-sql-maps-config.xml
+         *  BY DEFAULT autocommit = false (what means autocommit=0 until session.close is called)
+         */
+        session = sqlSessionFactory.openSession(ExecutorType.BATCH);
+
+        try {
+            logger.info("Last insert");
+            final AdMapper adMapper = session.getMapper(AdMapper.class);
+            final Ad adTest = new Ad();
+            adTest.setAdMobileImage("mobileImage.jpg");
+            adTest.setCompanyCategId(200L);
+            adTest.setCreatedAt(new Date());
+            adTest.setCompanyId(2L);
+            adTest.setUpdatedAt(new Date());
+            
+            /** 
+             * WITH BATCH OPERATIONS, INSERT WILL NOT BE PEFORMED UNTIL CALLING COMMIT
+             * (IF WE DON'T USE THE flushStatements METHOD)
+             * BUT set autocommit=0 WILL BE PEFORMED JUST IN THIS VERY MOMENT EVEN IF WE ARE NOT
+             * YET SENDING DATA!!!!! So, it is as always but without sending data.
+             */
+            adMapper.insert(adTest);
+            
+            /** 
+             *  JUST IN THIS VERY MOMENT DATA ARE SENT TO SERVER, BUT NOT BEFORE!!!!
+             *  (BECAUSE WE DID NOT USE THE flushStatements METHOD)
+             *  THE commit COMMAND IS SENT RIGHT NOW!!!!!
+             */
+            session.commit();
+
+        } finally {
+            // Besides this will restore the auto-commit value.
+               /** 
+             *  JUST IN THIS VERY MOMENT set autocommit=1 IS SENT!!!!!
+             *  
+             *  This is what @Transactional (Spring annotation) do when
+             *  returning from an annotated transactional method.
+             */
+            session.close();
+        }
+
+        session = sqlSessionFactory.openSession();
+
+        try {
+            logger.info("Using criteria");
+
+            final AdCriteria adCriteria = new AdCriteria();
+
+            adCriteria.or().andAdMobileImageEqualTo("mobileImage.jpg")
+                    .andCreatedAtNotEqualTo(new Date());
+
+            adCriteria.or().andAdMobileImageNotEqualTo("noMobileImage.jpg")
+                    .andAdMobileImageIsNotNull();
+
+            // where (ad_mobile_image = "mobileImage.jpg" and created_at <> Now())
+            // or (ad_mobile_image <> "noMobileImage.jpg" and ad_mobile_image is not null)
+
+            final AdMapper adMapper = session.getMapper(AdMapper.class);
+            final List<Ad> adLists = adMapper.selectByExampleWithBLOBs(adCriteria);
+            for (final Ad ad : adLists) {
+                logger.info("Ad id: " + ad.getId());
+                if (ad.getAdGps() != null) {
+                    logger.info("Ad GPS: " + new String(ad.getAdGps(), "UTF-8"));
+                }
+                logger.info("Ad mobileImage: " + ad.getAdMobileImage());
+                logger.info("Ad companyCategId: " + ad.getCompanyCategId());
+                logger.info("Ad companyId: " + ad.getCompanyId());
+                logger.info("Ad createdAt: " + ad.getCreatedAt());
+                logger.info("Ad updatedAt: " + ad.getUpdatedAt());
+                logger.info("\n");
+            }
+        } finally {
+            // Besides this will restore the auto-commit value.
+               /** 
+             *  JUST IN THIS VERY MOMENT set autocommit=1 IS SENT!!!!!
+             *  
+             *  This is what @Transactional (Spring annotation) do when
+             *  returning from an annotated transactional method.
+             */
+            session.close();
+        }
+    }
+
+}