[华为云在线课程][MyBatis入门][二][参数和返回值定义][学习笔记]
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 + '\'' +
'}';
}
}
- 点赞
- 收藏
- 关注作者
评论(0)