乐趣区

关于java:MyBatisPlus码之重器-lambda-表达式使用指南开发效率瞬间提升80

一、回顾

当初越来越风行基于 SpringBoot 开发 Web 利用,其中利用 Mybatis 作为数据库 CRUD 操作已成为支流。楼主以 MySQL 为例,总结了九大类应用 Mybatis 操作数据库 SQL 小技巧分享给大家。

  1. 分页查问
  2. 预置 sql 查问字段
  3. 一对多级联查问
  4. 一对一级联查问
  5. foreach 搭配 in 查问
  6. 利用 if 标签拼装动静 where 条件
  7. 利用 choose 和 otherwise 组合标签拼装查问条件
  8. 动静绑定查问参数:_parameter
  9. 利用 set 配合 if 标签,动静设置数据库字段更新值

01 分页查问

利用 limit 设置每页 offset 偏移量和每页 size 大小。

select * from sys_user u
LEFT JOIN sys_user_site s ON u.user_id = s.user_id
LEFT JOIN sys_dept d ON d.dept_id = s.dept_id
LEFT JOIN sys_emailinfo e ON u.user_id = e.userid AND e.MAIN_FLAG = 'Y'
<where>
 <include refid="userCondition"/>
</where>
limit #{offset}, #{limit}

02 预置 sql 查问字段

<sql id="columns">
  id,title,content,original_img,is_user_edit,province_id,status,porder
</sql>

查问 select 语句援用 columns:

<select id="selectById" resultMap="RM_MsShortcutPanel">
 seelct
 <include refid="columns"/>
 from cms_self_panel
 where
 id = #{_parameter}
</select>

03 一对多级联查问

利用 mybatis 的 collection 标签,能够在每次查问文章主体同时通过 queryparaminstancelist 级联查问出关联表数据。

<resultMap id="BaseResultMap" type="com.unicom.portal.pcm.entity.ArticleEntity">
 <id column="id" jdbcType="BIGINT" property="id"/> 
 <collection property="paramList" column="id" select="queryparaminstancelist"/>
</resultMap>

queryparaminstancelist 的 sql 语句

<select id="queryparaminstancelist" resultMap="ParamInstanceResultMap">
 select * from `cms_article_flow_param_instance` where article_id=#{id} 
</select>

04 一对一级联查问

利用 mybatis 的 association 标签,一对一查问关联表数据。

<resultMap id="BaseResultMap" type="com.unicom.portal.pcm.entity.ArticleEntity">
 <association property="articleCount" javaType="com.unicom.portal.pcm.entity.MsArticleCount"/>
</resultMap>

查问 sql 语句:

MsArticlecount 实体对象的属性值能够从 下面的 select 后的 sql 字段进行匹配映射获取。

05 foreach 搭配 in 查问

利用 foreach 遍历 array 汇合的参数,拼成 in 查问条件

<foreach collection="array" index="index" item="item" open="(" separator="," close=")">
 #{item}
</foreach>

06 利用 if 标签拼装动静 where 条件

select r.*, (select d.org_name from sys_dept d where d.dept_id = r.dept_id) deptName from sys_role r
<where>
r.wid = #{wid}
<if test="roleName != null and roleName.trim() !=''">
and r.`role_name` like concat('%',#{roleName},'%')
</if>
<if test="status != null and status.trim() !=''">
and r.`status` = #{status}
</if>
</where>

07 利用 choose 和 otherwise 组合标签拼装查问条件

<choose>
 <when test="sidx != null and sidx.trim() !=''">
 order by r.${sidx} ${order}
 </when>
 <otherwise>
 order by r.role_id asc
 </otherwise>
</choose>

08 隐形绑定参数:_parameter

_parameter 参数的含意

当 Mapper、association、collection 指定只有一个参数时进行查问时,能够应用 _parameter,它就代表了这个参数。

另外,当应用 Mapper 指定办法应用 @Param 的话,会应用指定的参数值代替。

SELECT id, grp_no grpNo, province_id provinceId, status FROM tj_group_province
<where> 
 ...
 <if test="_parameter!=null">
 and grp_no = #{_parameter}
 </if>
</where>

09 利用 set 配合 if 标签,动静设置数据库字段更新值

<update id="updateById">
 UPDATE cms_label
 <set>
   <if test="labelGroupId != null">
     label_group_id = #{labelGroupId},
   </if>
   dept_id = #{deptId},
   <if test="recommend != null">
     is_recommend = #{recommend},
   </if>
 </set>
 WHERE label_id = #{labelId} 
</update

二、Mybatis-Plus Lambda 表达式实践篇

背景

如果 Mybatis-Plus 是扳手,那 Mybatis Generator 就是生产扳手的工厂。

MyBatis 是一种操作数据库的 ORM 框架,提供一种 Mapper 类,反对让你用 java 代码进行增删改查的数据库操作,省去了每次都要手写 sql 语句的麻烦。然而有一个前提,你得先在 xml 中写好 sql 语句,也是很麻烦的。

题外话:Mybatis 和 Hibernate 的比拟

  • Mybatis 是一个半 ORM 框架;Hibernate 是一个全 ORM 框架。Mybatis 须要本人编写 sql。
  • Mybatis 间接编写原生 sql,灵便度高,能够严格控制 sql 执行性能;Hibernate 的主动生成 hql,因为更好的封装型,开发效率进步的同时,sql 语句的调优比拟麻烦。
  • Hibernate 的 hql 数据库移植性比 Mybatis 更好,Hibernate 的底层对 hql 进行了解决,对于数据库的兼容性更好,
  • Mybatis 间接写的原生 sql 都是与数据库相干,不同数据库 sql 不同,这时就须要多套 sql 映射文件。
  • Hibernate 在级联删除的时候效率低;数据量大,表多的时候,基于关系操作会变得复杂。
  • Mybatis 和 Hibernate 都能够应用第三方缓存,而 Hibernate 相比 Mybatis 有更好的二级缓存机制。

微信搜公众号「猿芯」,后盾私信回复 1024 收费支付 SpringCloud、SpringBoot,微信小程序、Java 面试、数据结构、算法等全套视频材料。

为什么要抉择 Lambda 表达式?

Mybatis-Plus 的存在就是为了稍稍补救 Mybatis 的有余。

在咱们应用 Mybatis 时会发现,每当要写一个业务逻辑的时候都要在 DAO 层写一个办法,再对应一个 SQL,即便是简略的条件查问、即便仅仅扭转了一个条件都要在 DAO 层新增一个办法,针对这个问题,Mybatis-Plus 就提供了一个很好的解决方案:lambda 表达式,它能够让咱们防止许多重复性的工作。

想想 Mybatis 官网提供的 CRUD 例子吧,基本上 xml 配置占据了绝大部分。而用 Lambda 表达式写的 CRUD 代码十分简洁,真正做到零配置,不须要在 xml 或用注解(@Select)写大量原生 SQL 代码。

LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
lqw.eq(UserEntity::getSex, 0L)
        .like(UserEntity::getUserName, "dun");
List<UserEntity> userList = userMapper.selectList(lqw);
userList.forEach(u -> System.out.println("like 全蕴含关键字查问::" + u.getUserName()));

lambda 表达式的实践根底

Java 中的 lambda 表达式本质上是一个匿名办法,但该办法并非独立执行,而是用于实现由函数式接口定义的惟一形象办法。

应用 lambda 表达式时,会创立实现了函数式接口的一个匿名类实例,如 Java8 中的线程 Runnable 类实现了函数接口:@FunctionalInterface。

@FunctionalInterface
public interface Runnable {public abstract void run();
}

平时咱们执行一个 Thread 线程:

new Thread(new Runnable() {
  @Override
  public void run() {System.out.println("xxxx");
  }
}).start();

如果用 lambda 会十分简洁,一行代码搞定。

 new Thread(()-> System.out.println("xxx")).start();

所以在某些场景下应用 lambda 表达式真的能缩小 java 中一些简短的代码,减少代码的优雅性。

lambda 条件结构器根底类:包装器模式(装璜模式)之 AbstractWrapper AbstractWrapper 条件结构器阐明

  1. 呈现的第一个入参 boolean condition 示意该条件是否退出最初生成的 sql 中,例如:query.like(StringUtils.isNotBlank(name), Entity::getName, name) .eq(age!=null && age >= 0, Entity::getAge, age)
  2. 代码块内的多个办法均为从上往下补全个别 boolean 类型的入参, 默认为 true
  3. 呈现的泛型 Param 均为 Wrapper 的子类实例 (均具备 AbstractWrapper 的所有办法)
  4. 办法在入参中呈现的 R 为泛型,在一般 wrapper 中是 String,在 LambdaWrapper 中是函数 (例:Entity::getId,Entity 为实体类,getId 为字段 id 的 getMethod)
  5. 办法入参中的 R column 均示意数据库字段,当 R 具体类型为 String 时则为数据库字段名 (字段名是数据库关键字的本人用本义符包裹!)! 而不是实体类数据字段名!!!,另当 R 具体类型为 SFunction 时我的项目 runtime 不反对 eclipse 自家的编译器!
  6. 应用一般 wrapper,入参为 Map 和 List 的均以 json 模式体现!
  7. 应用中如果入参的 Map 或者 List 为空, 则不会退出最初生成的 sql 中!

正告:

不反对以及不赞成在 RPC 调用中把 Wrapper 进行传输。

Wrapper 很重 传输 Wrapper 能够类比为你的 controller 用 map 接管值 (开发一时爽,保护火葬场) 正确的 RPC 调用姿态是写一个 DTO 进行传输,被调用方再依据 DTO 执行相应的操作 咱们拒绝接受任何对于 RPC 传输 Wrapper 报错相干的 issue 甚至 pr。

AbstractWrapper 内部结构

从上图,咱们理解到 AbstractWrapper 的实际上实现了五大接口:

  • SQL 片段函数接口:ISqlSegment
@FunctionalInterface
public interface ISqlSegment extends Serializable {
    /**
     * SQL 片段
     */
    String getSqlSegment();}
  • 比拟值接口 Compare<Children, R>,如 等值 eq、不等于:ne、大于 gt、大于等于:ge、小于 lt、小于等于 le、between、含糊查问:like 等等
  • 嵌套接口 Nested<Param, Children>,如 and、or
  • 拼接接口 Join<Children>,如 or、exists
  • 函数接口 Func<Children, R>,如 in 查问、groupby 分组、having、order by 排序等

微信搜公众号「猿芯」,后盾私信回复 1024 收费支付 SpringCloud、SpringBoot,微信小程序、Java 面试、数据结构、算法等全套视频材料。

罕用的 where 条件表达式 eq、like、in、ne、gt、ge、lt、le。

@Override
public Children in(boolean condition, R column, Collection<?> coll) {return doIt(condition, () -> columnToString(column), IN, inExpression(coll));
}

public Children notIn(boolean condition, R column, Collection<?> coll)

public Children inSql(boolean condition, R column, String inValue)

public Children notInSql(boolean condition, R column, String inValue)

public Children groupBy(boolean condition, R... columns)

public Children orderBy(boolean condition, boolean isAsc, R... columns)
    
public Children eq(boolean condition, R column, Object val)

public Children ne(boolean condition, R column, Object val)

public Children gt(boolean condition, R column, Object val)

public Children ge(boolean condition, R column, Object val)

public Children lt(boolean condition, R column, Object val)

public Children le(boolean condition, R column, Object val)

...

/**
 * 一般查问条件
 *
 * @param condition  是否执行
 * @param column     属性
 * @param sqlKeyword SQL 关键词
 * @param val        条件值
 */
protected Children addCondition(boolean condition, R column, SqlKeyword sqlKeyword, Object val) {return doIt(condition, () -> columnToString(column), sqlKeyword, () -> formatSql("{0}", val));
}

SQL 片段函数接口

lambda 这么好用的秘诀在于 SQL 片段函数接口:ISqlSegment,咱们在 doIt 办法找到 ISqlSegment 对象参数,打开 ISqlSegment 源码,发现它实在的庐山真面目,原来是基于 Java 8 的函数接口 @FunctionalInterface 实现!

ISqlSegment 就是对 where 中的每个条件片段进行组装。

/**
 * 对 sql 片段进行组装
 *
 * @param condition   是否执行
 * @param sqlSegments sql 片段数组
 * @return children
 */
protected Children doIt(boolean condition, ISqlSegment... sqlSegments) {if (condition) {expression.add(sqlSegments);
    }
    return typedThis;
}
   
@FunctionalInterface
public interface ISqlSegment extends Serializable {

    /**
     * SQL 片段
     */
    String getSqlSegment();}

从 MergeSegments 类中,咱们找到 getSqlSegment 办法,其中代码片段

sqlSegment = normal.getSqlSegment() + groupBy.getSqlSegment() + having.getSqlSegment() + orderBy.getSqlSegment()

这段代码表明,一条残缺的 where 条件 SQL 语句,最终由 normal SQL 片段,groupBy SQL 片段,having SQL 片段,orderBy SQL 片段拼接而成。

@Getter
@SuppressWarnings("serial")
public class MergeSegments implements ISqlSegment {private final NormalSegmentList normal = new NormalSegmentList();
    private final GroupBySegmentList groupBy = new GroupBySegmentList();
    private final HavingSegmentList having = new HavingSegmentList();
    private final OrderBySegmentList orderBy = new OrderBySegmentList();

    @Getter(AccessLevel.NONE)
    private String sqlSegment = StringPool.EMPTY;
    @Getter(AccessLevel.NONE)
    private boolean cacheSqlSegment = true;

    public void add(ISqlSegment... iSqlSegments) {List<ISqlSegment> list = Arrays.asList(iSqlSegments);
        ISqlSegment firstSqlSegment = list.get(0);
        if (MatchSegment.ORDER_BY.match(firstSqlSegment)) {orderBy.addAll(list);
        } else if (MatchSegment.GROUP_BY.match(firstSqlSegment)) {groupBy.addAll(list);
        } else if (MatchSegment.HAVING.match(firstSqlSegment)) {having.addAll(list);
        } else {normal.addAll(list);
        }
        cacheSqlSegment = false;
    }

    @Override
    public String getSqlSegment() {if (cacheSqlSegment) {return sqlSegment;}
        cacheSqlSegment = true;
        if (normal.isEmpty()) {if (!groupBy.isEmpty() || !orderBy.isEmpty()) {sqlSegment = groupBy.getSqlSegment() + having.getSqlSegment() + orderBy.getSqlSegment();
            }
        } else {sqlSegment = normal.getSqlSegment() + groupBy.getSqlSegment() + having.getSqlSegment() + orderBy.getSqlSegment();}
        return sqlSegment;
    }
}

三、Mybatis-Plus Lambda 表达式实战

01 环境筹备

1. Maven 依赖

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper-spring-boot-starter</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
  <groupId>com.baomidou</groupId>
  <artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-test</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
  <groupId>com.google.code.gson</groupId>
  <artifactId>gson</artifactId>
</dependency>

2. 实体(表)以及 Mapper 表映射文件

  • Base 实体
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PACKAGE)
@SuperBuilder(toBuilder = true)
@Data
public class BaseEntity {@TableField(value = "created_tm", fill = FieldFill.INSERT)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime createdTm;

    @TableField(value = "created_by", fill = FieldFill.INSERT)
    private String createdBy;

    @TableField(value = "modified_tm", fill = FieldFill.INSERT_UPDATE)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime modifiedTm;

    @TableField(value = "modified_by", fill = FieldFill.INSERT_UPDATE)
    private String modifiedBy;
}
  • 用户账号实体:UserEntity
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
@AllArgsConstructor(access = AccessLevel.PACKAGE)
@SuperBuilder(toBuilder = true)
@Data
@TableName("sys_user")
public class UserEntity extends BaseEntity{
    private Long userId;
    private String userName;
    private Integer sex;
    private Integer age;
    private String mobile;
}

Mapper 操作类

List<UserDTO> selectUsers();

UserEntity selectByIdOnXml(long userId);

@Results(id = "userResult", value = {@Result(property = "user_id", column = "userId", id = true),
        @Result(property = "userName", column = "user_name"),
        @Result(property = "sex", column = "sex"),
        @Result(property = "mobile", column = "mobile"),
        @Result(property = "age", column = "age")
})
@Select("select * from sys_user where user_id = #{id}")
UserEntity selectByIdOnSelectAnnotation(@Param("id") long id);

@SelectProvider(type = UserSqlProvider.class, method = "selectById")
@ResultMap("BaseResultMap")
UserEntity selectByIdOnSelectProviderAnnotation(long id);

@Select("select * from sys_user where user_id = #{id} and user_name=#{userName}")
@ResultMap("BaseResultMap")
UserEntity selectByIdOnParamAnnotation(@Param("id") long id, @Param("userName") String uerName);

Mapper 表映射文件

<mapper namespace="com.dunzung.mybatisplus.query.mapper.UserMapper">
    <resultMap id="BaseResultMap" type="com.dunzung.mybatisplus.query.entity.UserEntity">
        <id column="user_id" property="userId"/>
        <result column="user_name" property="userName"/>
        <result column="sex" property="sex"/>
        <result column="age" property="age"/>
        <result column="mobile" property="mobile"/>
    </resultMap>
    
    <resultMap id="RelationResultMap" type="com.dunzung.mybatisplus.query.entity.UserDTO" extends="BaseResultMap">
        <association property="card" column="{userId,user_id}"
                     select="com.dunzung.mybatisplus.query.mapper.CardMapper.selectCardByUserId"/>
        <collection property="orders" column="{userId,user_id}"
                    select="com.dunzung.mybatisplus.query.mapper.OrderMapper.selectOrders"/>
    </resultMap>
    
    <select id="selectUsers" resultMap="RelationResultMap">
        select * from sys_user
    </select>
    
    <select id="selectByIdOnXml" resultMap="BaseResultMap">
        select * from sys_user where user_id = #{userId}
   </select>
    
</mapper>
  • 订单实体:OrderEntity
@Data
@TableName("sys_user_card")
public class CardEntity {
    private Long cardId;
    private String cardCode;
    private Long userId;
}

Mapper 操作类

@Mapper
public interface OrderMapper extends BaseMapper<OrderEntity> {}

Mapper 表映射文件

<mapper namespace="com.dunzung.mybatisplus.query.mapper.OrderMapper">
    <resultMap id="BaseResultMap" type="com.dunzung.mybatisplus.query.entity.OrderEntity">
        <id column="order_id" property="orderId"/>
        <result column="order_name" property="orderName"/>
        <result column="user_id" property="userId"/>
        <result column="price" property="price"/>
        <result column="created_tm" property="createdTm"/>
    </resultMap>

    <select id="selectOrders" resultMap="BaseResultMap">
         select * from biz_order where user_id = #{userId}
    </select>

</mapper>
  • 身份证实体:CardEntity
@Data
@TableName("biz_order")
public class OrderEntity {
    private Long orderId;
    private String orderName;
    private Integer userId;
    private Date createdTm;
    private Integer price;
}

Mapper 操作类

@Mapper
public interface CardMapper extends BaseMapper<CardEntity> {}

Mapper 表映射文件

<mapper namespace="com.dunzung.mybatisplus.query.mapper.CardMapper">
    <resultMap id="BaseResultMap" type="com.dunzung.mybatisplus.query.entity.CardEntity">
        <id column="card_id" property="cardId"/>
        <result column="card_code" property="cardCode"/>
        <result column="user_id" property="userId"/>
    </resultMap>
    <select id="selectCardByUserId" resultMap="BaseResultMap">
        select * from sys_user_card where user_id = #{userId}
    </select>
</mapper>

02 Lambda 根底篇

lambda 构建简单的查问条件结构器:LambdaQueryWrapper

LambdaQueryWrapper 四种不同的 lambda 构造方法

  • 形式一 应用 QueryWrapper 的成员办法办法 lambda 构建 LambdaQueryWrapper
LambdaQueryWrapper<UserEntity> lambda = new QueryWrapper<UserEntity>().lambda();
  • 形式二 间接 new 出 LambdaQueryWrapper
LambdaQueryWrapper<UserEntity> lambda = new  LambdaQueryWrapper<>();
  • 形式三 应用 Wrappers 的静态方法 lambdaQuery 构建 LambdaQueryWrapper 举荐
LambdaQueryWrapper<UserEntity> lambda = Wrappers.lambdaQuery();
  • 形式四:链式查问
List<UserEntity> users = new LambdaQueryChainWrapper<UserEntity>(userMapper)
            .like(User::getName, "雨").ge(User::getAge, 20).list();

笔者举荐应用 Wrappers 的静态方法 lambdaQuery 构建 LambdaQueryWrapper 条件结构器。

Debug 调试

为了 Debug 调试不便,须要在 application.yml 启动文件开启 Mybatis-Plus SQL 执行语句全栈打印:

#mybatis
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

执行成果如下:

1 等值查问:eq

@Test
public void testLambdaQueryOfEq() {
    //eq 查问
    // 相当于 select * from sys_user where user_id = 1
    LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
    lqw.eq(UserEntity::getUserId, 1L);
    UserEntity user = userMapper.selectOne(lqw);
    System.out.println("eq 查问::" + user.getUserName());
}

eq 查问等价于原生 sql 的等值查问。

select * from sys_user where user_id = 1

2 范畴查问:in

@Test
public void testLambdaQueryOfIn() {List<Long> ids = Arrays.asList(1L, 2L);
    LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();  
    lqw.in(UserEntity::getUserId, ids);
    List<UserEntity> userList = userMapper.selectList(lqw);
    userList.forEach(u -> System.out.println("in 查问::" + u.getUserName()));
}

in 查问等价于原生 sql 的 in 查问

select * from sys_user where user_id in (1,2)  

3 通配符含糊查问:like

@Test
public void testLambdaQueryOfLikeAll() {LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
    lqw.eq(UserEntity::getSex, 0L)
            .like(UserEntity::getUserName, "dun");
    List<UserEntity> userList = userMapper.selectList(lqw);
    userList.forEach(u -> System.out.println("like 全蕴含关键字查问::" + u.getUserName()));
}

like 查问等价于原生 sql 的 like 全通配符含糊查问。

select * from sys_user where sex = 0 and user_name like '%dun%'

4 右通配符含糊查问:likeRight

@Test
public void testLambdaQueryOfLikeRight() {LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
    lqw.eq(UserEntity::getSex, 0L)
            .likeRight(UserEntity::getUserName, "dun");
    List<UserEntity> userList = userMapper.selectList(lqw);
    userList.forEach(u -> System.out.println("like Right 含关键字查问::" + u.getUserName()));
}

likeRight 查问相当于原生 sql 的 like 右通配符含糊查问。

select * from sys_user where sex = 0 and user_name like 'dun%'

5 左通配符含糊查问:likeLeft

@Test
public void testLambdaQueryOfLikeLeft() {LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
    lqw.eq(UserEntity::getSex, 0L)
            .likeLeft(UserEntity::getUserName, "zung");
    List<UserEntity> userList = userMapper.selectList(lqw);
    userList.forEach(u -> System.out.println("like Left 含关键字查问::" + u.getUserName()));
}

likeLeft 查问相当于原生 sql 的 like 左通配符含糊查问。

select * from sys_user where sex = 0 and user_name like '%zung'

6 条件判断查问

条件判断查问相似于 Mybatis 的 if 标签,第一个入参 boolean condition 示意该条件是否退出最初生成的 sql 中。

@Test
public void testLambdaQueryOfBoolCondition() {UserEntity condition = UserEntity.builder()
            .sex(1)
            .build();
    //eq 或 like 条件判断查问
    LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
    lqw.eq(condition.getSex() != null, UserEntity::getSex, 0L)
            // 满足 bool 判断,是否进查问按字段 userName 查问
            .like(condition.getUserName() != null, UserEntity::getUserName, "dun");
    List<UserEntity> userList = userMapper.selectList(lqw);
    userList.forEach(u -> System.out.println("like 查问::" + u.getUserName()));
}

7 利用 or 和 and 构建简单的查问条件

@Test
public void testLambdaQueryOfOr_And() {LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
    lqw.eq(UserEntity::getSex, 0L)
            .and(wrapper->wrapper.eq(UserEntity::getUserName,"dunzung")
                    .or().ge(UserEntity::getAge, 50));
    List<UserEntity> userList = userMapper.selectList(lqw);
    userList.forEach(u -> System.out.println("like 查问::" + u.getUserName()));
}

下面实例查问等价于原生 sql 查问:

select * from sys_user where sex = 0 and (use_name = 'dunzung' or age >=50)

8 长于利用分页利器 PageHelpler

@Test
public void testLambdaPage() {
    //PageHelper 分页查问
    // 相当于 select * from sys_user limit 0,2
    int pageNumber = 0;
    int pageSize = 2;
    PageHelper.startPage(pageNumber + 1, pageSize);
    LambdaQueryWrapper<UserEntity> lqw = Wrappers.lambdaQuery();
    lqw.orderByAsc(UserEntity::getAge)
          .orderByDesc(UserEntity::getMobile);
    List<UserEntity> userList = userMapper.selectList(lqw); 
    userList.forEach(u -> System.out.println("page 分页查问::" + u.getUserName()));
}

下面实例查问等价于原生 sql 分页查问:

select * from sys_user order by age desc,mobile desc limit 0,2

另外,Mybatis-Plus 自带分页组件,BaseMapper 接口提供两种分页办法来实现物理分页。

  • 第一个返回实体对象容许 null
  • 第二个人返回 map 对象多用于在指定放回字段时应用,防止为指定字段 null 值呈现
IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);

留神,Mybatis-Plus 自带分页组件时,须要配置 PaginationInterceptor 分页插件。

@Bean
public PaginationInterceptor paginationInterceptor() {return new PaginationInterceptor();
}

9 更新条件结构器:LambdaUpdateWrapper

@Test
public void testLambdaUpdate() {LambdaUpdateWrapper<UserEntity> luw = Wrappers.lambdaUpdate();
    luw.set(UserEntity::getUserName, "dunzung01")
            .set(UserEntity::getSex, 1);
    luw.eq(UserEntity::getUserId, 1);
    userMapper.update(null, luw);
}

03 进阶篇

1. Association

Association 标签实用于表和表之间存在一对一的关联关系,如用户和身份证存在一个人只会有一个身份证号,反过来也成立。

@Test
public void testOnAssociationTag() {List<UserDTO> userList = userMapper.selectUsers();
    userList.forEach(u -> System.out.println(u.getUserName()));
}

XML 配置

<resultMap id="RelationResultMap" type="com.dunzung.mybatisplus.query.entity.UserDTO" extends="BaseResultMap">
  <association property="card" column="{userId,user_id}"
               select="com.dunzung.mybatisplus.query.mapper.CardMapper.selectCardByUserId"/>
</resultMap>

2. Collection

Collection 标签实用于表和表之间存在一对多的关联关系,如用户和订单存在一个人能够购买多个物品,产生多个购物订单。

@Test
public void testOnCollectionTag() {List<UserDTO> userList = userMapper.selectUsers();
    userList.forEach(u -> System.out.println(u.getUserName()));
}

XML 配置

<resultMap id="RelationResultMap" type="com.dunzung.mybatisplus.query.entity.UserDTO" extends="BaseResultMap">
  <collection property="orders" 
      column="{userId,user_id}" 
      select="com.dunzung.mybatisplus.query.mapper.OrderMapper.selectOrders"/>
  </resultMap>

留神 Association 和 Collection 先后关系,在编写 ResultMap 时,association 在前,collection 标签在后。

 <resultMap id="RelationResultMap" type="com.dunzung.mybatisplus.query.entity.UserDTO" extends="BaseResultMap">
        <association property="card" column="{userId,user_id}"
                     select="com.dunzung.mybatisplus.query.mapper.CardMapper.selectCardByUserId"/>
        <collection property="orders" column="{userId,user_id}"
                    select="com.dunzung.mybatisplus.query.mapper.OrderMapper.selectOrders"/>
    </resultMap>

如果二者颠倒程序会提醒谬误。

3. 元对象字段填充属性值:MetaObjectHandler

MetaObjectHandler 元对象字段填充器的填充原理是间接给 entity 的属性设置值,提供默认办法的策略均为:

如果属性有值则不笼罩,如果填充值为 null 则不填充,字段必须申明 TableField 注解,属性 fill 抉择对应策略,该申明告知 Mybatis-Plus 须要预留注入 SQL 字段。TableField 注解则是指定该属性在对应状况下必有值,如果无值则入库会是 null。

自定义填充处理器 MyMetaObjectHandler 在 Spring Boot 中须要申明 @Component 或 @Bean 注入,要想依据注解 FieldFill.xxx,如:

@TableField(value = "created_tm", fill = FieldFill.INSERT)
private LocalDateTime createdTm;

@TableField(value = "modified_tm", fill = FieldFill.INSERT_UPDATE)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime modifiedTm;

和字段名以及字段类型来辨别必须应用父类的 setInsertFieldValByName 或者 setUpdateFieldValByName 办法,不须要依据任何来辨别能够应用父类的 setFieldValByName 办法。

/**
 * 属性值填充 Handler
 *
 * @author 猿芯
 * @since 2021/3/30
 */
@Component
public class FillMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {this.setInsertFieldValByName("createdTm", LocalDateTime.now(), metaObject);
        this.setInsertFieldValByName("createdBy", MvcContextHolder.getUserName(), metaObject);
        this.setFieldValByName("modifiedTm", LocalDateTime.now(), metaObject);
        this.setFieldValByName("modifiedBy", MvcContextHolder.getUserName(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {this.setUpdateFieldValByName("modifiedTm", LocalDateTime.now(), metaObject);
        this.setUpdateFieldValByName("modifiedBy", MvcContextHolder.getUserName(), metaObject);
    }
}

个别 FieldFill.INSERT 用父类的 setInsertFieldValByName 办法更新创立属性(创建人、创立工夫)值;FieldFill.INSERT_UPDATE 用父类的 setUpdateFieldValByName 办法更新批改属性(批改人、批改工夫)值;如果想让诸如 FieldFill.INSERT 或 FieldFill.INSERT_UPDATE 任何时候不起作用,用父类的 setFieldValByName 设置属性(创建人、创立工夫、批改人、批改工夫)值即可。

微信搜公众号「猿芯」,后盾私信回复 1024 收费支付 SpringCloud、SpringBoot,微信小程序、Java 面试、数据结构、算法等全套视频材料。

4. 自定义 SQL

应用 Wrapper 自定义 SQL 须要 mybatis-plus 版本 >= 3.0.7,param 参数名要么叫 ew,要么加上注解 @Param(Constants.WRAPPER),应用 ${ew.customSqlSegment} 不反对 Wrapper 内的 entity 生成 where 语句。

注解形式

@Select("select * from mysql_data ${ew.customSqlSegment}")
List<MysqlData> getAll(@Param(Constants.WRAPPER) Wrapper wrapper);

XML 配置

List<MysqlData> getAll(Wrapper ew);
<select id="getAll" resultType="MysqlData">
 SELECT * FROM mysql_data ${ew.customSqlSegment}
</select>

四、Mybatis-Plus lambda 表达式的劣势与劣势

通过下面丰盛的举例详解以及分析 lambda 底层实现原理,想必大家会问:”lambda 表达式仿佛只反对单表操作?”

据我对 Mybatis-Plus 官网的理解,目前的确是这样。依笔者理论使用教训来看,其实程序员大部分开发的性能基本上都是针对单表操作的,Lambda 表达式的劣势在于帮忙开发者缩小在 XML 编写大量反复的 CRUD 代码,这点是十分重要的 nice 的。很显然,Lambda 表达式对于进步程序员的开发效率是显而易见的,我想这点也是我作为程序员十分喜爱 Mybatis-Plus 的一个重要起因。

微信搜公众号「猿芯」,后盾私信回复 1024 收费支付 SpringCloud、SpringBoot,微信小程序、Java 面试、数据结构、算法等全套视频材料。

然而,如果波及对于多表之间的关联查问,lambda 表达式就显得力不从心了,因为 Mybatis-Plus 并没有提供相似于 join 查问的条件结构器。

lambda 表达式长处:

  1. 单表操作,代码十分简洁,真正做到零配置,如不须要在 xml 或用注解(@Select)写大量原生 SQL 代码
  2. 并行计算
  3. 预测代表将来的编程趋势

lambda 表达式毛病:

  1. 单表操作,对于多表关联查问反对不好
  2. 调试艰难
  3. 底层逻辑简单

五、总结

Mybatis-Plus 推出的 lambda 表达式致力于构建简单的 where 查问结构器式并不是银弹,它能够解决你理论我的项目中 80% 的开发效率问题,然而针对一些简单的大 SQL 查问条件反对地并不好,例如一些简单的 SQL 报表统计查问。

所以,笔者举荐单表操作用 lambda 表达式,查问举荐用 LambdaQueryWrapper,更新用 LambdaUpdateWrapper;多表操作还是老老实实写一些原生 SQL,至于原生 SQL 写在哪里?Mapper 文件或者基于注解,如 @Select 都是能够的。

参考

  • https://mp.baomidou.com/guide…
  • https://www.jianshu.com/p/613…
  • https://blog.csdn.net/Solitud…
  • https://blog.csdn.net/weixin_…
  • https://blog.csdn.net/weixin_…

作者:猿芯

起源:https://www.toutiao.com/i6951…

本文首发于公众号:Java 版 web 我的项目,欢送关注获取更多精彩内容

退出移动版