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);
...
}
}
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.