话不多说,本文手把手教你 怎么用 H2 数据库当做缓存来使用增强系统性能 1、H2数据库是一个开源的关系型数据库。H2采用java语言编写,不受平台的限制,同时支持网络版和嵌入式版本,有比较好的兼容性,支持相当标准的sql标准 2、提供JDBC、ODBC访问接口,提供了非常友好的基于web的数据库管理界面 数据库管理界面 官网: http://www.h2database.com/ H2有三种运行模式。 1、内嵌模式(Embedded Mode) 内嵌模式下,应用和数据库同在一个JVM中,通过JDBC进行连接。 可持久化,但同时只能一个客户端连接。内嵌模式性能会比较好。 2、服务器模式(Server Mode) 使用服务器模式和内嵌模式一样,只不过它可以跑在另一个进程里。 3、 混合模式 第一个应用以内嵌模式启动它,对于后面的应用来说它是服务器模式跑着的。混合模式是内嵌模式和服务器模式的组合。第一个应用通过内嵌模式与数据库建立连接,同时也作为一个服务器启动,于是另外的应用(运行在不同的进程或是虚拟机上)可以同时访问同样的数据。第一个应用的本地连接与嵌入式模式的连接性能一样的快,而其它连接理论上会略慢。 其他数据库的比较 一般情况下我们都会已经有一个数据源了,H2 也是一个数据库,所以我们需要配置多数据源 首先: 在 application.properties 中增加如下内容 file: 后面是文件的路径地址 ~ 代表当前用户目录 然后: 在原有数据源配置上增加 @Primary 注解 作为主要数据源 最后: 增加H2数据源配置 先定义一个标准的缓存接口类 初始化 常用接口实现 GitHub: https://github.com/qiaohhgz/spring-boot-cache/tree/h2-cache一、H2 简介
二、H2 运行模式
三、H2 优势
四、H2 集成SpringBoot 项目
spring.datasource.h2.url=jdbc:h2:file:~/.h2/h2
spring.datasource.h2.username=
spring.datasource.h2.password=
spring.datasource.h2.driver-class-name=org.h2.Driver
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration
public class H2DataSourceConfig {
@Bean(name = "h2")
@Qualifier("h2")
@ConfigurationProperties(prefix="spring.datasource.h2")
DataSource h2(){
return DataSourceBuilder.create().build();
}
@Bean(name = "h2JdbcTemplate")
JdbcTemplate h2JdbcTemplate(@Autowired @Qualifier("h2") DataSource h2){
return new JdbcTemplate(h2);
}
}
五、定义缓存接口规范
public interface CacheService {
String getValue(String key);
boolean hasKey(String key);
void setValue(String key, String val);
void setValue(String key, String val, long seconds);
}
六、实现H2 的缓存
@PostConstruct
public void init() {
// 验证数据库是否存在
String createTableSql = "CREATE CACHED TABLE PUBLIC.MAP(\n" +
" KEY TEXT NOT NULL,\n" +
" VALUE TEXT,\n" +
" EXPIRE LONG\n" +
")";
List<Map<String, Object>> list = h2JdbcTemplate.queryForList("SELECT t.TABLE_NAME FROM INFORMATION_SCHEMA.TABLES t WHERE TABLE_SCHEMA = 'PUBLIC' and TABLE_NAME = 'MAP' LIMIT 1");
if (list == null || list.isEmpty()) {
logger.info("auto create table execute sql > " + createTableSql);
h2JdbcTemplate.update(createTableSql);
}
Thread thread = new Thread() {
@Override
public void run() {
while (true) {
try {
// 查询缓存数量
Map<String, Object> map = h2JdbcTemplate.queryForMap("select count(*) as total from map");
long total = (long) map.get("total");
// 清除过期的缓存数据
long date = System.currentTimeMillis() / 1000;
int rows = h2JdbcTemplate.update("delete from map where expire <= ?", date);
logger.info("total caches {} , delete expire date rows {}", total, rows);
// 等待一会
Thread.sleep(10 * 1000);
} catch (Exception e) {
logger.warn("auto delete expire data thread error " + e.getMessage());
}
}
}
};
thread.setDaemon(true);
thread.start();
}
@Override
public String getValue(String key) {
String sql = "select value, expire from map where key = ? limit 1";
List<Map<String, Object>> list = h2JdbcTemplate.queryForList(sql, key);
if (list == null || list.isEmpty()) {
return null;
}
Map<String, Object> data = list.get(0);
Long expire = (Long) data.get("expire");
long date = System.currentTimeMillis() / 1000;
if (expire != null && expire <= date) {
h2JdbcTemplate.update("delete from map where key = ?", key);
return null;
}
return (String) data.get("value");
}
@Override
public boolean hasKey(String key) {
String sql = "select value, expire from map where key = ? limit 1";
List<Map<String, Object>> list = h2JdbcTemplate.queryForList(sql, key);
if (list == null || list.isEmpty()) {
return false;
}
Map<String, Object> data = list.get(0);
Long expire = (Long) data.get("expire");
long date = System.currentTimeMillis() / 1000;
if (expire != null && expire <= date) {
logger.info("key {} is expired", key);
h2JdbcTemplate.update("delete from map where key = ?", key);
return false;
}
return true;
}
@Override
public void setValue(String key, String val) {
if (hasKey(key)) {
String sql = "update map set value = ? where key = ?";
h2JdbcTemplate.update(sql, val, key);
} else {
String sql = "insert into map (key, value) values(?,?)";
h2JdbcTemplate.update(sql, key, val);
}
}
@Override
public void setValue(String key, String val, long seconds) {
long expire = System.currentTimeMillis() / 1000 + seconds;
if (hasKey(key)) {
String sql = "update map set value = ?, expire = ? where key = ?";
h2JdbcTemplate.update(sql, val, expire, key);
} else {
String sql = "insert into map values(?,?,?)";
h2JdbcTemplate.update(sql, key, val, expire);
}
}
业务使用
@Override
public List<HelpCategoryVo> selectList(HelpCategoryForm form) {
String finger = cacheService.getFinger(form);
String key = String.format("category:selectList:%s", finger);
if (cacheService.hasKey(key)) {
String json = cacheService.getValue(key);
return JSON.parseObject(json, new ArrayList<HelpCategoryVo>().getClass());
} else {
log.info("query category list from db > " + JSON.toJSON(form));
List<HelpCategoryVo> data = helpCategoryDao.selectList(form);
cacheService.setValue(key, JSON.toJSONString(data), 30);
return data;
}
}
七、案例源码地址