一.普通查询
1.单个id查询
@Test
public void testSelectById() {
User user = userMapper.selectById(1L);
System.out.println(user);
}
2.批量id查询
@Test
public void testSelectByBatchId() {
List<User> users = userMapper.selectBatchIds(Arrays.asList(1, 2, 3));
users.forEach(System.out::println);
}
3.按条件查询之一使用map操作
@Test
public void testSelectByMap() {
HashMap<String, Object> map = new HashMap<>();
// 自定义要查询
map.put("name", "test");
map.put("age", 18);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
二.分页查询
一般来说有三种分页,我们先说下MP自带的分页再说一下pagehelper
- 1、原始的 limit 进行分页(较少使用)
- 2、pageHelper 等第三方插件
- 3、MP 其实也内置了分页插件 官方文档地址
1.分页测试
首先要引入mabtisplus的分页拦截器,官方已经提供MybatisPlusConfig
/**
* 用于配置mybatisplus相关信息
* @author zyh
*/
@Configuration
@MapperScan({"com.future.test.dao","com.future.test.easycodetest.dao","com.future.test.easycodetest.mapper"})
public class MybatisPlusConfig {
/**
* 默认分页插件相关配置
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//配置数据库类型
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
@Test
public void selectPageTest(){
//设置分页对象,查第一页,每页只要3条
Page<User> page = new Page<>(1, 3);
Page<User> userPage = userMapper.selectPage(page, null);
List<User> users = userPage.getRecords();
users.forEach(System.out::println);
}
三,高级构造器查询Wrapper官方文档地址
官方文档里介绍queryWrapper的全部的条件,可以自行去看,这里给出部分样例
@Test
void contextLoads() {
// 查询name不为空的用户,并且邮箱不为空的用户,年龄大于等于12
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.isNotNull("name")
.isNotNull("email")
.ge("age", 12);
userMapper.selectList(wrapper).forEach(System.out::println); // 和我们刚才学的map对比一下
}
@Test
void test2() {
// 查询名字zyh
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("name", "zyh");
User user = userMapper.selectOne(wrapper); // 查询一个数据,出现多个结果使用List或者 Map
System.out.println(user);
}
@Test
void test3() {
// 查询年龄在 20 ~ 30 岁之间的用户
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.between("age", 20, 30); // 区间
Integer count = userMapper.selectCount(wrapper);// 查询结果数
System.out.println(count);
}
@Test
void test4() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper
.notLike("name", "e")
.likeRight("email", "t");
List<Map<String, Object>> maps = userMapper.selectMaps(wrapper);
maps.forEach(System.out::println);
}
// 模糊查询
@Test
void test5() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
// id 在子查询中查出来
wrapper.inSql("id", "select id from user where id<3");
List<Object> objects = userMapper.selectObjs(wrapper);
objects.forEach(System.out::println);
}
//测试六
@Test
void test6() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 通过id进行排序
wrapper.orderByAsc("id");
List<User> users = userMapper.selectList(wrapper);
users.forEach(System.out::println);
}
mybatisPlus使用分页插件pagehelper
我们上面看到了,MP自带的插件一般用于我们使用其自带的sql操作api,比如查询的时候加一个ipage,这一般不适用于我们自己写的sql操作,这里介绍一款我们mybatis和mybatisplus都可以使用的分页插件pagehelper
mybatis引用
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
springboot項目引用
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.13</version>
</dependency>
pagehelper的相關配置
pagehelper.helper-dialect=mysql
pagehelper.reasonable=false
pagehelper.support-methods-arguments=true
pagehelper.offset-as-page-num=true
pagehelper.row-bounds-with-count=true
pagehelper.propertyName=propertyValue
关于各个属性的相关含义可以看其官方文档mp参数配置
使用方法
@Test
public void test() {
PageHelper.startPage(1, 2);
List<User> users = userService.queryAll(new User());
PageInfo<User> pageInfo = new PageInfo<>(users);
PageHelper.clearPage();
List<User> list = pageInfo.getList();
list.forEach(System.out::println);
}
非常简单,我们仅需要在调用查询的方法前后加上分页语句即可,其中 PageHelper.startPage(1, 2);表明开始使用分页查询,查第一页,两条数据,但是这里要注意我们中间必须夹紧,也就说两个pagehelper语句直接不能有空行,且中间的语句是直接操作数据库的,不含有其他业务操作,所以我们一般会卸载dao层或者service层。
注意,这里用了pagehelper.clearPage()是干啥的?
PageHelper 是较为常用的分页插件,通过实现 Mybatis 的 Interceptor 接口完成对 query sql 的动态分页,其中分页参数由 ThreadLocal 进行保存。
简单的 分页执行过程:
- 1.设置 page 参数
- 2.执行 query 方法
- 3.Interceptor 接口 中校验 ThreadLocal 中是否存在有设置的 page 参数
- 4.存在 page 参数,重新生成 count sql 和 page sql,并执行查询。不存在 page 参数,直接返回 查询结果
- 5.执行
LOCAL_PAGE.remove()
清除 page 参数 threadlocal
这里就存在一个问题了
观察上述的执行过程,可以发现,如果在第 1 步和第 2 步 之间发生异常,那么 LOCAL_PAGE 中当前线程对应的 page 参数并不会 remove。
在不使用线程池的情况下,当前线程在执行完毕后会被销毁,这时 当前线程 中的 threadLocals 参数 将会被情况,也就清空 了 LOCAL_PAGE
中 当前线程的 page 参数。
但是如果使用了线程池,当前线程执行完毕,并不会被销毁,而是会将当前线程再次存放到池中,标记为空闲状态,以便后续使用。在后续使用这个线程的时候,由于 线程 的 threadLocals 依旧存在有值,尽管我们在第 1 步时未设置 page 参数,第 3 步 的也能获取到page参数,从而生成 count sql 和 page sql,从而影响我们的正常查询我。
另外SpringBoot 项目中会使用内置的 Tomcat 作为服务器,而Tomcat会默认使用线程来处理请求,从而便引发了上述问题
解决方案,在每次使用完分页语句后执行pagehelper.clearpage()如上demo所示,但是这样比较麻烦
我们可以实现 HandlerInterceptor , WebRequestInterceptor 对 request 请求的拦截器,q清理我们localthread里的page
@Component
public class UrlInterceptor implements HandlerInterceptor, WebMvcConfigurer {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
PageHelper.clearPage();
return true;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(this).addPathPatterns("/**");
}
}