springboot集成mybatis详细教程同时实现自定义拦截器分页功能

import java.util.List;

/**

  • Created by Feng

*/

public interface UserDao {

List getUser(@Param(“user”) User user);

List getUser(@Param(“pager”) Pager pager, @Param(“user”) User user);

User loadUserByUserName(String userName);

int addUser(User user);

int addUsers(List userList);

}

package database.support;

import javax.validation.constraints.NotNull;

/**

  • Created by Feng

*/

public class Pager {

@NotNull

private int pageNo;

private int pageSize = 10;

private int total;

private String sortBy;

private String rank = “DESC”;

public int getPageNo() {

return pageNo;

}

public int getPageSize() {

return pageSize;

}

public int getTotal() {

return total;

}

public String getSortBy() {

return sortBy;

}

public String getRank() {

return rank;

}

public Pager setPageNo(int pageNo) throws IllegalAccessException {

if(pageNo <= 0){

throw new IllegalAccessException(“分页页码必须大于0”);

}

this.pageNo = pageNo;

return this;

}

public Pager setPageSize(int pageSize) throws IllegalAccessException {

if(pageSize <= 0){

throw new IllegalAccessException(“单页数据量必须大于0”);

}

this.pageSize = pageSize;

return this;

}

public Pager setTotal(int total) {

this.total = total;

return this;

}

public Pager setSortBy(String sortBy) {

this.sortBy = sortBy;

return this;

}

public Pager setRank(String rank) {

this.rank = rank;

return this;

}

}

我们会用到UserDao中重载过的两个getUser方法,其中一个只接受用于查询的User参数,另一个额外接受一个Pager对象用于分页。

然后在spring配置文件中指定的包内编写用于映射UserDao的mybatis映射配置文件UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>

ID,USERNAME,U_NAME,PHONE,EMAIL,CITY_ID,CITY_NAME,PROVIENCE_ID,PROVIENCE_NAME,ADDRESS,PHOTO,BIRTHDAY,ORGANIZATION_ID,ROLE_ID

,PASSWORD

SELECT

FROM USER

WHERE USERNAME = #{userName}

SELECT

FROM USER

ID = #{id}

AND USERNAME = #{userName}

AND U_NAME LIKE #{uName}

AND PHONE = #{phone}

AND EMAIL = #{email}

AND CITY_ID = #{cityID}

AND PROVIENCE_ID = #{provienceID}

AND ADDRESS LIKE #{address}

AND ORGANIZATION_ID = #{organizationID}

AND ROLE_ID = #{roleID}

insert into USER (ID,USERNAME,PASSWORD,U_NAME,PHONE,EMAIL,CITY_ID,CITY_NAME,PROVIENCE_ID,PROVIENCE_NAME,ADDRESS,PHOTO,BIRTHDAY,ORGANIZATION_ID,ROLE_ID)

values (#{id},

#{userName},

#{passWord},

#{uName},

#{phone},

#{email},

#{cityID},

#{cityName},

#{provienceID},

#{provienceName},

#{address},

#{photo},

#{birthday},

#{organizationID},

#{roleID})

这里需要说明的是我们在UserDao中有两个getUser方法,但在配置文件中只映射了一个select标签,这是因为mybatis和dao层之间的映射只与方法名称有关,而与方法参数无关。所以在sql语句不变,只是参数增加的情况下我们可以用一个select标签映射多个方法。唯一要注意的是传入多个对象参数时要在dao层中使@Param注解为参数指定名称。

然后添加一个mybatis配置类MybatisConfig

package com.feng.config;

import database.mybatis.plug.PageInterceptor;

import org.mybatis.spring.annotation.MapperScan;

import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

/**

  • Created by Feng

*/

@MapperScan(“com.feng.dao”)

@Configuration

public class MybatisConfig {

}

使用MapperScan用于指定dao层位置。

这个时候我们已经可以通过注入的方式使用UserDao来查询用户数据了,但为了规范,我们在dao层之上增加一个service层,编写UserService接口和实现类

package com.feng.service;

import com.feng.entity.User;

import database.support.Pager;

import java.util.List;

/**

  • Created by Feng

*/

public interface UserService {

public User getUserByID(int id);

public int removeUserByID(int id);

public User loadUserByUserName(String userName);

public List getUser(Pager pager,User user);

public List getUser(User user);

}

package com.feng.service;

import com.feng.dao.UserDao;

import com.feng.entity.User;

import database.support.Pager;

import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import java.util.List;

/**

  • Created by Feng

*/

@Service

public class UserServiceImpl implements UserService {

@Resource

private UserDao userDao;

@Override

public User getUserByID(int id) {

User queryUser = new User();

queryUser.setId(id);

List users = userDao.getUser(queryUser);

if(users != null && users.size() > 0){

return users.get(0);

}

return null;

}

@Override

public int removeUserByID(int id) {

return 0;

}

@Override

public User loadUserByUserName(String userName) {

return userDao.loadUserByUserName(userName);

}

@Override

public List getUser(Pager pager, User user) {

return userDao.getUser(pager,user);

}

@Override

public List getUser(User user) {

return userDao.getUser(user);

}

}

创建启动类和junit测试类

package com.feng;

import org.springframework.boot.SpringApplication;

@SpringBootApplication

public class SpringBootWebApplication {

public static void main(String[] args){

SpringApplication.run(SpringBootWebApplication.class, args);

}

}

import com.feng.SpringBootWebApplication;

import com.feng.entity.User;

import com.feng.service.UserService;

import database.support.Pager;

import org.junit.Test;

import org.junit.runner.RunWith;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

/**

  • Created by Feng

*/

@RunWith(SpringJUnit4ClassRunner.class)

@SpringBootTest(classes = SpringBootWebApplication.class)

public class JDBCTest {

@Autowired

private UserService userService;

@Test

public void testQuery() throws IllegalAccessException {

List users = userService.getUser(new User());

System.out.println(users);

}

}

测试普通查询成功

在这里插入图片描述

4.使用自定义拦截器分页


mybatis分页的三种方法就不废话了,基本上很少有用内存或者sql语句来分页的,在实际生产中,大多数还是使用拦截器分页。我在网上找相关中文资料,百分之九十都是直接用pageHelper插件来做。很费解的是一个中文文档这么全面的,由国人编写的插件为啥还要写一大堆教程,直接看文档不好吗。。。

言归正传,这里我们自己编写拦截器(插件)用于自动拦截包含Pager参数的查询方法

先看官方文档里关于拦截器的描述

插件(plugins)

MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)

ParameterHandler (getParameterObject, setParameters)

ResultSetHandler (handleResultSets, handleOutputParameters)

StatementHandler (prepare, parameterize, batch, update, query)

这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看 MyBatis 发行包中的源代码。 如果你想做的不仅仅是监控方法的调用,那么你最好相当了解要重写的方法的行为。 因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏 MyBatis 的核心模块。 这些都是更低层的类和方法,所以使用插件的时候要特别当心。

通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定想要拦截的方法签名即可。

至于这几个对象和其内部方法的作用,大家可以在网上找相关资料,我们要拦截的是StatementHandler.prepare方法,这个方法用于生成statement,参数包含一个Connection对象,我们可以借助它在查询发起前先先执行计数操作并替换原有的sql语句

先编写一个工具类用于构建分页和计数sql语句

package database.support;

/**

  • Created by Feng

*/

public interface JDBCSupport {

public String generatePageSql(Pager pager,String sql);

public String generateCountSql(String sql);

}

package database.support;

import org.springframework.util.StringUtils;

/**

  • Created by Feng

*/

public class MySqlSupport implements JDBCSupport {

/**

  • 拼接分页sql

  • @param pager

  • @param sql

  • @return

*/

@Override

public String generatePageSql(Pager pager, String sql) {

long startIndex = (pager.getPageNo() - 1) * pager.getPageSize()+1;

long endIndex = startIndex + pager.getPageSize()-1;

StringBuilder sqlBuilder = new StringBuilder(sql);

if(!StringUtils.isEmpty(pager.getSortBy())){

sqlBuilder.append(" ORDER BY ");

sqlBuilder.append(pager.getSortBy());

sqlBuilder.append(pager.getRank());

}

sqlBuilder.append(" limit ");

sqlBuilder.append(startIndex);

sqlBuilder.append(“,”);

sqlBuilder.append(endIndex);

return sqlBuilder.toString().toUpperCase();

}

/**

  • 拼接计数sql

  • @param sql

  • @return

*/

@Override

public String generateCountSql(String sql) {

return (“SELECT COUNT(*) FROM (” + sql + “) A”).toUpperCase();

}

}

接下来就是最重要的拦截器了

package database.mybatis.plug;

import database.support.JDBCSupport;

import database.support.MySqlSupport;

import database.support.Pager;

import org.apache.ibatis.executor.resultset.ResultSetHandler;

import org.apache.ibatis.executor.statement.StatementHandler;

import org.apache.ibatis.plugin.*;

import org.apache.ibatis.reflection.MetaObject;

import org.apache.ibatis.reflection.SystemMetaObject;

import java.sql.Connection;

import java.sql.PreparedStatement;

import java.sql.ResultSet;

import java.sql.Statement;

import java.util.Map;

import java.util.Properties;

/**

  • Created by Feng

*/

@Intercepts({

@Signature(type = StatementHandler.class, method = “prepare”, args = {Connection.class,Integer.class}),

})

public class PageInterceptor implements Interceptor {

private JDBCSupport jdbcSupport = new MySqlSupport();

@Override

public Object intercept(Invocation invocation) throws Throwable {

StatementHandler statementHandler = (StatementHandler)invocation.getTarget();

Map<String,Object> paramsMap = (Map<String, Object>) statementHandler.getParameterHandler().getParameterObject();

String sql = statementHandler.getBoundSql().getSql();

for(String key:paramsMap.keySet()){

if(paramsMap.get(key) instanceof Pager){

Pager pager = (Pager) paramsMap.get(key);

Connection connection = (Connection)invocation.getArgs()[0];

PreparedStatement countStatement = connection.prepareStatement(jdbcSupport.generateCountSql(sql));

ResultSet rs = countStatement.executeQuery();

if(rs.next()) {

pager.setTotal(rs.getInt(1));

}

MetaObject metaObject = SystemMetaObject.forObject(statementHandler);

String pageSql = jdbcSupport.generatePageSql(pager,sql);

metaObject.setValue(“delegate.boundSql.sql”, pageSql);

break;

}

}

return invocation.proceed();

}

@Override

public Object plugin(Object o) {

if (o instanceof StatementHandler) {

return Plugin.wrap(o, this);

} else {

return o;

}

}

@Override

public void setProperties(Properties properties) {

}

}

Intercepts注解用于标识该拦截器作用的目标对象和目标方法。在intercept方法中我们先获取查询方法的所有参数并进行遍历,当发现包含Pager参数后我们先构建计数语句执行查询并将结果写入Pager中,然后借助mybatis自带的反射工具将sql替换成构建好的分页查询语句。

在之前的mybatis配置类中添加这个拦截器

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

其他的内容都可以按照路线图里面整理出来的知识点逐一去熟悉,学习,消化,不建议你去看书学习,最好是多看一些视频,把不懂地方反复看,学习了一节视频内容第二天一定要去复习,并总结成思维导图,形成树状知识网络结构,方便日后复习。

这里还有一份很不错的《Java基础核心总结笔记》,特意跟大家分享出来

目录:

部分内容截图:


《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!
在。**

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-7DwodkeQ-1712080317488)]

[外链图片转存中…(img-B3NAaz0y-1712080317489)]

[外链图片转存中…(img-kzKWJjhB-1712080317489)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

其他的内容都可以按照路线图里面整理出来的知识点逐一去熟悉,学习,消化,不建议你去看书学习,最好是多看一些视频,把不懂地方反复看,学习了一节视频内容第二天一定要去复习,并总结成思维导图,形成树状知识网络结构,方便日后复习。

这里还有一份很不错的《Java基础核心总结笔记》,特意跟大家分享出来

目录:

[外链图片转存中…(img-ZxfSRxC0-1712080317490)]

部分内容截图:

[外链图片转存中…(img-5g2g9jOi-1712080317490)]

[外链图片转存中…(img-sX9aklRi-1712080317491)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》点击传送门即可获取!

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值