2017-04-23 98 views
0

我想在我的应用中使用容器管理事务的mybatis。我使用mybatis 3.4.2和mybatis-cdi 1.0.0。MyBatis CDI +容器管理事务

我的代码有效,但目前我手动打开和关闭会话,我不知道如何将SqlSessionMapper注入到我的EJB中。

看来mybatis-cdi在我的情况下不能正常工作。

这是我的部署结构:

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)

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

EchoService.java(EJB接口)

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

Configuration.java(简单POJO)

class with getters/setters 

的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)

@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映射器,这里没有什么特别)

@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):

@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

same then before 

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

问题出现我修改了SessionFactoryProducerImpl按照官方文档http://www.mybatis.org/cdi/injection.html的建议(删除@Stateless和接口引用,添加@Produces,@ApplicationScoped,@SessionFactoryProvider注释),之后

  1. 我注入后org.apache.ibatis.session.SqlSession而不是我的SessionFactoryProducerImpl我在部署EAR到服务器时得到Unsatisfied dependencies for type SqlSession with qualifiers @Default
  2. 我注入MyBatis映射器,在我的情况下是ConfigurationDao然后我得到There are no SqlSessionFactory producers properly configured错误。

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


UPDATE-1

我试图通过名字来注入的SqlSessionFactory:

//@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.]] 

但我仍然得到异常,当我尝试使用它:

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 

任何想法是错误的,我的代码?


UPDATE-2

有趣的事情。我也只是增加了一个新的servlet战争项目来显示可用豆子的CDI容器名单:

@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>() {}); 

     ... 
    } 
} 

我可以看到,有74种豆类在我的EE容器中,最重要的豆类:

(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/[email protected][[email protected]] 
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/[email protected][[email protected]] 
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.

但是当我调用我的EJB的echo(...)方法时,仍然出现org.mybatis.cdi.MybatisCdiConfigurationException: There are no SqlSessionFactory producers properly configured错误。

我想不知何故MyBatis需要使用a.b.ejb.SessionFactoryProducerImpl的生产者方法。但如何将它告诉mybatis-cdi?

+0

首先尝试部署示例应用程序。它已经过许多应用程序服务器的测试,并且有一个示例EJB。然后,只需更改mybatis-config_1.xml以使用MANAGED事务。 Session Factory Producer并不打算由mybatis-cdi直接在内部使用。 链接到示例应用程序:https://github.com/mnesarco/mybatis-cdi-samples – mnesarco

回答

0

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

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

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

我想@StatelessSessionFactoryProducer变成一个EJB,然后可以在相同的上下文/范围内使用,并可以注入到另一个。

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

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

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

你是对的。 “@Local”和“执行”关键字不是必需的。正如我所见,“@Stateless”也不是必需的。正如你在UPDATE-2中看到的那样,SqlSessionFactory被注入了,但是仍然出现错误:( – zappee

+0

my mybatis.xml和你的非常相似 – zappee

+0

所有的魔法是只需要注释,CDI完成所有的工作。无状态“是必需的:尽管SessionFactoryProducer被实例化,但它在某种程度上无法访问(不同的范围......这是错误日志所说的),并且使用SqlSession/Mapper注入EJB,”@ Stateless“将它变成一个EJB ,不像使用简单的webapp(没有EJB)时 – blackwizard