java---mybatis-plus

559 阅读4分钟

参考链接:mp.baomidou.com/guide

快速开始

  • 1.用idea springinit 初始化ssm项目(tools:Lombok web:spring web )
  • 2.pom中添加
 <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.1</version>
 </dependency>
  • 3.在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹
@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
  • 4.service + mapper层
// service 层
import com.baomidou.mybatisplus.extension.service.IService;
public interface UserService extends IService<User> {

    void export(UserExportDTO userExportDTO);
}

// 实现层
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

}


// mapper层
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
public interface UserMapper extends BaseMapper<User> {
}

注解

  • @TableName
属性类型默认值描述
valueString""表名
autoResultMapbooleanfalse是否自动构建 resultMap 并使用
autoResultMap: true  需要配合非xml等才能生效
如下代码
@Data
@TableName(value = "user_info", autoResultMap = true)
public class User{
   @TableId(type = IdType.AUTO)
   private Integer id;
   private String name;
   private String gender;
   private String age;

   @TableField(typeHandler = JacksonTypeHandler.class)
   private List<String> roleIds;
   private Date joinDate;
}
作用等同于之前xml里面这样实现 ,出表时的role_ids parse成想要的类型,而入表时能toJson转成字符类型
 <resultMap id="getUserById" type="com.my.demo.model.entity.User">
       <result column="role_ids" property="roleIds" typeHandler="com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler"></result>
   </resultMap>

   <select id="getUserById" resultMap="getUserById">
       select * from user_info where id = #{id}
   </select>
  • @TableField
属性类型默认值描述
valueString""表中字段名,默认会下划线转成小驼峰
existbooleantrue当这个属性不为表中字段时,需要设置为true
insertStrategyEnumDEFAULT当插入整个实体时,默认 或者 为FieldStrategy.NOT_NULL ,则实体属性为null判定为不插入;当为FieldStrategy.IGNORED,即使实体某个属性为null也会插入表中
updateStrategyEnumDEFAULT同insertStrategy。
fillEnumFieldFill.DEFAULT字段自动填充策略,当为FieldFill.UPDATE 更新时填充;INSERT_UPDATE 插入更新时填充。需要配合字段填充拦截器使用
typeHandlerClass<? extends TypeHandler>UnknownTypeHandler.class类型处理器 ,应用场景: set 入表时对字段的操作,譬如加密; get 出表时对字段的操作,譬如解密

条件构造器

  • eq
eq(R column, Object val)
eq(boolean condition, R column, Object val)
eq("name", "老王") ---> name = '老王'
  • ne
ne(R column, Object val)
ne(boolean condition, R column, Object val)
ne("name", "老王") ---> name <> '老王'
```java
- gt ge lt le
```java
gt(R column, Object val)
gt(boolean condition, R column, Object val)
gt("age", 18) ---> age > 18
  • between notBetween
notBetween(R column, Object val1, Object val2)
notBetween(boolean condition, R column, Object val1, Object val2)
between("age", 18, 30) ---> age between 18 and 30
  • like notLike likeLeft likeRight
like("name", "王" ) ---> name like '%王%'
likeLeft("name", "王" ) ---> name like '%王'
  • isNull isNotNull
isNotNull(R column)
isNotNull(boolean condition, R column)
isNull("name")--->name is null
  • in notIn
in("age",{1,2,3})--->age in (1,2,3)
  • inSql notInSql
inSql(R column, String inValue)
inSql(boolean condition, R column, String inValue)
inSql("id", "select id from table where id < 3") ---> id in (select id from table where id < 3)
  • groupBy
groupBy(R... columns)
groupBy(boolean condition, R... columns)
groupBy("id", "name")--->group by id,name
  • orderByAsc orderByDesc orderBy
orderByDesc(R... columns)
orderByDesc(boolean condition, R... columns)
orderBy(boolean condition, boolean isAsc, R... columns)
  • having
having(String sqlHaving, Object... params)
having(boolean condition, String sqlHaving, Object... params)
 having("sum(age) > 10" )---> having sum(age) > 10
having("sum(age) > {0}", 11 )---> having sum(age) > 11
  • or 一般使用or条件构造器,需要配合使用and
eq("id",1).or().eq("name","老王")--->id = 1 or name = '老王'
  • and
and(i -> i.eq("name", "李白").ne("status", "活着"))--->and (name = '李白' and status <> '活着')
  • apply
apply(String applySql, Object... params)
apply(boolean condition, String applySql, Object... params)
当一般的条件无法满足时使用apply, 
譬如 
   select * from "user_info" where jsonb_exists_all(role_ids, array ['88']) ;
   apply("jsonb_exists_all(role_ids, array [{0}])", "\'88\'")
  • exists
过滤
exists(String existsSql)
exists(boolean condition, String existsSql)
例如:table1作为主表查询,这时还需要筛选出满足 table2.dept_code in (1,2,3) 这个条件的数据才返回出去
exists (select id from table2 where table2.id = table1.id and table2.dept_code in (1,2,3))

常见写法

//接口层:
public interface UserService extends IService<User> {
     void userUpdate(UserDTO userDTO);
     UserBO userGet(UserDTO userDTO);
}
//实现层
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
 // some code ...
}
//mapper层
public interface UserMapper extends BaseMapper<User> {
}
//然后实现层就可以畅快的写条件构造器了,还可以调用plus提供的 crud 的方法
 User user = lambdaQuery().eq(User::getId, userDTO.getId()).one();
 
 List<User> list = lambdaQuery().eq(User::getId, userDTO.getId()).list();
 
lambdaUpdate().eq(User::getId, userDTO.getId()).set(User::getName,userDTO.getName()).update();


save() / saveOrUpdate() / remove() / removeById() / update() / updateById()
// 然后,如果依赖其他表的查询,就引入表对应的service即可。

wrapper 和 xml 混用

// service层
 QueryWrapper<User> queryWrapper = new QueryWrapper<>();
 queryWrapper.lambda().eq(User::getId, userDTO.getId());
 User user = getBaseMapper().getUser(queryWrapper, userDTO);
 
 // mapper层
 User getUser(@Param(Constants.WRAPPER) QueryWrapper<User> wrapper, @Param("userDTO") UserDTO userDTO);

//xml层
<select id="getUser" resultType="com.my.demo.model.entity.User">
       select * from user_info where
       name = #{userDTO.name}
       and ${ew.sqlSegment}
 </select>

分页插件

@Configuration
public class MybatisPlusConfig {
    // 分页拦截器
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.H2));
        return interceptor;
    }
}
// 有设置拦截器后
Page<User> userPage = this.getBaseMapper().selectPage(new Page<>(2, 2), null);

自动填充功能

// 配置填充器
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    // 实体属性名称,非表中字段名称
    private static final String UPDATEDDATE = "updatedDate";

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("insertFill....");
        if (metaObject.hasGetter(UPDATEDDATE)) {
            Object updatedDate = getFieldValByName(UPDATEDDATE, metaObject);
            if (updatedDate == null) {
                setFieldValByName(UPDATEDDATE,  new Date(), metaObject);
            }
        }
    }

@Override
public void updateFill(MetaObject metaObject) {
    if (metaObject.hasGetter(UPDATEDDATE)) {
        // MetaObjectHandler提供的默认方法的策略均为:如果属性有值则不覆盖。所以确保更新时填充成功,需要先置空填充字段的值
        metaObject.setValue(UPDATEDDATE, null);
        this.fillStrategy(metaObject, UPDATEDDATE, new Date());
    }
  }
}
// 有了填充器后
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updatedDate;

批量插入的注意事项

发现MySQL JDBC驱动默认情况下会无视executeBatch()语句,会把批量语句拆散一条一条的发给数据库执行,批量插入实际上是单条插入,直接造成较低的性能。

解决方式
  • 方式1
数据库连接参数配置了  rewriteBatchedStatements=true 最终得以解决
  • 方式2
List<List<String>> newList = Lists.partition(oldList, 500);
newList.forEach(list -> {
   //调用自定义的分批插入sql
});

逻辑删除

  • 查找: 追加 where 条件过滤掉已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
  • 更新: 追加 where 条件防止更新到已删除数据,且使用 wrapper.entity 生成的 where 条件会忽略该字段
  • 删除: 转变为 更新
@TableLogic
private Integer deleted;

-   删除: `update user set deleted=1 where id = 1 and deleted=0`
-   查找: `select id,name,deleted from user where deleted=0`

批量插入或更新

// 注意更新是须按照表主键一致更新。所以users需要提前设置好存在项的主键。
saveOrUpdateBatch(users);

QueryWrapper 的粘性

wrapper有粘性,为了避免循环中wrapper残留上次的条件,需要循环开始时,初始化wrapper
  • 必要时 初始化wrapper 或者 clone() 或者 clear()
private List<User> queryByUserId(List<String> ids, QueryWrapper<User> wrapper){
    List<User> users = new ArrayList<>();
    Lists.partition(ids, 500).forEach(idPartition -> {
        // 为了避免循环中 wrapper 残留上次循环结束时的条件,需要循环开始时,初始化wrapper
        QueryWrapper<User> wrapperClone = wrapper.clone();
        users.addAll(list(wrapperClone.lambda()
                .select(User::getId, User::getName)
                .in(User::getId, idPartition)));
    });
    return users;
}