1. 程式人生 > >Spring Boot 整合mybatis 用Redis做二級快取

Spring Boot 整合mybatis 用Redis做二級快取

一:整合druid資料來源

一:導包

                <dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.10</version>
		</dependency>

二:配置

用的是druid的連線池,jpa的自動建表,mytabis框架

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot
    username: root
    password: root
    
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
    
mybatis:
  type-aliases-package: com.tom.bean
  mapper-locations: classpth:mapper/*mapper.xml

druid的額外配置

# 初始化大小,最小,最大
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
# 配置獲取連線等待超時的時間
spring.datasource.maxWait=60000
# 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連線,單位是毫秒
spring.datasource.timeBetweenEvictionRunsMillis=60000
# 配置一個連線在池中最小生存的時間,單位是毫秒
spring.datasource.minEvictableIdleTimeMillis=300000
# 校驗SQL,Oracle配置 spring.datasource.validationQuery=SELECT 1 FROM DUAL,如果不配validationQuery項,則下面三項配置無用
spring.datasource.validationQuery=SELECT 'x'
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
# 開啟PSCache,並且指定每個連線上PSCache的大小
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
# 配置監控統計攔截的filters,去掉後監控介面sql無法統計,'wall'用於防火牆
spring.datasource.filters=stat,wall,log4j
# 通過connectProperties屬性來開啟mergeSql功能;慢SQL記錄
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合併多個DruidDataSource的監控資料
spring.datasource.useGlobalDataSourceStat=true

三:Mapper,Service,Controller

bean,用的自動建表

@Entity
public class teacher implements Serializable{
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private int id;
	private String name;
	private int age;
        ...
}

mapper,暫時只寫兩個方法

@Mapper
public interface teachMapper {
	@Insert("insert into teacher values(null,#{age},#{name})") 
	public void save(teacher t);
	@Select("select * from teacher where id=#{id}")
	public teacher getById(int id);
	
}

service

@Service
public class teachService {
	@Autowired
	private teachMapper tm;

	public teacher getById(int id) {
		return tm.getById(id);
	}
	
	public void save(teacher t) {
		tm.save(t);
	}
}

Controller

@RestController
@RequestMapping("/teacher")
public class teachController {
	@Autowired
	private teachService ts;

	@GetMapping("/{id}")
	public teacher get(@PathVariable("id") int id) {
		return ts.getById(id);
	}
	@PostMapping("/save")
	public String save(teacher t) {
		ts.save(t);
		return "儲存成功";
	}
}

四:druid配置

用druid的原因:一個因為聽說它效能很強,另一個主要的原因為可以進行實時監控


@WebFilter(filterName="druidWebStatFilter",urlPatterns="/*",
initParams={
    @WebInitParam(name="exclusions",value="*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*")// 忽略資源
})
public class DruidStatFilter extends WebStatFilter {

}
@SuppressWarnings("serial")
@WebServlet(urlPatterns = "/druid/*", 

		initParams = { @WebInitParam(name = "allow", value = "127.0.0.1"), // IP白名單
		@WebInitParam(name = "deny", value = "192.168.0.0"), // IP黑名單 (存在共同時,deny優先於allow)
		@WebInitParam(name = "loginUsername", value = "admin"), // 使用者名稱
		@WebInitParam(name = "loginPassword", value = "admin"), // 密碼
		@WebInitParam(name = "resetEnable", value = "false")// 禁用HTML頁面上的“Reset All”功能
})
public class DruidStatViewServlet extends StatViewServlet {

}

最後,在啟動類上面加上@ServletComponentScan

@SpringBootApplication
//注意要加上@ServletComponentScan註解,否則Servlet無法生效
@ServletComponentScan
public class TomApplication {

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

簡直優秀。

二:將Redis作為mysql的二級快取

配置Redis

spring:
 redis:
    host: localhost
    port: 6379
    database: 1
    jedis:
      pool:
        max-idle: 8
        min-idle: 1
        max-active: 8
        max-wait: -1

接下來的步驟:①開啟mybatis的二級快取 ②重寫快取類  ③將mybatis的快取替換為們重寫的

重點:我們重寫mybatis的cache類,它只會對配置檔案型別的對映檔案起作用

<cache type=“我們重寫的cache類”/>

主要的作用如下:

1.所有在對映檔案裡的 select 語句都將被快取。 2.所有在對映檔案裡 insert,update 和 delete 語句會清空快取。 3.快取使用“最近很少使用”演算法來回收 4.快取不會被設定的時間所清空。

一:開啟二級快取,配置中加一條就好

mybatis:
      configuration:
        cache-enabled: true

二:重寫快取類

這裡也有一個問題,由於cache不是容器中的類,我們需要獲得applicationContext

@Component
public class ApplicationContextHolder implements ApplicationContextAware{

    private static ApplicationContext applicationContext;

    /**
     * 實現ApplicationContextAware介面的context注入函式, 將其存入靜態變數.
     */
    public void setApplicationContext(ApplicationContext applicationContext) {
        ApplicationContextHolder.applicationContext = applicationContext; // NOSONAR
    }

    /**
     * 取得儲存在靜態變數中的ApplicationContext.
     */
    public static ApplicationContext getApplicationContext() {
        checkApplicationContext();
        return applicationContext;
    }

    /**
     * 從靜態變數ApplicationContext中取得Bean, 自動轉型為所賦值物件的型別.
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) {
        checkApplicationContext();
        return (T) applicationContext.getBean(name);
    }

    /**
     * 從靜態變數ApplicationContext中取得Bean, 自動轉型為所賦值物件的型別.
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(Class<T> clazz) {
        checkApplicationContext();
        return (T) applicationContext.getBeansOfType(clazz);
    }

    /**
     * 清除applicationContext靜態變數.
     */
    public static void cleanApplicationContext() {
        applicationContext = null;
    }

    private static void checkApplicationContext() {
        if (applicationContext == null) {
            throw new IllegalStateException("applicaitonContext未注入,請在applicationContext.xml中定義SpringContextHolder");
        }
    }


}

重寫快取類

public class RedisCache implements Cache {
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private final String id; // cache instance id
    private RedisTemplate redisTemplate;
    private static final long EXPIRE_TIME_IN_MINUTES = 30; // redis過期時間
    public RedisCache(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
    @SuppressWarnings("unchecked")
    public void putObject(Object key, Object value) {
        RedisTemplate redisTemplate = getRedisTemplate();
        ValueOperations opsForValue = redisTemplate.opsForValue();
        opsForValue.set(key, value, EXPIRE_TIME_IN_MINUTES, TimeUnit.MINUTES);
        System.out.println("放入結果到快取");
    }
    /**
     * Get cached query result from redis
     *
     * @param key
     * @return
     */
    @Override
    public Object getObject(Object key) {
        RedisTemplate redisTemplate = getRedisTemplate();
        ValueOperations opsForValue = redisTemplate.opsForValue();
        System.out.println("獲取結果從快取");
        return opsForValue.get(key);
    }
    /**
     * Remove cached query result from redis
     *
     * @param key
     * @return
     */
    @Override
    @SuppressWarnings("unchecked")
    public Object removeObject(Object key) {
        RedisTemplate redisTemplate = getRedisTemplate();
        redisTemplate.delete(key);
        System.out.println("從快取刪除了");
        return null;
    }
    /**
     * Clears this cache instance
     */
    @Override
    public void clear() {
        RedisTemplate redisTemplate = getRedisTemplate();
        redisTemplate.execute((RedisCallback) connection -> {
            connection.flushDb();
            return null;
        });
        System.out.println("清空快取");
    }
    @Override
    public int getSize() {
        return 0;
    }
    @Override
    public ReadWriteLock getReadWriteLock() {
        return readWriteLock;
    }
    private RedisTemplate getRedisTemplate() {
        if (redisTemplate == null) {
            redisTemplate = ApplicationContextHolder.getBean("redisTemplate");
        }
        return redisTemplate;
    }
}

我在對映檔案中寫了增,刪,查3個方法

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.tom.mapper.teachMapper">
	<cache type="com.tom.redisConfig.RedisCache"/>
	<select id="getAll" resultType="teacher">
	select * from teacher 
	</select>
	<select id="getById" resultType="teacher">
	select * from teacher where id=#{id}
	</select>
	<insert id="save">
	insert into teacher values(null,#{age},#{name})
	</insert>
	<delete id="delete">
	delete from teacher where id=#{id}
	</delete>
</mapper>

OK!!讓我看一下效果

結果為:select會新增資料到redis,insert,update,delete會清空快取

但是這樣子好像有點坑了,除select外的操作都要清空快取,有待研究