2017-04-23 62 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