• Spring Boot 实践 :Spring Boot + MyBatis


    Spring Boot 实践系列,Spring Boot + MyBatis 。

    目的

    将 MyBatis 与 Spring Boot 应用程序一起使用来访问数据库。

    本次使用的Library

    • spring-boot-starter:2.2.0.M4
    • mybatis-spring-boot-starter:2.0.1
    • mybatis-spring-boot-starter-test:2.0.1
    • h2:1.4.199
    • lombok:1.18.8

    数据库访问

    在实现 MyBatis 之前,首先进行 DB 访问的设置。

    数据库访问定义

    DataSourceSpring Boot自动定义了访问 DB等 bean。
    默认情况下,它访问内存中的 H2 数据库,因此您需要将 h2 添加到依赖项中。

    pom.xml

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

    注意
    :您可以通过更改 Spring Boot 配置文件中的驱动程序和连接目标来访问另一个 DB。(需要添加依赖对应的驱动)

    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

    如您所见,我们在 schema.sql 中定义了 DDL,在 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
    );

    mybatis-spring-boot-starter

    它是使用 MyBatis 和 Spring Boot 的入门者。
    通过使用 Spring Boot 的 Auto Configuration 机制,自动完成在 Spring Boot 应用中使用 MyBatis 的 Bean 定义。开发者只需要将 mybatis-spring-boot-starter 添加到他们的依赖项中。

    pom.xml

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

    src/main/java/*/Application.java

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

    主类没有改变 Spring Boot 应用程序中的默认值。

    领域类

    映射 DB 数据的域类对于普通的 Java Bean 是可以的。
    在下文中,Lombok 用于省略 Getter 和 Setter 的实现@Data

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

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

    存储库接口

    MyBatis 的 Mapper 对应 Spring 的 Repository 接口。
    MyBatis 的 Mapper 可以通过以下任意一种方式实现。

    • 创建Repository接口对应的Mapper XML
    • @Mapper授予存储库接口


    这次我将根据注解库 Spring Boot 用 MyBatis 的 Mapper 来实现@Mapper

    src/main/java/*/repository/TodoRepository.java

     
    @Mapper // (1)
    public interface TodoRepository {
    
        // (2)
        @Select("SELECT todo_id, todo_title, finished, created_at FROM todo WHERE todo_id = #{todoId}")
        Optional<Todo> findById(String todoId);
    
        @Select("SELECT todo_id, todo_title, finished, created_at FROM todo")
        Collection<Todo> findAll();
    
        @Insert("INSERT INTO todo (todo_title, finished, created_at) VALUES (#{todoTitle}, #{finished}, #{createdAt})")
        @Options(useGeneratedKeys = true, keyProperty = "todoId") // (3)
        void create(Todo todo);
    
        @Update("UPDATE todo SET todo_title = #{todoTitle}, finished = #{finished}, created_at = #{createdAt} WHERE todo_id = #{todoId}")
        boolean updateById(Todo todo);
    
        @Delete("DELETE FROM todo WHERE todo_id = #{todoId}")
        void deleteById(Todo todo);
    
        @Select("SELECT COUNT(*) FROM todo WHERE finished = #{finished}")
        long countByFinished(boolean finished);
    }

    (1)如果添加到Repository界面@Mapper,MyBatis会自动扫描并注册到Mapper中。让我们将Repository接口放在主类下的包中。

    (2)在给方法的...中实现要执行的SQL 。由于参数在 SQL 中使用,因此比 XML 更容易理解,因为它在同一个文件中进行了描述。@Select@Insert@Update@Delete#{}

    (3) @Options在需要执行异常设置的 SQL 时给出。这里,由于表的关键项是IDENTITY列,所以在DB端是自动编号的,但是@Options你可以通过using来使用自动编号的ID。

    多行 SQL

    虽然上面的 SQL 是用一行描述的,但还是建议多行描述,因为它不可读。
    它比 XML 更难看,因为它是一种像这样的字符串连接形式。

     
        @Select("SELECT"
                + " todo_id,"
                + " todo_title,"
                + " finished,"
                + " created_at"
                + " FROM todo"
                + " WHERE"
                + " todo_id = #{todoId}")
        Optional<Todo> findById(String todoId);

    选择结果的自动和手动映射

    Select 结果映射失败,因为图示的 Select 语句的结果列名称和 Todo 类的属性名称不同。(todo_title等等todoTitle

    有以下方法之一可以解决此问题。

    • MyBatis 命名规则自动映射
    • @Results@ResultMap手动映射和

    就个人而言,为属性命名以便自动映射可用,并且仅在它偏离规则时才进行手动映射是一个好主意。

    自动映射

    如果下划线分隔的列名与驼峰式的属性名匹配,则可以通过 MyBatis 的命名规则进行自动映射。

    将以下设置添加到 Spring Boot 配置文件中。

    src/main/resources/application.yml

     
    mybatis:
      configuration:
        map-underscore-to-camel-case: true

    注意
    mybatis.configuration.*:您可以在属性中更改 MyBatis 设置。

    手动映射

    如果列名和属性名不匹配,则@Results需要@ResultMap为每个 SQL 定义手动映射。

    • @Results在中定义手动映射规则
    • @Results如果要使用中定义的规则@ResultMap@Results请指定中的 ID。
     
        @Select("SELECT todo_id, todo_title, finished, created_at FROM todo WHERE todo_id = #{todoId}")
        @Results(id = "todo", value = {
                @Result(column = "todo_id", property = "todoId"),
                @Result(column = "todo_title", property = "todoTitle"),
                @Result(column = "finished", property = "finished"),
                @Result(column = "created_at", property = "createdAt") })
        Optional<Todo> findById(String todoId);
    
        @Select("SELECT todo_id, todo_title, finished, created_at FROM todo")
        @ResultMap("todo")
        Collection<Todo> findAll();

    与动态 SQL 通用

    使用 Mapper XML 可能实现的动态 SQL ( <if>) 和通用性()<sql>现在通过 SqlProvider 实现。

     
        // (2)
        @SelectProvider(type = TodoSqlProvider.class, method = "find")
        Optional<Todo> findById(String todoId);
    
        @SelectProvider(type = TodoSqlProvider.class, method = "find")
        Collection<Todo> findAll();
    
        // (1)
        public class TodoSqlProvider {
            public String find(String todoId) {
                return new SQL() {{
                    SELECT("todo_id", "todo_title", "finished", "created_at");
                    FROM("todo");
                    if (todoId != null) {
                        WHERE("todo_id = #{todoId}");
                    }
                }}.toString();
            }
        }

    (1) 实现SqlProvider类,并new SQL()在方法中使用组装SQL。例子中使用了实例初始化器( new SQL() {{ココ}}),当然你也可以正常写在方法链中。

    注意
    :您可以直接在 WHERE 子句中嵌入参数,但一定#{}要使用。
    #{}防止 SQL 注入。

    (2)通过赋值@Select而不是@SelectProvider方法来指定实现的SqlProvider类和方法。

    注意
    :接口默认方法不能是 SqlProvider 方法。你需要上课。
    这是因为当您使用 SqlProvider 方法时,会创建一个 SqlProvider 类的实例。

    mybatis-spring-boot-starter-test

    使用 Spring Boot 测试 MyBatis 的入门者。

    通过使用 Spring Boot Auto Configuration 机制,Spring Boot 应用会自动定义一个 bean 用于测试 MyBatis Mapper。开发者只需要在自己的依赖中加入 mybatis-spring-boot-starter-test 并做一些设置即可。

    pom.xml

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

    JUnit 测试用例

    在 JUnit 测试用例中,您所要做的就是提供类并@MyBatisTest制作要测试的存储库@Autowired

    src/test/java/*/repository/TodoRepositoryTest.java

     
    @MybatisTest
    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
            Collection<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()));
        }

    @MyBatisTestDataSource会自动定义使用 MyBatis、访问 DB 等的 bean ,但@Transactional也会添加更多。

    @Transactional@Sql测试完成后会回滚测试中执行的SQL。这是安全的,因为即使在访问和测试不在内存中的实际数据库时,也可以保持测试的独立性。

     

  • 相关阅读:
    安防监控/视频汇聚/云存储/AI智能视频分析平台EasyCVR下级海康设备无法级联是什么原因?
    ISO三体系认证需要什么材料,办理流程
    01 数据库和MySQL简介
    x64dbg 插件开发SDK环境配置
    在vue2和vue3中Vuex的使用及其持久化存储,mutation和actions的区别
    基础算法(六):回溯算法
    基于粒子群优化算法的最佳方式设置无线传感器节点的位置,以减轻由于任何能量耗尽节点而产生的覆盖空洞(Matlab代码实现)
    压缩包加密、解密
    揭秘弹幕游戏制作
    如何在Kubernetes 里添加自定义的 API 对象(一)
  • 原文地址:https://www.cnblogs.com/kiwis2/p/16386906.html