一、回顾
当初越来越风行基于 SpringBoot 开发 Web 利用,其中利用 Mybatis 作为数据库 CRUD 操作已成为支流。楼主以 MySQL 为例,总结了九大类应用 Mybatis 操作数据库 SQL 小技巧分享给大家。
- 分页查问
- 预置 sql 查问字段
- 一对多级联查问
- 一对一级联查问
- foreach 搭配 in 查问
- 利用if 标签拼装动静 where 条件
- 利用 choose 和 otherwise组合标签拼装查问条件
- 动静绑定查问参数:_parameter
- 利用 set 配合 if 标签,动静设置数据库字段更新值
01 分页查问
利用 limit 设置每页 offset 偏移量和每页 size 大小。
select * from sys_user uLEFT JOIN sys_user_site s ON u.user_id = s.user_idLEFT JOIN sys_dept d ON d.dept_id = s.dept_idLEFT 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。
@FunctionalInterfacepublic 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 条件结构器阐明
- 呈现的第一个入参 boolean condition 示意该条件是否退出最初生成的 sql 中,例如:query.like(StringUtils.isNotBlank(name), Entity::getName, name) .eq(age!=null && age >= 0, Entity::getAge, age)
- 代码块内的多个办法均为从上往下补全个别 boolean 类型的入参,默认为 true
- 呈现的泛型 Param 均为 Wrapper 的子类实例(均具备 AbstractWrapper 的所有办法)
- 办法在入参中呈现的 R 为泛型,在一般 wrapper 中是 String ,在 LambdaWrapper 中是函数(例:Entity::getId,Entity 为实体类,getId为字段id的getMethod)
- 办法入参中的 R column 均示意数据库字段,当 R 具体类型为 String 时则为数据库字段名(字段名是数据库关键字的本人用本义符包裹!)!而不是实体类数据字段名!!!,另当 R 具体类型为 SFunction 时我的项目 runtime 不反对 eclipse 自家的编译器!
- 应用一般 wrapper,入参为 Map 和 List 的均以 json 模式体现!
- 应用中如果入参的 Map 或者 List为空,则不会退出最初生成的 sql 中!
正告:
不反对以及不赞成在 RPC 调用中把 Wrapper 进行传输。
“
Wrapper 很重 传输 Wrapper 能够类比为你的 controller 用 map 接管值(开发一时爽,保护火葬场) 正确的 RPC 调用姿态是写一个 DTO 进行传输,被调用方再依据 DTO 执行相应的操作 咱们拒绝接受任何对于 RPC 传输 Wrapper 报错相干的 issue 甚至 pr。
AbstractWrapper 内部结构
从上图,咱们理解到 AbstractWrapper 的实际上实现了五大接口:
- SQL 片段函数接口:ISqlSegment
@FunctionalInterfacepublic 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。
@Overridepublic 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;} @FunctionalInterfacepublic 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)@Datapublic 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 操作类
@Mapperpublic 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 操作类
@Mapperpublic 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 执行语句全栈打印:
#mybatismybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
执行成果如下:
1 等值查问:eq
@Testpublic 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
@Testpublic 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
@Testpublic 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
@Testpublic 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
@Testpublic 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 中。
@Testpublic 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 构建简单的查问条件
@Testpublic 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
@Testpublic 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 分页插件。
@Beanpublic PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor();}
9 更新条件结构器:LambdaUpdateWrapper
@Testpublic 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 标签实用于表和表之间存在一对一的关联关系,如用户和身份证存在一个人只会有一个身份证号,反过来也成立。
@Testpublic 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 标签实用于表和表之间存在一对多的关联关系,如用户和订单存在一个人能够购买多个物品,产生多个购物订单。
@Testpublic 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 */@Componentpublic 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 表达式长处:
- 单表操作,代码十分简洁,真正做到零配置,如不须要在 xml 或用注解(@Select)写大量原生 SQL 代码
- 并行计算
- 预测代表将来的编程趋势
lambda 表达式毛病:
- 单表操作,对于多表关联查问反对不好
- 调试艰难
- 底层逻辑简单
五、总结
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我的项目,欢送关注获取更多精彩内容