问题描述
我想在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 (删除@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
- 我注入
org.apache.ibatis.session。 SqlSession
而不是我的SessionFactoryProducerImpl
我得到一个类型为SqlSession的不满意依赖项,带有限定符@Default
在部署EAR到服务器期间 - 我注入MyBatis映射器,在我的情况下是
ConfigurationDao
然后我得到没有正确配置的SqlSessionFactory生成器
错误。
- I inject
org.apache.ibatis.session.SqlSession
instead of mySessionFactoryProducerImpl
I get anUnsatisfied dependencies for type SqlSession with qualifiers @Default
during deploy EAR to server - I inject MyBatis mapper which is the
ConfigurationDao
in my case then I get aThere 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 SessionFactoryProducer
is 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 +容器管理事务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!