MyBatis-Plus使用详解(中)

简介: MyBatis-Plus使用详解

更新操作


 BaseMapper 中提供了2个更新方法


updateById(T entity)

   根据入参 entity 的 id (主键)进行更新,对于 entity 中非空的属性,会出现在UPDATE语句的SET后面,即 entity 中非空的属性,会被更新到数据库,示例如下

@RunWith(SpringRunner.class)  
  @SpringBootTest  
  public class UpdateTest {  
   @Autowired  
   private UserMapper userMapper;  
   @Test  
   public void testUpdate() {  
    User user = new User();  
    user.setId(2L);  
    user.setAge(18);  
    userMapper.updateById(user);  
   }  
  }


update(T entity, Wrapper wrapper)

根据实体 entity 和条件构造器 wrapper 进行更新,示例如下

@Test  
   public void testUpdate2() {  
    User user = new User();  
    user.setName("王三蛋");  
    LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();  
    wrapper.between(User::getAge, 26,31).likeRight(User::getName,"吴");  
    userMapper.update(user, wrapper);  
   }


额外演示一下,把实体对象传入 Wrapper ,即用实体对象构造WHERE条件的案例

@Test  
   public void testUpdate3() {  
    User whereUser = new User();  
    whereUser.setAge(40);  
    whereUser.setName("王");  
    LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>(whereUser);  
    User user = new User();  
    user.setEmail("share@baomidou.com");  
    user.setManagerId(10L);  
    userMapper.update(user, wrapper);  
   }


当我们需要更新部分字段时,不用new 对象的方式

LambdaUpdateWrapper<Users> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(Users::getUserId, 100)
        .set(EmptyUtil.isNotEmpty(name), Users::getUserName, name);
int result = baseMapper.update(null, wrapper);


注意到我们的User类中,对 name 属性和 age 属性进行了如下的设置

@Data  
public class User {  
 private Long id;  
 @TableField(condition = SqlCondition.LIKE)  
 private String name;  
 @TableField(condition = "%s &gt; #{%s}")  
 private Integer age;  
 private String email;  
 private Long managerId;  
 private LocalDateTime createTime;  
}


再额外演示一下,链式lambda条件构造器的使用

@Test  
 public void testUpdate5() {  
  LambdaUpdateChainWrapper<User> wrapper = new LambdaUpdateChainWrapper<>(userMapper);  
  wrapper.likeRight(User::getEmail, "share")  
    .like(User::getName, "飞飞")  
    .set(User::getEmail, "ff@baomidou.com")  
    .update();  
 }


反思


由于 BaseMapper 提供的2个更新方法都是传入一个实体对象去执行更新,这 在需要更新的列比较多时还好 ,若想要更新的只有那么一列,或者两列,则创建一个实体对象就显得有点麻烦。针对这种情况, UpdateWrapper 提供有 set 方法,可以手动拼接SQL中的SET语句,此时可以不必传入实体对象,示例如下

@Test  
 public void testUpdate4() {  
  LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();  
  wrapper.likeRight(User::getEmail, "share").set(User::getManagerId, 9L);  
  userMapper.update(null, wrapper);  
 }


删除操作


BaseMapper 一共提供了如下几个用于删除的方法


deleteById 根据主键id进行删除

deleteBatchIds 根据主键id进行批量删除

deleteByMap 根据Map进行删除(Map中的key为列名,value为值,根据列和值进行等值匹配)

delete(Wrapper wrapper) 根据条件构造器 Wrapper 进行删除

与前面查询和更新的操作大同小异


自定义SQL


 当mp提供的方法还不能满足需求时,则可以自定义SQL。


原生mybatis


示例如下


注解方式

package com.example.mp.mappers;  
import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
import com.example.mp.po.User;  
import org.apache.ibatis.annotations.Select;  
import java.util.List;  
/**  
 * @Author yogurtzzz  
 * @Date 2021/3/18 11:21  
 **/  
public interface UserMapper extends BaseMapper<User> {  
 @Select("select * from user")  
 List<User> selectRaw();  
}


xml方式

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
<mapper namespace="com.example.mp.mappers.UserMapper">  
 <select id="selectRaw" resultType="com.example.mp.po.User">  
        SELECT * FROM user  
    </select>  
</mapper>
package com.example.mp.mappers;  
import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
import com.example.mp.po.User;  
import org.apache.ibatis.annotations.Select;  
import java.util.List;  
public interface UserMapper extends BaseMapper<User> {  
 List<User> selectRaw();  
}


使用xml时, 若xml文件与mapper接口文件不在同一目录下 ,则需要在 application.yml 中配置mapper.xml的存放路径

mybatis-plus:  
  mapper-locations: /mappers/*


若有多个地方存放mapper,则用数组形式进行配置

mybatis-plus:  
  mapper-locations:   
  - /mappers/*  
  - /com/example/mp/*


测试代码如下

@Test  
 public void testCustomRawSql() {  
  List<User> users = userMapper.selectRaw();  
  users.forEach(System.out::println);  
 }
1


mybatis-plus


 也可以使用mp提供的Wrapper条件构造器,来自定义SQL


示例如下


注解方式

package com.example.mp.mappers;  
import com.baomidou.mybatisplus.core.conditions.Wrapper;  
import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
import com.baomidou.mybatisplus.core.toolkit.Constants;  
import com.example.mp.po.User;  
import org.apache.ibatis.annotations.Param;  
import org.apache.ibatis.annotations.Select;  
import java.util.List;  
public interface UserMapper extends BaseMapper<User> {  
    // SQL中不写WHERE关键字,且固定使用${ew.customSqlSegment}  
 @Select("select * from user ${ew.customSqlSegment}")  
 List<User> findAll(@Param(Constants.WRAPPER)Wrapper<User> wrapper);  
}


xml方式

package com.example.mp.mappers;  
import com.baomidou.mybatisplus.core.conditions.Wrapper;  
import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
import com.example.mp.po.User;  
import java.util.List;  
public interface UserMapper extends BaseMapper<User> {  
 List<User> findAll(Wrapper<User> wrapper);  
}
<!-- UserMapper.xml -->  
<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">  
<mapper namespace="com.example.mp.mappers.UserMapper">  
    <select id="findAll" resultType="com.example.mp.po.User">  
        SELECT * FROM user ${ew.customSqlSegment}  
    </select>  
</mapper>


分页查询


BaseMapper 中提供了2个方法进行分页查询,分别是 selectPage 和 selectMapsPage ,前者会将查询的结果封装成Java实体对象,后者会封装成 Map<String,Object> 。分页查询的使用示例如下


创建mp的分页拦截器,注册到Spring容器中

package com.example.mp.config;  
   import com.baomidou.mybatisplus.annotation.DbType;  
   import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;  
   import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;  
   import org.springframework.context.annotation.Bean;  
   import org.springframework.context.annotation.Configuration;  
   @Configuration 
   // @MapperScan("com.atguigu.mybatisplus.mapper") 有了配置类建议直接添加到配置类上 
   public class MybatisPlusConfig {  
       /** 新版mp **/  
    @Bean  
    public MybatisPlusInterceptor mybatisPlusInterceptor() {  
     MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();  
     interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));  
     return interceptor;  
    }  
       /** 旧版mp 用 PaginationInterceptor **/  
   }


执行分页查询

@Test  
    public void testPage() {  
     LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();  
     wrapper.ge(User::getAge, 28);  
           // 设置分页信息, 查第3页, 每页2条数据  
     Page<User> page = new Page<>(3, 2);  
           // 执行分页查询  
     Page<User> userPage = userMapper.selectPage(page, wrapper);  
     System.out.println("总记录数 = " + userPage.getTotal());  
     System.out.println("总页数 = " + userPage.getPages());  
     System.out.println("当前页码 = " + userPage.getCurrent());  
           // 获取分页查询结果  
     List<User> records = userPage.getRecords();  
     records.forEach(System.out::println);  
    }


分页查询总共发出了2次SQL,一次查总记录数,一次查具体数据。 若希望不查总记录数,仅查分页结果 。可以通过 Page 的重载构造函数,指定 isSearchCount 为 false 即可

public Page(long current, long size, boolean isSearchCount)


在实际开发中,可能遇到 多表联查 的场景,此时 BaseMapper 中提供的单表分页查询的方法无法满足需求,需要 自定义SQL ,示例如下(使用单表查询的SQL进行演示,实际进行多表联查时,修改SQL语句即可)


在mapper接口中定义一个函数,接收一个Page对象为参数,并编写自定义SQL

// 这里采用纯注解方式。当然,若SQL比较复杂,建议还是采用XML的方式  
@Select("SELECT * FROM user ${ew.customSqlSegment}")  
Page<User> selectUserPage(Page<User> page, @Param(Constants.WRAPPER) Wrapper<User> wrapper);


执行查询

@Test  
public void testPage2() {  
  LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();  
  wrapper.ge(User::getAge, 28).likeRight(User::getName, "王");  
  Page<User> page = new Page<>(3,2);  
  Page<User> userPage = userMapper.selectUserPage(page, wrapper);  
  System.out.println("总记录数 = " + userPage.getTotal());  
  System.out.println("总页数 = " + userPage.getPages());  
 userPage.getRecords().forEach(System.out::println);  
}


XML自定义分页


/*** 
根据年龄查询用户列表,分页显示 
* @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位 * @param age 年龄 
* @return */ 
IPage<User> selectPageVo(@Param("page") Page<User> page, @Param("age") Integer age);


UserMapper.xml中编写SQL

<!--SQL片段,记录基础字段--> 
<sql id="BaseColumns">id,username,age,email</sql> 
<!--IPage<User> selectPageVo(Page<User> page, Integer age);--> 
<select id="selectPageVo" resultType="User"> 
  SELECT <include refid="BaseColumns"></include> FROM t_user WHERE age > # {age} 
</select>


测试

@Test 
public void testSelectPageVo(){ 
  //设置分页参数 
  Page<User> page = new Page<>(1, 5); 
  userMapper.selectPageVo(page, 20); 
  //获取分页数据 
  List<User> list = page.getRecords(); 
  list.forEach(System.out::println); 
  System.out.println("当前页:"+page.getCurrent()); 
  System.out.println("每页显示的条数:"+page.getSize()); 
  System.out.println("总记录数:"+page.getTotal()); 
  System.out.println("总页数:"+page.getPages()); 
  System.out.println("是否有上一页:"+page.hasPrevious()); 
  System.out.println("是否有下一页:"+page.hasNext()); 
}


AR模式


ActiveRecord模式,通过操作实体对象,直接操作数据库表。与ORM有点类似。


示例如下


让实体类User继承自Model

package com.example.mp.po;  
   import com.baomidou.mybatisplus.annotation.SqlCondition;  
   import com.baomidou.mybatisplus.annotation.TableField;  
   import com.baomidou.mybatisplus.extension.activerecord.Model;  
   import lombok.Data;  
   import lombok.EqualsAndHashCode;  
   import java.time.LocalDateTime;  
   @EqualsAndHashCode(callSuper = false)  
   @Data  
   public class User extends Model<User> {  
    private Long id;  
    @TableField(condition = SqlCondition.LIKE)  
    private String name;  
    @TableField(condition = "%s &gt; #{%s}")  
    private Integer age;  
    private String email;  
    private Long managerId;  
    private LocalDateTime createTime;  
   }


直接调用实体对象上的方法

@Test  
    public void insertAr() {  
     User user = new User();  
     user.setId(15L);  
     user.setName("我是AR猪");  
     user.setAge(1);  
     user.setEmail("ar@baomidou.com");  
     user.setManagerId(1L);  
     boolean success = user.insert(); // 插入  
     System.out.println(success);  
    }


其他示例

// 查询  
 @Test  
 public void selectAr() {  
  User user = new User();  
        user.setId(15L);  
  User result = user.selectById();  
  System.out.println(result);  
 }  
 // 更新  
 @Test  
 public void updateAr() {  
  User user = new User();  
  user.setId(15L);  
  user.setName("王全蛋");  
  user.updateById();  
 }  
 //删除  
 @Test  
 public void deleteAr() {  
  User user = new User();  
  user.setId(15L);  
  user.deleteById();  
 }


主键策略


在定义实体类时,用 @TableId 指定主键,而其 type 属性,可以指定主键策略。


mp支持多种主键策略,默认的策略是基于雪花算法的自增id。全部主键策略定义在了枚举类 IdType 中, IdType 有如下的取值


AUTO

 数据库ID自增,依赖于数据库 。在插入操作生成SQL语句时,不会插入主键这一列


NONE

 未设置主键类型。若在代码中没有手动设置主键,则会 根据主键的全局策略 自动生成(默认的主键全局策略是基于雪花算法的自增ID)


INPUT

 需要手动设置主键,若不设置。插入操作生成SQL语句时,主键这一列的值会是null 。oracle的序列主键需要使用这种方式


ASSIGN_ID

 当没有手动设置主键,即实体类中的主键属性为空时,才会自动填充,使用雪花算法


ASSIGN_UUID

 当实体类的主键属性为空时,才会自动填充,使用UUID


…(还有几种是已过时的,就不再列举)

 可以针对每个实体类,使用@TableId 注解指定该实体类的主键策略,这可以理解为 局部策略。若希望对所有的实体类,都采用同一种主键策略,挨个在每个实体类上进行配置,则太麻烦了,此时可以用主键的 全局策略。只需要在application.yml 进行配置即可。比如,配置了全局采用自增主键策略

# application.yml  
mybatis-plus:  
  global-config:  
    db-config:  
      id-type: auto
相关文章
|
9天前
|
SQL 缓存 Java
Mybatis汇总
Mybatis汇总
14 1
|
SQL Java 数据库连接
MyBatis大全
MyBatis大全
52 0
|
Java 数据库连接 mybatis
|
SQL XML Java
|
SQL Java 数据库连接
|
3月前
|
SQL Java 数据库连接
Mybatis02(一)
Mybatis02(一)
28 0
|
4月前
|
SQL Java 数据库连接
Mybatis(四)
Mybatis(四)
35 0
|
XML Java 数据库连接
|
6月前
|
SQL 缓存 Java
mybatis使用总结
mybatis使用总结
|
6月前
|
缓存 Java 数据库连接
MyBatis
MyBatis