柚子快報邀請碼778899分享:mybatis實(shí)踐篇(一)
柚子快報邀請碼778899分享:mybatis實(shí)踐篇(一)
日志(logImpl)
StdOutImpl
Slf4jImpl
引入maven文件
編寫配置文件
org.slf4j.simpleLogger.defaultLogLevel=debug
org.slf4j.simpleLogger.showDateTime=true
org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss
org.slf4j.simpleLogger.levelInBrackets=true
org.slf4j.simpleLogger.logFile=System.out
執(zhí)行器
默認(rèn)執(zhí)行器(SimpleExecutor)
try(SqlSession sqlSession = sqlSessionFactory.openSession()) {
// 使用SqlSession獲取映射器實(shí)例
FullCityMapper mapper = sqlSession.getMapper(FullCityMapper.class);
// 使用映射器執(zhí)行操作
FullCity fullCity = mapper.selectByName("廣東省");
System.out.println("城市的名稱:"+fullCity.getName());
}
重用執(zhí)行器(ReuseExecutor)
try(SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.REUSE)) {
// 使用SqlSession獲取映射器實(shí)例
FullCityMapper mapper = sqlSession.getMapper(FullCityMapper.class);
// 使用映射器執(zhí)行操作
FullCity fullCity = mapper.selectByName("廣東省");
System.out.println("城市的名稱:"+fullCity.getName());
}
批量執(zhí)行器(BatchExecutor)
try(SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
// 使用SqlSession獲取映射器實(shí)例
FullCityMapper mapper = sqlSession.getMapper(FullCityMapper.class);
// 使用映射器執(zhí)行操作
FullCity fullCity = mapper.selectByName("廣東省");
System.out.println("城市的名稱:"+fullCity.getName());
}
起別名
配置文件
mapper文件
select * from d_full_city where name = #{name}
插件
注解
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
/**
* Returns method signatures to intercept.
*
* @return method signatures
*/
Signature[] value();
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
// Executor.class
Class> type();
// update
String method();
// MappedStatement.class, Object.class
Class>[] args();
}
主要用到了上面兩個注解:@Intercepts和@Signature 方法名和參數(shù)的定義如下:
時間插件
作用:打印SQL語句執(zhí)行的時間,分析慢查詢原因(一般針對查詢query來說)
package com.wyl.mybatis.intercept;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* @Description 時間插件
* @Author WuYiLong
* @Date 2024/2/29 9:51
*/
@Intercepts({@Signature(
type = Executor.class,
method = "query",
args = {
MappedStatement.class,
Object.class,
RowBounds.class,
ResultHandler.class
})})
public class TimeIntercept implements Interceptor {
private final static Logger log = LoggerFactory.getLogger(TimeIntercept.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement)args[0];
MapperMethod.ParamMap pm = ( MapperMethod.ParamMap) args[1];
Map
pm.forEach((k,v)->{
String key = String.valueOf(k);
if(!key.contains("param")) {
paramsMap.put(String.valueOf(k),v);
}
});
BoundSql boundSql = ms.getBoundSql(ms.getParameterMap());
// sql執(zhí)行之前
long startTime = System.currentTimeMillis();
Object proceed = invocation.proceed();
JSONArray jsonArray = JSONUtil.parseArray(proceed);
long endTime = System.currentTimeMillis();
log.info("----sql執(zhí)行語句: {}",boundSql.getSql());
log.info("----sql輸入?yún)?shù): {}", JSONUtil.toJsonStr(paramsMap));
log.info("----sql輸出結(jié)果數(shù): {}", jsonArray.size());
log.info("----sql執(zhí)行花費(fèi)時間: {}", (endTime-startTime)/1000);
// sql執(zhí)行之后
return proceed;
}
}
分頁插件
FullCityMapper mapper = sqlSession.getMapper(FullCityMapper.class);
for (int i = 1; i <= 3 ; i++) {
Page
List
log.info("當(dāng)前頁:{}",page.getCurrentPage());
log.info("頁數(shù):{}",page.getPageSize());
log.info("總數(shù):{}",page.getTotal());
log.info("列表:{}", JSONUtil.toJsonStr(fullCities));
}
作用:mysql數(shù)據(jù)庫物理分頁,簡化分頁流程
package com.wyl.mybatis.page;
import java.util.List;
/**
* @Description
* @Author WuYiLong
* @Date 2024/3/13 13:48
*/
public class Page
private Integer currentPage = 1;
private Integer pageSize = 10;
private Integer total;
private List
public Page(Integer currentPage,Integer pageSize) {
this.currentPage = currentPage;
this.pageSize = pageSize;
}
public Page(){};
public Integer getCurrentPage() {
return currentPage;
}
public void setCurrentPage(Integer currentPage) {
this.currentPage = currentPage;
}
public Integer getPageSize() {
return pageSize;
}
public void setPageSize(Integer pageSize) {
this.pageSize = pageSize;
}
public Integer getTotal() {
return total;
}
public void setTotal(Integer total) {
this.total = total;
}
public List
return records;
}
public void setRecords(List
this.records = records;
}
}
/**
* 分頁
* @param page
* @return
*/
@Select("select * from d_full_city")
List
package com.wyl.mybatis.intercept;
import cn.hutool.db.sql.SqlBuilder;
import com.alibaba.druid.DbType;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.druid.sql.ast.SQLStatement;
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
import com.wyl.mybatis.config.SqlSessionFactoryConfig;
import com.wyl.mybatis.page.Page;
import com.wyl.mybatis.util.SqlParamUtil;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.Map;
/**
* @Description
* @Author WuYiLong
* @Date 2024/3/13 12:06
*/
@Intercepts({
@Signature(
type = Executor.class,
method = "query",
args = {
MappedStatement.class,
Object.class,
RowBounds.class,
ResultHandler.class
}),
@Signature(
type = Executor.class,
method = "query",
args = {MappedStatement.class,
Object.class,
RowBounds.class,
ResultHandler.class,
CacheKey.class,
BoundSql.class})})
public class PageIntercept implements Interceptor {
private final static Logger log = LoggerFactory.getLogger(PageIntercept.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
Executor executor = (Executor) invocation.getTarget();
Object[] args = invocation.getArgs();
MappedStatement ms = (MappedStatement) args[0];
Object params = args[1];
RowBounds rowBounds = (RowBounds) args[2];
BoundSql boundSql = ms.getBoundSql(params);
String sql = boundSql.getSql();
ResultHandler resultHandler = (ResultHandler) args[3];
Page page = null;
Map
for (Map.Entry
Object v = mapEntry.getValue();
if (v instanceof Page) {
page = (Page) v;
}
}
if(page != null) {
String countSql = countSql(sql);
int count = 0;
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryConfig.buildSqlSessionFactory();
try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
Connection connection = sqlSession.getConnection();
PreparedStatement preparedStatement = connection.prepareStatement(countSql);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
count = resultSet.getInt("count");
}
}
page.setTotal(count);
rowBounds = new RowBounds(page.getCurrentPage()-1,page.getPageSize());
}
CacheKey cacheKey;
if (args.length == 4) {
cacheKey = executor.createCacheKey(ms, params, rowBounds, boundSql);
} else {
cacheKey = (CacheKey) args[4];
boundSql = (BoundSql) args[5];
}
List
return query;
}
@Override
public Object plugin(Object target) {
if (target instanceof Executor) {
return Plugin.wrap(target, this);
}
return target;
}
private String countSql(String sql) {
SqlBuilder sqlBuilder = new SqlBuilder();
sqlBuilder.select("count(*) count");
List
SQLSelectStatement sqlStatement = (SQLSelectStatement) sqlStatements.get(0);
MySqlSelectQueryBlock queryBlock = (MySqlSelectQueryBlock) sqlStatement.getSelect().getQueryBlock();
sqlBuilder.from(queryBlock.getFrom().toString());
if (queryBlock.getWhere() != null) {
sqlBuilder.where(queryBlock.getWhere().toString());
}
return sqlBuilder.build();
}
}
這里需要注意的是CacheKey緩存key,因?yàn)閟ql的變化直接影響了查詢的輸出,從上面可以看出分頁參數(shù),是不需要用戶輸入的,通過內(nèi)置分頁插件即可完成分頁,所以說sql實(shí)質(zhì)上是沒有發(fā)生變化的,從而導(dǎo)致重新生成的緩存key都是一樣的,如源碼所示:
@Override
public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
if (closed) {
throw new ExecutorException("Executor was closed.");
}
CacheKey cacheKey = new CacheKey();
cacheKey.update(ms.getId());
cacheKey.update(rowBounds.getOffset());
cacheKey.update(rowBounds.getLimit());
cacheKey.update(boundSql.getSql());
List
TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
// mimic DefaultParameterHandler logic
for (ParameterMapping parameterMapping : parameterMappings) {
if (parameterMapping.getMode() != ParameterMode.OUT) {
Object value;
String propertyName = parameterMapping.getProperty();
if (boundSql.hasAdditionalParameter(propertyName)) {
value = boundSql.getAdditionalParameter(propertyName);
} else if (parameterObject == null) {
value = null;
} else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
value = parameterObject;
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
value = metaObject.getValue(propertyName);
}
cacheKey.update(value);
}
}
if (configuration.getEnvironment() != null) {
// issue #176
cacheKey.update(configuration.getEnvironment().getId());
}
return cacheKey;
}
看到了cacheKey的update方法,所以我們只需要每次請求改變其中之一即可,很明顯,我們改變下RowBounds對象的參數(shù)就好,這個對象也是控制行數(shù)的,從它的名字就可以直接看出來。
項(xiàng)目地址
demo地址
柚子快報邀請碼778899分享:mybatis實(shí)踐篇(一)
精彩文章
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。