EnhanceBatchDelete.class 增强批量删除
package com.jm.common.conf.mybatis;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
public class EnhanceBatchDelete extends AbstractMethod {
protected EnhanceBatchDelete(String methodName) {
super(methodName);
}
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
final String sql = "<script>update "
+ tableInfo.getTableName()
+ " set deleted=1"
+ " where "
+ tableInfo.getKeyColumn()
+ " in(<foreach collection=\"list\" item=\"i\" separator=\",\">#{i}</foreach>)</script>";
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
final String id = "enhanceBatchDelete";
return this.addDeleteMappedStatement(mapperClass, id, sqlSource);
}
}
EnhanceBatchInsert .class 增强批量新增
package com.jm.common.conf.mybatis;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import lombok.Setter;
import lombok.experimental.Accessors;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import java.util.List;
import java.util.function.Predicate;
public class EnhanceBatchInsert extends AbstractMethod {
@Setter
@Accessors(chain = true)
private Predicate<TableFieldInfo> predicate;
protected EnhanceBatchInsert(String methodName) {
super(methodName);
}
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
KeyGenerator keyGenerator = new NoKeyGenerator();
String keyProperty = null;
String keyColumn = null;
final String id = "enhanceBatchInsert";
if (tableInfo.havePK()) {
if (tableInfo.getIdType() == IdType.AUTO) {
keyGenerator = new Jdbc3KeyGenerator();
keyProperty = tableInfo.getKeyProperty();
keyColumn = tableInfo.getKeyColumn();
} else {
if (null != tableInfo.getKeySequence()) {
keyGenerator = TableInfoHelper.genKeyGenerator(id, tableInfo, builderAssistant);
keyProperty = tableInfo.getKeyProperty();
keyColumn = tableInfo.getKeyColumn();
}
}
}
SqlSource sqlSource = languageDriver.createSqlSource(configuration, this.generateSql(tableInfo.getFieldList(), tableInfo), modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, id, sqlSource, keyGenerator, keyProperty, keyColumn);
}
private String generateSql(List<TableFieldInfo> fields, TableInfo tableInfo) {
StringBuilder sb = new StringBuilder();
sb.append("<script>insert into ").append(tableInfo.getTableName())
.append("(<trim suffixOverrides=\",\"><if test=\"list.get(0).")
.append(tableInfo.getKeyProperty()).append("!=null\">")
.append(tableInfo.getKeyColumn()).append(",</if>");
fields.forEach(i -> sb.append("<if test=\"list.get(0).").append(i.getProperty()).append("!=null\">").append(i.getColumn()).append(",</if>"));
sb.append("</trim>) values <foreach collection=\"list\" item=\"et\" separator=\",\">(<trim suffixOverrides=\",\"><if test=\"list.get(0).")
.append(tableInfo.getKeyProperty())
.append("!=null\">#{et.")
.append(tableInfo.getKeyColumn())
.append("},</if>");
fields.forEach(i -> sb.append("<if test=\"list.get(0).").append(i.getProperty()).append("!=null\">#{et.").append(i.getProperty()).append("},</if>"));
sb.append("</trim>) </foreach></script>");
return sb.toString();
}
}
EnhanceBatchUpdate.class 增强批量修改
package com.jm.common.conf.mybatis;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import java.util.List;
public class EnhanceBatchUpdate extends AbstractMethod {
protected EnhanceBatchUpdate(String methodName) {
super(methodName);
}
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
SqlSource sqlSource = languageDriver.createSqlSource(configuration, this.generateSql(tableInfo.getFieldList(), tableInfo), modelClass);
final String id = "enhanceBatchUpdate";
return this.addUpdateMappedStatement(mapperClass, modelClass, id, sqlSource);
}
private String generateSql(List<TableFieldInfo> fields, TableInfo tableInfo) {
StringBuilder sb = new StringBuilder();
fields.forEach(i -> sb
.append("<if test=\"list.get(0).").append(i.getProperty()).append("!=null\">")
.append("<trim prefix=\"")
.append(i.getColumn())
.append("=case\" suffix=\"end,\">")
.append("<foreach collection=\"list\" item=\"et\">")
.append("<if test=\"et!=null\">")
.append(" when ")
.append(tableInfo.getKeyColumn()).append("=#{et.").append(tableInfo.getKeyProperty()).append("} then #{et.").append(i.getProperty()).append("}")
.append("</if>")
.append("</foreach>")
.append("</trim>")
.append("</if>"));
return "<script>update " + tableInfo.getTableName()
+ "<trim prefix=\"set\" suffixOverrides=\",\">"
+ sb
+ "</trim> where " + tableInfo.getKeyColumn() + " in <foreach collection=\"list\" item=\"et\" separator=\",\" open=\"(\" close=\")\"> #{et."
+ tableInfo.getKeyProperty() + "} </foreach> </script>";
}
}
EnhanceDelete .class 增强删除 sql 追加 limit 1
package com.jm.common.conf.mybatis;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
public class EnhanceDelete extends AbstractMethod {
protected EnhanceDelete(String methodName) {
super(methodName);
}
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
final String sql = "<script>update " + tableInfo.getTableName() + " set deleted=1 where deleted=0 and " + tableInfo.getKeyColumn() + "=#{id} limit 1</script>";
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
final String id = "enhanceDelete";
return this.addDeleteMappedStatement(mapperClass, id, sqlSource);
}
}
EnhanceUpdate 增强更新 追加limit 1
package com.jm.common.conf.mybatis;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import java.util.List;
public class EnhanceUpdate extends AbstractMethod {
protected EnhanceUpdate(String methodName) {
super(methodName);
}
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
SqlSource sqlSource = languageDriver.createSqlSource(configuration, this.generateSql(tableInfo.getFieldList(), tableInfo), modelClass);
final String id = "enhanceUpdate";
return this.addUpdateMappedStatement(mapperClass, modelClass, id, sqlSource);
}
private String generateSql(List<TableFieldInfo> fields, TableInfo tableInfo) {
StringBuilder sb = new StringBuilder();
fields.forEach(i -> sb
.append("<if test=\"")
.append(i.getProperty()).append("!=null\">")
.append(i.getColumn()).append("=").append("#{").append(i.getProperty()).append("},")
.append("</if>")
);
return "<script>update " + tableInfo.getTableName()
+ "<trim prefix=\"set\" suffixOverrides=\",\">"
+ sb
+ "</trim> where " + tableInfo.getKeyColumn() + "=#{" + tableInfo.getKeyProperty() + "} limit 1</script>";
}
}
增强实现相关常量
package com.jm.common.conf.mybatis;
public class EnhanceConstant {
public static final String REDIS_PREFIX = "jm:model:";
private EnhanceConstant() {
}
}
增强mapper 接口
package com.jm.common.conf.mybatis;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.Collection;
public interface EnhancePlusMapper<T> extends BaseMapper<T> {
int enhanceBatchInsert(@Param("list") Collection<T> rows);
int enhanceBatchUpdate(@Param("list") Collection<T> rows);
int enhanceUpdate(T t);
int enhanceBatchDelete(@Param("list") Collection<String> rows);
int enhanceDelete(Object id);
}
EnhanceService 接口
package com.jm.common.conf.mybatis;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.jm.common.bean.base.PageParam;
import com.jm.common.bean.base.PageVO;
import java.io.Serializable;
import java.util.List;
public interface EnhanceService<T> extends IService<T> {
void delCache(T t);
void delCache(Serializable id);
boolean add(T t);
boolean modify(T t);
boolean del(String id);
boolean del(List<String> ids);
T get(Serializable id);
PageVO<T> getPage(PageParam param) throws IllegalAccessException;
PageVO<T> getPage(PageParam param, LambdaQueryWrapper<T> pageWrapper, LambdaQueryWrapper<T> countWrapper,
LambdaQueryWrapper<T> lastWrapper, int expireSecond, String id) throws IllegalAccessException;
PageVO<T> getPage(PageParam param, List<T> pls);
}
查询-自动缓存 自动预热、通用高性能分页
package com.jm.common.conf.mybatis;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jm.common.bean.base.PageParam;
import com.jm.common.bean.base.PageVO;
import com.jm.common.conf.constant.SysRedisConstant;
import com.jm.common.tool.ClassTool;
import com.jm.common.tool.LogTool;
import com.jm.common.tool.ParseTool;
import com.jm.common.tool.RandomTool;
import com.jm.common.tool.function.ThrowTool;
import com.jm.common.tool.pool.ThreadPoolTool;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@Slf4j
public class EnhanceServiceImpl<M extends EnhancePlusMapper<T>, T> extends ServiceImpl<M, T> implements EnhanceService<T> {
public final String KEY_PREFIX;
public final ValueOperations<String, Object> redisValue;
public final RedisTemplate<String, Object> redis;
public final Class<T> classz;
public final Field primaryKeyField;
public final String primaryKeyType;
public final String primaryKeyName;
public final RedissonClient redissonClient;
public EnhanceServiceImpl(RedisTemplate<String, Object> redis, RedissonClient redissonClient) {
this.redis = redis;
this.redisValue = redis.opsForValue();
this.redissonClient = redissonClient;
this.classz = (Class<T>) ClassTool.getClassz(this.getClass());
TableName tableName = this.classz.getAnnotation(TableName.class);
this.KEY_PREFIX = EnhanceConstant.REDIS_PREFIX + tableName.value() + ":";
this.primaryKeyField = this.getPrimaryKeyField();
this.primaryKeyType = this.primaryKeyField.getType().getTypeName();
TableId tableId = this.primaryKeyField.getAnnotation(TableId.class);
this.primaryKeyName = tableId.value();
}
public String getKeyPrefix() {
return this.KEY_PREFIX;
}
public Class<T> getClassz() {
Type type = this.getClass().getGenericSuperclass();
Type[] types = ((ParameterizedType) type).getActualTypeArguments();
return (Class<T>) types[1];
}
public String getClassName() {
return this.classz.getSimpleName();
}
public Field getPrimaryKeyField() {
List<Field> fields = FieldUtils.getFieldsListWithAnnotation(this.classz, TableId.class);
return fields.get(0);
}
public List<Field> getFields() {
return FieldUtils.getAllFieldsList(getClassz());
}
public String getPrimaryKeyValue(T t) throws IllegalAccessException {
return String.valueOf(FieldUtils.readField(t, this.primaryKeyField.getName(), true));
}
@Override
public void delCache(T t) {
try {
redis.delete(KEY_PREFIX + this.getPrimaryKeyValue(t));
} catch (IllegalAccessException e) {
log.error("数据删除异常 ", e);
}
}
@Override
public void delCache(Serializable id) {
redis.delete(KEY_PREFIX + id);
}
@Override
public boolean add(T t) {
return save(t);
}
protected void checkPrimaryValue(final String id) {
if (Pattern.matches("\\d*", id.replaceAll("\"", ""))) {
long number = Long.parseLong(id);
ThrowTool.isTrue(number <= 0).throwMsg("id不合法");
}
}
@Override
public boolean modify(T t) {
try {
final String id = this.getPrimaryKeyValue(t);
this.checkPrimaryValue(id);
Field field = ClassTool.getField(this.classz, "updateTime");
if (field != null) {
field.set(t, LocalDateTime.now());
}
final boolean flag = this.baseMapper.enhanceUpdate(t) > 0;
this.delCache(id);
return flag;
} catch (Exception e) {
log.error("数据修改异常 ", e);
}
return false;
}
@Override
public boolean del(String sid) {
this.checkPrimaryValue(sid);
Object id = ParseTool.parseObject(sid, this.primaryKeyType);
final boolean flag = this.baseMapper.enhanceDelete(id) > 0;
this.delCache(sid);
return flag;
}
@Override
public boolean del(List<String> ids) {
ThrowTool.isTrue(CollectionUtils.isEmpty(ids)).throwMsg("empty collection");
if (ids.size() > 1) {
final boolean flag = removeBatchByIds(ids);
CompletableFuture.runAsync(() -> redis.delete(ids.parallelStream().map(i -> KEY_PREFIX + i).collect(Collectors.toList())), ThreadPoolTool.EXECUTOR).exceptionally(LogTool::err);
return flag;
}
return del(ids.get(0));
}
@Override
public boolean removeBatchByIds(Collection<?> list) {
return this.baseMapper.enhanceBatchDelete(list.parallelStream().map(String::valueOf).collect(Collectors.toList())) > 0;
}
@Override
public T get(Serializable id) {
this.checkPrimaryValue(String.valueOf(id));
final String key = this.KEY_PREFIX + id;
Object o = redisValue.get(key);
T t;
if (o == null) {
t = getOne(new LambdaQueryWrapper<T>().apply(this.primaryKeyName + "='" + id + "'").last("limit 1"));
if (t == null) {
CompletableFuture.runAsync(() -> redis.delete(key), ThreadPoolTool.EXECUTOR).exceptionally(LogTool::err);
} else {
CompletableFuture.runAsync(() -> redisValue.set(key, t, 3600, TimeUnit.SECONDS), ThreadPoolTool.EXECUTOR).exceptionally(LogTool::err);
}
} else {
t = (T) o;
CompletableFuture.runAsync(() -> this.renewal(key), ThreadPoolTool.EXECUTOR).exceptionally(LogTool::err);
}
return t;
}
public void renewal(final String key) {
RLock lock = redissonClient.getLock("lock_" + key.replaceAll(":", "_"));
try {
boolean is = lock.tryLock(20, 10, TimeUnit.SECONDS);
if (is) {
long expireTime = Optional.ofNullable(redis.getExpire(key)).orElse(0L);
if (expireTime < 3600) {
CompletableFuture.runAsync(() -> redis.expire(key, expireTime + 3600 + RandomTool.random(999, 100), TimeUnit.SECONDS), ThreadPoolTool.EXECUTOR).exceptionally(LogTool::err);
}
}
} catch (InterruptedException e) {
log.error("redis 缓存续期 错误 ", e);
} finally {
lock.unlock();
}
}
@Override
public PageVO<T> getPage(PageParam param) throws IllegalAccessException {
LambdaQueryWrapper<T> w = new LambdaQueryWrapper<>();
return this.getPage(param, w, w, new LambdaQueryWrapper<T>()
.select(this.classz, e -> false).last("order by id desc limit 1"), 600, "");
}
@Override
public PageVO<T> getPage(PageParam param, LambdaQueryWrapper<T> pageWrapper, LambdaQueryWrapper<T> countWrapper,
LambdaQueryWrapper<T> lastWrapper, int expireSecond, String id) throws IllegalAccessException {
final String key = SysRedisConstant.PAGE_COUNT + this.getClassName() + id;
long count = Long.parseLong(Optional.ofNullable((String) redis.opsForValue().get(key)).orElse("0"));
long lastId;
if (StringUtils.isBlank(param.getCurrentLastId())) {
T t = getOne(lastWrapper);
if (t == null) {
lastId = 0;
} else {
lastId = Long.parseLong(this.getPrimaryKeyValue(t));
}
} else {
lastId = Long.parseLong(param.getCurrentLastId());
}
if (Optional.ofNullable(param.getNexted()).orElse(Boolean.TRUE)) {
if (param.getCurrent() == 1) {
pageWrapper.apply("id<=" + lastId);
} else {
pageWrapper.apply("id<" + lastId);
}
} else {
pageWrapper.apply("id <=" + Optional.ofNullable(param.getTopFirstId()).orElse("0"));
}
pageWrapper.last("order by id desc limit " + param.getSize());
return new PageVO<T>().setData(list(pageWrapper)).setCurrent(param.getCurrent()).setSize(param.getSize()).setPages(count / param.getSize()).setTotal(count);
}
@Override
public PageVO<T> getPage(PageParam param, List<T> pls) {
if (CollectionUtils.isEmpty(pls)) {
return new PageVO<>();
}
final int count = pls.size();
final int current = param.getCurrent().intValue();
final int size = param.getSize().intValue();
final int start = (current - 1) * size;
final int end = Math.min(size * current, count);
if (start > end) {
return new PageVO<T>().setPages((long) ((count + size - 1) / size)).setTotal((long) count).setSize(param.getSize()).setCurrent(param.getCurrent());
} else {
pls = pls.subList(start, end);
return new PageVO<T>().setPages((long) ((count + size - 1) / size)).setTotal((long) count).setSize(param.getSize()).setCurrent(param.getCurrent()).setData(pls);
}
}
}
完整代码请看项目git地址
https://gitee.com/sunuping/jianmu-example-jdk17.git