微信搜索
江南一点雨

MyBatis Mapper 详解

公众号江南一点雨后台回复 SSM 下载视频笔记。

5. 引入 Mapper

前面我们所写的增删改查是存在问题的。主要问题就是冗余代码过多,模板化代码过多。例如,我想开发一个 UserDao,可能是下面这样:

public class UserDao {
    private SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getInstance();

    public User getUserById(Integer id) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        User user = (User) sqlSession.selectOne("org.javaboy.mybatis.mapper.UserDao.getUserById", id);
        sqlSession.close();
        return user;
    }

    public Integer addUser(User user) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        int insert = sqlSession.insert("org.javaboy.mybatis.mapper.UserDao.addUser", user);
        sqlSession.commit();
        sqlSession.close();
        return insert;
    }

    public Integer addUser2(User user) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        int insert = sqlSession.insert("org.javaboy.mybatis.mapper.UserDao.addUser2", user);
        sqlSession.commit();
        sqlSession.close();
        return insert;
    }

    public Integer deleteUserById(Integer id) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        int delete = sqlSession.delete("org.javaboy.mybatis.mapper.UserDao.deleteUserById", id);
        sqlSession.commit();
        sqlSession.close();
        return delete;
    }

    public Integer updateUser(User user) {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        int delete = sqlSession.delete("org.javaboy.mybatis.mapper.UserDao.updateUser", user);
        sqlSession.commit();
        sqlSession.close();
        return delete;
    }

    public List<User> getAllUser() {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        List<User> users = sqlSession.selectList("org.javaboy.mybatis.mapper.UserDao.getAllUser");
        sqlSession.close();
        return users;
    }
}

然后,和这个 UserDao 对应的,还有一个 UserMapper.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="org.javaboy.mybatis.mapper.UserDao">

    <select id="getUserById" resultType="org.javaboy.mybatis.model.User">
        select * from user where id=#{id};
    </select>
    <insert id="addUser" parameterType="org.javaboy.mybatis.model.User">
        insert into user (username,address) values (#{username},#{address});
    </insert>
    <insert id="addUser2" parameterType="org.javaboy.mybatis.model.User">
        <selectKey resultType="java.lang.String" keyProperty="id" order="BEFORE">
            select uuid();
        </selectKey>
        insert into user (id,username,address) values (#{id},#{username},#{address});
    </insert>

    <delete id="deleteUserById" parameterType="java.lang.Integer">
        delete from user where id=#{id}
    </delete>

    <update id="updateUser" parameterType="org.javaboy.mybatis.model.User">
        update user set username = #{username} where id=#{id};
    </update>

    <select id="getAllUser" resultType="org.javaboy.mybatis.model.User">
        select * from user;
    </select>
</mapper>

此时,我们分析这个 UserDao,发现它有很多可以优化的地方。每个方法中都要获取 SqlSession,涉及到增删改的方法,还需要 commit,SqlSession 用完之后,还需要关闭,sqlSession 执行时需要的参数就是方法的参数,sqlSession 要执行的 SQL ,和 XML 中的定义是一一对应的。这是一个模板化程度很高的代码。

既然模板化程度很高,我们就要去解决它,原理很简单,就是前面 Spring 中所说的动态代理。我们可以将当前方法简化成 一个接口:

package org.javaboy.mapper;

public interface UserMapper {
    User getUserById(Integer id);

    Integer addUser(User user);

    Integer addUser2(User user);

    Integer deleteUserById(Integer id);

    Integer updateUser(User user);

    List<User> getAllUser();
}

这个接口对应的 Mapper 文件如下(注意,UserMapper.xml 和 UserMapper 需要放在同一个包下面):

<?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="org.javaboy.mybatis.mapper.UserMapper">

    <select id="getUserById" resultType="org.javaboy.mybatis.model.User">
        select * from user where id=#{id};
    </select>
    <insert id="addUser" parameterType="org.javaboy.mybatis.model.User">
        insert into user (username,address) values (#{username},#{address});
    </insert>
    <insert id="addUser2" parameterType="org.javaboy.mybatis.model.User">
        <selectKey resultType="java.lang.String" keyProperty="id" order="BEFORE">
            select uuid();
        </selectKey>
        insert into user (id,username,address) values (#{id},#{username},#{address});
    </insert>

    <delete id="deleteUserById" parameterType="java.lang.Integer">
        delete from user where id=#{id}
    </delete>

    <update id="updateUser" parameterType="org.javaboy.mybatis.model.User">
        update user set username = #{username} where id=#{id};
    </update>

    <select id="getAllUser" resultType="org.javaboy.mybatis.model.User">
        select * from user;
    </select>
</mapper>

使用这个接口,完全可以代替上面的 UserDao,为什么呢?因为这个接口提供了 UserDao 所需要的最核心的东西,根据这个接口,就可以自动生成 UserDao:

  • 首先,UserDao 中定义了 SqlSessionFactory,这是一套固定的代码
  • UserMapper 所在的包+UserMapper 类名+UserMapper 中定义好的方法名,就可以定位到要调用的 SQL
  • 要调用 SqlSession 中的哪个方法,根据定位到的 SQL 节点就能确定

因此,我们在 MyBatis 开发中,实际上不需要自己提供 UserDao 的实现,我们只需要提供一个 UserMapper 即可。

然后,我们在 MyBatis 的全局配置中,配置一下 UserMapper:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///test01?serverTimezone=Asia/Shanghai"/>
                <property name="username" value="root"/>
                <property name="password" value="123"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <package name="org.javaboy.mybatis.mapper"/>
    </mappers>
</configuration>

然后,加载配置文件,获取 UserMapper,并调用它里边的方法:

public class Main2 {
    public static void main(String[] args) {
        SqlSessionFactory instance = SqlSessionFactoryUtils.getInstance();
        SqlSession sqlSession = instance.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> allUser = mapper.getAllUser();
        System.out.println(allUser);
    }
}

注意,在 Maven 中,默认情况下,Maven 要求我们将 XML 配置、properties 配置等,都放在 resources 目录下,如果我们强行放在 java 目录下,默认情况下,打包的时候这个配置文件会被自动忽略掉。对于这两个问题,我们有两种解决办法:

  • 不要忽略 XML 配置:

我们可以在 pom.xml 中,添加如下配置,让 Maven 不要忽略我在 java 目录下的 XML 配置:

<build>
    <resources>
        <resource>
            <directory>src/main/java</directory>
            <includes>
                <include>**/*.xml</include>
            </includes>
        </resource>
        <resource>
            <directory>src/main/resources</directory>
        </resource>
    </resources>
</build>
  • 按照 Maven 的要求来

按照 Maven 的要求来,将 xml 文件放到 resources 目录下,但是,MyBatis 中默认情况下要求,UserMapper.xml 和 UserMapper 接口,必须放在一起,所以,我们需要手动在 resources 目录下,创建一个和 UserMapper 接口相同的目录:

这样,我们就不需要在 pom.xml 文件中添加配置了,因为这种写法同时满足了 Maven 和 MyBatis 的要求。

公众号江南一点雨后台回复 SSM 下载视频笔记。

7. Mapper 映射文件

mapper 映射文件,是 MyBatis 中最重要的部分,涉及到的细节也是非常非常多。

7.1 parameterType

这个表示输入的参数类型。

7.1.1 $#

这是一个非常非常高频的面试题,虽然很简单。在面试中,如果涉及到 MyBatis,一般情况下,都是这个问题。

在 MyBatis 中,我们在 mapper 引用变量时,默认使用的是 #,像下面这样:

<select id="getUserById" resultType="org.javaboy.mybatis.model.User">
    select * from user where id=#{id};
</select>

除了使用 # 之外,我们也可以使用 $ 来引用一个变量:

<select id="getUserById" resultType="org.javaboy.mybatis.model.User">
    select * from user where id=${id};
</select>

在旧的 MyBatis 版本中,如果使用 $,变量需要通过 @Param 取别名,在最新的 MyBatis 中,无论是 # 还是 $,如果只有一个参数,可以不用取别名,如下:

public interface UserMapper {
    User getUserById(Integer id);
}

既然 #$ 符号都可以使用,那么他们有什么区别呢?

我们在 resources 目录下,添加 log4j.properties ,将 MyBatis 执行时的 SQL 打印出来:

log4j.rootLogger=DEBUG,stdout
log4j.logger.org.mybatis=DEBUG
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n 

然后添加日志依赖:

<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.5</version>
</dependency>
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-log4j12</artifactId>
    <version>1.7.5</version>
</dependency>

然后,我们可以分别观察 $# 执行时的日志:

上面这个日志,是 $ 符号执行的日志,可以看到,SQL 直接就拼接好了,没有参数。

下面这个,是 # 执行的日志,可以看到,这个日志中,使用了预编译的方式:

在 JDBC 调用中,SQL 的执行,我们可以通过字符串拼接的方式来解决参数的传递问题,也可以通过占位符的方式来解决参数的传递问题。当然,这种方式也传递到 MyBatis 中,在 MyBatis 中,$ 相当于是参数拼接的方式,而 # 则相当于是占位符的方式。

一般来说,由于参数拼接的方式存在 SQL 注入的风险,因此我们使用较少,但是在一些特殊的场景下,又不得不使用这种方式。

有的 SQL 拼接实际上可以通过数据库函数来解决,例如模糊查询:

<select id="getUserByName" resultType="org.javaboy.mybatis.model.User">
    select * from user where username like concat('%',#{name},'%');
</select>

但是有的 SQL 无法使用 # 来拼接,例如传入一个动态字段进来,假设我想查询所有数据,要排序查询,但是排序的字段不确定,需要通过参数传入,这种场景就只能使用 $,例如如下方法:

List<User> getAllUser(String orderBy);

定义该方法对应的 XML 文件:

<select id="getAllUser" resultType="user">
    select * from user order by ${orderBy}
</select>

测试一下:

SqlSessionFactory instance = SqlSessionFactoryUtils.getInstance();
SqlSession sqlSession = instance.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> allUser = mapper.getAllUser("id");
System.out.println(allUser);

小结:

面试中,遇到这个问题,一定要答出来 Statement 和 PreparedStatement 之间的区别,这个问题才算理解到位了。

7.1.2 简单类型

简单数据类型传递比较容易,像前面的根据 id 查询一条记录就算是这一类的。

这里再举一个例子,比如根据 id 修改用户名:

Integer updateUsernameById(String username, Integer id);

再定义该方法对应的 mapper:

<update id="updateUsernameById">
    update user set username = #{username} where id=#{id};
</update>

此时,如果直接调用该方法,会抛出异常:

这里是说,找不到我们定义的 username 和 id 这两个参数。同时,这个错误提示中指明,可用的参数名是 [arg1, arg0, param1, param2],相当于我们自己给变量取的名字失效了,要使用系统提供的默认名字,默认名字实际上是两套体系:

第一套就是 arg0、arg1、、、、
第二套就是 param1、param2、、、

注意,这两个的下标是不一样的。

因此,按照错误提示,我们将参数改为下面这样:

<update id="updateUsernameById">
    update user set username = #{arg0} where id=#{arg1};
</update>

或者下面这样:

<update id="updateUsernameById">
    update user set username = #{param1} where id=#{param2};
</update>

这两种方式,都可以使该方法顺利执行。

但是,默认的名字不好记,容易出错,我们如果想要使用自己写的变量的名字,可以通过给参数添加 @Param 来指定参数名(一般在又多个参数的时候,需要加),一旦用 @Param 指定了参数类型之后,可以省略掉参数类型,就是在 xml 文件中,不用定义 parameterType 了:

Integer updateUsernameById(@Param("username") String username, @Param("id") Integer id);

这样定义之后,我们在 mapper.xml 文件中,就可以直接使用 username 和 id 来引用变量了。

7.1.3 对象参数

对象参数。

例如添加一个用户:

Integer addUser(User user);

对应的 mapper 文件如下:

<insert id="addUser" parameterType="org.javaboy.mybatis.model.User">
    insert into user (username,address,favorites) values (#{username},#{address},#{favorites,typeHandler=org.javaboy.mybatis.typehandler.List2VarcharHandler});
</insert>

我们在引用的时候,直接使用属性名就能够定位到对象了。如果对象存在多个,我们也需要给对象添加 @Param 注解,如果给对象添加了 @Param 注解,那么对象属性的引用,会有一些变化。如下:

Integer addUser(@Param("user") User user);

如果对象参数添加了 @Param 注解,Mapper 中的写法就会发生变化:

<insert id="addUser" parameterType="org.javaboy.mybatis.model.User">
    insert into user (username,address,favorites) values (#{user.username},#{user.address},#{user.favorites,typeHandler=org.javaboy.mybatis.typehandler.List2VarcharHandler});
</insert>

注意多了一个前缀,这个前缀不是变量名,而是 @Param 注解中定义名称。

如果对象中还存在对象,用 . 继续取访问就可以了。

7.1.4 Map 参数

一般不推荐在项目中使用 Map 参数。如果想要使用 Map 传递参数,技术上来说,肯定是没有问题的。

Integer updateUsernameById(HashMap<String,Object> map);

XML 文件写法如下:

<update id="updateUsernameById">
    update user set username = #{username} where id=#{id};
</update>

引用的变量名,就是 map 中的 key。基本上和实体类是一样的,如果给 map 取了别名,那么在引用的时候,也要将别名作为前缀加上,这一点和实体类也是一样的。

7.2 resultType

resultType 是返回类型,在实际开发中,如果返回的数据类型比较复杂,一般我们使用 resultMap,但是,对于一些简单的返回,使用 resultType 就够用了。

resultType 返回的类型可以是简单类型,可以是对象,可以是集合,也可以是一个 hashmap,如果是 hashmap,map 中的 key 就是字段名,value 就是字段的值。

输出 pojo 对象和输出 pojo 列表在 sql 中定义的 resultType 是一样的。
返回单个 pojo 对象要保证 sql 查询出来的结果集为单条,内部使用 sqlSession.selectOne 方法调用,mapper 接口使用 pojo 对象作为方法返回值。返回 pojo 列表表示查询出来的结果集可能为多条,内部使用 sqlSession.selectList 方法,mapper 接口使用 List 对象作为方法返回值。

7.3 resultMap

在实际开发中,resultMap 是使用较多的返回数据类型配置。因为实际项目中,一般的返回数据类型比较丰富,要么字段和属性对不上,要么是一对一、一对多的查询,等等,这些需求,单纯的使用 resultType 是无法满足的,因此我们还需要使用 resultMap,也就是自己定义映射的结果集。

先来看一个基本用法:

首先在 mapper.xml 中定义一个 resultMap:

<resultMap id="MyResultMap" type="org.javaboy.mybatis.model.User">
    <id column="id" property="id"/>
    <result column="username" property="username"/>
    <result column="address" property="address"/>
</resultMap>

在这个 resultMap 中,id 用来描述主键,column 是数据库查询出来的列名,property 则是对象中的属性名。

然后在查询结果中,定义返回值时使用这个 ResultMap:

<select id="getUserById" resultMap="MyResultMap">
    select * from user where id=#{id};
</select>

注意,在旧版的 MyBatis 中,要求实体类一定要有一个无参构造方法,新版的 MyBatis 没有这个要求。

当然,我们也可以在 resultMap 中,自己指定要调用的构造方法,指定方式如下:

<resultMap id="MyResultMap" type="org.javaboy.mybatis.model.User">
    <constructor>
        <idArg column="id" name="id"/>
        <arg column="username" name="username"/>
    </constructor>
</resultMap>

这个就表示使用两个参数的构造方法取构造一个 User 实例。注意,name 属性表示构造方法中的变量名,默认情况下,变量名是 arg0、arg1、、、、或者 param1、param2、、、,如果需要自定义,我们可以在构造方法中,手动加上 @Param 注解。

public class User {
    private Integer id;
    private String username;
    private String address;
    private List<String> favorites;

    public User(@Param("id") Integer id, @Param("username") String username) {
        this.id = id;
        this.username = username;
        System.out.println("--------------------");
    }

    public User(Integer id, String username, String address, List<String> favorites) {
        this.id = id;
        this.username = username;
        this.address = address;
        this.favorites = favorites;
        System.out.println("-----------sdfasfd---------");
    }

    public List<String> getFavorites() {
        return favorites;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                ", favorites=" + favorites +
                '}';
    }

    public void setFavorites(List<String> favorites) {
        this.favorites = favorites;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

7.4 动态 SQL

动态 SQL 是 MyBatis 中非常强大的一个功能。例如一些常见的查询场景:

  • 查询条件不确定
  • 批量插入
  • ….

这些类似需求,我们都可以通过 MyBatis 提供的动态 SQL 来解决。

MyBatis 中提供的动态 SQL 节点非常多。

7.4.1 if

if 是一个判断节点,如果满足某个条件,节点中的 SQL 就会生效。例如分页查询,要传递两个参数,页码和查询的记录数,如果这两个参数都为 null,那我就查询所有。

我们首先来定义接口方法:

List<User> getUserByPage(@Param("start") Integer start, @Param("count") Integer count);

接口定义成功后,接下来在 XML 中定义 SQL:

<select id="getUserByPage" resultType="org.javaboy.mybatis.model.User">
    select * from user
    <if test="start !=null and count!=null">
        limit #{start},#{count}
    </if>
</select>

if 节点中,test 表示判断条件,如果判断结果为 true,则 if 节点的中的 SQL 会生效,否则不会生效。也就是说,在方法调用时,如果分页的两个参数都为 null,则表示查询所有数据:

public class Main2 {
    public static void main(String[] args) {
        SqlSessionFactory instance = SqlSessionFactoryUtils.getInstance();
        SqlSession sqlSession = instance.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = mapper.getUserByPage(null, null);
        System.out.println(list);
        list = mapper.getUserByPage(2, 2);
        System.out.println(list);
        sqlSession.commit();
    }
}

7.4.2 where

where 用来处理查询参数。例如我存在下面一个查询函数:

List<User> getUserByUsernameAndId(@Param("id") Integer id, @Param("name") String name);

这个查询的复杂之处在于:每个参数都是可选的,如果 id 为 null,则表示根据 name 查询,name 为 null,则表示根据 id 查询,两个都为 null,表示查询所有。

<select id="getUserByUsernameAndId" resultType="org.javaboy.mybatis.model.User">
    select * from user
    <where>
        <if test="id!=null">
            and id>#{id}
        </if>
        <if test="name!=null">
            and username like concat('%',#{name},'%')
        </if>
    </where>
</select>

用 where 节点将所有的查询条件包起来,如果有满足的条件,where 节点会自动加上,如果没有,where 节点也将不存在,在有满足条件的情况下,where 还会自动处理 and 关键字。

public class Main2 {
    public static void main(String[] args) {
        SqlSessionFactory instance = SqlSessionFactoryUtils.getInstance();
        SqlSession sqlSession = instance.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> list = mapper.getUserByUsernameAndId(2, "java");
        System.out.println(list);
        list = mapper.getUserByUsernameAndId(null, "javaboy");
        System.out.println(list);
        list = mapper.getUserByUsernameAndId(5, null);
        System.out.println(list);
        list = mapper.getUserByUsernameAndId(null, null);
        System.out.println(list);
    }
}

7.4.3 foreach

foreach 用来处理数组/集合参数。

例如,我们有一个批量查询的需求:

List<User> getUserByIds(@Param("ids")Integer[] ids);

对应的 XML 如下:

<select id="getUserByIds" resultType="org.javaboy.mybatis.model.User">
    select * from user where id in
    <foreach collection="ids" open="(" close=")" item="id" separator=",">
        #{id}
    </foreach>
</select>

在 mapper 中,通过 foreach 节点来遍历数组,collection 表示数组变量,open 表示循环结束后,左边的符号,close 表示循环结束后,右边的符号,item 表示循环时候的单个变量,separator 表示循环的元素之间的分隔符。

注意,默认情况下,无论你的数组/集合参数名字是什么,在 XML 中访问的时候,都是 array,开发者可以通过 @Param 注解给参数重新指定名字。

例如我还有一个批量插入的需求:

Integer batchInsertUser(@Param("users") List<User> users);

然后,定义该方法对应的 mapper:

<insert id="batchInsertUser">
    insert into user (username,address) values 
    <foreach collection="users" separator="," item="user">
        (#{user.username},#{user.address})
    </foreach>
</insert>

然后,在 Main 方法中进行测试:

public class Main2 {
    public static void main(String[] args) {
        SqlSessionFactory instance = SqlSessionFactoryUtils.getInstance();
        SqlSession sqlSession = instance.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = new ArrayList<>();
        User u1 = new User();
        u1.setUsername("zhangsan");
        u1.setAddress("shenzhen");
        users.add(u1);
        User u2 = new User();
        u2.setUsername("lisi");
        u2.setAddress("广州");
        users.add(u2);
        mapper.batchInsertUser(users);
        sqlSession.commit();
    }
}

7.4.4 sql 片段

大家知道,在 SQL 查询中,一般不建议写 *,因为 select * 会降低查询效率。但是,每次查询都要把字段名列出来,太麻烦。这种使用,我们可以利用 SQL 片段来解决这个问题。

例如,我们先在 mapper 中定义一个 SQL 片段:

<sql id="Base_Column">
    id,usename,address
</sql>

然后,在其他 SQL 中,就可以引用这个变量:

<select id="getUserByIds" resultType="org.javaboy.mybatis.model.User">
    select <include refid="Base_Column" /> from user where id in
    <foreach collection="ids" open="(" close=")" item="id" separator=",">
        #{id}
    </foreach>
</select>

7.4.5 set

set 关键字一般用在更新中。因为大部分情况下,更新的字段可能不确定,如果对象中存在该字段的值,就更新该字段,不存在,就不更新。例如如下方法:

Integer updateUser(User user);

现在,这个方法的需求是,根据用户 id 来跟新用户的其他属性,所以,user 对象中一定存在 id,其他属性则不确定,其他属性要是有值,就更新,没值(也就是为 null 的时候),则不处理该字段。

我们结合 set 节点,写出来的 sql 如下:

<update id="updateUser" parameterType="org.javaboy.mybatis.model.User">
    update user
    <set>
        <if test="username!=null">
            username = #{username},
        </if>
        <if test="address!=null">
            address=#{address},
        </if>
        <if test="favorites!=null">
            favorites=#{favorites},
        </if>
    </set>
    where id=#{id};
</update>
赞(12)
未经允许不得转载:江南一点雨 » MyBatis Mapper 详解
分享到: 更多 (0)
扫码关注微信公众号【江南一点雨】,回复 1024,查看松哥原创 Java 实战教程(图文+视频)。

专注 Java 一百年

关注我们国际站