From 4f38f3be63f55ce0695adaf929e77a729c09a1fb Mon Sep 17 00:00:00 2001 From: brian Date: Thu, 27 Jul 2023 18:36:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=80=E5=90=AFmybatis=E4=BA=8C=E7=BA=A7?= =?UTF-8?q?=E7=BC=93=E5=AD=98-=E4=BD=BF=E7=94=A8redis=E5=AD=98=E5=82=A8?= =?UTF-8?q?=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- pom.xml | 4 - .../top/peng/answerbi/MainApplication.java | 6 +- .../top/peng/answerbi/bizmq/BiMqConfig.java | 2 +- .../answerbi/config/MyBatisPlusConfig.java | 2 +- .../top/peng/answerbi/config/RedisConfig.java | 79 +++++++++++++ .../manager/MybatisRedisCacheManager.java | 111 ++++++++++++++++++ .../top/peng/answerbi/mapper/ChartMapper.java | 5 +- .../top/peng/answerbi/mapper/UserMapper.java | 7 +- .../service/impl/UserServiceImpl.java | 26 ++-- src/main/resources/application-dev.yml | 12 +- src/main/resources/application-prod.yml | 14 +-- src/main/resources/application.yml | 4 +- 13 files changed, 230 insertions(+), 44 deletions(-) create mode 100644 src/main/java/top/peng/answerbi/config/RedisConfig.java create mode 100644 src/main/java/top/peng/answerbi/manager/MybatisRedisCacheManager.java diff --git a/README.md b/README.md index bef20a9..b4683b6 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,7 @@ yarn run dev - [x] 引入Guava RateLimiter(单机) 和 Redisson RateLimiter(分布式) 两种限流机制 - [x] 支持用户对失败的图表进行手动重试 - [ ] 图表数据分表存储,提高查询灵活性和性能 -- [ ] 引入redis缓存提高加载速度 +- [x] 引入redis缓存提高加载速度 - [ ] 给任务执行增加 guava Retrying重试机制,保证系统可靠性 - [ ] 定时任务把失败状态的图表放到队列中(补偿机制) - [ ] 给任务的执行增加超时时间,超时自动标记为失败(超时控制) diff --git a/pom.xml b/pom.xml index 174cd9c..4f27f41 100644 --- a/pom.xml +++ b/pom.xml @@ -17,10 +17,6 @@ 1.8 - - org.springframework.boot - spring-boot-starter-freemarker - org.springframework.boot spring-boot-starter-web diff --git a/src/main/java/top/peng/answerbi/MainApplication.java b/src/main/java/top/peng/answerbi/MainApplication.java index 3ff98a6..69046c8 100644 --- a/src/main/java/top/peng/answerbi/MainApplication.java +++ b/src/main/java/top/peng/answerbi/MainApplication.java @@ -1,9 +1,7 @@ package top.peng.answerbi; -import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.scheduling.annotation.EnableScheduling; @@ -13,9 +11,7 @@ import org.springframework.scheduling.annotation.EnableScheduling; * @author yunpeng * @version 1.0 2023/5/16 */ -// todo 如需开启 Redis,须移除 exclude 中的内容 -@SpringBootApplication(exclude = {RedisAutoConfiguration.class}) -@MapperScan("top.peng.answerbi.mapper") +@SpringBootApplication @EnableScheduling @EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) public class MainApplication { diff --git a/src/main/java/top/peng/answerbi/bizmq/BiMqConfig.java b/src/main/java/top/peng/answerbi/bizmq/BiMqConfig.java index 0bc04df..9bf0e16 100644 --- a/src/main/java/top/peng/answerbi/bizmq/BiMqConfig.java +++ b/src/main/java/top/peng/answerbi/bizmq/BiMqConfig.java @@ -66,7 +66,7 @@ public class BiMqConfig { .to(biExchange()) .with(BiMqConstant.BI_ROUTING_KEY); } - //绑定Bi分析业务队列到Bi分析业务交换机 + //绑定死信队列到死信交换机 @Bean public Binding DeadLetterBinding(){ return BindingBuilder diff --git a/src/main/java/top/peng/answerbi/config/MyBatisPlusConfig.java b/src/main/java/top/peng/answerbi/config/MyBatisPlusConfig.java index 10a0a14..f4bdafb 100644 --- a/src/main/java/top/peng/answerbi/config/MyBatisPlusConfig.java +++ b/src/main/java/top/peng/answerbi/config/MyBatisPlusConfig.java @@ -14,7 +14,7 @@ import org.springframework.context.annotation.Configuration; * @version 1.0 2023/5/16 */ @Configuration -@MapperScan("top.peng.springbootinit.mapper") +@MapperScan("top.peng.answerbi.mapper") public class MyBatisPlusConfig { /** diff --git a/src/main/java/top/peng/answerbi/config/RedisConfig.java b/src/main/java/top/peng/answerbi/config/RedisConfig.java new file mode 100644 index 0000000..6fbb8d4 --- /dev/null +++ b/src/main/java/top/peng/answerbi/config/RedisConfig.java @@ -0,0 +1,79 @@ +/* + * @(#)RedisTemplateConfig.java + * + * Copyright © 2023 YunPeng Corporation. + */ +package top.peng.answerbi.config; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator; +import java.time.Duration; +import java.util.Objects; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.CachingConfigurerSupport; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.cache.RedisCacheConfiguration; +import org.springframework.data.redis.cache.RedisCacheManager; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializationContext; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +/** + * RedisTemplateConfig + * + * @author yunpeng + * @version 1.0 2023/7/27 + */ +@Configuration +@EnableCaching +public class RedisConfig extends CachingConfigurerSupport { + + @Bean(name = "redisTemplate") + public RedisTemplate redisTemplate(RedisConnectionFactory factory){ + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(factory); + // 使用Jackson2JsonRedisSerialize 替换默认序列化 + Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class); + ObjectMapper objectMapper = new ObjectMapper(); + objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); + objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL); + jackson2JsonRedisSerializer.setObjectMapper(objectMapper); + + // 设置key和value的序列化规则 + redisTemplate.setKeySerializer(new StringRedisSerializer()); + redisTemplate.setValueSerializer(jackson2JsonRedisSerializer); + redisTemplate.setHashKeySerializer(new StringRedisSerializer()); + redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer); + //afterPropertiesSet和init-method之间的执行顺序是afterPropertiesSet 先执行,init - method 后执行。 + redisTemplate.afterPropertiesSet(); + return redisTemplate; + } + + @Bean + public CacheManager cacheManager(RedisTemplate redisTemplate) { + + //基本配置 + RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig() + //设置 key 为String + .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getStringSerializer())) + //设置 value 为自动转Json 的Object + .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer())) + //不缓存 null + .disableCachingNullValues() + //配置缓存失效时间30分钟 + .entryTtl(Duration.ofMinutes(30)); + //构造一个redis缓存管理器 + return RedisCacheManager.RedisCacheManagerBuilder + .fromConnectionFactory(Objects.requireNonNull(redisTemplate.getConnectionFactory())) + .cacheDefaults(defaultCacheConfig) + //配置同步修改或删除 + .transactionAware() + .build(); + } +} diff --git a/src/main/java/top/peng/answerbi/manager/MybatisRedisCacheManager.java b/src/main/java/top/peng/answerbi/manager/MybatisRedisCacheManager.java new file mode 100644 index 0000000..49b0a5a --- /dev/null +++ b/src/main/java/top/peng/answerbi/manager/MybatisRedisCacheManager.java @@ -0,0 +1,111 @@ +/* + * @(#)MyBatisRedisCache.java + * + * Copyright © 2023 YunPeng Corporation. + */ +package top.peng.answerbi.manager; + +import java.util.Set; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import lombok.extern.slf4j.Slf4j; +import org.apache.ibatis.cache.Cache; +import org.springframework.data.redis.connection.RedisServerCommands; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.util.CollectionUtils; +import top.peng.answerbi.utils.SpringContextUtils; + +/** + * MyBatisRedisCache mybaits 缓存工具类 + * + * @author yunpeng + * @version 1.0 2023/7/27 + */ +@Slf4j +public class MybatisRedisCacheManager implements Cache { + + private RedisTemplate redisTemplate; + + // 读写锁 + private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(true); + + // cache instance id + private final String id; + + private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis过期时间 + + public MybatisRedisCacheManager(String id) { + if (id == null) { + throw new IllegalArgumentException("Cache instances require an ID"); + } + this.id = id; + } + + @Override + public String getId() { + return id; + } + + /** + * Put query result to redis + * + * @param key + * @param value + */ + @Override + public void putObject(Object key, Object value) { + getRedisTemplate().opsForValue().set(key.toString(), value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES); + log.info("Put query result to redis, key={}",key); + } + + @Override + public Object getObject(Object key) { + try { + log.info("Get cached query result from redis, key={}",key); + return getRedisTemplate().opsForValue().get(key.toString()); + } catch (Exception e) { + log.error("Get cached query result from redis failed , key={}",key); + e.printStackTrace(); + } + return null; + } + + @Override + public Object removeObject(Object key) { + if (key != null){ + getRedisTemplate().delete(key.toString()); + log.info("Remove cached query result from redis, key={}",key); + } + return null; + } + + @Override + public void clear() { + Set keys = getRedisTemplate().keys("*:" + this.id + "*"); + if (!CollectionUtils.isEmpty(keys)) { + getRedisTemplate().delete(keys); + } + log.info("Clear all the cached query result from redis"); + } + + @Override + public int getSize() { + Long size = getRedisTemplate().execute(RedisServerCommands::dbSize); + if (size == null) return 0; + return size.intValue(); + } + + @Override + public ReadWriteLock getReadWriteLock() { + return readWriteLock; + } + + private RedisTemplate getRedisTemplate(){ + //通过SpringContextUtils工具类获取RedisTemplate + if (redisTemplate == null) { + redisTemplate = (RedisTemplate) SpringContextUtils.getBean("redisTemplate"); + } + return redisTemplate; + } +} diff --git a/src/main/java/top/peng/answerbi/mapper/ChartMapper.java b/src/main/java/top/peng/answerbi/mapper/ChartMapper.java index 4bdeb3a..ac2f322 100644 --- a/src/main/java/top/peng/answerbi/mapper/ChartMapper.java +++ b/src/main/java/top/peng/answerbi/mapper/ChartMapper.java @@ -1,5 +1,7 @@ package top.peng.answerbi.mapper; +import org.apache.ibatis.annotations.CacheNamespace; +import top.peng.answerbi.manager.MybatisRedisCacheManager; import top.peng.answerbi.model.entity.Chart; import com.baomidou.mybatisplus.core.mapper.BaseMapper; @@ -7,8 +9,9 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; * @author yunpeng.zhang * @description 针对表【chart(图表信息表)】的数据库操作Mapper * @createDate 2023-07-10 16:45:42 -* @Entity top.peng.springbootinit.model.entity.Chart +* @Entity top.peng.answerbi.model.entity.Chart */ +@CacheNamespace(implementation = MybatisRedisCacheManager.class) public interface ChartMapper extends BaseMapper { } diff --git a/src/main/java/top/peng/answerbi/mapper/UserMapper.java b/src/main/java/top/peng/answerbi/mapper/UserMapper.java index 6794fce..3bde919 100644 --- a/src/main/java/top/peng/answerbi/mapper/UserMapper.java +++ b/src/main/java/top/peng/answerbi/mapper/UserMapper.java @@ -1,14 +1,17 @@ package top.peng.answerbi.mapper; -import top.peng.answerbi.model.entity.User; import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.CacheNamespace; +import top.peng.answerbi.manager.MybatisRedisCacheManager; +import top.peng.answerbi.model.entity.User; /** * @author yunpeng.zhang * @description 针对表【user(用户)】的数据库操作Mapper * @createDate 2023-07-10 16:45:42 -* @Entity top.peng.springbootinit.model.entity.User +* @Entity top.peng.answerbi.model.entity.User */ +@CacheNamespace(implementation = MybatisRedisCacheManager.class) public interface UserMapper extends BaseMapper { } diff --git a/src/main/java/top/peng/answerbi/service/impl/UserServiceImpl.java b/src/main/java/top/peng/answerbi/service/impl/UserServiceImpl.java index 60b1cb1..c512c3f 100644 --- a/src/main/java/top/peng/answerbi/service/impl/UserServiceImpl.java +++ b/src/main/java/top/peng/answerbi/service/impl/UserServiceImpl.java @@ -3,17 +3,6 @@ package top.peng.answerbi.service.impl; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; -import top.peng.answerbi.common.ErrorCode; -import top.peng.answerbi.constant.CommonConstant; -import top.peng.answerbi.exception.BusinessException; -import top.peng.answerbi.mapper.UserMapper; -import top.peng.answerbi.model.dto.user.UserQueryRequest; -import top.peng.answerbi.model.entity.User; -import top.peng.answerbi.model.enums.UserRoleEnum; -import top.peng.answerbi.model.vo.LoginUserVO; -import top.peng.answerbi.model.vo.UserVO; -import top.peng.answerbi.service.UserService; -import top.peng.answerbi.utils.SqlUtils; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -23,7 +12,18 @@ import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.stereotype.Service; import org.springframework.util.DigestUtils; +import top.peng.answerbi.common.ErrorCode; +import top.peng.answerbi.constant.CommonConstant; import top.peng.answerbi.constant.UserConstant; +import top.peng.answerbi.exception.BusinessException; +import top.peng.answerbi.mapper.UserMapper; +import top.peng.answerbi.model.dto.user.UserQueryRequest; +import top.peng.answerbi.model.entity.User; +import top.peng.answerbi.model.enums.UserRoleEnum; +import top.peng.answerbi.model.vo.LoginUserVO; +import top.peng.answerbi.model.vo.UserVO; +import top.peng.answerbi.service.UserService; +import top.peng.answerbi.utils.SqlUtils; /** * 用户服务实现 @@ -60,7 +60,7 @@ public class UserServiceImpl extends ServiceImpl implements Us // 账户不能重复 QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("user_account", userAccount); - long count = this.baseMapper.selectCount(queryWrapper); + long count = this.count(queryWrapper); if (count > 0) { throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复"); } @@ -96,7 +96,7 @@ public class UserServiceImpl extends ServiceImpl implements Us QueryWrapper queryWrapper = new QueryWrapper<>(); queryWrapper.eq("user_account", userAccount); queryWrapper.eq("user_password", encryptPassword); - User user = this.baseMapper.selectOne(queryWrapper); + User user = this.getOne(queryWrapper); // 用户不存在 if (user == null) { log.info("user login failed, userAccount cannot match userPassword"); diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index b0dbaa3..3f915eb 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -16,9 +16,9 @@ spring: port: 6379 timeout: 5000 password: 123456 - # Elasticsearch 配置 - # todo 需替换配置 - elasticsearch: - uris: http://localhost:9200 - username: root - password: 123456 \ No newline at end of file + # rabbitMq 配置 + rabbitmq: + host: localhost + port: 5672 + username: guest + password: guest \ No newline at end of file diff --git a/src/main/resources/application-prod.yml b/src/main/resources/application-prod.yml index 7df7e1b..a4105a3 100644 --- a/src/main/resources/application-prod.yml +++ b/src/main/resources/application-prod.yml @@ -2,26 +2,24 @@ server: port: 8101 spring: # 数据库配置 - # todo 需替换配置 datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/answer_bi username: root password: 123456 # Redis 配置 - # todo 需替换配置 redis: database: 1 host: localhost port: 6379 timeout: 5000 password: 123456 - # Elasticsearch 配置 - # todo 需替换配置 - elasticsearch: - uris: http://localhost:9200 - username: root - password: 123456 + # rabbitMq 配置 + rabbitmq: + host: localhost + port: 5672 + username: guest + password: guest mybatis-plus: configuration: # 生产环境关闭日志 diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index e960e52..4a9a954 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -10,8 +10,7 @@ spring: matching-strategy: ant_path_matcher # session 配置 session: - # todo 取消注释开启分布式 session(须先配置 Redis) - # store-type: redis + store-type: redis # 30 天过期 timeout: 2592000 # 数据库配置 @@ -51,6 +50,7 @@ mybatis-plus: configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + cache-enabled: true global-config: db-config: logic-delete-field: deleted_flag # 全局逻辑删除的实体字段名