工具接口及类
BaseMapper
BaseMapper是mp提供给我们用于增强mapper接口方法:覆盖了大量的针对于单表的查询操作
public interface BaseMapper<T> extends Mapper<T> { //新增 int insert(T entity); int deleteById(Serializable id); //根据字段删除 int deleteByMap(@Param("cm") Map<String, Object> columnMap); int delete(@Param("ew") Wrapper<T> wrapper); //批量删除 int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList); //单条更新 int updateById(@Param("et") T entity); int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper); //单条查询根据id T selectById(Serializable id); //查询多条记录根据id List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList); //查询多条记录根据属性字段 List<T> selectByMap(@Param("cm") Map<String, Object> columnMap); T selectOne(@Param("ew") Wrapper<T> queryWrapper); Integer selectCount(@Param("ew") Wrapper<T> queryWrapper); List<T> selectList(@Param("ew") Wrapper<T> queryWrapper); List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper); List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper); <E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper); <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper); }
IService与ServiceImpl(接口定义与实现)
Service CRUD 接口—官方文档
下面是ServiceImpl给我们实现的抽象类方法
你能想到的单表查询几乎都有。
public interface IService<T> { int DEFAULT_BATCH_SIZE = 1000; default boolean save(T entity) { return SqlHelper.retBool(this.getBaseMapper().insert(entity)); } @Transactional( rollbackFor = {Exception.class} ) default boolean saveBatch(Collection<T> entityList) { return this.saveBatch(entityList, 1000); } boolean saveBatch(Collection<T> entityList, int batchSize); @Transactional( rollbackFor = {Exception.class} ) default boolean saveOrUpdateBatch(Collection<T> entityList) { return this.saveOrUpdateBatch(entityList, 1000); } boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize); default boolean removeById(Serializable id) { return SqlHelper.retBool(this.getBaseMapper().deleteById(id)); } default boolean removeByMap(Map<String, Object> columnMap) { Assert.notEmpty(columnMap, "error: columnMap must not be empty", new Object[0]); return SqlHelper.retBool(this.getBaseMapper().deleteByMap(columnMap)); } default boolean remove(Wrapper<T> queryWrapper) { return SqlHelper.retBool(this.getBaseMapper().delete(queryWrapper)); } default boolean removeByIds(Collection<? extends Serializable> idList) { return CollectionUtils.isEmpty(idList) ? false : SqlHelper.retBool(this.getBaseMapper().deleteBatchIds(idList)); } default boolean updateById(T entity) { return SqlHelper.retBool(this.getBaseMapper().updateById(entity)); } default boolean update(Wrapper<T> updateWrapper) { return this.update((Object)null, updateWrapper); } default boolean update(T entity, Wrapper<T> updateWrapper) { return SqlHelper.retBool(this.getBaseMapper().update(entity, updateWrapper)); } @Transactional( rollbackFor = {Exception.class} ) default boolean updateBatchById(Collection<T> entityList) { return this.updateBatchById(entityList, 1000); } boolean updateBatchById(Collection<T> entityList, int batchSize); boolean saveOrUpdate(T entity); default T getById(Serializable id) { return this.getBaseMapper().selectById(id); } default List<T> listByIds(Collection<? extends Serializable> idList) { return this.getBaseMapper().selectBatchIds(idList); } default List<T> listByMap(Map<String, Object> columnMap) { return this.getBaseMapper().selectByMap(columnMap); } default T getOne(Wrapper<T> queryWrapper) { return this.getOne(queryWrapper, true); } T getOne(Wrapper<T> queryWrapper, boolean throwEx); Map<String, Object> getMap(Wrapper<T> queryWrapper); <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper); default int count() { return this.count(Wrappers.emptyWrapper()); } default int count(Wrapper<T> queryWrapper) { return SqlHelper.retCount(this.getBaseMapper().selectCount(queryWrapper)); } default List<T> list(Wrapper<T> queryWrapper) { return this.getBaseMapper().selectList(queryWrapper); } default List<T> list() { return this.list(Wrappers.emptyWrapper()); } default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) { return this.getBaseMapper().selectPage(page, queryWrapper); } default <E extends IPage<T>> E page(E page) { return this.page(page, Wrappers.emptyWrapper()); } default List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper) { return this.getBaseMapper().selectMaps(queryWrapper); } default List<Map<String, Object>> listMaps() { return this.listMaps(Wrappers.emptyWrapper()); } default List<Object> listObjs() { return this.listObjs(Function.identity()); } default <V> List<V> listObjs(Function<? super Object, V> mapper) { return this.listObjs(Wrappers.emptyWrapper(), mapper); } default List<Object> listObjs(Wrapper<T> queryWrapper) { return this.listObjs(queryWrapper, Function.identity()); } default <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) { return (List)this.getBaseMapper().selectObjs(queryWrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList()); } default <E extends IPage<Map<String, Object>>> E pageMaps(E page, Wrapper<T> queryWrapper) { return this.getBaseMapper().selectMapsPage(page, queryWrapper); } default <E extends IPage<Map<String, Object>>> E pageMaps(E page) { return this.pageMaps(page, Wrappers.emptyWrapper()); } BaseMapper<T> getBaseMapper(); default QueryChainWrapper<T> query() { return ChainWrappers.queryChain(this.getBaseMapper()); } default LambdaQueryChainWrapper<T> lambdaQuery() { return ChainWrappers.lambdaQueryChain(this.getBaseMapper()); } default UpdateChainWrapper<T> update() { return ChainWrappers.updateChain(this.getBaseMapper()); } default LambdaUpdateChainWrapper<T> lambdaUpdate() { return ChainWrappers.lambdaUpdateChain(this.getBaseMapper()); } default boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) { return this.update(entity, updateWrapper) || this.saveOrUpdate(entity); } }
IPage与Page
在Page对象中,包含了关于分页的相关信息,其中包含对应的查询结果集records以及对于分页的相关属性:
可以说满足了基本的需求了。
一、springboot集成Mybatis plus实现CRUD
Mapper CRUD 接口
前提准备
引入依赖:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.4.0</version> </dependency>
yml配置项:
spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://127.0.0.1:3306/mybatisplusExer?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai username: root password: 123456 # 启动时需要初始化的建表sql语句 schema: classpath:sql/schema.sql # 执行好就修改为never initialization-mode: always # always为始终执行初始化,embedded只初始化内存数据库(默认值),如h2等,never为不执行初始化 #控制台打印sql(默认不会有打印sql语句) mybatis-plus: configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 默认开启时会进行pojo字段映射如:lastName => last_name mapUnderscoreToCamelCase: false # 取消自动驼峰命名规则映射(默认是开启的),这里仅仅是用于测试
sql:
DROP TABLE IF EXISTS user; CREATE TABLE user ( id BIGINT(20) NOT NULL COMMENT '主键ID', name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名', age INT(11) NULL DEFAULT NULL COMMENT '年龄', email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (id) ); DELETE FROM user; INSERT INTO user (id, name, age, email) VALUES (1, 'Jone', 18, 'test1@baomidou.com'), (2, 'Jack', 20, 'test2@baomidou.com'), (3, 'Tom', 28, 'test3@baomidou.com'), (4, 'Sandy', 21, 'test4@baomidou.com'), (5, 'Billie', 24, 'test5@baomidou.com');
pojo:
/** * @ClassName User * @Author ChangLu * @Date 2021/8/16 22:11 * @Description TODO */ @Data @AllArgsConstructor @NoArgsConstructor @TableName("user") public class User { //mp会默认将pojo类名当做表名(首字母小写),若是类名与表名不一致可使用注解 //默认会识别id作为主键,这里是对id自动生成进行设置: IdType.AUTO为自动,还有其他很多策略 @TableId(value = "id",type = IdType.AUTO) private Long id; // 设置表字段别名(当表属性不一致时即可进行指定) 默认是自动开启别名转换的(这里只是为了熟悉使用) @TableField("last_name") private String lastName; //建议pojo字段名与表名像lastName=>last_name要对应,否则使用(如name=>last_name)包装器select()查询返回赋值会无效。 private Integer age; private String email; }
默认mybatis-plus中会将你的pojo类自动进行转换,如字段lastName,之后来查询数据库会自动转为last_name来进行查询。
若是你插入一条数据的id(mp默认会认为你的id为主键id)没有设置,mp会自动帮你生成一个全局唯一的id(字符串)!并且你插入后该对象就会得到对应自动生成的id值。
UserMapper:
/** * @ClassName UserMapper * @Author ChangLu * @Date 2021/8/16 22:11 * @Description TODO */ public interface UserMapper extends BaseMapper<User> { }
springboot启动类上标注自动扫描mapper包:
@MapperScan("com.changlu.mybatisplusexer.mapper")
CRUD操作
这里暂时没有Wrapper相关的操作,只是进行了对selectByMap及deleteByMap以及其他普通的测试用例,其是根据传入map的属性来进行字段查询的,可设置多个字段,这里仅有一个字段。
特别说明的是:mp中对于查询的键值对,传入与数据库中不同类型的也可以兼容,真的是强大呀,例如数据库中的age字段为int类型,你设置map属性字段中传入put(“age”,“20”)也完全是ok的。
import com.changlu.mybatisplusexer.mapper.UserMapper; import com.changlu.mybatisplusexer.pojo.User; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import javax.annotation.Resource; import java.util.HashMap; import java.util.List; import java.util.Map; @SpringBootTest class MybatisplusexerApplicationTests { @Resource private UserMapper userMapper; @Test void queryAll() { System.out.println(("----- selectAll method test ------")); List<User> userList = userMapper.selectList(null); userList.forEach(System.out::println); } @Test void query(){ System.out.println(userMapper.selectById(1)); } @Test void query2(){ //selectByMap测试:根据条件筛选 Map<String, Object> columnMap = new HashMap<>(1); columnMap.put("age",20);//条件为age=20 List<User> users = userMapper.selectByMap(columnMap); System.out.println(users); } @Test void insert(){ User user = new User(null, "changlu", 18, "939974883@qq.com"); //执行保存sql前会先生成id到user中 int result = userMapper.insert(user); System.out.println("user=>"+user+",result=>"+result); } @Test public void delete(){ int result = userMapper.deleteById(1); System.out.println(result); } @Test public void delete2(){ //deleteByMap:根据指定字段删除 Map<String, Object> columnMap = new HashMap<>(1); columnMap.put("age","20");//条件为age=20, int result = userMapper.deleteByMap(columnMap); System.out.println(result); } @Test public void update(){ User user = new User((long)2, "changlu", 20, "939974883@qq.com"); int result = userMapper.updateById(user); System.out.println(result); } }
二、通用IService使用
在一中介绍的是对mapper接口进行的增强,在mp中还可以对service进行增强,也就是说一些常见的普通的service方法已经帮你进行封账好了,能够更快的进行一系列的操作。
Service CRUD 接口
说明:
//更新操作 //①若是更新某个model实体类,使用updateWrapper一定要进行指定id,否则就会更新整张表 //②若是批量对多个model的某个字段进行更新,调用方法 updateBatchById() save():执行完后返回的是一个布尔值,表示是否插入成功,并且若是你没有填写id,会自动先生成主键到指定实体类中,再进行执行sql保存操作
准备
在示例一的基础上,我们来添加service接口以及service实现类,对应的接口需要实现IService,实现类需要继承一个抽象Service,同样也是mp给我们封装号的对应增强方法接口的实现体。
UserService:service接口定义 public interface UserService extends IService<User> { }
UserServiceImpl:实现类定义
@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { }
测试
在mp给我们提供的service方法中,对于增删改操作也都包含了对应的事务处理,总体来说还是特别方便的!
@SpringBootTest class MybatisplusexerApplicationTests { @Autowired private UserService userService; //saveOrUpdate:先根据id查,若是有则进行更新,没有进行插入 @Test public void saveOrUpdate(){ //测试插入 User user = new User((long)1,"changlu",18,"9999"); boolean b = userService.saveOrUpdate(user);//true:成功;false:失败 System.out.println(b); } //saveOrUpdate:先根据id查,若是有则进行更新,没有进行插入 @Test public void saveOrUpdate2(){ //测试更新 User user = new User((long)1,"liner",22,"368"); boolean b = userService.saveOrUpdate(user); System.out.println(b); } //listByIds:根据多个id来进行批量查询。(底层采用in方式来进行查询) @Test public void query(){ List<User> users = userService.listByIds(Arrays.asList(1, 3, 5)); System.out.println(users); } }
三、分页查询
3.1中的分页方法只对调用IService里的分页方法才有效!
我们编写请求实现类时,若是进行分页可以直接继承下面的实体类就是表示分页和:
@Data public class BasePage implements Serializable { private static final long serialVersionUID = 8491572200460447988L; protected long size = 10; protected long current = 1; }
3.1、借助mp的分页插件
准备 分页插件
在mp的Iservice中提高了四个分页方法:
在BaseMapper中提供了两个分页方法:
<E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper); <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);
注意:要想使用mp中的分页功能,就必须需要安装分页插件,否则不会生效!
step1:编写一个mp的配置类,在该配置类中添加分页插件
@Configuration @MapperScan("com.changlu.mybatisplusexer.mapper") //使用了分配类,即可将启动器的自动打包放置在这里 public class MybatisPlusConfig { // 最新版 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //设置方言为MySQL return interceptor; } }
实操
数据库中有四条记录,我们来对其进行测试:
@SpringBootTest class MybatisplusexerApplicationTests { @Autowired private UserService userService; @Test public void saveOrUpdate() throws JsonProcessingException { //当前页与每页数量 IPage<User> page = new Page<>(1,2); //分页查询 IPage<User> pageResult = userService.page(page); //对象转JSON字符串 ObjectMapper om = new ObjectMapper(); String json = om.writeValueAsString(pageResult); System.out.println(json); } }
可以看到查询了两条sql,一个是查询数量,另一个是进行limit查询操作!
为了方便查看Page的相关属性,我们将其转为JSON字符串并且借助工具网站来美化显示其中的内容:
records:分页查询的记录结果。
total:总记录数。
size:每页显示条数,根据初始定义的Page决定。
current:当前页。
pages:总共2页。
这样的结果内容,对于前台简直不要太舒服了!!!
3.2、XML 自定义分页
前提准备 XML 自定义分页
对于我们自定义sql语句,也就是书写xml也可以实现自定义分页!
在mp中我们无需指定mapper位置,因为对应springboot启动器已经为我们设置默认mapper读取位置:
若是我们想要修改mapper映射配置的位置,直接在yml配置中进行配置即可:
mybatis-plus: mapper-locations: classpath*:/mapperxxx/**/*.xml
<br/0
实操
直接在resources下的mapper文件夹中编写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.changlu.mybatisplusexer.mapper.UserMapper"> <select id="queryByAge" resultType="com.changlu.mybatisplusexer.pojo.User"> select * from Blog where age = #{age} </select> </mapper>
若是想要对xml的查询结果进行分页,那么只需要在mapper中接口返回值以及属性中设置即可!
public interface UserMapper extends BaseMapper<User> { //此时就会根据我们传入的page来进行对结果进行分页返回 IPage<User> queryByAge(Page<?> page,Integer age); }
测试一下:
@Resource private UserMapper userMapper; @Test public void saveOrUpdate() throws JsonProcessingException { //当前页与每页数量 Page<User> page = new Page<>(1,1); IPage<User> userIPage = userMapper.queryByAge(page, 22); String json = new ObjectMapper().writeValueAsString(userIPage); System.out.println(json); }
根据设置的Page信息,查询出来的分页内容与实际情况符合,分页完成!
3.3、第三方插件:PageHelper
原理分析
底层使用了一个PageInterceptor分页拦截器
PageHelper文档
提前准备
引入依赖:
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.2.3</version> </dependency>
注意一下:若是你同时引入mybatis-plus以及pagehelper启动器依赖,运行时就会报异常如下。
如何解决呢?将对应pagehelper启动器中的指定mybatis依赖移除即可。
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper-spring-boot-starter</artifactId> <version>1.4.1</version> <exclusions> <exclusion> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> </exclusion> <exclusion> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
实操
主要就是test方法中的第1以及第3行,即可实现分页效果!
@GetMapping("/") public ResultBody test(@RequestParam("pagenum")Integer pageNum,@RequestParam("pagesize")Integer pageSize){ PageHelper.startPage(pageNum,pageSize);//设置起始页以及每页数量 List<User> users = userMapper.selectList(null);//此时底层对于select就是进行分页来得到的结果集 PageInfo<User> userPageInfo = new PageInfo<>(users);//封装PageInfo对象 return ResultBody.success(userPageInfo); }
最终拿到的返回值对象:其中包含了很多的关于分页的属性,前端判断就更加容易了