[华为云在线课程][MyBatis入门][二][参数和返回值定义][学习笔记]

举报
John2021 发表于 2022/07/12 07:19:06 2022/07/12
【摘要】 1.Mapper中的参数 1.1.#和$的区别这是一个高频面试题,在MyBatis中,我们在mapper引用变量时,默认使用的是#,代码如下:<select id="getUserById" resultType="org.example.mybatis01.model.User"> select * from user where id=#{id};</select>除了使用#之外...

1.Mapper中的参数

1.1.#和$的区别

这是一个高频面试题,在MyBatis中,我们在mapper引用变量时,默认使用的是#,代码如下:

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

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

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

在旧版MyBatis中,如果使用$,变量需要通过@Param取别名,最新的MyBatis中,无论是$还是#,如果只有一个参数,可以不用取别名,这两个符号的区别可以通过观察log4j.properties打印的日志来一探究竟。

[DEBUG] 2022-07-02 19:53:09,093[yyyy-MM-dd] method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:137)
==>  Preparing: select * from user where id=?;
[DEBUG] 2022-07-02 19:53:09,124[yyyy-MM-dd] method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:137)
==> Parameters: 1(Integer)
[DEBUG] 2022-07-02 19:53:09,156[yyyy-MM-dd] method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:137)
<==      Total: 1
User{id=1, username='aaaa', address='beijing'}

上面这个日志使用#符号,可以看到采用了预编译的方式。下面这个日志采用了$符号,可以看到SQL直接拼接好了,没有参数。

[DEBUG] 2022-07-02 20:09:10,585[yyyy-MM-dd] method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:137)
==>  Preparing: select * from user where id=1;
[DEBUG] 2022-07-02 20:09:10,616[yyyy-MM-dd] method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:137)
==> Parameters: 
[DEBUG] 2022-07-02 20:09:10,632[yyyy-MM-dd] method:org.apache.ibatis.logging.jdbc.BaseJdbcLogger.debug(BaseJdbcLogger.java:137)
<==      Total: 1
User{id=1, username='aaaa', address='beijing'}

在JDBC调用中,可以通过字符串拼接的方式来解决参数的传递问题,也可以通过占位符方式来解决参数的传递问题。在MyBatis中,$相当于参数拼接,#相当于占位符。
一般来说,采用参数拼接方式存在SQL注入的风险,所以较少使用,但在一些特殊情况下又不得不使用该方法。有的SQL拼接实际上可以通过数据库函数来解决,比如模糊查询:

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

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

package org.example.mybatis01.mapper;
import org.example.mybatis01.model.User;
import java.util.List;
public interface UserMapper {
    List<User> getAllUser(String orderBy);
}
<select id="getAllUserOrderBy" resultType="org.example.mybatis01.model.User">
    select * from user order by ${orderBy};
</select>
public class Main3 {
    public static void main(String[] args) {
        SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtils.getInstance();
        SqlSession sqlSession = sqlSessionFactory.openSession();
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> getAllUserOrderBy = userMapper.getAllUser("id");
        System.out.println(getAllUserOrderBy);
    }
}

输出结果:

[User{id=1, username='aaaa', address='beijing'}, User{id=2, username='bb', address='shanghai'}, User{id=3, username='cc', address='guangzhou'}, User{id=4, username='dd', address='shenzhen'}]

理解这两个符号使用上区别,最关键的地方在于要明白statement和preparedstatement的区别。

1.2.Mapper中多个参数如何处理

我们目前都是传递单个参数,但如果想传递多个参数是否可行,这里用一个根据id修改用户名的例子来说明:

Integer updateUsernameById(String username, String id);
<update id="updateUsernameById">
    update user set username=#{username} where id=#{id};
</update>
@Test
public void updateUsernameById() {
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    Integer updateUsernameById = userMapper.updateUsernameById("ffff", "6");
    System.out.println(updateUsernameById);
    sqlSession.commit();
}
org.apache.ibatis.exceptions.PersistenceException: 
### Error updating database.  Cause: org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [arg1, arg0, param1, param2]
### The error may exist in org/example/mybatis01/mapper/UserMapper.xml
### The error may involve defaultParameterMap
### The error occurred while setting parameters
### SQL: update user set username=? where id=?;
### Cause: org.apache.ibatis.binding.BindingException: Parameter 'username' not found. Available parameters are [arg1, arg0, param1, param2]

发现运行会报出异常,说找不到定义的username和id两个参数。同时指出可用的参数名是[arg1, arg0, param1, param2],相当于我们给变量取的名字失效了,要使用系统默认名字,可以是arg0、arg1,也可以是param1、param2。

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

这两种方式都可以成功运行。我们如果想要使用自己写的变量名,可以通过添加@Param来指定参
数名(一般有多个参数的时候添加),一旦用@Param指定了参数类型,可以省略掉参数类型,也就是在XML文件中,不用再定义parameterType。

Integer updateUsernameById(@Param("username") String username, @Param("id") String id);
<update id="updateUsernameById">
    update user set username=#{username} where id=#{id};
</update>
@Test
public void updateUsernameById() {
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    Integer updateUsernameById = userMapper.updateUsernameById("ffffff", "6");
    System.out.println(updateUsernameById);
    sqlSession.commit();
}

1.3.Mapper中使用实体类做参数

以一个添加用户为例子,演示使用实体类作为参数

Integer addUser(User user);
<insert id="addUser" parameterType="org.example.mybatis01.model.User">
    insert into user(username,address) values(#{username},#{address});
</insert>
@Test
public void addUser() {
    User user = new User();
    user.setUsername("test01");
    user.setAddress("testAddress01");
    int insert = sqlSession.insert("org.example.mybatis01.mapper.UserMapper.addUser", user);
    System.out.println(insert);
    sqlSession.commit();
}

在引用时直接使用属性名就能够定位到对象了。但如果对象有多个,也需要给对象添加@Param注解,如果给对象添加了@Param注解,那么对象属性引用也会发生变化:

Integer addUser(@Param("user") User user);
<insert id="addUser" parameterType="org.example.mybatis01.model.User">
    insert into user(username,address) values(#{user.username},#{user.address});
</insert>
@Test
public void addUser() {
    User user = new User();
    user.setUsername("test02");
    user.setAddress("testAddress02");
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    Integer integer = userMapper.addUser(user);
    System.out.println(integer);
    sqlSession.commit();
}

这里的xml文件多了个前缀,这就是@Param注解中定义的名称。如果对象中还有对象,继续用 . 来访问即可。

1.4.Mapper中使用Map做参数

Integer updateUsernameById(HashMap<String, Object> map);
<update id="updateUsernameById">
    update user set username=#{username} where id=#{id};
</update>

引用的变量名和实体类是一样的,如果取了别名就要在前缀加上,这点也和实体类一样的。

2.返回值的定义

2.1.MyBatis两种主键回填方式

主键回填一般用在添加操作中,把插入数据时插入为null的主键id数据填回去,存入到Java对象和主键对应的属性中(数据库主键字段为id则填回实体类的id属性),这样就实现了插入数据为null主键同时查询到主键信息。
例如我们在购物下订单时会返回一个订单编号,其实这个编号就是数据库中的主键,因为在插入数据时写了null,所以订单编号也为null也就返回不了订单编号的数据。如果是批量插入数据,明显不能用每次手动添加id的方法。
主键回填有resultType和resultMap两种方式。

2.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对象作为方法返回值。

<select id="getAllUser2" resultType="java.util.HashMap">
    select * from user;
</select>
List<Map<String,Object>> getAllUser2();
@Test
public void getAllUser2() {
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
    List<Map<String, Object>> allUser2 = userMapper.getAllUser2();
    for (Map<String, Object> map : allUser2) {
        System.out.println("map = "+map);
    }
    //输出结果
    /*
    * <==      Total: 9
        map = {address=beijing, id=1, username=aaaa}
        map = {address=shanghai, id=2, username=bb}
        map = {address=guangzhou, id=3, username=cc}
        map = {address=shenzhen, id=4, username=dd}
        map = {address=moon, id=5, username=ee}
        map = {address=mars, id=6, username=ffffff}
        map = {address=testAddress01, id=7, username=test01}
        map = {address=testAddress01, id=8, username=test01}
        map = {address=testAddress02, id=9, username=test02}
    * 
    * */
}

3.3.resultMap使用详解

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

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

id是用来描述主键,column是数据库查询出来的列明,property是对象中的属性名。
在查询结果中,定义返回值时使用这个MyResultMap

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

注意:旧版MyBatis中要求实体类一定要有一个无参构造方法,新版MyBatis没有这个要求。
当然,也可以在resultMap中,指定要调用的构造方法:

<resultMap id="MyResultMap1" type="org.example.mybatis01.model.User">
    <constructor>
        <idArg column="id" name="id"/>
        <arg column="username" name="username"/>
    </constructor>
</resultMap>

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

package org.example.mybatis01.model;

import org.apache.ibatis.annotations.Param;

public class User {
    private Integer id;
    private String username;
    private String address;

    public User() {

    }

    public User(Integer id, String username, String address) {
        this.id = id;
        this.username = username;
        this.address = address;
    }

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

    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;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", address='" + address + '\'' +
                '}';
    }
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。