Spring Boot 训练营:Spring Boot + Spring Data JDBC + MyBatis编

这是Spring Boot训练营系列的一部分,讲的是Spring Boot + Spring Data JDBC + MyBatis。

这次的目的

使用Spring Boot应用程序,并利用Spring Data JDBC的MyBatis集成功能来访问数据库。

    • Spring Data JDBCを利用したDBアクセスはこちら

 

    MyBatisを利用したDBアクセスはこちら

这次使用的库

    • spring-boot-starter:2.2.0.M4

 

    • spring-boot-starter-data-jdbc:2.2.0.M4

 

    • mybatis-spring-boot-starter:2.0.1

 

    • spring-boot-starter-test:2.2.0.M4

 

    • mybatis-spring-boot-starter-test:2.0.1

 

    • h2:1.4.199

 

    lombok:1.18.8

通过使用Spring Initializr创建项目,可以解决下面的几个步骤。

数据库访问

在进行Spring Data JDBC + MyBatis的实现之前,首先需要进行数据库访问的配置。

数据库访问的定义

在Spring Boot中,它将自动为访问数据库的DataSource等Bean进行定义。
默认情况下,它将访问内存中的H2数据库,因此您需要添加h2依赖项。

pom.xml翻译为汉语:“项目对象模型”

    <dependencies>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
    </dependencies>

注意。
在Spring Boot的配置文件中更改驱动程序和连接目标,即可访问其他数据库。(需要增加对应依赖关系的驱动程序)

src/main/resources/application.yml

spring:
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://localhost:5432/testdb
username: postgres
password: postgres

如果在src/main/resources的直接下方创建以下的SQL文件,那么在应用程序启动时也可以自动初始化数据库。
默认情况下,会识别以下的SQL文件。

    • schema.sql

 

    • schema-${platform}.sql

 

    • data.sql

 

    data-${platform}.sql

我們將DDL定義在schema.sql中,並在data.sql中定義DML。

注意。
${platform}在spring.datasource.platform属性中指定。
在单元测试时可以使用H2,在集成测试时可以使用Postgresql来进行区分。

这次将创建一个schema.sql文件并创建表格。

主要资源/架构文件的路径为 src/main/resources/schema.sql。

create table if not exists todo (
    todo_id identity,
    todo_title varchar(30),
    finished boolean,
    created_at timestamp
);

spring-boot-starter-data-jdbc 和 mybatis-spring-boot-starter 的组合

这是一个用于在Spring Boot中使用Spring Data JDBC和MyBatis的启动器。它利用了Spring Boot的自动配置机制,可以自动为Spring Boot应用程序定义使用Spring Data JDBC和MyBatis的Bean。

只需要添加这些依赖项并实现一个用@Configuration类来协调Spring Data JDBC和MyBatis的集成即可。

pom.xml的汉语翻译可以是:项目对象模型.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.0.1</version>
        </dependency>
    </dependencies>

src/main/java/*/SpringDataJdbc.java 的中文本地化的一个选择:

@Configuration
public class SpringDataJdbcConfig {
    @Bean
    @Primary
    public DataAccessStrategy mybatisDataAccessStrategy(SqlSession sqlSession) {
        return new MyBatisDataAccessStrategy(sqlSession);
    }
}

为了协同工作,我们将在Spring Data JDBC的DataAccessStrategy中使用MyBatisDataAccessStrategy类。
只需定义Bean,DataAccessStrategy将自动应用。

注意。
由于DefalutDataAccessStrategy类的Bean将自动地生成,因此在DataAccessStrategy的Bean定义中不加上@Primary注解会导致Bean重复并出现错误。

src/main/java/*/Application.java的中文本地化选项:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

主类没有对Spring Boot应用程序的默认进行任何更改。
如果@Configuration类实现在主类的子包中,它将自动扫描。

可以使用Spring Data JDBC + MyBatis做什么

当执行CrudRepository的方法时,可以使用MyBatis而不是Spring Data的NamedParameterJdbcTemplate来发出SQL。

MyBatis Mapper, which is linked from CrudRepository, follows certain naming rules in Chinese.

[DomainクラスのFQCN]Mapper.[予約メソッド名]

如果是com.example.domain.Todo类,Mapper的名称将会是com.example.domain.TodoMapper。

请参考Spring Data JDBC – MyBatis Integration以获取更详细信息,以下是预约方法的示例名称。

    • MapperのfindById -> CrudRepositoryのfindByIdメソッドで使用される

 

    Mapperのinsert -> CrudRepositoryのsaveやsaveAllメソッドで使用される

在这里需要注意的是,不能在MyBatis中自由地创建自定义查询。
如果想要创建自定义查询,可以使用Spring Data JDBC中的@Query,或者直接使用MyBatis。

此外,CrudRepository方法的参数和Mapper的返回值将与MyBatisContext协同工作。
因此,如果仅使用MyBatis,则Mapper的实现方式会稍有不同。

域类

在将数据库数据映射到领域类的过程中,给予标有@ID的注解来识别主键。
为了简化以下代码中Getter和Setter的实现,使用了Lombok的@Data注解。

src/main/java/*/domain/Todo.java可以用下列方式进行汉语翻译:

src/main/java/*/domain/Todo.java

@Data
public class Todo {
    @Id
    private String todoId;
    private String todoTitle;
    private boolean finished;
    private LocalDateTime createdAt;
}

注意。
当表名和领域类名不同时,只需要在类上加上@Table(“表名”)即可。

存储库接口

Spring的Repository接口继承自Spring Data的CrudRepository接口。

src/main/java/*/repository/TodoRepository.java 可以用以下方式在中文中进行转述:源代码主文件夹/src/main/java/*/repository/TodoRepository.java

// (1)
public interface TodoRepository extends CrudRepository<Todo, String> {

   // (2)
}

(1) Repository接口继承自CrudRepository。泛型为<Domain类的类型, ID的类型>。

(2) 如果继承 CrudRepository,则会自动支持CRUD的默认方法(查询)。可以查阅CrudRepository的JavaDoc以了解提供的方法。

MyBatis的Mapper接口

MyBatis的Mapper接口将实现在与Domain类相同的包中。
可以使用XML和带有@Mapper注解的接口来实现Mapper,但在这里我们决定使用接口实现。

src/main/java/*/domain/TodoMapper.java 的路径是需要的。

// (1)
@Mapper
public interface TodoMapper {

    // (2)
    @Select("SELECT todo_id, todo_title, finished, created_at FROM todo WHERE todo_id = #{id}")
    Optional<Todo> findById(MyBatisContext context); // (3)

    @Select("SELECT todo_id, todo_title, finished, created_at FROM todo")
    Collection<Todo> findAll();

    @Insert("INSERT INTO todo (todo_title, finished, created_at) VALUES (#{instance.todoTitle}, #{instance.finished}, #{instance.createdAt})")
    @Options(useGeneratedKeys = true, keyProperty = "instance.todoId")
    Todo insert(MyBatisContext context);

    @Update("UPDATE todo SET todo_title = #{instance.todoTitle}, finished = #{instance.finished}, created_at = #{instance.createdAt} WHERE todo_id = #{id}")
    Todo update(MyBatisContext context);

    @Delete("DELETE FROM todo WHERE todo_id = #{id}")
    void delete(MyBatisContext context);

    @Select("SELECT COUNT(*) FROM todo WHERE finished = 'FALSE'")
    long count(MyBatisContext context);
}

(1) 当在Mapper接口上添加@Mapper注解时,MyBatis会自动扫描并将其注册为Mapper。请将Mapper接口放置在主类所在的包下。

(2) 在方法上使用@Select、@Insert、@Update、@Delete注解,来实现要执行的SQL语句。在SQL语句中使用#{}来使用参数,但需要遵守以下规则。

    • Domainクラスの@ID項目 -> #{id}

Domainクラスの@ID以外の項目 -> #{instance.[プロパティ名]}

(3) 以下是方法签名的规则。

    • メソッド名は先述の予約メソッド名にする必要があります。メソッド名が異なると呼び出されません。

 

    • メソッド引数は必ずMyBatisContextにする必要があります。

 

    • メソッド戻り値はCrudRepositoryメソッドと合わせる必要がありますが、いくつか異なります。

OptionalはあってもなくてもOKでした。

Iterableは対応しておらず、Collection等にする必要がありました。

请注意。
为了弥合表中列名与Domain类的属性名之间的差异,在Spring Boot的配置文件中指定了mybatis.configuration.map-underscore-to-camel-case属性。

将Domain类和Mapper接口的包进行分离。

前面提到将Domain类和Mapper接口放在同一包中,但是随着数量的增加,整理变得麻烦,为了整理,将包分离。

可以通过实现NamespaceStrategy接口来更改与CrudRepository协作的Mapper命名规则。
然而,NamespaceStrategy只能更改[Domain类的FQCN]Mapper部分,要更改保留方法名称,需要扩展MyBatisDataAccessStrategy。

在这里,我们将实现NamespeceStrategy并将命名规则更改如下。

[Domainクラスのパッケージ名].mapper.[Domainクラス名]Mapper.[予約メソッド名]

/src/main/java/*/SpringDataJdbc.config 可以用中文来简述是:源代码中的主要路径下的Java文件夹中的SpringDataJdbc.config文件。

@Configuration
public class SpringDataJdbcConfig {
    @Bean
    @Primary
    public DataAccessStrategy mybatisDataAccessStrategy(SqlSession sqlSession) {
        MyBatisDataAccessStrategy strategy = new MyBatisDataAccessStrategy(sqlSession);
        strategy.setNamespaceStrategy(new NamespaceStrategy() {
            @Override
            public String getNamespace(Class<?> domainType) {
                return domainType.getPackage().getName() + ".mapper." + domainType.getSimpleName() + "Mapper";
            }
        });
        return strategy;
    }
}

实现NamespaceStrategy接口,并在getNamespace方法中确定Mapper的名称,然后将其设置到MyBatisDataAccessStrategy中。

只要将 Mapper 接口移动到 mapper 包中,就可以了。

春季启动器测试 + Mybatis春季启动器测试

这是一个用于测试Spring Boot和MyBatis的启动器。通过利用Spring Boot的自动配置机制,它可以自动为Spring Boot应用程序在使用Spring Data JDBC和MyBatis进行测试时进行Bean定义。

要想测试Spring Data JDBC和MyBatis的联合使用,只需在依赖关系中添加它们,并进行少量的配置即可。

pom.xml 可以被翻译为「项目对象模型文件」。

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter-test</artifactId>
            <version>2.0.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

注意:
严格来说,不仅可以使用mybatis-spring-boot-starter-test,只用mybatis-spring-boot-starter也是可以的,但为了清楚地区分主要语法和测试语法,在这里使用后者。

JUnit测试用例

在JUnit测试案例中,只需为类添加以下注解,就可以自动装配要测试的仓库。

@DataJdbcTest -> Spring Data JDBCを有効化します。

@AutoConfigureMybatis -> MyBatisを有効化します。

@Import -> MyBatisDataAccessStrategyを有効化する設定を読み込みます。

/src/test/java/*/repository/TodoRepositoryTest.java 可以翻译成以下方式:

源代码/测试/Java文件夹/仓库/TodoRepositoryTest.java

@DataJdbcTest
@AutoConfigureMybatis // (1)
@Import(SpringDataJdbcConfig.class) // (2)
@Transactional // (3)
class TodoRepositoryTest {

    @Autowired
    TodoRepository todoRepository;

    @Test
    @Sql(statements = "INSERT INTO todo (todo_title, finished, created_at) VALUES ('sample todo', false, '2019-01-01')")
    void testFindAll() {
        // execute
        Iterable<Todo> todos = todoRepository.findAll();

        // assert
        assertThat(todos)
                .hasSize(1)
                .extracting(Todo::getTodoTitle, Todo::isFinished, Todo::getCreatedAt)
                .containsExactly(tuple("sample todo", false, LocalDate.of(2019, 1, 1).atStartOfDay()));
    }

由于无法同时使用@DataJdbcTest和@MyBatisTest注解,因此可以通过在@DataJdbcTest注解中添加@AutoConfigureMybatis来进行使用。

(2)像@DataJdbcTest这样的测试专用Auto-Config为了轻量化只会有限地加载Bean,不会加载自定义的@Configuration类等。由于需要在应用程序运行时和测试时进行不同的设置,开发人员需要理解一定程度的Auto-Config才能进行操作。

@DataJdbcTest无法自动添加@Transactional,因此在测试结束后进行回滚,您需要自己添加@Transactional。

注意。
从Spring Boot 2.2.2版本开始,@DataJdbcTest注解上将会自动应用@Transactional注解。

总结

使用Spring Data JDBC可以很容易地实现与MyBatis的集成,几乎无需任何配置。但是,使用MyBatis会有一些限制,所以说到与其集成,有什么好处呢,嗯。。。

我认为这种设计思想的目的是将MyBatis限定在对Domain类进行CRUD操作上,或者在处理复杂数据库表作为View的CRUD访问的有限情况下使用。

广告
将在 10 秒后关闭
bannerAds