Spring Boot 缓存之内嵌H2数据库

IT实战联盟 2019-02-20 15:13:22 ⋅ 83 阅读

本文手把手教你 怎么用 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<StringObject>> 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<StringObject> 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<StringObject>> list = h2JdbcTemplate.queryForList(sql, key);
        if (list == null || list.isEmpty()) {
            return null;
        }
        Map<StringObject> 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<StringObject>> list = h2JdbcTemplate.queryForList(sql, key);
        if (list == null || list.isEmpty()) {
            return false;
        }
        Map<StringObject> 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

关注我们

---------------END----------------

后续的内容同样精彩

长按关注“IT实战联盟”哦




全部评论: 0

    我有话说:

    微服务架构学习笔记:gRPC Spring Boot Starter 2.2.0 发布,及使用步骤

    gRPC Spring Boot Starter 项目是一个 gRPC 的 Spring Boot 模块。内一个 gRPC Server 对外提供服务,并支持 Spring Cloud 的服务发现

    Spring Boot 2.4.0-RC1, 2.1.18, 2.2.11 和 2.3.5 发布

    Spring Boot 多个分支发布了新版本,分别是 2.4.0-RC1, 2.1.18, 2.2.11 和 2.3.5。 Spring Boot 2.4.0-RC1 此版本是 

    架构实战篇(十四):Spring Boot缓存实战

    多场景下的不同缓存策略解决方案

    Spring Boot 2.1.0新特性 ,准备好了吗?!

    Spring Boot官方在10月30号正式发布了v2.1.0.RELEASE版本,由于本人最近比较忙,直到今天才着重抽空学习和了解这次升级一共带来了9大新特性,根据官方描述,正在逐渐支持 java

    微服务架构实战篇(六):Spring boot2.x 集成阿里大鱼短信接口详解与Demo

    Spring boot2.x 集成阿里大鱼短信接口,发送短信验证码及短信接口详解。

    使用Spring Boot Actuator监控应用

    Actuator是Spring Boot提供的对应用系统的自省和监控的集成功能,可以对应用系统进行配置查看

    Spring Boot 开发利器

    工欲善其事,必先利其器每次创建新项目都要找各种依赖的包是不是很烦?今天给大家介绍下 Spring 官方提供的 Online 项目脚手架

    架构实战篇(十七):Spring Boot Assembly 整合 thymeleaf

    如何让服务器上的 sprig boot 项目升级变的方便快捷

    码云推荐:一个优秀的分布式spring boot/Spring Cloud API限流框架,特别适合微服务架构

    一个优秀的分布式spring boot/Spring Cloud API限流框架,特别适合微服务架构.

    架构实战篇(三)-Spring Boot架构搭建RESTful API案例

    之前分享了Spring Boot 整合Swagger 让API可视化和前后端分离架构 受到了大家一致好评 ,本节就接着上节的代码做了详细的查询代码的补充和完善并搭建RESTful API架构案例。

    架构实战篇(八):Spring Boot 集成 Druid 数据源监控

    Druid是目前最好的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。

    架构实战篇(七):Spring Boot Data JPA 快速入门

    Spring Data JPA 是Spring Data 的一个子项目,它通过提供基于JPA的Repository极大了减少了操作JPA的代码。

    架构实战篇(六):Spring Boot RestTemplate的使用

    RestTemplate是Spring提供的用于访问Rest服务的客户端,RestTemplate提供了多种便捷访问远程Http服务的方法,能够大大提高客户端的编写效率。

    Spring Boot 2.3.6 发布,Bugfix 版本

    Spring Boot 2.3.6 已发布,可从 Maven 中央仓库获取新版本。 <!-- https://mvnrepository.com/artifact/org

    架构实战篇(十五):Spring Boot 解耦之事件驱动

    通过使用spring 事件来解决业务代码的耦合

    Spring Boot 2.4.3、2.3.9 版本发布

    Spring Boot 2.4.3 、2.3.9 已经发布。现在可从 repo.spring.io 和 Maven Central 获得。 <parent>   

    架构实战篇(一)-Spring Boot+MyBatis基础架构搭建

    Spring的追求一定是简单点简单点,让java的开发变得更加简单、容易。瞧瞧的告诉你们直接copy就能用哦~~~

    架构实战篇(五):Spring Boot 表单验证和异常处理

    为了让API 能够更好的提供服务,表单数据验证和异常的处理是必不可少的,让我们来看看怎么处理......