Thursday, April 9, 2015

Adding MyBatis mapper files dynamically

I'm currently working in a project that we needed to implement such case. Assuming you are using MyBatis CDI in a EJB/CDI context, the bellow code will produce the MyBatis SqlSessionFactory for the MyBatis mapper files that are defined in a static way.

First, your Configuration.xml will only contain the database connection as bellow:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="UNPOOLED">
                <property name="driver" value="oracle.jdbc.driver.OracleDriver"/>
                <property name="url" value="jdbc:oracle:thin:@<HOST>:<PORT>:<DB_NAME>"/>
                <property name="username" value="<SCHEMA>"/>
                <property name="password" value="<PASSWORD>"/>
            </dataSource>
        </environment>
    </environments>
</configuration>



Second, the bellow class will produce the SqlSessionFactory after loading the mapper XML files dynamically.
import java.io.IOException;
import java.io.InputStream;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Produces;

import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

/**
 * 
 * @author Belal
 *
 */
public class SessionFactoryProvider {
 
 private static final String mapperFiles[] = {"FirstTableMapper.xml", "SecondTableMapper.xml", "ThirdTableMapper.xml"};
 
 @Produces
 @ApplicationScoped
 public SqlSessionFactory produceFactory() { 
  String resource = "Configuration.xml";
  InputStream inputStream = null;
  try {
   inputStream = Resources.getResourceAsStream(resource);
  } catch (IOException e) {
   throw new RuntimeException("Fatal Error.  Cause: " + e, e);
  }
  SqlSessionFactory  sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  for(String mapperFile : mapperFiles) {
   InputStream in = getClass().getClassLoader().getResourceAsStream(mapperFile) ; 
   Configuration configuration = sqlSessionFactory.getConfiguration() ;  
   if (in != null){
    XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(in, configuration, mapperFile, configuration.getSqlFragments());
    xmlMapperBuilder.parse();
   }
   else {
    System.err.println("Error file not loaded " + mapperFile);
   }
  }
  return sqlSessionFactory;
 }
}

Now, you can inject your mappers files in the service classes as bellow:
import javax.ejb.Stateless;
import javax.inject.Inject;
import com.ithinkisink.blog.mybatis.mapper;

/**
 * 
 * @author Belal
 *
 */
@Stateless
public class MyService {
 
 @Inject
 @Mapper
 FirstTableMapper firstTableMapper;
 
 @Inject
 @Mapper
 SecondTableMapper secondTableMapper;
 
 @Inject
 @Mapper
 ThirdTableMapper secondTableMapper;
 
 //body of the service
}

Don't forget to make sure you have added the MyBatis and MyBatis-CDI to your Maven dependencies (in case you are a Maven lover ;) ) as bellow:
<dependency>
 <groupId>org.mybatis</groupId>
 <artifactId>mybatis</artifactId>
 <version>3.2.8</version>
</dependency>
<dependency>
 <groupId>org.mybatis</groupId>
 <artifactId>mybatis-cdi</artifactId>
 <version>1.0.0-beta1</version>
</dependency>

1 comment:

  1. It seems the many things are missing from this post :(

    ReplyDelete