Category Archives: MyBatis

Spring MyBatis configuration

Here I describe the project configuration to use MyBatis as ORM and benefit of the transactions management provided by Spring.
For that, we have to add the MyBatis-Spring library to the basic MyBatis.

First, the Maven POM is like:

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.2.3</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.2.1</version>
</dependency>

And now, the Spring configuration file where we define the SqlSessionFactoryBean:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
                        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">
    
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="mapperLocations" value="classpath:xml/*Mapper.xml" />
    </bean>
</beans>

The SqlSessionFactoryBean needs a datasource, and also the location of the MyBatis xml mappers.

Now, the configuration is done and the mappers will be managed by Spring using with this:

<!-- this is the parent of our Mappers. It defines the sqlSessionFactory which is common to all our mapper beans -->
<bean id="mapper" abstract="true">
	<property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>
<!-- Place MyBatis Mappers here -->
<bean id="entityMapper" class="org.mybatis.spring.mapper.MapperFactoryBean" parent="mapper">
	<property name="mapperInterface"
		value="net.classnotfound.data.mapper.EntityMapper" />
</bean>

We can see that the mappers are instantiated by the org.mybatis.spring.mapper.MapperFactoryBean which needs a reference to the SqlSessionFactory object we defined earlier.
To avoid some oversights (and reduce the mount of lines I have to write 🙂 ), I use the “abstract” Spring feature to put the session factory in the abstract mapper, then I can reference it as a parent in all my MapperFactories.

Now the entityMapper we defined is “injectable” in our Spring managed beans and we can use our preferred way of transaction management.

Persist list with MyBatis

As MyBatis is a basic ORM, when we need to persista list of objects, we have to implement it manually, but without any copy/paste between classes. For this purpose, I designed a generic merger which is responsible of that task.
It has to go through a list of objects and determine:

  1. what is new and needs to be created
  2. what is not there meaning it has been deleted
  3. what was already here and needs to be updated

To identify in which case we are, we need to provide it a java.util.Comparator and also a Dao to be able to interact with the database.

This is done in one main class (which uses also what I described in a previous post)

package net.classnotfound.data.dao.utils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import net.classnotfound.capability.Identifiable;
import net.classnotfound.data.dao.Dao;

/**
 * This class is used to merge a list with another list. It is helpful when we need to marge a list of elements
 * which come from the GUI with the list in the database
 * @author roussehe
 *
 * @param <T> the type of element we have to manage
 */
public class MergerList<T extends Identifiable> {
    
    private Dao<T> dao;
    
    private Finder<T> finder;
    
    /**
     * 
     * @param dao the dao used to merge the new or existing entries and to delete the removed ones 
     * @param comparator the comparator used to know if an element already exists in the DB
     */
    public MergerList(Dao<T> dao, Comparator<T> comparator) {
        super();
        this.dao = dao;
        this.finder = new Finder<>(comparator);
    }

    /**
     * merge the 2 lists, making comparison between existing/new/removed entries and persist the data in the database
     * @param oldEntryList the list coming from the db (usually)
     * @param newEntryList the new list we need to persist
     */
    public void mergeList(List<T> oldEntryList, List<T> newEntryList) {
        List<T> mergedEntries = new ArrayList<>();
        List<T> deletedEntries = new ArrayList<>();
        //we have to go through the 2 lists and check it one by one
        if(oldEntryList!=null&& !oldEntryList.isEmpty()) {
            //here, we check the entries which are present or not in the old list
            for (T entry : oldEntryList) {
                T found = finder.findMember(entry, newEntryList);
                if (found!=null) {//if found, we have to update it
                    mergedEntries.add(found);
                } else {//not found means it must be deleted from db
                    deletedEntries.add(entry);
                }
            }
        }
        if(newEntryList!=null&& !newEntryList.isEmpty()) {
            //here we check the entries which are only present in the new list
            for (T entry : newEntryList) {
                T found = finder.findMember(entry, oldEntryList);
                if(found == null) {//if not found in db, it's a new entry
                    mergedEntries.add(entry);
                }//if found, it is already managed in the previous loop
            }
        }
        //process records
        deleteEntries(deletedEntries);
        mergeEntries(mergedEntries);
    }

    
    /** As my Dao can manage create and update, it is done in one time.
     * @param mergeEntries
     */
    private void mergeEntries(List<T> mergeEntries) {
        for (T t: mergeEntries) {
            dao.merge(t);
        }
        
    }

    private void deleteEntries(Collection<T> deletedItems) {
        for (T t: deletedItems) {
            dao.delete(t);
        }
    }
    
    /**
     * This class is a helper class to find an object in a list of items
     *
     * @param <R>
     */
    private class Finder<R>{
        private Comparator<R> comp;
        
        Finder(Comparator<R> comp) {
            super();
            this.comp = comp;
        }

        /**
         * This method is responsible of getting object from the provided list.
         * @param entry
         * @param list
         * @return the object if found, or null otherwise
         */
        R findMember(R entry, List<R> list) {
            R found = null;
            if(list!=null&&!list.isEmpty()) {
                for (Iterator<R> iterator = list.iterator(); iterator.hasNext()&&found==null;) {
                    R currEntry = iterator.next();
                    if(comp.compare(entry, currEntry)==0)
                        found = currEntry;
                }
            }
            return found;
        }
    }
}

Done 😀

Improve server startup using Informix and MyBatis

Working on a project with an Informix database and using MyBatis as the persistence layer, I noted that the start-up of the server takes a long time (about 2 minutes). It is not really a problem if the performance of the application are good, as you don’t start your server all the day, but when you are in the implementation phase…
After step-by-step execution, I discovered that MyBatis gathers the metadata of the database to adapt its behavior, but this part was responsible of the delay (maybe Informix specific?).
I decided to replace this step, fortunately, using MyBatis with Spring, it’s not really a big deal 🙂
As the main purpose of this step is to identify the database provider, I defined a class which returns always the same information: Informix database.

  1. first, you need to define a new DatabaseIdProvider:
    public class IfxDatabaseIdProvider 
            implements org.apache.ibatis.mapping.DatabaseIdProvider {
      
      private static final Log log = LogFactory.getLog(IfxDatabaseIdProvider.class);
    
      private Properties properties;
      
      public String getDatabaseId(DataSource dataSource) {
        
        return "Informix Dynamic Server";
      }
    
      public void setProperties(Properties p) {
        this.properties = p;
      }
    
      public Properties getProperties() {
        return properties;
      }
    }
    
  2. now, you need to tell to MyBatis to use the new class, your Spring config file looks like:

        <bean id="ifxDatabaseIdProvider" 
            class="myPackage.IfxDatabaseIdProvider"/>
        
        <bean id="sqlSessionFactory" 
            class="org.mybatis.spring.SqlSessionFactoryBean">
          <property name="dataSource" ref="dataSource" />
          <property name="mapperLocations" value="classpath:mapper/*Mapper.xml"/>
          <property name="databaseIdProvider" ref="ifxDatabaseIdProvider"/>
        </bean>
    

Done!