在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″。

spring-initializr.png

我们在下面的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