在Spring Boot 1.4(Spring 4)中使用MyBatis2(iBatis)的方法
这次我们将介绍如何在Spring Boot 1.4(Spring 4.3)上使用MyBatis 2(iBatis更为人所熟知)的方法。也许大多数人会想为什么现在才谈论MyBatis 2?但我写这篇文章的动机是因为Spring 3系将于今年12月(只剩半个月了…)正式停止支持(EOL)。
MyBatis 2与Spring的关系
Spring从3.x系列开始提供与MyBatis 2的集成组件,但是从4.x系列开始,它被华丽地删除了。因此,使用MyBatis 2的应用程序(使用遗留架构构建的应用程序)无法升级到Spring 4。以前,如果发现重要的漏洞或安全漏洞,会反馈给Spring 3系列,但从明年开始将不再反馈。考虑到安全漏洞等问题,应考虑升级到Spring 4系列(或预计于明年4月左右发布的Spring 5系列)!
顺便提一下,MyBatis的最新版本是3.4.1,但与MyBatis 2完全不兼容。此外,MyBatis 3与Spring的集成组件并非由Spring官方提供,而是由MyBatis开发社区提供的。
所以,该怎么办呢?
也许很少人知道,但是从MyBatis的开发社区提供了一个名为”ibatis-spring”的东西。
它的实质是一个完全克隆了与Spring 3系提供的MyBatis 2的协作组件,所以只需将jar文件导入,就可以在Spring 4上使用MyBatis 2了。
让我们在Spring Boot 1.4中尝试使用MyBatis 2!
我們將以證據為依據,進一步確認在Spring Boot 1.4(Spring 4.3)上是否可運行MyBatis 2。
首先,我们将使用”SPRING INITIALIZR”来创建开发项目。在依赖项中,指定”JDBC”和”H2″。
我们在下面的GitHub仓库中公开了完成品。
- https://github.com/kazuki43zoo/mybatis2-demo
注意事项:
2016/12/17 13:30 追加记录我已经在Spring Boot 2.0(Spring 5.0)的SNAPSHOT版本上进行了操作确认!结果当然是成功的。
将MyBatis2和iBatis-Spring添加为依赖项。
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis2</artifactId>
<version>2.3.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-2-spring</artifactId>
<version>1.0.3</version>
</dependency>
创建todo表
在H2内置数据库中创建一个名为todo的表。如果使用Spring Boot的自动配置创建的DataSource,可以将名为schema.sql和data.sql的文件放置在类路径下,在Spring Boot启动时会读取并执行这些文件中的SQL语句。
CREATE TABLE todo (
id INTEGER
,title TEXT NOT NULL
,details TEXT
,finished BOOLEAN NOT NULL
);
CREATE SEQUENCE seq_todo;
注意:
其实,本来应该将id列设置为ID列,并且使用JDBC 3.0中新增的Statement#getGeneratedKeys()方法进行操作,但是…似乎MyBatis 2并不支持该方法。(MyBatis 3可以进行协作)
所以…在这里我们采用传统的序列方式(请谅解)。
创建领域对象
这次,我们将创建一个名为Todo的类作为领域对象。
package com.example.domain;
public class Todo {
private int id;
private String title;
private String details;
private boolean finished;
// ... setter and getter
}
创建 DAO 类
这次,我们将创建一个提供对Todo进行CRUD操作的DAO类。
package com.example.dao;
import com.example.domain.Todo;
import org.springframework.orm.ibatis.SqlMapClientOperations;
import org.springframework.stereotype.Repository;
@Repository
@SuppressWarnings("deprecation") // 非推奨の警告を削除・・・(知っておりますw)
public class TodoDao {
private final SqlMapClientOperations operations;
public TodoDao(SqlMapClientOperations operations) {
this.operations = operations;
}
public void insert(Todo todo) {
operations.insert("com.example.dao.TodoDao.insert", todo);
}
public Todo select(int id) {
return (Todo) operations.queryForObject("com.example.dao.TodoDao.select", id);
}
}
注意:
由于ibatis-spring是Spring 3系列的完全克隆,因此仍带有@Deprecated。尽管我们十分了解它的不推荐使用,但我们还是在类级别上添加@SuppressWarnings(“deprecation”)来消除警告。
创建MyBatis 2的配置文件
创建MyBatis 2的配置文件,并指定MyBatis 2的操作。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMapConfig
PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">
<sqlMapConfig>
<settings useStatementNamespaces="true"/> <!-- ネームスペースを有効にする -->
</sqlMapConfig>
创建SQL映射文件
创建SQL映射文件,并编写SQL以执行对Todo对象的CRUD操作。
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE sqlMap
PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
"http://ibatis.apache.org/dtd/sql-map-2.dtd">
<sqlMap namespace="com.example.dao.TodoDao">
<insert id="insert" parameterClass="com.example.domain.Todo">
<selectKey keyProperty="id" resultClass="int" type="pre">
SELECT seq_todo.nextval
</selectKey>
INSERT INTO todo
(
id
,title
,details
,finished
)
values(
#id#
,#title#
,#details#
,#finished#
)
</insert>
<select id="select" parameterClass="int" resultClass="com.example.domain.Todo">
SELECT
id, title, details, finished
FROM
todo
WHERE
id = #id#
</select>
</sqlMap>
MyBatis 2所使用的Bean定义
我们将对SqlMapClient和SqlMapClientTemplate进行Bean定义。在这里,我们创建了一个名为Config的类作为Mybatis2DemoApplication的内部类。
package com.example;
import com.ibatis.sqlmap.client.SqlMapClient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.orm.ibatis.SqlMapClientFactoryBean;
import org.springframework.orm.ibatis.SqlMapClientOperations;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import javax.sql.DataSource;
import java.io.IOException;
@SpringBootApplication
public class Mybatis2DemoApplication {
public static void main(String[] args) {
SpringApplication.run(Mybatis2DemoApplication.class, args);
}
// MyBatis 2用のBean定義
@Configuration
@SuppressWarnings("deprecation")
static class MyBatisConfig {
private final ResourcePatternResolver resourcePatternResolver;
MyBatisConfig(ResourcePatternResolver resourcePatternResolver) {
this.resourcePatternResolver = resourcePatternResolver;
}
@Bean
SqlMapClientFactoryBean sqlMapClient(DataSource dataSource) throws IOException {
SqlMapClientFactoryBean factoryBean = new SqlMapClientFactoryBean();
factoryBean.setConfigLocations(resourcePatternResolver.getResources("classpath*:/mybatis/sqlMapConfig.xml"));
factoryBean.setMappingLocations(resourcePatternResolver.getResources("classpath*:/mybatis/**/*-sqlmap.xml"));
factoryBean.setDataSource(dataSource);
return factoryBean;
}
@Bean
SqlMapClientOperations sqlMapClientOperations(SqlMapClient sqlMapClient) {
return new SqlMapClientTemplate(sqlMapClient);
}
}
}
修改Mybatis2DemoApplication并启动Spring Boot应用程序。
我将修改Mybatis2DemoApplication,并通过DAO类(ibatis-spring)来访问数据库。
package com.example;
import com.example.dao.TodoDao;
import com.example.domain.Todo;
import com.ibatis.sqlmap.client.SqlMapClient;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.orm.ibatis.SqlMapClientFactoryBean;
import org.springframework.orm.ibatis.SqlMapClientOperations;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import org.springframework.transaction.annotation.Transactional;
import javax.sql.DataSource;
import java.io.IOException;
@SpringBootApplication
public class Mybatis2DemoApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(Mybatis2DemoApplication.class, args);
}
private final TodoDao todoDao;
Mybatis2DemoApplication(TodoDao todoDao) {
this.todoDao = todoDao;
}
@Transactional
@Override
public void run(String... args) throws Exception {
Todo newTodo = new Todo();
newTodo.setTitle("飲み会");
newTodo.setDetails("銀座 19:00");
todoDao.insert(newTodo); // 新しいTodoをインサートする
Todo loadedTodo = todoDao.select(newTodo.getId()); // インサートしたTodoを取得して標準出力する
System.out.println("ID : " + loadedTodo.getId());
System.out.println("TITLE : " + loadedTodo.getTitle());
System.out.println("DETAILS : " + loadedTodo.getDetails());
System.out.println("FINISHED : " + loadedTodo.isFinished());
}
@Configuration
@SuppressWarnings("deprecation")
static class MyBatisConfig {
private final ResourcePatternResolver resourcePatternResolver;
MyBatisConfig(ResourcePatternResolver resourcePatternResolver) {
this.resourcePatternResolver = resourcePatternResolver;
}
@Bean
SqlMapClientFactoryBean sqlMapClient(DataSource dataSource) throws IOException {
SqlMapClientFactoryBean factoryBean = new SqlMapClientFactoryBean();
factoryBean.setConfigLocations(resourcePatternResolver.getResources("classpath*:/mybatis/sqlMapConfig.xml"));
factoryBean.setMappingLocations(resourcePatternResolver.getResources("classpath*:/mybatis/**/*-sqlmap.xml"));
factoryBean.setDataSource(dataSource);
return factoryBean;
}
@Bean
SqlMapClientOperations sqlMapClientOperations(SqlMapClient sqlMapClient) {
return new SqlMapClientTemplate(sqlMapClient);
}
}
}
当你修改了MybatisDemo2Application后,它将以Spring Boot应用程序的形式启动。
$ ./mvnw spring-boot:run
...
2016-12-17 10:37:21.407 INFO 10597 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
ID : 1
TITLE : 飲み会
DETAILS : 銀座 19:00
FINISHED : false
2016-12-17 10:37:21.501 INFO 10597 --- [ main] com.example.Mybatis2DemoApplication : Started Mybatis2DemoApplication in 1.463 seconds (JVM running for 4.619)
...
Todo的状态已经输出到标准输出上。
顺带一提,开发项目将会处于以下状态:
.
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
├── main
│ ├── java
│ │ └── com
│ │ └── example
│ │ ├── Mybatis2DemoApplication.java // 修正したファイル
│ │ ├── dao
│ │ │ └── TodoDao.java // 追加したファイル
│ │ └── domain
│ │ └── Todo.java // 追加したファイル
│ └── resources
│ ├── application.properties
│ ├── mybatis
│ │ ├── sqlMapConfig.xml // 追加したファイル
│ │ └── todo-sqlmap.xml // 追加したファイル
│ └── schema.sql // 追加したファイル
└── test
└── java
└── com
└── example
└── Mybatis2DemoApplicationTests.java
总结
暫時來說,確認問題無法正常運作了!!
就我個人而言,如果想在Spring 4上使用MyBatis,我認為應該使用MyBatis 3。但是,也有一些因為成人的情況而不能這樣做的情況。我理解你想要盡可能地將影響範圍縮小並進行EOL對策,所以我建議那些正在考慮Spring 3系的EOL對策的人,可以考慮升級到Spring 4.3並考慮使用”ibatis-spring”。
如果您正在考虑从Spring 3系升级到Spring 4系(或Spring 5系),我认为以下网站(Spring官方的迁移指南,TERASOLUNA的迁移指南)可能会对您有所帮助。
-
- https://github.com/spring-projects/spring-framework/wiki#migrating-from-earlier-versions
- https://github.com/terasolunaorg/terasoluna-gfw/wiki