更新操作
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 > #{%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 > #{%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