MyBatis 是一个支持自定义 SQL 、存储过程和高级映射的持久层框架,它使用 XML 或者 注解 配置对象与关系之间的映射,免除了几乎所有设置参数和获取结果的 JDBC 代码。

引入依赖

pom.xml中添加 MyBatis 的依赖:

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

因为这里使用 MySQL 数据库,所以还需添加数据库驱动的依赖

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>

配置数据库连接

application.yml中配置数据库连接:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/bookkeeping?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
    username: ${MYSQL_USER}
    password: ${MYSQL_PASSWORD}
    driver-class-name: com.mysql.cj.jdbc.Driver

定义数据表和对象

在 MySQL 中定义数据表:

create table card
(
   c_id                 int not null auto_increment,
   u_id                 int not null,
   c_name               varchar(20) not null,
   primary key (c_id)
);

在 Spring 中定义类:

package com.comp2024b.tocountornot.bean;

import lombok.Data;
import org.springframework.stereotype.Component;

@Data
@Component
public class Card {
    private int id;
    private int user;
    private String name;
}

这里我们使用@Component注解把 Card 注册为需要 Spring 管理的通用组件。我们还使用了 lombok@Data注解以生成 Getters 和 Setters 方法。

配置映射

CardMapper.java

package com.comp2024b.tocountornot.dao;

import com.comp2024b.tocountornot.bean.Card;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;

import java.util.List;

@Mapper
@Repository
public interface CardMapper {
    @Insert("insert into card (c_id,u_id,c_name) values (#{id},#{user},#{name})")
    @Options(useGeneratedKeys = true, keyColumn = "c_id", keyProperty = "id")
    void insertCard(Card card);

    @Delete("delete from card where c_id=#{id}")
    void deleteCard(@Param("id") int id);

    @Update("update card set c_name=#{name} where c_id=#{id}")
    void updateCard(Card card);

    @Select("select c_id as id,c_name as name from card where u_id=#{uid}")
    List<Card> getAllCard(@Param("uid") int uid);

    @Select("select c_id as id,c_name as name from card where c_id=#{id} and u_id=#{uid}")
    Card getCardById(@Param("id") int id, @Param("uid") int uid);
}

这里我们使用 spring 的@Repository注解把 CardMapper 注册为需要 Spring 管理的数据访问层组件。我们还使用了@Mapper注解把 CardMapper 标记为 MyBatis 的映射接口类。

对于不同的 SQL 语句,我们需要使用与之对应的注解来完成语句映射,如@Insert@Delete@Update@Select。SQL 语句中不确定的参数需要使用类似#{param}的形式标识。如果参数是一个对象,参数名需要和对象的属性名保持一致。如果是单独的参数,需要使用@Param注解指定相应的参数。

需要根据 SQL 语句执行结果的类型指定返回值的类型,如果要返回一个对象,结果中的数据列名需要与对象的属性名保持一致。如果返回多条记录,需要指定返回类型为对象的列表。

我们还可以使用@Optional注解和useGeneratedKeys来获取自增的主键。其中keyColumn是数据表中对应的列,keyProperty是对象中的对应的属性。

因为这里的 SQL 语句比较简单,所以我们使用注解完成映射。对于一些复杂的 SQL 语句,应考虑使用 XML 文件完成映射:

CardMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.comp2024b.tocountornot.dao.CardMapper">
  <select id="getCardById" resultType="Card">
    select c_id as id,c_name as name from card where c_id=#{id} and u_id=#{uid}
  </select>
</mapper>

有关 XML 映射,可以参考 XML映射器

执行语句

每一个基于 MyBatis 的应用都以一个SqlSessionFactory的实例为核心。SqlSessionFactory的实例可以由SqlSessionFactoryBuilder根据配置文件构建得到,在这里application.yml提供了需要的数据源配置信息。我们可以从SqlSessionFactory获取SqlSession的实例,然后通过SqlSession实例执行已映射的 SQL 语句:

try (SqlSession session = sqlSessionFactory.openSession()) {
    CardMapper mapper = session.getMapper(CardMapper.class);
    Card Card = mapper.getCardById(1, 1);
}

在 Spring 中,我们可以通过构造器把 mapper 注入到 service 中并进行调用:

CardService.java

package com.comp2024b.tocountornot.service;

import com.comp2024b.tocountornot.dao.CardMapper;
import com.comp2024b.tocountornot.exception.NotFoundException;
import org.springframework.stereotype.Service;
import com.comp2024b.tocountornot.bean.Card;

@Service
public class CardService {

    private final CardMapper cardMapper;

    public CardService(CardMapper cardMapper) {
        this.cardMapper = cardMapper;
    }

    public Card getCardById(int id, int uid) {
        Card card = cardMapper.getCardById(id, uid);
        if (card != null) {
            return card;
        } else {
            throw new NotFoundException("card not found");
        }
    }
}

这里我们使用@Service注解把 CardService 注册为需要 Spring 管理的服务层组件。