redis-cache

1. 依赖

implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.0.1'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'mysql:mysql-connector-java'
annotationProcessor 'org.projectlombok:lombok'

implementation 'org.apache.commons:commons-lang3:3.9'
implementation 'commons-beanutils:commons-beanutils:1.9.3'

compile group: 'tk.mybatis', name: 'mapper-spring-boot-starter', version: '2.1.5'

2. 配置

#---mysql---
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/demo?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=123456
#---mybatis---
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.example.mybatisdemo.po
mybatis.type-handlers-package=com.example.mybatisdemo.typehandler
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.interceptors=
#---mapper---
mapper.mappers=com.example.mybatisdemo.dao.BaseMapper
mapper.not-empty=false
mapper.identity=MYSQL
mapper.enum-as-simple-type=true
#---cache---
spring.cache.cache-names=redisCache
spring.cache.redis.key-prefix=demo:
spring.cache.redis.cache-null-values=true
spring.cache.redis.time-to-live=0ms
spring.cache.redis.use-key-prefix=true
spring.cache.type=REDIS
#---redis---
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.database=0
spring.redis.jedis.pool.max-active=10
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.min-idle=5
spring.redis.jedis.pool.max-wait=2000

3. 启动类

  1. 因为我们使用了tk.mybatis作通用的mapper,这里的MapperScan要使用tk的

  2. 开启缓存使用@EnableCaching

//import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.stereotype.Repository;

import tk.mybatis.spring.annotation.MapperScan;

@SpringBootApplication
@MapperScan(value = "com.example.mybatisdemo.dao", annotationClass = Repository.class)
@EnableCaching
public class MybatisDemoApplication {


    public static void main(String[] args) {
        SpringApplication.run(MybatisDemoApplication.class, args);
    }

}

4. mybatis日志插件

package com.example.mybatisdemo.component;


import java.sql.Connection;
import java.util.List;
import java.util.Properties;
import java.util.StringTokenizer;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare",args = { Connection.class,Integer.class }) })
public class MybatisSqlLogInterceptor implements Interceptor {



    public Object intercept(Invocation invocation) throws Exception {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();
        BoundSql bSql = statementHandler.getBoundSql();
        Object param = statementHandler.getParameterHandler().getParameterObject();

        StringBuffer sb = new StringBuffer();

        sb.append(new StringBuilder().append(removeBreakingWhitespace(bSql.getSql())).toString());

        List<ParameterMapping> paramList = bSql.getParameterMappings();
        for (ParameterMapping mapping : paramList) {
            String proName = mapping.getProperty();
            try {
                sb.append(new StringBuilder().append("[").append(proName).append(":")
                        .append(BeanUtils.getProperty(param, proName)).append("]").toString());
            } catch (Exception e) {
                sb.append(new StringBuilder().append("[").append(proName).append(":").append(param).append("]").toString());
            }
        }
        Object result = null;
        try {
            result = invocation.proceed();
            log.info(sb.toString());
        } catch (Exception e) {
            log.error(sb.toString(), e);
            throw e;
        }

        return result;
    }


    protected String removeBreakingWhitespace(String original) {
        StringTokenizer whitespaceStripper = new StringTokenizer(original);
        StringBuilder builder = new StringBuilder();
        for (; whitespaceStripper.hasMoreTokens(); builder.append(" ")) {
            builder.append(whitespaceStripper.nextToken());
        }
        return builder.toString();
    }


    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }


    public void setProperties(Properties arg0) {
    }
}

5. rest

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.mybatisdemo.po.User;
import com.example.mybatisdemo.service.UserService;

import lombok.extern.slf4j.Slf4j;

@RestController
@RequestMapping("user")
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Slf4j
public class UserController {


    @Autowired
    private UserService userService;

    //curl localhost:8080/user/get/2
    @GetMapping("get/{id}")
    public Object getUser(@PathVariable(value = "id", required = true) String id) throws Exception {
        log.info("=======id={}", id);
         User user = userService.getUser(id);
         log.info("======user={}", user);
         return user;
    }
    //curl localhost:8080/user/save -X POST -H "Content-Type: application/json" 
    //--data '{"id":"2","username":"jack","password":"123","status":"ENABLED"}'
    @PostMapping("save")
    public Object getUser(@RequestBody User user) throws Exception {

         log.info("======user={}", user);
         return userService.saveUser(user);
    }
    @PostMapping("delete/{id}")
    public Object deleteUser(@PathVariable(value = "id", required = true) String id) throws Exception {

        log.info("=======id={}", id);
         int deleteUser = userService.deleteUser(id);
         return deleteUser;
    }

}

6. dao

import tk.mybatis.mapper.common.ConditionMapper;
import tk.mybatis.mapper.common.IdsMapper;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.MySqlMapper;

public interface BaseMapper<T> extends Mapper<T>, MySqlMapper<T>, IdsMapper<T>, ConditionMapper<T> {
}
import org.springframework.stereotype.Repository;

import com.example.mybatisdemo.po.User;


@Repository
public interface UserDao extends BaseMapper<User> {

    User getUserById(String id);

}

7. enum

po里面定义了枚举,json返回的是这个枚举的拼写

import lombok.AllArgsConstructor;
import lombok.Getter;

@AllArgsConstructor
@Getter
public enum StatusEnum {

    ENABLED(1, "enabled"), DISABLED(2, "disabled");

    private Integer statusCode;


    private String statusDesc;


    public static String getDesc(Integer statusCode) {

        for (StatusEnum en : StatusEnum.values()) {
            if (en.statusCode.equals(statusCode)) {
                return en.statusDesc;
            }
        }
        return null;

    }



    public static StatusEnum getEnumByCode(Integer code) {
        for (StatusEnum en : StatusEnum.values()) {
            if (en.statusCode.equals(code)) {
                return en;
            }
        }

        return null;
    }

}

8. po

缓存的对象必须实现序列化

import java.io.Serializable;

import javax.persistence.Id;

import org.apache.ibatis.type.Alias;

import com.example.mybatisdemo.enums.StatusEnum;

import lombok.Data;
//要想缓冲 这里必须实现序列化  
@Data
@Alias("user")
public class User implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = -7411979954615843818L;

    @Id
    private String id;

    private String username;

    private String password;

    private StatusEnum status;

}

9. service

缓存注解中的value就是配置文件中定义的spring.cache.cache-names=redisCache

package com.example.mybatisdemo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.example.mybatisdemo.dao.UserDao;
import com.example.mybatisdemo.po.User;

@Service
@Transactional
public class UserService {

    @Autowired
    private UserDao userDao;

    @Cacheable(value = "redisCache", key = "'redis_user_'+#id")
    public User getUser(String id) {
        return userDao.getUserById(id);
    }
    @CachePut(value = "redisCache", key = "'redis_user_'+#result.id")
    public User saveUser(User user) {
         userDao.insert(user);
         return user;
    }
    @CacheEvict(value = "redisCache", key = "'redis_user_'+#id", beforeInvocation = false)
    public int deleteUser(String id) {
        return userDao.deleteByPrimaryKey(id);
    }
    @CachePut(value = "redisCache", key = "'redis_user_'+#result.id", condition = "#result != 'null'")
    public User updateUser(User user) {
        User user2 = getUser(user.getId());

        if(null == user2) {
            return null;
        }

         userDao.updateByPrimaryKeySelective(user);
         return user;
    }

}

10. typeHandler

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import com.example.mybatisdemo.enums.StatusEnum;

@MappedJdbcTypes(JdbcType.INTEGER)
@MappedTypes(StatusEnum.class)
public class StatusTypeHandler extends BaseTypeHandler<StatusEnum> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int idx, StatusEnum statusEnum, JdbcType jdbcType)
            throws SQLException {
        ps.setInt(idx, statusEnum.getStatusCode());

    }

    @Override
    public StatusEnum getNullableResult(ResultSet rs, String columnName) throws SQLException {
        int statusCode = rs.getInt(columnName);
        return StatusEnum.getEnumByCode(statusCode);
    }

    @Override
    public StatusEnum getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        int statusCode = rs.getInt(columnIndex);
        return StatusEnum.getEnumByCode(statusCode);
    }

    @Override
    public StatusEnum getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        int statusCode = cs.getInt(columnIndex);
        return StatusEnum.getEnumByCode(statusCode);
    }

}

Last updated