Search code examples
javacdimybatisear

mybatis is not initialized in case of ejb.jar


It seems what I am doing is not complete and something is still missing from my code.

I would like to use MyBatis + mybatis-cdi within an EJB. My jar contains only one stateless ejb class and a session factory producer class. I think that the problem is that my MyBatis Session Factory Producer is never called by EE container (I can not see the "MyBatis SessionFactory is initializing..." log entry in the log file).

The following error appears during deployment time:

WELD-001408: Unsatisfied dependencies for type ConfigurationDao with qualifiers @Default at injection point ....

When I pack the same classes (EJB + session factory producer classes) to war then I can see in the log that session factory producer is called. But I need to pack them to a simple ejb instead of war because I am building an EJB service which is used by another wars.

This is my assembly structure:

EAR
  +--- commons.jar (interfaces)
  +--- configuration-service.jar (stateless ejb + mybatis mapper interface + session factory producer)
  +--- restapi-1.war
  +--- restapi-2.war

The following classes sit in configuration-service.jar:

Session Factory Producer

//@Stateless
public class SessionFactoryProducer {

    private static final Logger LOGGER = ...;

    @ApplicationScoped
    @Produces
    @SessionFactoryProvider
    public SqlSessionFactory produce() throws Exception {
        LOGGER.info("MyBatis SessionFactory is initializing...");

        try (Reader reader = Resources.getResourceAsReader("mybatis.xml")) {
            return new SqlSessionFactoryBuilder().build(reader);
        }
    }
}

Stateless ejb

@Stateless
public class ConfigurationBean implements ConfigurationService {

   @Inject
   private ConfigurationDao configurationDao;

   public void setStringValue(final Configuration configuration) {
      ...
      // save to database
      configurationDao.insert(configuration);
      ...
   }

   public String getStringValue(final String key) {
      return configurationDao.findByKey(key).map(Configuration::getValue).orElse(null);
   }
}

MyBatis mapper:

@Mapper
public interface ConfigurationDao {

    @Select("SELECT ...")
    Configuration findByKey(@Param("key") String key);

    @Insert("INSERT INTO ...")
    void insert(Configuration configuration);
}

Usage from war (jersey rest)

@Path("/")
public class MyClass {
    @Inject
    private ConfigurationService configurationService;

    @GET
    @Produces(MediaType.APPLICATION_UTF8_JSON)
    public String doSomething() {
        String something = configurationService.getStringValue(KEY);
        ...
    }
}

Solution

  • For my ear/ejb app, here is how I declared my SessionFactoryProvider:

    @Local(ISqlSessionFactoryProvider.class)
    @Stateless
    public class SqlSessionFactoryProvider implements ISqlSessionFactoryProvider
    
    
    @Local
    public interface ISqlSessionFactoryProvider {
    
        SqlSessionFactory produceFactory() throws IOException;
    
    }
    

    Then in EJBs I do inject SqlSession and retrieve Mapper from it. But injecting directly mappers should work as specified in the documentation.

    In my other app: a simple webapp, I declare SessionFactoryProvider same way you do.

    It might not be the best way to do, but its works.

    I think the presence of file "empty" beans.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- This file can be an empty text file (0 bytes) -->
    <!-- We're declaring the schema to save you time if you do have to configure 
      this in the future -->
    <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="
            http://java.sun.com/xml/ns/javaee 
            http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
    
    </beans>
    

    in WEB-INF (for webapp) or META-INF (for ejb-jar) directory may allow container server to active/load few things. I have just checked: it switches CDI on.

    I actually uses it to declare TransactionInterceptor that is not supposed to be related.