MyBatis CDI + 容器管理事务
MyBatis CDI + Container-Managed Transaction
我想在我的应用程序中使用 mybatis 和容器管理的事务。我用的是mybatis 3.4.2和mybatis-cdi 1.0.0.
我的代码有效,但目前我手动打开和关闭会话,我不知道如何将 SqlSession
或 Mapper
注入我的 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)。
问题出现在我根据官方文档http://www.mybatis.org/cdi/injection.html的建议修改了我的SessionFactoryProducerImpl
(删除@Stateless和接口引用,添加@Produces、@ApplicationScoped、@SessionFactoryProvider注解)之后那
- 我注入
org.apache.ibatis.session.SqlSession
而不是我的 SessionFactoryProducerImpl
我在将 EAR 部署到服务器期间得到 Unsatisfied dependencies for type SqlSession with qualifiers @Default
- 我注入 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.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
有趣的事情。我刚刚向 war 项目添加了一个新的 servlet,以显示 CDI 容器中可用 bean 的列表:
@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 是:
(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.
但是当我调用我的 EJB 的 echo(...)
方法时,我仍然得到 org.mybatis.cdi.MybatisCdiConfigurationException: There are no SqlSessionFactory producers properly configured
错误。
我猜 MyBatis 不知何故需要使用 a.b.ejb.SessionFactoryProducerImpl
中的生产者方法。但是怎么告诉mybatis-cdi呢?
文档没有告诉删除 @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
甚至可能不是强制性的。
我想 @Stateless
将 SessionFactoryProducer
变成一个 EJB,然后在同一个 context/scope 中可用,并且可以注入另一个。
SessionFactoryProducer
只需要在场即可。之后这是 CDI 的工作,当它找到 SqlSession 或 Mapper 注入点时调用它。
这就是我所做的,会话由 EJB 管理。当然提供mybatis-config.xml:
<environments default="development">
<environment id="development">
<transactionManager type="MANAGED">
<dataSource type="JNDI">
我想在我的应用程序中使用 mybatis 和容器管理的事务。我用的是mybatis 3.4.2和mybatis-cdi 1.0.0.
我的代码有效,但目前我手动打开和关闭会话,我不知道如何将 SqlSession
或 Mapper
注入我的 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)。
问题出现在我根据官方文档http://www.mybatis.org/cdi/injection.html的建议修改了我的SessionFactoryProducerImpl
(删除@Stateless和接口引用,添加@Produces、@ApplicationScoped、@SessionFactoryProvider注解)之后那
- 我注入
org.apache.ibatis.session.SqlSession
而不是我的SessionFactoryProducerImpl
我在将 EAR 部署到服务器期间得到Unsatisfied dependencies for type SqlSession with qualifiers @Default
- 我注入 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.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
有趣的事情。我刚刚向 war 项目添加了一个新的 servlet,以显示 CDI 容器中可用 bean 的列表:
@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 是:
(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.
但是当我调用我的 EJB 的 echo(...)
方法时,我仍然得到 org.mybatis.cdi.MybatisCdiConfigurationException: There are no SqlSessionFactory producers properly configured
错误。
我猜 MyBatis 不知何故需要使用 a.b.ejb.SessionFactoryProducerImpl
中的生产者方法。但是怎么告诉mybatis-cdi呢?
文档没有告诉删除 @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
甚至可能不是强制性的。
我想 @Stateless
将 SessionFactoryProducer
变成一个 EJB,然后在同一个 context/scope 中可用,并且可以注入另一个。
SessionFactoryProducer
只需要在场即可。之后这是 CDI 的工作,当它找到 SqlSession 或 Mapper 注入点时调用它。
这就是我所做的,会话由 EJB 管理。当然提供mybatis-config.xml:
<environments default="development">
<environment id="development">
<transactionManager type="MANAGED">
<dataSource type="JNDI">