DAO 模式 + JNDI 数据源 + CDI with MyBatis
DAO pattern + JNDI datasouce + CDI with MyBatis
我打算从我的项目中删除 JPA (Eclipselink) 并使用 MyBatis 代替它。我正在寻找一些很好的指南,什么是在 EE 容器(无状态会话 ejb)和 JNDI 数据源 + DAO 模式 + CDI(我没有使用 Spring!)中使用 MyBatis 的最佳实践。不幸的是,我没有找到任何关于它的好文档。
有没有不用xml配置文件初始化MyBatis和使用JNDI数据源的方法?
MyBatis 实现 DAO 模式并将我的 dao classes 注入到带有 CDI 的无状态 EJB 的最佳方式是什么?
我使用 Java 8 + Glassfish (Payara EE 服务器) + MyBatis 3.4.2.
UPDATE-1
我按照此页面上的说明进行操作 http://www.mybatis.org/cdi/getting-started.html 但它对我不起作用。
这是我使用 Glassfish(实际上它是一个 Payara)应用程序服务器得到的运行时异常:
[2017-02-14T22:02:23.715+0100] [Payara 4.1] [INFO] [AS-WEB-GLUE-00172] [javax.enterprise.web] [tid: _ThreadID=101 _ThreadName=admin-listener(6)] [timeMillis: 1487106143715] [levelValue: 800] [[
Loading application [mybatis-demo-1.0#mybatis-demo-war-1.0.war] at [/demo]]]
[2017-02-14T22:02:23.770+0100] [Payara 4.1] [INFO] [] [javax.enterprise.system.core] [tid: _ThreadID=101 _ThreadName=admin-listener(6)] [timeMillis: 1487106143770] [levelValue: 800] [[
mybatis-demo-1.0 was successfully deployed in 1,526 milliseconds.]]
[2017-02-14T22:03:00.333+0100] [Payara 4.1] [INFO] [] [javax.enterprise.web] [tid: _ThreadID=25 _ThreadName=http-listener-1(2)] [timeMillis: 1487106180333] [levelValue: 800] [[
WebModule[null] ServletContext.log():Marking servlet a.b.war.HelloServlet as unavailable]]
[2017-02-14T22:03:00.334+0100] [Payara 4.1] [WARNING] [] [javax.enterprise.web] [tid: _ThreadID=25 _ThreadName=http-listener-1(2)] [timeMillis: 1487106180334] [levelValue: 900] [[
StandardWrapperValve[a.b.war.HelloServlet]: Allocate exception for servlet a.b.war.HelloServlet
org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type PersonMapper with qualifiers @Default
at injection point [BackedAnnotatedField] @Inject private a.b.war.HelloServlet.personMapper
at a.b.war.HelloServlet.personMapper(HelloServlet.java:0)
这是我的测试 servlet:
@WebServlet("/servlet")
public class HelloServlet extends HttpServlet {
@Inject
private PersonMapper personMapper;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().println(personMapper.getPerson(1L).toString());
}
我的映射器class:
@Mapper
public interface PersonMapper {
@Select("SELECT * FROM person WHERE id = #{id}")
Person getPerson(@Param("id") long id);
}
似乎从未调用 MyBatisSQLSessionFactory.getSqlSessionFactory() 方法,因为我在日志文件中看不到任何内容。
我的 SessionFactory class:
public class MyBatisSQLSessionFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(MyBatisSQLSessionFactory.class);
@Produces
@ApplicationScoped
@SessionFactoryProvider
public SqlSessionFactory getSqlSessionFactory() throws IOException {
LOGGER.info("MyBatis is initializing...");
String resource = "mybatis-configuration.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
LOGGER.info("MyBatis has been initialized, SQL Session Factory: {}", sqlSessionFactory.toString());
return sqlSessionFactory;
}
}
最后是我的 war 的结构:
*.war
│ index.html
│
├───META-INF
│ │ bean.xml
│ │ MANIFEST.MF
│ │
│ └───maven
│ ...
│
└───WEB-INF
├───classes
│ └───a
│ └───b
│ └───war
│ │ HelloServlet.class
│ │
│ └───mybatis
│ │ MyBatisSQLSessionFactory.class
│ │
│ └───dao
│ PersonMapper.class
│
└───lib
可能是我记错了,忘记了什么...
UPDATE-2
“如果未被至少一个正确的 CDI bean 使用,则 Weld 不会检测映射器。并且 Servlet 不是正确的 CDI bean。
解决方法是用@Dependent 注释 servlet。”
您可以找到更多详细信息 here.
是的,在 Java EE 环境,尤其是 CDI 中,有一个很好的方法可以使用 myBatis。
有一个 mybatis-cdi 扩展可以为您解决许多问题并确保完美集成。 GitHub repository is here and it links to official documentation.
您应该通读official documentation。特别是 "Getting started" 部分(在左侧菜单中,就在介绍之后)。他们解释了如何设置所有内容。
此外,还有一个很好的示例项目 to be found on GitHub. There is even an example with JSF used。
我的回答在这里,为了清楚起见,还报告了我的评论。
不是第一期,但可能是下一期:mybatis-configuration。xml 在类路径中至少需要定义 transactionManager 和引用 jndi dataSource。
以防万一,为 MyBatisSQLSessionFactory 添加一个带日志的默认构造函数。应该只实例化一次。
对于 WAR,beans.xml 必须在 WEB-INF 中。否则在单独的依赖项中提取业务代码 ejb.jar,全部打包在 EAR 中。
我建议激活JTA Transactions:填写beans.xml。
我在 EJB 中 @Inject @SessionFactory protected SqlSession session;
,然后在它的方法中:new DAO(session);
因为事务是在 EJB 级别管理的,并且注入会话注入的 DAO 没有按预期工作。
同时在调用多个语句时注释 EJB 方法 @Transactional(executorType=ExecutorType.REUSE)
。这将使用相同的连接;对所有交易报表执行操作,并且仅准备一次唯一报表。没弄清楚就先试试
我打算从我的项目中删除 JPA (Eclipselink) 并使用 MyBatis 代替它。我正在寻找一些很好的指南,什么是在 EE 容器(无状态会话 ejb)和 JNDI 数据源 + DAO 模式 + CDI(我没有使用 Spring!)中使用 MyBatis 的最佳实践。不幸的是,我没有找到任何关于它的好文档。
有没有不用xml配置文件初始化MyBatis和使用JNDI数据源的方法?
MyBatis 实现 DAO 模式并将我的 dao classes 注入到带有 CDI 的无状态 EJB 的最佳方式是什么?
我使用 Java 8 + Glassfish (Payara EE 服务器) + MyBatis 3.4.2.
UPDATE-1
我按照此页面上的说明进行操作 http://www.mybatis.org/cdi/getting-started.html 但它对我不起作用。
这是我使用 Glassfish(实际上它是一个 Payara)应用程序服务器得到的运行时异常:
[2017-02-14T22:02:23.715+0100] [Payara 4.1] [INFO] [AS-WEB-GLUE-00172] [javax.enterprise.web] [tid: _ThreadID=101 _ThreadName=admin-listener(6)] [timeMillis: 1487106143715] [levelValue: 800] [[
Loading application [mybatis-demo-1.0#mybatis-demo-war-1.0.war] at [/demo]]]
[2017-02-14T22:02:23.770+0100] [Payara 4.1] [INFO] [] [javax.enterprise.system.core] [tid: _ThreadID=101 _ThreadName=admin-listener(6)] [timeMillis: 1487106143770] [levelValue: 800] [[
mybatis-demo-1.0 was successfully deployed in 1,526 milliseconds.]]
[2017-02-14T22:03:00.333+0100] [Payara 4.1] [INFO] [] [javax.enterprise.web] [tid: _ThreadID=25 _ThreadName=http-listener-1(2)] [timeMillis: 1487106180333] [levelValue: 800] [[
WebModule[null] ServletContext.log():Marking servlet a.b.war.HelloServlet as unavailable]]
[2017-02-14T22:03:00.334+0100] [Payara 4.1] [WARNING] [] [javax.enterprise.web] [tid: _ThreadID=25 _ThreadName=http-listener-1(2)] [timeMillis: 1487106180334] [levelValue: 900] [[
StandardWrapperValve[a.b.war.HelloServlet]: Allocate exception for servlet a.b.war.HelloServlet
org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type PersonMapper with qualifiers @Default
at injection point [BackedAnnotatedField] @Inject private a.b.war.HelloServlet.personMapper
at a.b.war.HelloServlet.personMapper(HelloServlet.java:0)
这是我的测试 servlet:
@WebServlet("/servlet")
public class HelloServlet extends HttpServlet {
@Inject
private PersonMapper personMapper;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().println(personMapper.getPerson(1L).toString());
}
我的映射器class:
@Mapper
public interface PersonMapper {
@Select("SELECT * FROM person WHERE id = #{id}")
Person getPerson(@Param("id") long id);
}
似乎从未调用 MyBatisSQLSessionFactory.getSqlSessionFactory() 方法,因为我在日志文件中看不到任何内容。
我的 SessionFactory class:
public class MyBatisSQLSessionFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(MyBatisSQLSessionFactory.class);
@Produces
@ApplicationScoped
@SessionFactoryProvider
public SqlSessionFactory getSqlSessionFactory() throws IOException {
LOGGER.info("MyBatis is initializing...");
String resource = "mybatis-configuration.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
LOGGER.info("MyBatis has been initialized, SQL Session Factory: {}", sqlSessionFactory.toString());
return sqlSessionFactory;
}
}
最后是我的 war 的结构:
*.war
│ index.html
│
├───META-INF
│ │ bean.xml
│ │ MANIFEST.MF
│ │
│ └───maven
│ ...
│
└───WEB-INF
├───classes
│ └───a
│ └───b
│ └───war
│ │ HelloServlet.class
│ │
│ └───mybatis
│ │ MyBatisSQLSessionFactory.class
│ │
│ └───dao
│ PersonMapper.class
│
└───lib
可能是我记错了,忘记了什么...
UPDATE-2
“如果未被至少一个正确的 CDI bean 使用,则 Weld 不会检测映射器。并且 Servlet 不是正确的 CDI bean。 解决方法是用@Dependent 注释 servlet。” 您可以找到更多详细信息 here.
是的,在 Java EE 环境,尤其是 CDI 中,有一个很好的方法可以使用 myBatis。
有一个 mybatis-cdi 扩展可以为您解决许多问题并确保完美集成。 GitHub repository is here and it links to official documentation.
您应该通读official documentation。特别是 "Getting started" 部分(在左侧菜单中,就在介绍之后)。他们解释了如何设置所有内容。
此外,还有一个很好的示例项目 to be found on GitHub. There is even an example with JSF used。
我的回答在这里,为了清楚起见,还报告了我的评论。
不是第一期,但可能是下一期:mybatis-configuration。xml 在类路径中至少需要定义 transactionManager 和引用 jndi dataSource。
以防万一,为 MyBatisSQLSessionFactory 添加一个带日志的默认构造函数。应该只实例化一次。
对于 WAR,beans.xml 必须在 WEB-INF 中。否则在单独的依赖项中提取业务代码 ejb.jar,全部打包在 EAR 中。
我建议激活JTA Transactions:填写beans.xml。
我在 EJB 中 @Inject @SessionFactory protected SqlSession session;
,然后在它的方法中:new DAO(session);
因为事务是在 EJB 级别管理的,并且注入会话注入的 DAO 没有按预期工作。
同时在调用多个语句时注释 EJB 方法 @Transactional(executorType=ExecutorType.REUSE)
。这将使用相同的连接;对所有交易报表执行操作,并且仅准备一次唯一报表。没弄清楚就先试试