Skip to content

👉 MyBatis参数传递

📋 概述

​ 参数传递是十分重要的功能,它能有效地将应用程序中的数据传递给预定的SQL语句或存储过程,实现动态条件查询、分页查询、数据更新等功能。

​ 在《快速入门》这一章节中,我们只能查询用户名为"fatgod"的用户信息,但有了参数传递功能,我们就能根据指定的用户名来查询用户信息了。

​ 在MyBatis中,Mapper接口中的一个方法通常会映射一个SQL语句,方法中的参数列表值可以作为参数传递给这个SQL语句,而该SQL语句中可以通过符号${}#{}接收这些参数。

​ 需要注意的是,SQL语句不一定来自于XML映射文件,也可以来自于注解。例如,在《快速入门》这一章节中的best()方法对应的SQL语句可以直接书写于注解@Select中。

注解SQL

tip: 查询、新增、更改、删除操作对应的XML标签分别为<select><insert><update><delete>,对应的方法注解分别为@Select@Insert@Update@Delete

📍 ${}和#{}

${}占位符本质是字符串拼接,可以把整个SQL语句看成一个普通的字符串,而${}会被替换成相对应的参数值。这种参数接收方式非常灵活,可定制性强,不仅可以实现动态条件等功能,还能实现动态拼接列名、表名、条件等复杂功能。然而,需要注意这种灵活性也带来了SQL 注入的风险,因此在使用时,应用程序需谨慎处理用户输入的参数值,确保 SQL的安全性。

​ 例如,以下的getUsersByCondition方法可以查询出符合给定条件condition的用户列表,brushAllUsers方法可以将fg_user表中的给定字段刷成给定值。

tips:

  1. 当数据记录大于一行时,需用集合类型接收。
  2. 当对表作增删改操作时,方法返回值可设置成voidint类型,后者表示受影响的行数,但如果SQL来自于注解,则只能使用void类型返回值。
java
public interface UserMapper {
    @Select("select * from fg_user where username = 'fatgod'")
    User best();

    List<User> getUserByCondition(String condition);

    int brushAllUsers(String field, String value);
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--xml文件的dtd约束-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.fatgod.learn.mybatis.mapper.UserMapper">
    <select id="getUserByCondition" resultType="cn.fatgod.learn.mybatis.entity.User">
        select * from fg_user where ${condition}
    </select>

    <update id="brushAllUsers">
        update fg_user set ${field} = ${value}
    </update>
</mapper>

​ 下面,我们来测试一下getUserByConditionbrushAllUsers方法。具体来说,利用getUserByCondition方法获取年龄大于等于 18 岁且用户名以字符串 fat 开头的用户列表,并利用brushAllUsers方法将所有用户的邮箱号刷成 fat13511310611@163.com

java
public class MybatisTest {
    @Test
    public void test() {
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(ResourceUtil.getStream("mybatis-config.xml"));
        try (SqlSession sqlSession = sqlSessionFactory.openSession(true)) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            List<User> users = userMapper.getUserByCondition("age >= 18 and username like 'fat%'");
            int rows = userMapper.brushAllUsers("email", "'y13511310612@163.com'");
            System.out.println("getUserByCondition方法查询的用户列表:" + users);
            System.out.println("brushAllUsers方法影响的行数:" + rows);
        }
    }
}

​ 输出的结果如下,可以看到,两个方法成功运行😁。

美元方式传递参数

tip: SqlSession实例默认是手动提交事务的,但能够使用sqlSessionFactory.openSession(true)的方式来创建自动提交事务的SqlSession实例。


#{}占位符本质是MyBatis对JDBC中PreparedStatement预编译语句中的?占位符的一种封装和扩展。这种参数接收方式相对安全有效,能够有效解决SQL注入问题。然而,这种方式也存在一定的局限性,参数传递必须符合?占位符的语法规范,通常只能为某个字段设定值,缺乏一些灵活性。

​ 例如,以下的saveUser方法可以根据指定的User实体对象新增一条用户数据记录,deleteUserById方法可以根据指定的id删除用户。

java
public interface UserMapper {
    @Select("select * from fg_user where username = 'fatgod'")
    User best();

    List<User> getUserByCondition(String condition);

    int brushAllUsers(String field, String value);

    int saveUser(User user);

    int deleteUserById(long id);
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--xml文件的dtd约束-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.fatgod.learn.mybatis.mapper.UserMapper">
    <select id="getUserByCondition" resultType="cn.fatgod.learn.mybatis.entity.User">
        select * from fg_user where ${condition}
    </select>

    <update id="brushAllUsers">
        update fg_user set ${field} = ${value}
    </update>

    <insert id="saveUser">
        insert fg_user(id, username, age, email)
        values (#{id}, #{username}, #{age}, #{email})
    </insert>

    <delete id="deleteUserById">
        delete from fg_user where id = #{id}
    </delete>
</mapper>

​ 下面,我们来测试一下saveUserdeleteUserById方法。具体来说,利用saveUser方法新增一条id200usernameLeonage22emailLeon24@163.com 的用户记录,并利用deleteUserById方法删除一个id300 的用户记录。在该测试中,我们选择使用手动提交事务。

java
public class MybatisTest {
    @Test
    public void test() {
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder()
                .build(ResourceUtil.getStream("mybatis-config.xml"));
        try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            User user = new User().setId(500L).setUsername("Leon")
                    .setAge(22).setEmail("Leon24@163.com");
            int rows1 = userMapper.saveUser(user);
            int rows2 = userMapper.deleteUserById(300);
            System.out.println("saveUser方法影响的用行数:" + rows1);
            System.out.println("deleteUserById方法影响的行数:" + rows2);
            sqlSession.commit(); //手动提交事务
        }
    }
}

​ 输出的结果如下,可以看到,两个方法成功运行😁。

井号方式传递参数

🔧 多种参数传递的情况

​ 为了演示多种参数传递情况。我们可以把UserMapper接口和UserMapper.xml映射文件中的内容都清空(每次演示前,都会清空)。

java
public interface UserMapper {
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--xml文件的dtd约束-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.fatgod.learn.mybatis.mapper.UserMapper">
</mapper>

​ 同时,SqlSessionFactory生产SqlSession实例的这一过程,我们可以将其封装成一个静态方法。

java
public class SqlSessionGenerator {
    @Getter
    private final static SqlSessionFactory FACTORY;

    static {
        FACTORY = new SqlSessionFactoryBuilder()
                .build(ResourceUtil.getStream("mybatis-config.xml"));
    }

    public static SqlSession generate(boolean autoCommit) {
        return FACTORY.openSession(autoCommit);
    }

    public static SqlSession generate() {
        return generate(false);
    }
}

一个简单数据类型

​ 本文中的简单数据类型指的是Java中的基本数据类型、基本数据类型包装类、String类型、JDK内置的时间日期类型等。

​ 如果方法的参数列表只有一个且为简单数据类型,那么在SQL语句中可以使用任意的占位符名称来接收这个参数值。为了增强代码语义,我们建议使用方法的参数(形参)名称。

​ 例如,以下的getByUsername方法可以根据指定的用户名获取用户信息。由于用户名在数据库层面是唯一的,因此该方法的返回值类型应为实体类User,而非集合类型。

java
public interface UserMapper {
    User getByUsername(String username);
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--xml文件的dtd约束-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.fatgod.learn.mybatis.mapper.UserMapper">
    <select id="getByUsername" resultType="cn.fatgod.learn.mybatis.entity.User">
        select * from fg_user where username = #{username}
    </select>
</mapper>

​ 下面为getByUsername方法的测试用例,用户名输入为fatgod

java
public class MybatisTest {
    @Test
    public void testGetByUsername() {
        try (SqlSession sqlSession = SqlSessionGenerator.generate()) {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user = mapper.getByUsername("fatgod");
            System.out.println(user);
        }
    }
}

​ 输出的结果如下,可以看到,我们成功查询到了用户名为fatgod的用户信息。

一个简单数据类型输出

多个简单数据类型

​ 如果方法的参数列表为多个简单数据类型,那么在SQL语句中可以使用参数(形参)名称或者param + 参数索引(从1开始)这两种占位符名称来接收指定的参数值。

多个简单数据类型传参

​ 在较低的MyBatis版本中,其可能并不支持形参名称占位,它们支持参数索引(从0开始)或者arg + 参数索引(从0开始)这两种方式,例如#{0}#{arg0}#{1}#{arg1}。但都支持param + 参数索引(从1开始)这种方式,例如#{param1}#{param2}

​ 除了以上几种方式外,MyBatis还提供了一种声明式的参数接收方式,即使用注解@Param。通过给方法参数添加@Param注解,可以在SQL语句中使用该注解的value值作为占位符名称来接收对应的参数值。MyBatis官方也推荐使用这种声明式的方法来传递接收参数,同时需要注意的是,此时依旧可以通过param + 参数索引(从1开始)作为占位符名称来接收参数。

​ 例如,以下的checkEmail方法可以获取指定用户名和邮箱号的用户数据记录数。

java
public interface UserMapper {
    int checkEmail(@Param("username") String username, @Param("email") String email);
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--xml文件的dtd约束-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.fatgod.learn.mybatis.mapper.UserMapper">
    <select id="checkEmail" resultType="int">
        select count(1) from fg_user where username = #{username} and email = #{email}
    </select>
</mapper>

​ 下面为checkEmail方法的测试用例,用户名和邮箱号输入分别为fatgodFakeemail@qq.com。如果记录数大于 0,则表示邮箱号正确,否则表示邮箱号错误。

java
public class MybatisTest {
    @Test
    public void testCheckEmail() {
        try (SqlSession sqlSession = SqlSessionGenerator.generate()) {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            int count = mapper.checkEmail("fatgod", "Fakeemail@qq.com");
            System.out.println(count > 0 ? "邮箱号正确" : "邮箱号错误");
        }
    }
}

​ 输出的结果如下,可以看到,用户名fatgod与邮箱号Fakeemail@qq.com并不匹配。

多个简单数据类型输出

Map类型

​ 如果方法的参数为Map类型,那么在SQL语句中可以使用键名作为占位符名称来接收该键映射的值。此外,Map类型的方法参数也可以使用@Param注解修饰,此时占位符的命名规则为@Param注解的value值.键名,比如map.username

​ 例如,以下的getByRange方法可以根据指定字段的指定范围获取用户信息集合。

java
public interface UserMapper {
    List<User> getByRange(Map<String, String> map);
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--xml文件的dtd约束-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.fatgod.learn.mybatis.mapper.UserMapper">

    <select id="getByRange" resultType="cn.fatgod.learn.mybatis.entity.User">
        select * from fg_user where ${field} in (${range});
    </select>
</mapper>

​ 下面为getByRange方法的测试用例,输入的Map为{field: "age", range: "15,16,17,18"}

java
public class MybatisTest {
    @Test
    public void testUpdateById() {
        try (SqlSession sqlSession = SqlSessionGenerator.generate()) {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            Map<String, String> map = new HashMap<>(2);
            map.put("field", "age");
            map.put("range", "15,16,17,18");
            List<User> users = mapper.getByRange(map);
            System.out.println(users);
        }
    }
}

​ 输出的结果如下,可以看到,我们成功查询到了年龄为 15161718 的所有用户信息。

Map类型输出

实体类型

​ 如果方法的参数为实体类型,那么在SQL语句中可以使用属性名作为占位符名称来接收对应的属性值。此外,实体类型的方法参数也可以使用@Param注解修饰,此时占位符的命名规则为@Param注解的value值.属性名,比如user.username

​ 例如,以下的updateById方法可以根据指定的主键修改用户信息。

java
public interface UserMapper {
    int updateById(User user);
}
xml
<?xml version="1.0" encoding="UTF-8" ?>
<!--xml文件的dtd约束-->
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.fatgod.learn.mybatis.mapper.UserMapper">
    <update id="updateById">
        update fg_user set username = #{username}, age = #{age},email = #{email} where id = #{id}
    </update>
</mapper>

​ 下面为updateById方法的测试用例,需修改的用户实体数据为{id: 200, email: "littlebaby@136.com", username: "littlebaby", age: 2}

java
public class MybatisTest {
    @Test
    public void testUpdateById() {
        try (SqlSession sqlSession = SqlSessionGenerator.generate(true)) {
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            User user = new User().setId(200L).setEmail("littlebaby@136.com")
                    .setUsername("littlebaby").setAge(2);
            int rows = mapper.updateById(user);
            System.out.println("更改受影响的行数:" + rows);
        }
    }
}

​ 输出的结果如下,可以看到,我们成功修改了主键id200 的用户信息。

实体类型输出

上次更新于: