Mybatis-Plus项目中使用注意

问一:更新传入值为null的属性,数据库不更新.

使用MP提供的更新接口时,我们传入某个属性为空的对象,进行数据库数据更新时,我们发现当前字段并没有更新。

Entity

@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@TableName("mnt_app")
@ApiModel(value="App对象", description="")
public class App extends BasicModel<App> {

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "项目id")
    @TableId(value = "app_id", type = IdType.AUTO)
    private Long appId;

    @ApiModelProperty(value = "项目名称")
    private String name;

    @ApiModelProperty(value = "完成时间")
    private LocalDateTime finishTime;
    
    @Override
    protected Serializable pkVal() {
        return this.appId;
    }

}

controller

“App”:{
“appId”: 6,
“name”:张三,
“finishTime”: null,
}

        LambdaQueryWrapper<App> queryWrapper = Wrappers.<App>lambdaQuery()
                .eq(App::getAppId, app.getAppId());
        int update = appMapper.update(app, queryWrapper);

SQL执行日志

UPDATE mnt_app SET name='张三' WHERE is_deleted=0 AND (app_id = 6) ;

通过sql的执行日志我们可以看出,我们的finishTime属性并没有传入更新。

解决方式

上述问题的原因很简单MP为我们提供了一个非主键注解@TableField,其中有一个属性FiledFill属性,它的默认是FieldFill.DEFAULT,默认不处理表示的是当属性值为null时,进行增删改操作时,就忽略这个字段。因此我们进行更新操作时,出现了并没有set finishTime这个操作。
在这里插入图片描述
如果我们不想忽略此字段,只需在实体类中,根据使用场景,在属性上添加 @TableField(fill = FieldFill.UPDATE) 即可。

最后的测试

//添加@TableField(fill = FieldFill.UPDATE),当进行更新操作时,值为null不忽略该字段设置。
    @TableField(fill = FieldFill.UPDATE)
    @ApiModelProperty(value = "完成时间")
    private LocalDateTime finishTime;

实体类

“App”: {
“appId”: 6,
“finishTime”: null,
“name”: “张三”
}

最终效果

UPDATE mnt_app SET name='张三'finish_time=NULL WHERE is_deleted=0 AND (app_id = 6) 

此时,我们就完成字段置空的更新了。

问二:@Param如何使用 mybatis版本3.5.6

@Param是mybatis为我们提供的一个mapper参数与sql的映射注解。简单来说,就是将增删改查条件传入到sql语句中。

官方文档解释

如果你的映射方法接受多个参数,就可以使用这个注解自定义每个参数的名字。否则在默认情况下,除 RowBounds 以外的参数会以 “param” 加参数位置被命名。例如 #{param1}, #{param2}。如果使用了 @Param(“person”),参数就会被命名为 #{person}。

我的解读

官方解释的意思就是,如果我在mapper的参数中不使用@Param,那么在sql写条件时,我写入的条件名称,都会被当做 #{param1}, #{param2}处理。

举个例子

学生实体

之后我们的每个例子,都是基于这个student操作的

@Data
@EqualsAndHashCode(callSuper = false)
@ApiModel(value="Student对象", description="")
public class Student extends Model<Student> {
    
    

    private static final long serialVersionUID = 1L;

    @ApiModelProperty(value = "id")
      @TableId(value = "id", type = IdType.AUTO)
    private Long id;

    @ApiModelProperty(value = "学号")
    private String stuId;

    @ApiModelProperty(value = "名字")
    private String name;

    @ApiModelProperty(value = "性别 0 男,1 女")
    private Integer sex;

    @ApiModelProperty(value = "手机号")
    private String phone;

    @ApiModelProperty(value = "个人简介")
    private String info;

    @ApiModelProperty(value = "头像,picture表id")
    private Long picture;

    @ApiModelProperty(value = "班级")
    private String className;

    @ApiModelProperty(value = "专业")
    private String major;

    @ApiModelProperty(value = "所在小组")
    private Long gId;

    @ApiModelProperty(value = "0 未删除,1 已删除")
    private Boolean isDeleted;

    @Override
    protected Serializable pkVal() {
    
    
        return this.id;
    }

}

通过学生id,查询某一个学生的基本信息举例

        Student student = studentMapper.selectStudentById(id);

mapper

    @Select("<script>" +
           "SELECT s.id,name,stu_id,class_name,group_name " +
            "FROM student s, group_admin g " +
            "WHERE s.`g_id`=g.`id` AND s.id=#{id}"+
            "</script>")
    Student selectStudentById(Long id);
  1. 虽然我们这里使用了#{id},但是实际运行时,mybatis还是把它当做#{param1}处理了。如果我们在前面加一个@Param(“id”),则作为#{id}处理,同时也可以作为#{param1}处理。

那么问题来了,竟然id能被转换成param1,是不是这里随便写一个,即使不和方法中的参数名对照,也会自动转换成param1,而不会报错呢?
我们把#{id}换成#{ids}

    @Select("<script>" +
           "SELECT s.id,name,stu_id,class_name,group_name " +
            "FROM student s, group_admin g " +
            "WHERE s.`g_id`=g.`id` AND s.id=#{ids}"+
            "</script>")
    Student selectStudentById(Long id);

经过测试,顺利通过。
在这里插入图片描述

  1. 旧版本使用${id}时,可能会出现报错(在18年之前的blog中看到的),但是我测试了一下,没有报错,可能官方已经优化了。

不同情况下使用@Param分析

1. 传递一个属性值作为参数

不使用@Param

见上方例子

使用@Param

我们可以自定义sql与mapper的映射,当然,@Param(“id”)也是不成问题的

    @Select("<script>" +
           "SELECT s.id,name,stu_id,class_name,group_name " +
            "FROM student s, group_admin g " +
            "WHERE s.`g_id`=g.`id` AND s.id=#{student_id}"+
            "</script>")
    Student selectStudentById(@Param("student_id") Long id);
2. 传递一个对象作为参数

不使用@Param

service

        Student student = new Student();
        student.setId(1L);
        studentMapper.selectStudentByMessage(student);

mapper

    @Select("<script>" +
            "SELECT s.id,name,stu_id,class_name,group_name " +
            "FROM student s, group_admin g " +
            "WHERE s.`g_id`=g.`id` AND s.id=#{id}"+
            "</script>")
    Student selectStudentByMessage(Student student);
  1. 如果不使用@Param,我们可以直接在#{}中写 所需要的对象的属性id,就可以自动获取student中的id属性。
  2. 如果我们这样写#{student.id},则会报错

There is no getter for property named ‘student’ in ‘class
com.marchsoft.group.manager.system.entity.Student’
提示没有getStudent()这个方法,说明当没有@Param时,我们不能在#{对象.属性}来获取对象的值。

使用@Param

service

        Student student = new Student();
        student.setId(1L);
        studentMapper.selectStudentByMessage(student);

mapper

    @Select("<script>" +
            "SELECT s.id,name,stu_id,class_name,group_name " +
            "FROM student s, group_admin g " +
            "WHERE s.`g_id`=g.`id` AND s.id=#{student.id}"+
            "</script>")
    Student selectStudentByMessage(@Param("student") Student student);
  1. 如果使用@Param,则必须在#{}指明要获取对象的哪一个属性。可以是#{student.id},也可以是#{param1.id}
  2. 如果我们使用#{id},直接获取属性的值,则会报错

Parameter ‘id’ not found. Available parameters are [student, param1]
提示我们没有id这个参数,只有student和param1这两个参数,同时也可见,student同时也可以用param1代替。
我们使用param1测试,发现可以正常执行。

3. 传递多个属性作为参数

不使用@Param

service

 Student student = studentMapper.selectStudentById(id,"张三");

mapper

    @Select("<script>" +
           "SELECT s.id,name,stu_id,class_name,group_name " +
            "FROM student s, group_admin g " +
            "WHERE s.`g_id`=g.`id` AND s.id=#{id} AND s.name=#{name}"+
            "</script>")
    Student selectStudentById(Long id,String name);

大家能看出上述代码的问题吗?额,好像没什么问题,结果运行,纳尼?报错了,我们来看报错信息

Parameter ‘id’ not found. Available parameters are [arg1, arg0, param1, param2]
参数id不存在。但是却有 [arg1, arg0, param1, param2],看来mybatis自动的把我们这两个参数替换了。

正确使用

    @Select("<script>" +
           "SELECT s.id,name,stu_id,class_name,group_name " +
            "FROM student s, group_admin g " +
            "WHERE s.`g_id`=g.`id` AND s.id=#{param1} AND s.name=#{param2}"+
            "</script>")
    Student selectStudentById(Long id,String name);

或者用arg0和arg1。前者表示id,后者表示name

使用@Param

service

 Student student = studentMapper.selectStudentById(id,"张三");

mapper

 @Select("<script>" +
           "SELECT s.id,name,stu_id,class_name,group_name " +
            "FROM student s, group_admin g " +
            "WHERE s.`g_id`=g.`id` AND s.id=#{student_id} AND s.name=#{student_name}"+
            "</script>")
Student selectStudentById(@Param("student_id")Long id,@Param("student_name") String name);

我们使用student_id和student_name,分别表示id和name的参数映射。运行完全没有问题

我们看一下,还可以怎么设置#{}里面的值。
在这里插入图片描述

Parameter ‘student’ not found. Available parameters are [stu_name, student_id, param1, param2]
从错误中我们可以看出,我们还可以使用param1和param2

4. 传递多个对象作为参数

接下来,我们来测试一下多个对象作为参数来测试,可能这个场景实际开发中很少用到,也可能我的业务逻辑比较简单,用不到吧,不过既然来了,我们还有有必要测试一下的。

测试场景

用户表user
在这里插入图片描述
学生表student
在这里插入图片描述
我们联表查user的id为1,并且student的name 为张三的人

用户表的username和student表的stu_id是相互关联的。

查询语句

SELECT s.student_id,u.username,s.name FROM user u ,student s WHERE
u.id=? AND u.username=s.student_id AND s.name=?;

不使用@Param

service

   Student student = new Student();
        student.setName("张三");
        User user = new User();
        user.setId(1L);
        Student student1 = studentMapper.selectStudentByMessage(student, user);

mapper

    @Select("<script>" +
           "SELECT s.stu_id,u.username,s.name " +
            "FROM user u ,student s " +
            "WHERE u.id=#{id} AND u.username=s.stu_id AND s.name=#{name}"+
            "</script>")
    Student selectStudentByMessage(Student student, User user);

之前一个对象时,我们可以直接#{属性名称},照样是可以识别的,那两个是否能识别呢?

Parameter ‘id’ not found. Available parameters are [arg1, arg0, param1, param2]
显然,是不行的。

我们再试一下用student.id 是否可行

@Select("<script>" +
           "SELECT s.stu_id,u.username,s.name " +
            "FROM user u ,student s " +
            "WHERE u.id=#{user.id} AND u.username=s.stu_id AND s.name=#{student.name}"+
            "</script>")
    Student selectStudentByMessage(Student student, User user);

Parameter ‘user’ not found. Available parameters are [arg1, arg0, param1, param2]

看来,在不使用@Param的情况下,多个对象时,只能使用arg或param了。

使用@Param

service

   Student student = new Student();
        student.setName("张三");
        User user = new User();
        user.setId(1L);
        Student student1 = studentMapper.selectStudentByMessage(student, user);

mapper

 @Select("<script>" +
           "SELECT s.stu_id,u.username,s.name " +
            "FROM user u ,student s " +
            "WHERE u.id=#{user.id} AND u.username=s.stu_id AND s.name=#{student.name}"+
            "</script>")
    Student selectStudentByMessage(@Param("student") Student student, @Param("user")User user);

同样的,我们还可以使用@Param1.属性,和@Param2.属性代替,但是注意的是,如果我们就写一个#{user}或#{Param1}是会产生错误的。形参未对象时必须要指定到属性名称。

总结

在上面我们分析了四种情况下@Param的使用,不难不发现,传递一个参数时,使不使用@Param的结果都一样,都会把我们方法中的参数映射成#{param}或者#{arg}。但是传递多个参数时,如果使用@Param,则必须使用param,但是多个参数时使用param,会特别的不方便我们读取sql语句,比如id=#{param1.id},我们还得去分析param1到底是什么。

所以,在实际使用中,我们要视情况而定,我通常是这样使用的。

  1. 单独一个属性参数,直接传参,不使用@Param
  2. 多个属性参数,使用@Param,不然你的sql中都是@{Param1-N}
  3. 单独一个对象参数,使用@Param,具体指向对象的某个属性,便于阅读
  4. 多个对象参数,使用@Param,原因就不解释了。

问三:动态sql如何使用及使用注意

script

要在带注解的映射器接口类中使用动态 SQL,可以使用 script 元素。比如:

更新
    @Update({
    
    "<script>",
      "update Author",
      "  <set>",
      "    <if test='username != null'>username=#{username},</if>",
      "    <if test='password != null'>password=#{password},</if>",
      "    <if test='email != null'>email=#{email},</if>",
      "    <if test='bio != null'>bio=#{bio}</if>",
      "  </set>",
      "where id=#{id}",
      "</script>"})
    void updateAuthorValues(Author author);

或者

    @Update("<script>" +
            "UPDATE t_libellee" +
            "<trim prefix ='SET' suffixOverrides=','>" +
            "<if test='updateLibeller.markRgbStatus!=null'>" +
            "mark_rgb_status=#{updateLibeller.markRgbStatus}" +
            "</if>" +
            "</trim>" +
            "WHERE id=#{updateLibeller.id} AND is_deleted=0" +
            "</script>")
    int updateRiskLibeller(@Param("updateLibeller") UpdateLibellerRiskDTO updateLibeller);

trim可用于替代set。trim是where的升级版。

foreach

@Insert({
    
    "<script>",
            "INSERT INTO sys_users_roles_apps(app_id,role_id,user_id) VALUES",
            "<foreach collection='userIds' item='item' index ='index' separator=','>",
            "(#{appId},#{roleId},#{item})",
            "</foreach>",
            "</script>"})
    void insertUserRoleApp(Long appId, Long roleId, List<Long> userIds);

执行sql:INSERT INTO sys_users_roles_apps(app_id,role_id,user_id) VALUES (74,9,10) , (74,9,11) , (74,9,12)注意:foreach是针对与一条sql记录插的,并不是生成多条sql语句。

if和where

      @Select({
    
    "<script>" +
            "SELECT id FROM sys_user us " +
            "<where>\n" +
            "AND us.id IN (SELECT u.id FROM sys_user u, mnt_app a, sys_users_roles_apps sa\n" +
            "WHERE u.`id` IN (" +
            "<foreach collection='ids' item ='id' index='index' separator=','>" +
            " #{id}" +
            "</foreach> " +
            ") \n" +
            "AND a.`app_id` IN (SELECT app_id FROM mnt_app WHERE is_deleted=FALSE AND STATUS IN (0,4))\n" +
            "AND u.`id`=sa.`user_id` \n" +
            "AND sa.`app_id`=a.`app_id` GROUP BY u.`id`) \n" +
            "</where>" +
            "</script>"})
    Set<Long> selectJoinAppId(IPage<User> user, @Param(Constants.WRAPPER) LambdaQueryWrapper<User> wrapper, Set<Long> ids);

IPage user:分页条件
@Param(Constants.WRAPPER) LambdaQueryWrapper wrapper:查询条件
Integer status:状态
if和where通常会放在一起使用。

在这里插入图片描述

使用where,如果之前没有where,条件成立时,会自动填上where关键字。
如果条件成立,但是where后紧跟着and,则会自动把and给消除掉。

参考文章

官方文档

猜你喜欢

转载自blog.csdn.net/zhang19903848257/article/details/114706320