1
votes

I am planning to remove JPA (Eclipselink) from my project and use MyBatis instead of it. I was searching for some nice guide what is the best practice to use MyBatis in a EE container (statelass session ejb) and JNDI datasource + DAO pattern + CDI (I am not using Spring!). Unfortunately I have not found any good documentation about it.

Is there any way to initialize MyBatis and use JNDI data source without xml configuration file?

What is the best MyBatis way to implement DAO pattern and inject my dao classes to stateless EJB with CDI?

I use Java 8 + Glassfish (Payara EE server) + MyBatis 3.4.2.


UPDATE-1

I followed the instruction on this page http://www.mybatis.org/cdi/getting-started.html but it does not work for me.

This is the runtime exception what I got with Glassfish (actually it is a Payara) application server:

[2017-02-14T22:02:23.715+0100] [Payara 4.1] [INFO] [AS-WEB-GLUE-00172] [javax.enterprise.web] [tid: _ThreadID=101 _ThreadName=admin-listener(6)] [timeMillis: 1487106143715] [levelValue: 800] [[
  Loading application [mybatis-demo-1.0#mybatis-demo-war-1.0.war] at [/demo]]]

[2017-02-14T22:02:23.770+0100] [Payara 4.1] [INFO] [] [javax.enterprise.system.core] [tid: _ThreadID=101 _ThreadName=admin-listener(6)] [timeMillis: 1487106143770] [levelValue: 800] [[
  mybatis-demo-1.0 was successfully deployed in 1,526 milliseconds.]]

[2017-02-14T22:03:00.333+0100] [Payara 4.1] [INFO] [] [javax.enterprise.web] [tid: _ThreadID=25 _ThreadName=http-listener-1(2)] [timeMillis: 1487106180333] [levelValue: 800] [[
  WebModule[null] ServletContext.log():Marking servlet a.b.war.HelloServlet as unavailable]]

[2017-02-14T22:03:00.334+0100] [Payara 4.1] [WARNING] [] [javax.enterprise.web] [tid: _ThreadID=25 _ThreadName=http-listener-1(2)] [timeMillis: 1487106180334] [levelValue: 900] [[
  StandardWrapperValve[a.b.war.HelloServlet]: Allocate exception for servlet a.b.war.HelloServlet
org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type PersonMapper with qualifiers @Default
  at injection point [BackedAnnotatedField] @Inject private a.b.war.HelloServlet.personMapper
  at a.b.war.HelloServlet.personMapper(HelloServlet.java:0)

This is my test servlet:

@WebServlet("/servlet")
public class HelloServlet extends HttpServlet {
    @Inject
    private PersonMapper personMapper;

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().println(personMapper.getPerson(1L).toString());
    }

My mapper class:

@Mapper
public interface PersonMapper {
    @Select("SELECT * FROM person WHERE id = #{id}")
    Person getPerson(@Param("id") long id);
}

It seems that MyBatisSQLSessionFactory.getSqlSessionFactory() method is never called because I cannot see anything in my log file.

My SessionFactory class:

public class MyBatisSQLSessionFactory {
    private static final Logger LOGGER = LoggerFactory.getLogger(MyBatisSQLSessionFactory.class);

    @Produces
    @ApplicationScoped
    @SessionFactoryProvider
    public SqlSessionFactory getSqlSessionFactory() throws IOException {
        LOGGER.info("MyBatis is initializing...");
        String resource = "mybatis-configuration.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        LOGGER.info("MyBatis has been initialized, SQL Session Factory: {}", sqlSessionFactory.toString());
        return sqlSessionFactory;
    }
}

And finally the structure of my war:

*.war
│   index.html
│
├───META-INF
│   │   bean.xml
│   │   MANIFEST.MF
│   │
│   └───maven
│         ...
│
└───WEB-INF
    ├───classes
    │   └───a
    │       └───b
    │           └───war
    │               │   HelloServlet.class
    │               │
    │               └───mybatis
    │                   │   MyBatisSQLSessionFactory.class
    │                   │
    │                   └───dao
    │                           PersonMapper.class
    │
    └───lib

Maybe I made a mistake and I forgot something...


UPDATE-2

"Weld does not detect the mapper if not used by at least one proper CDI bean. And Servlet is not proper CDI bean. Workaround is to annotate the servlet with @Dependent." You can find more details here.

2
First of all what Version of Java EE are you using?jojo_Berlin
I added this information to the original post.zappee
not 1st issue but maybe next one: mybatis-configuration.xml is required in classpath at least to define transactionManager and reference jndi dataSource.blackwizard

2 Answers

1
votes

My answer here, also reporting my comments for clarity.

Not 1st issue but maybe next one: mybatis-configuration.xml is required in classpath at least to define transactionManager and reference jndi dataSource.

Just in case add a default constructor with log for MyBatisSQLSessionFactory. Should be instantiated only once.

For a WAR, the beans.xml must be in WEB-INF. Otherwise extract business code in separate dependency ejb.jar, all packaged in EAR.

I recommend to activate JTA Transactions: fill the beans.xml.

I @Inject @SessionFactory protected SqlSession session; in the EJB, then in its methods: new DAO(session); because the transaction is managed on EJB level and injecting the DAO injected with session did not work as expected.

Also annotate EJB methods @Transactional(executorType=ExecutorType.REUSE) when there are multiple statement called. This will use the same conne; ction for all statements of transaction and prepare unique statements only once. First try without to figure out.

0
votes

Yes, there is a good way to work with myBatis in Java EE envrionment, especially CDI.

There is a mybatis-cdi extension that solves many problems for you and ensures flawless integration. The GitHub repository is here and it links to official documentation.

You should read the official documentation thoroughly. Especially the "Getting started" section (in the menu on the left, just after introduction). They explain how to set up everything.

Also, there is a nice example project to be found on GitHub. There is even an example with JSF used.