MyBatis CDI +容器管理事务 [英] MyBatis CDI + Container-Managed Transaction

查看:170
本文介绍了MyBatis CDI +容器管理事务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在my aplication中使用mybatis和容器管理的事务。我使用mybatis 3.4.2和mybatis-cdi 1.0.0。

I would like to use mybatis with container-managed transaction in my aplication. I use mybatis 3.4.2 and mybatis-cdi 1.0.0.

我的代码可以正常工作,但此刻我手动打开和关闭会话,我不知道如何注入到我的EJB的 SqlSession Mapper

My code works but at the moment I open and close sessions manually and I do not know how to inject either SqlSession or Mapper to my EJB.

它似乎 mybatis-cdi 在我的情况下不能正常工作。

It seems that mybatis-cdi does not do its job properly in my case.

这是我的部署结构:

EAR
  +--- commons.jar (interfaces, POJOs)
  +--- ejb.jar (stateless EJBs + MyBatis mapper + session factory)
  +--- web.war (demo servlet which calls EJB)

commons.jar

/a/
/a/b/
/a/b/commons/
/a/b/commons/mybatis/
/a/b/commons/mybatis/SessionFactoryProducer.class
/a/b/commons/api/
/a/b/commons/api/EchoService.class
/a/b/commons/domain/
/a/b/commons/domain/Configuration.class
/META-INF/
/META-INF/beans.xml

SessionFactoryProducer.java(生成SqlSessionFactory的简单接口)

SessionFactoryProducer.java (simple interface to produce SqlSessionFactory)

public interface SessionFactoryProducer {
    SqlSessionFactory produce() throws IOException;
}

EchoService.java(EJB接口)

EchoService.java (EJB interface)

public interface EchoService {
    String echo(String str) throws IOException;
}

Configuration.java(简单POJO)

Configuration.java (simple POJO)

class with getters/setters

beans .xml

beans.xml

<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">

    <interceptors>
        <class>org.mybatis.cdi.JtaTransactionInterceptor</class>
    </interceptors>

</beans>

ejb.jar

/a/
/a/b/
/a/b/ejb/
/a/b/ejb/EchoServiceBean.class
/a/b/ejb/dao/
/a/b/ejb/dao/ConfigurationDao.class
/a/b/ejb/SessionFactoryProducerImpl.class
/META-INF/
/META-INF/beans.xml

EchoServiceBean.java(简单的无状态EJB )

EchoServiceBean.java (simple stateless EJB)

@Stateless
public class EchoServiceBean implements EchoService {

    //@Inject
    //private SessionFactoryProducer sqlSessionFactoryProducer;

    //@Inject
    //private SqlSession sqlSession;

    @Inject
    private ConfigurationDao configurationDao;

    @Override
    public String echo(String str) throws IOException {
        // SqlSession sqlSession = sqlSessionFactoryProducer.produce().openSession();
        // ConfigurationDao configurationDao = sqlSession.getMapper(ConfigurationDao.class);
        Configuration configuration = configurationDao.findByKey("something");
        LOGGER.info(configuration.toString());

        sqlSession.close();
        return new Date() + ": Hello";
    }
}

ConfigurationDao.java(简单的MyBatis映射器,这里没什么特别的)

ConfigurationDao.java (simple MyBatis mapper, nothing special here)

@Mapper
public interface ConfigurationDao {
    @Select("SELECT id, key_name, key_value, description "
            + "FROM application.configuration "
            + "WHERE key_name = #{key}")
    Configuration findByKey(@Param("key") String key);
}

SessionFactoryProducerImpl.java(EJB,生成MyBatis SqlSessionFactory):

SessionFactoryProducerImpl.java (EJB, produces MyBatis SqlSessionFactory):

@Stateless
public class SessionFactoryProducerImpl implements SessionFactoryProducer {
    @Override
    public SqlSessionFactory produce() throws IOException {
        LOGGER.info("MyBatis SessionFactory is initializing...");

        try (Reader reader = Resources.getResourceAsReader("mybatis.xml")) {
            SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(reader);
            LOGGER.info("Session factory has been obtained");
            return sessionFactory;
        }
    }
}

beans.xml

beans.xml

same then before

当我使用我的 SessionFactoryProducerImpl EJB获取MyBatis会话时,一切正常,但我想让EE容器管理SqlSession(open / close / commit / rollback) )。

When I use my SessionFactoryProducerImpl EJB to get MyBatis session then everything works fine, but I would like to let manage SqlSession by EE container (open/close/commit/rollback).

根据官方doc SessionFactoryProducerImpl 后出现问题//www.mybatis.org/cdi/injection.html\"rel =nofollow noreferrer> http://www.mybatis.org/cdi/injection.html (删除@Stateless和界面参考,添加@生成,@ ApplicationScoped,@ SessionFactoryProvider注释)之后

The issue appeared after I modified my SessionFactoryProducerImpl as per suggestion of the official doc http://www.mybatis.org/cdi/injection.html (remove @Stateless and interface reference, add @Produces, @ApplicationScoped, @SessionFactoryProvider annotations) and after that


  1. 我注入 org.apache.ibatis.session。 SqlSession 而不是我的 SessionFactoryProducerImpl 我得到一个类型为SqlSession的不满意依赖项,带有限定符@Default 在部署EAR到服务器期间

  2. 我注入MyBatis映射器,在我的情况下是 ConfigurationDao 然后我得到没有正确配置的SqlSessionFactory生成器错误。

  1. I inject org.apache.ibatis.session.SqlSession instead of my SessionFactoryProducerImpl I get an Unsatisfied dependencies for type SqlSession with qualifiers @Default during deploy EAR to server
  2. I inject MyBatis mapper which is the ConfigurationDao in my case then I get a There are no SqlSessionFactory producers properly configured error.

让EE容器管理的正确方法是什么MyBatis会话?

What is the proper way to let EE container to manage MyBatis session?

UPDATE-1

我试图按名称注入SqlSessionFactory:

I tried to inject SqlSessionFactory by name:

//@Stateless
public class SessionFactoryProducerImpl /*implements SessionFactoryProducer*/ {
    //@Override
    @ApplicationScoped
    @Produces
    @Named("fooManager")
    @SessionFactoryProvider
    public SqlSessionFactory produce() throws IOException {
       ...
    }
}

用法:

@Stateless
public class EchoServiceBean implements EchoService {
    @Inject @Named("fooManager") ConfigurationDao configurationDao;
    ...
}

从应用服务器登录:

[Payara 4.1] [INFO] [org.mybatis.cdi.MybatisExtension] [tid: _ThreadID=803 _ThreadName=admin-thread-pool::admin-listener(40)] [timeMillis: 1492972172749] [levelValue: 800] [[
  MyBatis CDI Module - Found class with @Mapper-annotation: ConfigurationDao]]

[Payara 4.1] [INFO] [org.mybatis.cdi.MybatisExtension] [tid: _ThreadID=803 _ThreadName=admin-thread-pool::admin-listener(40)] [timeMillis: 1492972172943] [levelValue: 800] [[
  MyBatis CDI Module - SqlSessionFactory producer SessionFactoryProducerImpl.produce]]

[Payara 4.1] [INFO] [org.mybatis.cdi.MybatisExtension] [tid: _ThreadID=803 _ThreadName=admin-thread-pool::admin-listener(40)] [timeMillis: 1492972172982] [levelValue: 800] [[
  MyBatis CDI Module - Activated]]

[Payara 4.1] [INFO] [org.mybatis.cdi.MybatisExtension] [tid: _ThreadID=803 _ThreadName=admin-thread-pool::admin-listener(40)] [timeMillis: 1492972172983] [levelValue: 800] [[
  MyBatis CDI Module - Found a bean, which needs a Mapper interface a.b.ejb.dao.ConfigurationDao]]

[Payara 4.1] [INFO] [org.mybatis.cdi.MybatisExtension] [tid: _ThreadID=803 _ThreadName=admin-thread-pool::admin-listener(40)] [timeMillis: 1492972172984] [levelValue: 800] [[
  MyBatis CDI Module - Managed Mapper dependency: a.b.ejb.dao.ConfigurationDao_fooManager, a.b.ejb.dao.ConfigurationDao]]

[Payara 4.1] [INFO] [org.mybatis.cdi.MybatisExtension] [tid: _ThreadID=803 _ThreadName=admin-thread-pool::admin-listener(40)] [timeMillis: 1492972172984] [levelValue: 800] [[
  MyBatis CDI Module - Managed SqlSession: org.apache.ibatis.session.SqlSession_fooManager, org.apache.ibatis.session.SqlSession]]

[Payara 4.1] [INFO] [AS-WEB-GLUE-00172] [javax.enterprise.web] [tid: _ThreadID=803 _ThreadName=admin-thread-pool::admin-listener(40)] [timeMillis: 1492972173275] [levelValue: 800] [[
  Loading application [ear-packager-1.0#web-1.0.war] at [/web]]]

[Payara 4.1] [INFO] [javax.enterprise.system.core] [tid: _ThreadID=803 _ThreadName=admin-thread-pool::admin-listener(40)] [timeMillis: 1492972173334] [levelValue: 800] [[
  ear-packager-1.0 was successfully deployed in 1,280 milliseconds.]]

但是当我尝试使用它时仍然会出现异常:

But I still get exception when I try to use it:

Caused by: org.mybatis.cdi.MybatisCdiConfigurationException: There are no SqlSessionFactory producers properly configured.
    at org.mybatis.cdi.CDIUtils.findSqlSessionFactory(CDIUtils.java:55)
    at org.mybatis.cdi.SerializableMapperProxy.getMapper(SerializableMapperProxy.java:57)
    at org.mybatis.cdi.SerializableMapperProxy.<init>(SerializableMapperProxy.java:44)
    at org.mybatis.cdi.MyBatisBean.create(MyBatisBean.java:116)
    at org.jboss.weld.context.unbound.DependentContextImpl.get(DependentContextImpl.java:70)
    at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.get(ContextualInstanceStrategy.java:100)
    at org.jboss.weld.bean.ContextualInstance.get(ContextualInstance.java:50)
    at org.jboss.weld.manager.BeanManagerImpl.getReference(BeanManagerImpl.java:744)
    at org.jboss.weld.manager.BeanManagerImpl.getInjectableReference(BeanManagerImpl.java:844)
    at org.jboss.weld.injection.FieldInjectionPoint.inject(FieldInjectionPoint.java:92)
    at org.jboss.weld.util.Beans.injectBoundFields(Beans.java:362)
    at org.jboss.weld.util.Beans.injectFieldsAndInitializers(Beans.java:373)
    at org.jboss.weld.injection.producer.DefaultInjector$1.proceed(DefaultInjector.java:71)
    at org.glassfish.weld.services.InjectionServicesImpl.aroundInject(InjectionServicesImpl.java:173)
    at org.jboss.weld.injection.InjectionContextImpl.run(InjectionContextImpl.java:46)
    at org.jboss.weld.injection.producer.DefaultInjector.inject(DefaultInjector.java:73)
    at org.jboss.weld.injection.producer.StatelessSessionBeanInjector.inject(StatelessSessionBeanInjector.java:60)
    at org.jboss.weld.injection.producer.ejb.SessionBeanInjectionTarget.inject(SessionBeanInjectionTarget.java:140)
    at org.glassfish.weld.services.JCDIServiceImpl.injectEJBInstance(JCDIServiceImpl.java:261)
    at com.sun.ejb.containers.BaseContainer.injectEjbInstance(BaseContainer.java:1698)
    at com.sun.ejb.containers.StatelessSessionContainer.createStatelessEJB(StatelessSessionContainer.java:488)
    ... 50 more

任何想法有什么问题我的代码?

Any idea what is wrong in my code?

UPDATE-2

有趣的事情。我刚刚在war项目中添加了一个新的servlet,以显示CDI容器中可用bean的列表:

Interesting thing. I have just added a new servlet to the war project to display the list of the available beans in CDI container:

@WebServlet("/cdi")
public class CdiServlet extends HttpServlet {

    @Inject
    BeanManager beanManager;

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {

        Set<Bean<?>> beans = beanManager.getBeans(Object.class, new AnnotationLiteral<Any>() {});

        ...
    }
}

我可以看到我的EE容器中有74个bean,最重要的bean是:

I can see that there are 74 beans in my EE container and the most important beans are:

(4)
toString(): Extension [class org.mybatis.cdi.MybatisExtension] with qualifiers [@Default]; jar:file:/home/soma/applications/servers/_gombi_/payara-middleware/glassfish/domains/domain1/applications/ear-packager-1.0/lib/mybatis-cdi-1.0.0.jar!/META-INF/services/javax.enterprise.inject.spi.Extension@1[org.mybatis.cdi.MybatisExtension@643ecfa7]
getName(): org.mybatis.cdi.MybatisExtension
getSimpleName(): MybatisExtension
getSuperclass(): class java.lang.Object
getPackage(): org.mybatis.cdi
getAnnotations(): []


(6)
toString(): Managed Bean [class org.mybatis.cdi.CDIUtils$SerializableAnyAnnotationLiteral] with qualifiers [@Any @Default]
getName(): org.mybatis.cdi.CDIUtils$SerializableAnyAnnotationLiteral
getSimpleName(): SerializableAnyAnnotationLiteral
getSuperclass(): class javax.enterprise.util.AnnotationLiteral
getPackage(): org.mybatis.cdi
getAnnotations(): []

(8)
toString(): Extension [class org.glassfish.cdi.transaction.TransactionalExtension] with qualifiers [@Default]; bundle://302.0:0/META-INF/services/javax.enterprise.inject.spi.Extension@1[org.glassfish.cdi.transaction.TransactionalExtension@665874cc]
getName(): org.glassfish.cdi.transaction.TransactionalExtension
getSimpleName(): TransactionalExtension
getSuperclass(): class java.lang.Object
getPackage(): org.glassfish.cdi.transaction
getAnnotations(): []

(18)
toString(): Managed Bean [class org.mybatis.cdi.SqlSessionManagerRegistry] with qualifiers [@Any @Default]
getName(): org.mybatis.cdi.SqlSessionManagerRegistry
getSimpleName(): SqlSessionManagerRegistry
getSuperclass(): class java.lang.Object
getPackage(): org.mybatis.cdi
getAnnotations(): [@javax.enterprise.context.ApplicationScoped()]

(35)
toString(): Managed Bean [class org.mybatis.cdi.CDIUtils] with qualifiers [@Any @Default]
getName(): org.mybatis.cdi.CDIUtils
getSimpleName(): CDIUtils
getSuperclass(): class java.lang.Object
getPackage(): org.mybatis.cdi
getAnnotations(): []

(46)
toString(): Managed Bean [class a.b.commons.domain.Configuration] with qualifiers [@Any @Default]
getName(): a.b.commons.domain.Configuration
getSimpleName(): Configuration
getSuperclass(): class java.lang.Object
getPackage(): a.b.commons.domain
getAnnotations(): []

(48)
toString(): Session bean [class a.b.ejb.EchoServiceBean with qualifiers [@Any @Default]; local interfaces are [EchoService]
getName(): a.b.ejb.EchoServiceBean
getSimpleName(): EchoServiceBean
getSuperclass(): class java.lang.Object
getPackage(): a.b.ejb
getAnnotations(): [@javax.ejb.Stateless(name=, description=, mappedName=)]

(55)
toString(): Managed Bean [class org.mybatis.cdi.CDIUtils$SerializableDefaultAnnotationLiteral] with qualifiers [@Any @Default]
getName(): org.mybatis.cdi.CDIUtils$SerializableDefaultAnnotationLiteral
getSimpleName(): SerializableDefaultAnnotationLiteral
getSuperclass(): class javax.enterprise.util.AnnotationLiteral
getPackage(): org.mybatis.cdi
getAnnotations(): []


(56)
toString(): Managed Bean [class a.b.web.CdiServlet] with qualifiers [@Any @Default]
getName(): a.b.web.CdiServlet
getSimpleName(): CdiServlet
getSuperclass(): class javax.servlet.http.HttpServlet
getPackage(): a.b.web
getAnnotations(): [@javax.servlet.annotation.WebServlet(loadOnStartup=-1, initParams=[], urlPatterns=[], displayName=, largeIcon=, name=, asyncSupported=false, description=, smallIcon=, value=[/cdi])]

(58)
toString(): Producer Method [SqlSessionFactory] with qualifiers [@Any @Default] declared as [[BackedAnnotatedMethod] @ApplicationScoped @Produces @SessionFactoryProvider public a.b.ejb.SessionFactoryProducerImpl.produce()]
getName(): a.b.ejb.SessionFactoryProducerImpl
getSimpleName(): SessionFactoryProducerImpl
getSuperclass(): class java.lang.Object
getPackage(): a.b.ejb
getAnnotations(): []

(70)
toString(): Managed Bean [class a.b.ejb.SessionFactoryProducerImpl] with qualifiers [@Any @Default]
getName(): a.b.ejb.SessionFactoryProducerImpl
getSimpleName(): SessionFactoryProducerImpl
getSuperclass(): class java.lang.Object
getPackage(): a.b.ejb
getAnnotations(): []

72)
toString(): Managed Bean [class a.b.commons.domain.Configuration] with qualifiers [@Any @Default]
getName(): a.b.commons.domain.Configuration
getSimpleName(): Configuration
getSuperclass(): class java.lang.Object
getPackage(): a.b.commons.domain
getAnnotations(): []

我可以看到我的 SessionFactoryProducerImpl 注入或不注入@Stateless注释(bean id 70)。我也可以看到SqlSessionFactory生成器也被注入,bean id 58。

I can see that my SessionFactoryProducerImpl is injected either with or without @Stateless annotation (bean id 70). I also can see that SqlSessionFactory producer is injected as well, bean id 58.

但是我仍然得到 org.mybatis.cdi.MybatisCdiConfigurationException:There当我调用EJB的 echo(...)方法时,没有正确配置的SqlSessionFactory生成器错误。

But I still get org.mybatis.cdi.MybatisCdiConfigurationException: There are no SqlSessionFactory producers properly configured error when I call the echo(...) method of my EJB.

我想MyBatis需要使用 abejb.SessionFactoryProducerImpl 中的生产者方法。但是怎么把它告诉mybatis-cdi?

I guess that somehow MyBatis needs to use producer method from a.b.ejb.SessionFactoryProducerImpl. But how to tell it to mybatis-cdi?

推荐答案

该文档没有说明删除 @Stateless ,它只是没有指定它是必需的,因为它描述了一般用例。然后尝试:

The documentation does not tell to remove @Stateless, it just does not specify it is required because it describes general use case. Then try:

@Stateless
@Local(SessionFactoryProducer.class)
public class SessionFactoryProducerImpl implements SessionFactoryProducer {
    //@Override
    @ApplicationScoped
    @Produces
    @Named("fooManager")
    @SessionFactoryProvider
    public SqlSessionFactory produce() throws IOException {
       ...
    }
}

@Local(SessionFactoryProducer.class)实现SessionFactoryProducer 甚至可能不是强制性的。

@Local(SessionFactoryProducer.class) and implements SessionFactoryProducer might even be not mandatory.

我想 @Stateless SessionFactoryProducer 转换为EJB,然后在相同的上下文/范围内可用可以注入另一个。

I suppose @Stateless turn the SessionFactoryProducer into an EJB that will then be available in the same context/scope and can be injected into another.

只需要存在 SessionFactoryProducer 。之后这是CDI在找到SqlSession或Mapper注入点时调用它的工作。

The SessionFactoryProduceris just required to be present. Afterwards this is CDI's job to invoke it when it finds SqlSession or Mapper injection points.

这几乎就是我所做的,会话由EJB管理。当然提供了mybatis-config.xml:

This is pretty much what I have done, the session is managed by EJB. Of course provided mybatis-config.xml:

<environments default="development">
  <environment id="development">
    <transactionManager type="MANAGED">
    <dataSource type="JNDI">

这篇关于MyBatis CDI +容器管理事务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆