玖叶教程网

前端编程开发入门

Spring Boot 缓存之内嵌H2数据库,内附GitHub源码

话不多说,本文手把手教你 怎么用 H2 数据库当做缓存来使用增强系统性能

一、H2 简介

1、H2数据库是一个开源的关系型数据库。H2采用java语言编写,不受平台的限制,同时支持网络版和嵌入式版本,有比较好的兼容性,支持相当标准的sql标准

2、提供JDBC、ODBC访问接口,提供了非常友好的基于web的数据库管理界面

数据库管理界面

官网: http://www.h2database.com/

二、H2 运行模式

H2有三种运行模式。

1、内嵌模式(Embedded Mode)

内嵌模式下,应用和数据库同在一个JVM中,通过JDBC进行连接。 可持久化,但同时只能一个客户端连接。内嵌模式性能会比较好。

2、服务器模式(Server Mode)

使用服务器模式和内嵌模式一样,只不过它可以跑在另一个进程里

3、 混合模式

第一个应用以内嵌模式启动它,对于后面的应用来说它是服务器模式跑着的。混合模式是内嵌模式和服务器模式的组合。第一个应用通过内嵌模式与数据库建立连接,同时也作为一个服务器启动,于是另外的应用(运行在不同的进程或是虚拟机上)可以同时访问同样的数据。第一个应用的本地连接与嵌入式模式的连接性能一样的快,而其它连接理论上会略慢。

三、H2 优势

  • 纯Java编写,不受平台的限制;
  • 只有一个jar文件,适合作为嵌入式数据库使用;
  • h2提供了一个十分方便的web控制台用于操作和管理数据库内容;
  • 功能完整,支持标准SQL和JDBC。麻雀虽小五脏俱全;
  • 支持内嵌模式、服务器模式和集群。
  • 占用空间小:大约1.5 MB JAR文件大小
  • 速度快

其他数据库的比较

四、H2 集成SpringBoot 项目

一般情况下我们都会已经有一个数据源了,H2 也是一个数据库,所以我们需要配置多数据源

首先: 在 application.properties 中增加如下内容

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

file: 后面是文件的路径地址

~ 代表当前用户目录

然后: 在原有数据源配置上增加 @Primary 注解

作为主要数据源

最后: 增加H2数据源配置

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;
 }
 }

七、案例源码地址

GitHub: https://github.com/qiaohhgz/spring-boot-cache/tree/h2-cache

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言