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
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);
}
}
测试普通查询成功
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开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。
下面的面试题答案都整理成文档笔记。也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)
最新整理电子书
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!
之前的mybatis配置类中添加这个拦截器
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。[外链图片转存中…(img-u0lEbiWJ-1712206846279)]
[外链图片转存中…(img-7zpMNIYf-1712206846279)]
[外链图片转存中…(img-RwfF5bq3-1712206846279)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)
最后
针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。
下面的面试题答案都整理成文档笔记。也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)
[外链图片转存中…(img-QFsW1nNO-1712206846280)]
最新整理电子书
[外链图片转存中…(img-MbDpycmP-1712206846280)]
《一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码》,点击传送门即可获取!