阅读 1517

MyBatis 与 SpringBoot 整合:注解和xml两种使用方式介绍

MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。


无论是使用注解还是 xml 映射文件配置方式,在使用之前有两步是必须的:

  1. 引入依赖

    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
    </dependency>
    复制代码
  2. 在启动类上加注解 @MapperScan 指明 mapper 类的位置

    @MapperScan("com.solo.coderiver.project.mapper")
    public class ProjectApplication {
        public static void main(String[] args) {
            SpringApplication.run(ProjectApplication.class, args);
        }
    }
    复制代码

一、注解方式

相比于 xml 映射方式,注解方式明显更简洁,但没有 xml 方式灵活。目前大部分公司还是主要用 xml 方式。

package com.solo.coderiver.project.mapper;

import com.solo.coderiver.project.dataobject.ProjectInfo;
import org.apache.ibatis.annotations.*;

/**
 * 注解方式使用 mybatis 增删改查
 */
@Mapper
public interface ProjectMapper {

    @Select("SELECT * FROM project_info WHERE project_id = #{id}")
    @Results({
            @Result(property = "projectId", column = "project_id"),
            @Result(property = "projectName", column = "project_name"),
            @Result(property = "projectAvatar", column = "project_avatar"),
            @Result(property = "projectDifficulty", column = "project_difficulty"),
            @Result(property = "categoryType", column = "category_type"),
            @Result(property = "categoryName", column = "category_name"),
            @Result(property = "projectProgress", column = "project_progress"),
            @Result(property = "projectStatus", column = "project_status"),
            @Result(property = "projectIntroduce", column = "project_introduce"),
            @Result(property = "projectCreatorId", column = "project_creator_id"),
            @Result(property = "teamId", column = "team_id"),
    })
    ProjectInfo findProjectById(String id);

    @Insert("INSERT INTO " +
            "project_info(project_id, project_name, project_avatar, project_difficulty," +
            " category_type, category_name, project_progress, project_status, " +
            "project_introduce, project_creator_id, team_id) " +
            "VALUES(#{projectId}, #{projectName}, #{projectAvatar}, #{projectDifficulty}," +
            "#{categoryType}, #{categoryName}, #{projectProgress}, #{projectStatus}," +
            "#{projectIntroduce}, #{projectCreatorId}, #{teamId})")
    int insertProject(ProjectInfo info);


    @Update("UPDATE project_info set project_name = #{name} where project_id = #{id}")
    int updateProjectName(String id, String name);

    @Delete("DELETE FROM project_info WHERE project_id = #{id}")
    int deleteProject(String id);

}
复制代码

直接将 sql 语句写在注解里,免去了配置 xml 文件。但有个显而易见的缺点是如果 sql 太长了,像字段多的 @Insert ,如果换行需要用 + 连接,不利于后期维护。

MyBatis 支持的注解属性表:

注解 使用对象 相对应的 XML 描述
@CacheNamespace <cache> 为给定的命名空间(比如类)配置缓存。属性有:implemetation, eviction, flushInterval, size, readWrite, blockingproperties
@Property N/A <property> 指定参数值或占位值(placeholder)(能被 mybatis-config.xml内的配置属性覆盖)。属性有:name, value。(仅在MyBatis 3.4.2以上版本生效)
@CacheNamespaceRef <cacheRef> 参照另外一个命名空间的缓存来使用。属性有:value, name。如果你使用了这个注解,你应设置 value 或者 name 属性的其中一个。value 属性用于指定 Java 类型而指定命名空间(命名空间名就是指定的 Java 类型的全限定名),name 属性(这个属性仅在MyBatis 3.4.2以上版本生效)直接指定了命名空间的名字。
@ConstructorArgs 方法 <constructor> 收集一组结果传递给一个结果对象的构造方法。属性有:value,它是形式参数数组。
@Arg N/A <arg> <idArg> 单参数构造方法,是 ConstructorArgs 集合的一部分。属性有:id, column, javaType, jdbcType, typeHandler, selectresultMap。id 属性是布尔值,来标识用于比较的属性,和<idArg> XML 元素相似。
@TypeDiscriminator 方法 <discriminator> 一组实例值被用来决定结果映射的表现。属性有:column, javaType, jdbcType, typeHandlercases。cases 属性是实例数组。
@Case N/A <case> 单独实例的值和它对应的映射。属性有:value, type, results。results 属性是结果数组,因此这个注解和实际的 ResultMap 很相似,由下面的 Results 注解指定。
@Results 方法 <resultMap> 结果映射的列表,包含了一个特别结果列如何被映射到属性或字段的详情。属性有:value, id。value 属性是 Result 注解的数组。这个 id 的属性是结果映射的名称。
@Result N/A <result> <id> 在列和属性或字段之间的单独结果映射。属性有:id, column, javaType, jdbcType, typeHandler, one, many。id 属性是一个布尔值,来标识应该被用于比较(和在 XML 映射中的<id>相似)的属性。one 属性是单独的联系,和 <association> 相似,而 many 属性是对集合而言的,和<collection>相似。它们这样命名是为了避免名称冲突。
@One N/A <association> 复杂类型的单独属性值映射。属性有:select,已映射语句(也就是映射器方法)的全限定名,它可以加载合适类型的实例。fetchType会覆盖全局的配置参数 lazyLoadingEnabled。注意 联合映射在注解 API中是不支持的。这是因为 Java 注解的限制,不允许循环引用。
@Many N/A <collection> 映射到复杂类型的集合属性。属性有:select,已映射语句(也就是映射器方法)的全限定名,它可以加载合适类型的实例的集合,fetchType 会覆盖全局的配置参数 lazyLoadingEnabled。注意 联合映射在注解 API中是不支持的。这是因为 Java 注解的限制,不允许循环引用
@MapKey 方法 这是一个用在返回值为 Map 的方法上的注解。它能够将存放对象的 List 转化为 key 值为对象的某一属性的 Map。属性有: value,填入的是对象的属性名,作为 Map 的 key 值。
@Options 方法 映射语句的属性 这个注解提供访问大范围的交换和配置选项的入口,它们通常在映射语句上作为属性出现。Options 注解提供了通俗易懂的方式来访问它们,而不是让每条语句注解变复杂。属性有:useCache=true, flushCache=FlushCachePolicy.DEFAULT, resultSetType=FORWARD_ONLY, statementType=PREPARED, fetchSize=-1, timeout=-1, useGeneratedKeys=false, keyProperty="id", keyColumn="", resultSets=""。值得一提的是, Java 注解无法指定 null 值。因此,一旦你使用了 Options 注解,你的语句就会被上述属性的默认值所影响。要注意避免默认值带来的预期以外的行为。 注意: keyColumn 属性只在某些数据库中有效(如 Oracle、PostgreSQL等)。请在插入语句一节查看更多关于 keyColumnkeyProperty 两者的有效值详情。
@Insert @Update @Delete @Select 方法 <insert> <update> <delete> <select> 这四个注解分别代表将会被执行的 SQL 语句。它们用字符串数组(或单个字符串)作为参数。如果传递的是字符串数组,字符串之间先会被填充一个空格再连接成单个完整的字符串。这有效避免了以 Java 代码构建 SQL 语句时的“丢失空格”的问题。然而,你也可以提前手动连接好字符串。属性有:value,填入的值是用来组成单个 SQL 语句的字符串数组。
@InsertProvider @UpdateProvider @DeleteProvider @SelectProvider 方法 <insert> <update> <delete> <select> 允许构建动态 SQL。这些备选的 SQL 注解允许你指定类名和返回在运行时执行的 SQL 语句的方法。(自从MyBatis 3.4.6开始,你可以用 CharSequence 代替 String 来返回类型返回值了。)当执行映射语句的时候,MyBatis 会实例化类并执行方法,类和方法就是填入了注解的值。你可以把已经传递给映射方法了的对象作为参数,"Mapper interface type" 和 "Mapper method" 会经过 ProviderContext (仅在MyBatis 3.4.5及以上支持)作为参数值。(MyBatis 3.4及以上的版本,支持多参数传入)属性有: type, methodtype 属性需填入类。method 需填入该类定义了的方法名。注意 接下来的小节将会讨论类,能帮助你更轻松地构建动态 SQL。
@Param 参数 N/A 如果你的映射方法的形参有多个,这个注解使用在映射方法的参数上就能为它们取自定义名字。若不给出自定义名字,多参数(不包括 RowBounds 参数)则先以 "param" 作前缀,再加上它们的参数位置作为参数别名。例如 #{param1}, #{param2},这个是默认值。如果注解是 @Param("person"),那么参数就会被命名为 #{person}
@SelectKey 方法 <selectKey> 这个注解的功能与 <selectKey> 标签完全一致,用在已经被 @Insert@InsertProvider@Update@UpdateProvider 注解了的方法上。若在未被上述四个注解的方法上作 @SelectKey 注解则视为无效。如果你指定了 @SelectKey 注解,那么 MyBatis 就会忽略掉由 @Options 注解所设置的生成主键或设置(configuration)属性。属性有:statement 填入将会被执行的 SQL 字符串数组,keyProperty 填入将会被更新的参数对象的属性的值,before 填入 truefalse 以指明 SQL 语句应被在插入语句的之前还是之后执行。resultType 填入 keyProperty 的 Java 类型和用 StatementPreparedStatementCallableStatement 中的 STATEMENTPREPAREDCALLABLE 中任一值填入 statementType。默认值是 PREPARED
@ResultMap 方法 N/A 这个注解给 @Select 或者 @SelectProvider 提供在 XML 映射中的 <resultMap> 的id。这使得注解的 select 可以复用那些定义在 XML 中的 ResultMap。如果同一 select 注解中还存在 @Results 或者 @ConstructorArgs,那么这两个注解将被此注解覆盖。
@ResultType 方法 N/A 此注解在使用了结果处理器的情况下使用。在这种情况下,返回类型为 void,所以 Mybatis 必须有一种方式决定对象的类型,用于构造每行数据。如果有 XML 的结果映射,请使用 @ResultMap 注解。如果结果类型在 XML 的 <select> 节点中指定了,就不需要其他的注解了。其他情况下则使用此注解。比如,如果 @Select 注解在一个将使用结果处理器的方法上,那么返回类型必须是 void 并且这个注解(或者@ResultMap)必选。这个注解仅在方法返回类型是 void 的情况下生效。
@Flush 方法 N/A 如果使用了这个注解,定义在 Mapper 接口中的方法能够调用 SqlSession#flushStatements() 方法。(Mybatis 3.3及以上)

二、xml 方式使用

2.1 快速实现

xml 配置方式使用 MyBatis 主要步骤有以下三步:

  1. 配置 application.yml
  2. 编写 mapper xml 文件
  3. 写 mapper java 代码

1. 配置 application.yml

首先在 application.yml 中配置数据库实体对象的位置和mapper文件的位置,配置了 type-aliases-package 后就可以在 xml 文件中直接写类名,而不用写全限定类名啦。

mybatis:
  type-aliases-package: com.solo.coderiver.project.dataobject
  mapper-locations: classpath:mapper/*.xml
复制代码

如果不配置 mapper-locations,会报如下错误:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.solo.coderiver.project.mapper.ProjectCategoryMapper.insert
复制代码

2. 编写 mapper xml 映射文件

以项目类型 ProjectCategory 为例,添加 MyBatis 的增删改查实现

ProjectCategoryMapper.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.solo.coderiver.project.mapper.ProjectCategoryMapper" >

    <resultMap id="BaseResultMap" type="ProjectCategory">
        <id column="category_id" property="categoryId" jdbcType="INTEGER"/>
        <result column="category_name" property="categoryName" jdbcType="VARCHAR" />
        <result column="category_type" property="categoryType" jdbcType="INTEGER" />
    </resultMap>

    <insert id="insert" parameterType="ProjectCategory">
        insert into project_category(category_id, category_name, category_type)
        values(#{categoryId, jdbcType=INTEGER}, #{categoryName, jdbcType=VARCHAR}, #{categoryType, jdbcType=INTEGER})
    </insert>

    <delete id="deleteByType" parameterType="java.lang.Integer">
        delete from project_category
        where category_type = #{type, jdbcType=INTEGER}
    </delete>

    <update id="updateByType" parameterType="ProjectCategory">
        update project_category
        set category_name = #{categoryName}
        where category_type = #{categoryType}
    </update>

    <select id="selectByType" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select * from project_category
        where category_type = #{categoryType}
    </select>
</mapper>
复制代码

后面会讲配置的详细属性。

3. 写 mapper java 代码

ProjectCategoryMapper.java

@Mapper
public interface ProjectCategoryMapper {

    int insert(ProjectCategory category);

    int deleteByType(Integer type);

    int updateByType(ProjectCategory category);

    ProjectCategory selectByType(Integer type);
}
复制代码

新建一个接口,方法名要跟 ProjectCategoryMapper.xml<insert><delete><update><select> 等 sql 操作语句的 id 保持一致,否则会报错。

然后再在类名上加注解 @Mapper 就可以啦。

ProjectCategory对象

package com.solo.coderiver.project.dataobject;

import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

@Entity
@Data
public class ProjectCategory {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer categoryId;

    private String categoryName;

    private Integer categoryType;

    public ProjectCategory() {
    }

    public ProjectCategory(Integer categoryType, String categoryName) {
        this.categoryName = categoryName;
        this.categoryType = categoryType;
    }
}
复制代码

2.2 xml 映射文件详解

SQL 映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):

  • cache – 给定命名空间的缓存配置。
  • cache-ref – 其他命名空间缓存配置的引用。
  • resultMap – 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
  • parameterMap – 已废弃!老式风格的参数映射。内联参数是首选,这个元素可能在将来被移除,这里不会记录。
  • sql – 可被其他语句引用的可重用语句块。
  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句

select

查询语句是 MyBatis 中最常用的元素之一,光能把数据存到数据库中价值并不大,如果还能重新取出来才有用,多数应用也都是查询比修改要频繁。对每个插入、更新或删除操作,通常对应多个查询操作。这是 MyBatis 的基本原则之一,也是将焦点和努力放到查询和结果映射的原因。

上文中实现了一个简单的 select 语句:

<select id="selectByType" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select * from project_category
    where category_type = #{categoryType}
</select>
复制代码

这个语句被称作 selectByType,接受一个 int(或 Integer)类型的参数,并返回一个 BaseResultMap 定义的 ProjectCategory 对象。这条语句的作用是根据传入的类型查询项目类型信息。

注意参数符号:#{categoryType} , 它表明接收传入的参数 categoryType

select 的属性表:

<select
  id="selectPerson"
  parameterType="int"
  parameterMap="deprecated"
  resultType="hashmap"
  resultMap="personResultMap"
  flushCache="false"
  useCache="true"
  timeout="10000"
  fetchSize="256"
  statementType="PREPARED"
  resultSetType="FORWARD_ONLY">
复制代码
属性 描述
id 在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType 将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
parameterMap 这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。
resultType 从这条语句中返回的期望类型的类的完全限定名或别名。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。使用 resultType 或 resultMap,但不能同时使用。
resultMap 外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,对其有一个很好的理解的话,许多复杂映射的情形都能迎刃而解。使用 resultMap 或 resultType,但不能同时使用。
flushCache 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:false。
useCache 将其设置为 true,将会导致本条语句的结果被二级缓存,默认值:对 select 元素为 true。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
fetchSize 这是尝试影响驱动程序每次批量返回的结果行数和这个设置值相等。默认值为 unset(依赖驱动)。
statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetType FORWARD_ONLY,SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE 中的一个,默认值为 unset (依赖驱动)。
databaseId 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。
resultOrdered 这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组了,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false
resultSets 这个设置仅对多结果集的情况适用,它将列出语句执行后返回的结果集并每个结果集给一个名称,名称是逗号分隔的。

像本文的例子一样,select 常用的属性其实就是 idparameterTyperesultMap 三个,其他的了解即可。

insert, update 和 delete

数据变更语句 insert,update 和 delete 的实现非常接近:

<insert
  id="insertAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  keyProperty=""
  keyColumn=""
  useGeneratedKeys=""
  timeout="20">

<update
  id="updateAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">

<delete
  id="deleteAuthor"
  parameterType="domain.blog.Author"
  flushCache="true"
  statementType="PREPARED"
  timeout="20">
复制代码

属性表

属性 描述
id 命名空间中的唯一标识符,可被用来代表这条语句。
parameterType 将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数,默认值为 unset。
parameterMap 这是引用外部 parameterMap 的已经被废弃的方法。使用内联参数映射和 parameterType 属性。
flushCache 将其设置为 true,任何时候只要语句被调用,都会导致本地缓存和二级缓存都会被清空,默认值:true(对应插入、更新和删除语句)。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为 unset(依赖驱动)。
statementType STATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys (仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。
keyProperty (仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认:unset。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
keyColumn (仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
databaseId 如果配置了 databaseIdProvider,MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。

三个更新操作常用的属性只有两个:idparameterType,其他的了解即可。

Result Maps

resultMap 元素是 MyBatis 中最重要最强大的元素。它可以让你从 90% 的 JDBC ResultSets 数据提取代码中解放出来,并在一些情形下允许你做一些 JDBC 不支持的事情。 实际上,在对复杂语句进行联合映射的时候,它很可能可以代替数千行的同等功能的代码。 ResultMap 的设计思想是,简单的语句不需要明确的结果映射,而复杂一点的语句只需要描述它们的关系就行了。

下面是 resultMap 元素的概念视图:

resultMap
  • constructor

    - 用于在实例化类时,注入结果到构造方法中

    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    • arg - 将被注入到构造方法的一个普通结果
  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能

  • result – 注入到字段或 JavaBean 属性的普通结果

  • association

    – 一个复杂类型的关联;许多结果将包装成这种类型

    • 嵌套结果映射 – 关联可以指定为一个 resultMap 元素,或者引用一个
  • collection

    – 一个复杂类型的集合

    • 嵌套结果映射 – 集合可以指定为一个 resultMap 元素,或者引用一个
  • discriminator

    – 使用结果值来决定使用哪个

    resultMap

    • case

      – 基于某些值的结果映射

      • 嵌套结果映射 – 一个 case 也是一个映射它本身的结果,因此可以包含很多相同的元素,或者它可以参照一个外部的 resultMap
属性 描述
id 当前命名空间中的一个唯一标识,用于标识一个result map.
type 类的完全限定名, 或者一个类型别名 (内置的别名可以参考上面的表格).
autoMapping 如果设置这个属性,MyBatis将会为这个ResultMap开启或者关闭自动映射。这个属性会覆盖全局的属性 autoMappingBehavior。默认值为:unset。

为了方便理解,再把上面项目中定义的 resultMap 贴出来

<resultMap id="BaseResultMap" type="ProjectCategory">
    <id column="category_id" property="categoryId" jdbcType="INTEGER"/>
    <result column="category_name" property="categoryName" jdbcType="VARCHAR" />
    <result column="category_type" property="categoryType" jdbcType="INTEGER" />
</resultMap>
复制代码

id 和 result 标签中的属性表如下:

属性 描述
property 映射到列结果的字段或属性。如果用来匹配的 JavaBeans 存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称 property 的字段。 无论是哪一种情形,你都可以使用通常的点式分隔形式进行复杂属性导航。比如,你可以这样映射一些简单的东西: “username” ,或者映射到一些复杂的东西: “address.street.number” 。
column 数据库中的列名,或者是列的别名。一般情况下,这和 传递给 resultSet.getString(columnName) 方法的参数一样。
javaType 一个 Java 类的完全限定名,或一个类型别名(参考上面内建类型别名 的列表) 。如果你映射到一个 JavaBean,MyBatis 通常可以断定类型。 然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证期望的行为。
jdbcType JDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可能为 null 的值指定这个类型。
typeHandler 我们在前面讨论过的默认类型处理器。使用这个属性,你可以覆盖默 认的类型处理器。这个属性值是一个类型处理 器实现类的完全限定名,或者是类型别名。

关于 jdbcType

发现有些小伙伴在写映射文件的时候,都习惯性的把所有的需要传入的参数都加上 jdbcType="",那到底什么情况下需要指明,什么情况下不需要指明呢?

查阅官方文档看到了官方的描述:

如果一个列允许 null 值,并且会传递值 null 的参数,就必须要指定 JDBC Type。
复制代码

也就是说只有当一个列允许 null 并有可能传入 null 时,才必须要指定 JDBC Type,其他情况是不需要指定的。当然指定了也没错,就是多写点代码。

明白了什么时候需要指定,那还有个问题,java 中的类型跟 jdbcType 的类型如何对应呢?如果对应关系写错了也会报错。下面就整理出了两者的对应关系:

JDBCTypeJavaType 对应关系

  JDBCType            JavaType
    CHAR                String
    VARCHAR             String
    LONGVARCHAR         String
    NUMERIC             java.math.BigDecimal
    DECIMAL             java.math.BigDecimal
    BIT                 boolean
    BOOLEAN             boolean
    TINYINT             byte
    SMALLINT            short
    INTEGER             int
    BIGINT              long
    REAL                float
    FLOAT               double
    DOUBLE              double
    BINARY              byte[]
    VARBINARY           byte[]
    LONGVARBINARY       byte[]
    DATE                java.sql.Date
    TIME                java.sql.Time
    TIMESTAMP           java.sql.Timestamp
    CLOB                Clob
    BLOB                Blob
    ARRAY               Array
    DISTINCT            mapping of underlying type
    STRUCT              Struct
    REF                 Ref
    DATALINK            java.net.URL[color=red][/color]
复制代码

2.3 补充

如果查询返回的结果是个列表怎么办?如何提取出可复用的 sql 呢?

日常使用中还有这两个常见的应用场景,下面就以项目成员表 ProejctMember 来演示一下。

一个项目可以对应多个成员,所以根据项目 id 查询成员的话肯定查出来多条数据。

ProjectMemberMapper.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.solo.coderiver.project.mapper.ProjectMemberMapper">

    <resultMap id="BaseResultMap" type="ProjectMember">
        <id column="id" property="id" jdbcType="INTEGER"/>
        <result column="project_id" property="projectId" jdbcType="VARCHAR"/>
        <result column="project_name" property="projectName" jdbcType="VARCHAR"/>
        <result column="project_avatar" property="projectAvatar" jdbcType="VARCHAR"/>
        <result column="user_id" property="userId" jdbcType="VARCHAR"/>
        <result column="user_name" property="userName" jdbcType="VARCHAR"/>
        <result column="user_avatar" property="userAvatar" jdbcType="VARCHAR"/>
        <result column="user_role" property="userRole" jdbcType="INTEGER"/>
        <result column="role_name" property="roleName" jdbcType="VARCHAR"/>
        <result column="status" property="status" jdbcType="INTEGER"/>
    </resultMap>

    <sql id="baseColumns">
        id, project_id, project_name, project_avatar, user_id, user_name,
        user_avatar, user_role, role_name, status
    </sql>

    <select id="selectByProjectId" parameterType="java.lang.String" resultMap="BaseResultMap">
        select
        <include refid="baseColumns"/>
        from project_member
        where project_id = #{projectId, jdbcType=VARCHAR}
    </select>
</mapper>
复制代码

ProjectMemberMapper.java

package com.solo.coderiver.project.mapper;

import com.solo.coderiver.project.dataobject.ProjectMember;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;

@Mapper
public interface ProjectMemberMapper {

    List<ProjectMember> selectByProjectId(String projectId);
}
复制代码

返回结果是列表的话,映射文件的 result 还是只给定 List 内的类型即可。

<sql> 标签来提出出可复用的 sql 语句

<sql id="baseColumns">
    id, project_id, project_name, project_avatar, user_id, user_name,
    user_avatar, user_role, role_name, status
</sql>
复制代码

在需要用到的地方用 <include> 标签引入 sql

<select id="selectByProjectId" parameterType="java.lang.String"    		resultMap="BaseResultMap">
    select
    <include refid="baseColumns"/>
    from project_member
    where project_id = #{projectId, jdbcType=VARCHAR}
</select>
复制代码

然后在 service 中引用 mapper

package com.solo.coderiver.project.service.mybatis;

import com.solo.coderiver.project.dataobject.ProjectMember;
import com.solo.coderiver.project.mapper.ProjectMemberMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class ProjectMemberServiceMyBatisImpl {

    @Autowired
    ProjectMemberMapper mapper;

    public List<ProjectMember> findByProjectId(String projectId){
        return mapper.selectByProjectId(projectId);
    }
}
复制代码

单元测试

package com.solo.coderiver.project.service.mybatis;

import com.solo.coderiver.project.ProjectApplicationTests;
import com.solo.coderiver.project.dataobject.ProjectMember;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

import static org.junit.Assert.*;

public class ProjectMemberServiceMyBatisImplTest extends ProjectApplicationTests {

    @Autowired
    ProjectMemberServiceMyBatisImpl service;

    @Test
    public void findByProjectId() {
        List<ProjectMember> list = service.findByProjectId("1541062468073593543");
        Assert.assertNotEquals(0, list.size());
    }
}
复制代码

以上就是 MyBatis 在 SpringBoot 中的简单使用方式介绍。


代码出自开源项目 coderiver,致力于打造全平台型全栈精品开源项目。

coderiver 中文名 河码,是一个为程序员和设计师提供项目协作的平台。无论你是前端、后端、移动端开发人员,或是设计师、产品经理,都可以在平台上发布项目,与志同道合的小伙伴一起协作完成项目。

coderiver河码 类似程序员客栈,但主要目的是方便各细分领域人才之间技术交流,共同成长,多人协作完成项目。暂不涉及金钱交易。

计划做成包含 pc端(Vue、React)、移动H5(Vue、React)、ReactNative混合开发、Android原生、微信小程序、java后端的全平台型全栈项目,欢迎关注。

项目地址:github.com/cachecats/c…

您的鼓励是我前行最大的动力,欢迎点赞,欢迎送小星星✨ ~

关注下面的标签,发现更多相似文章
评论