8.java程序员必知必会类库之嵌入式SQL数据库

前言

嵌入式内存数据库,作为嵌入到应用内部的数据库,在正常生产业务流程中使用不多。现在一般公司通用架构都是应用和数据分离,解耦数据和应用。但是,在某些特殊场景中,这种嵌入式数据库是比较好的选择。

  1. 在某些单元测试的时候,如果需要一个数据库验证你的SQL脚本,此时H2就是一个很好的选择
  2. 应用需要通过SQL计算大批量指标,这些指标如果放在mysql这种可能会很慢,通过内存计算管理会极大提高效率
  3. 开发的程序里面包含很多指标计算逻辑(通过SQL计算),当需要导出作为依赖包运行的时候,此时内存数据库就是一个很好的选择

1. H2

1.1 简介

  1. h2采用纯Java编写,因此不受平台的限制。
  2. h2只有一个jar文件,十分适合作为嵌入式数据库试用。
  3. h2提供了一个十分方便的web控制台用于操作和管理数据库内容。
  4. 开源代码,方便修改,自定义函数等需求

1.2 使用

1.2.1 pom坐标导入

<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>2.1.210</version>
    <scope>test</scope>
</dependency>

1.2.2 demo

1.2.2.1 H2连接工具类
public class H2DbUtil {
    private static final ArrayBlockingQueue<Connection> CONNECTION_POOL = new ArrayBlockingQueue<>(50);

    static {
        try {
            synchronized (CONNECTION_POOL) {
                Class.forName("org.h2.Driver");
                //String url = "jdbc:h2:mem:pbocdb;MODE=MySQL;DB_CLOSE_DELAY=-1;MULTI_THREADED=1;MV_STORE=FALSE;LOG=0;REDO_LOG_BINARY=0;UNDO_LOG=0";
                String url = "jdbc:h2:~/H2Database/h2/bin/test";

                for (int i = 0; i < 10; i++) {
                    CONNECTION_POOL.add(DriverManager.getConnection(url, "sa", ""));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public static List<Map<String, Object>> execute(String sql, String reportNo) throws Exception {
        List<Map<String, Object>> records = new ArrayList<>();
        Connection connection = null;
        try {
            connection = CONNECTION_POOL.take();
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            int count = preparedStatement.getParameterMetaData().getParameterCount();
            for (int i = 1; i <= count; i++) {
                preparedStatement.setString(i, reportNo);
            }
            ResultSet resultSet = preparedStatement.executeQuery();
            ResultSetMetaData md = resultSet.getMetaData();
            int columnCount = md.getColumnCount();

            // 数据
            Map<String, Object> rowData;
            while (resultSet.next()) {
                rowData = new HashMap<>(columnCount);
                for (int i = 1; i <= columnCount; i++) {
                    String columnName = md.getColumnLabel(i).toLowerCase().replaceAll("[ ()'`\r\n\t]","");
                    columnName = columnName.replaceAll("public\\.", "");
                    rowData.put(columnName, resultSet.getObject(i));
                }
                records.add(rowData);
            }
        } finally {
            if (connection != null) {
                release(connection);
            }
        }
        return records;
    }

    public static Connection getConnection() throws Exception {
        return CONNECTION_POOL.take();
    }

    public static void release(Connection connection){
        if (connection != null) {
            try {
                CONNECTION_POOL.put(connection);
            } catch (Exception ignored) {
            }
        }
    }

}
1.2.2.2 测试类

通过H2执行脚本示例如下:

@Test
public void testH2() throws Exception{

    Connection connection = H2DbUtil.getConnection();
    Statement stmt = connection.createStatement();
    // 如果存在USERS表就先删除USERS表
    stmt.execute("DROP TABLE IF EXISTS USERS");
    // 创建users表
    stmt.execute("create table users("
            + "    id int primary key,"
            + "    name varchar(40),"
            + "    password varchar(40))");
    // 新增
    stmt.executeUpdate("INSERT INTO users VALUES(1,'张三','12')");
    stmt.executeUpdate("INSERT INTO users VALUES(2,'李四','34')");
    stmt.executeUpdate("INSERT INTO users VALUES(3,'王五','56')");
    stmt.executeUpdate("INSERT INTO users VALUES(4,'麻六','78')");
    stmt.executeUpdate("INSERT INTO users VALUES(5,'邹七','90')");
    List<Map<String, Object>> execute = H2DbUtil.execute("select * from  users where  id = ?", "1");
    //遍历结果集
    for (Map<String, Object> objectMap : execute) {
        System.out.println(objectMap.toString());
    }
    H2DbUtil.release(connection);
}

代码输出结果:

{name=张三, password=12, id=1}

1.3 界面展示

1.3.1 新建一个空的springboot web项目

pom文件如下:


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

1.3.2 springboot配置文件添加如下内容

server.port=8080
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.h2.console.settings.web-allow-others=true

1.3.3 访问页面

启动项目,访问地址 : http://localhost:8080/h2-console/login.jsp
可以看到如下界面:
在这里插入图片描述
点击连接,进入展示如下,可以看到界面类似我们常用的数据库连接客户端navicat。可以在窗口执行数据的增删改查,但是注意这数据都是在内存中,应用重启,数据会丢失。
在这里插入图片描述

1.4 注意事项

  1. H2 维护的数据都是在内存中,应用重启数据会丢失
  2. H2 SQL语法和mysql上面极度相似,但是在具体的函数处理上面,两边处理的结果可能会有一些细微的差异,如果你想在H2上面跑MySql的脚本,要提前感知可能的差异性
  3. 如果比对出来不一致,可以将源码拉下来调整相关源码,做兼容处理,但是这只能查漏补缺,只有发现不一致,针对性调整。比如发现日期函数处理和mysql有差异,那可以调整源码对应函数做兼容,但是这没法发现所有不一致性。
  4. 在界面上,可能会误删除连接配置项,然后项目重启,浏览器重启还是无法连接H2,这是因为删除信息会写到本地磁盘里面, 需要删除window用户目录下面的h2.server.properties文件,liunx 是home文件下。

1.5 进阶

1.5.1 自定义函数

1.5.1.1 自定义函数类
public class CustomFunction {

    public static String hi() {
        return "hello";
    }

    public static String my_uuid() {
        return "test" + UUID.randomUUID().toString();
    }

    public static String now() {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date();
        String dateStr = simpleDateFormat.format(date);
        return dateStr;
    }

    public static String getIp() {
        try {
            InetAddress addr = InetAddress.getLocalHost();
            // 获得本机IP
            return addr.getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
            return "未知的IP地址";
        }
    }

    public static String date_format(String date, String pattern) {
        if (date != null) {
            SimpleDateFormat sdf = new SimpleDateFormat(pattern);
            try {
                Date temp = sdf.parse(date);
                return sdf.format(temp);
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
        return "";
    }
}
1.5.1.2 测试类
@BeforeClass
public static void testH2Function() throws Exception {

    Connection connection = H2DbUtil.getConnection();
    // 0、注册hi函数的SQL语句
    String sql0 = "CREATE ALIAS IF NOT EXISTS hello FOR \"com.wanlong.memoryDB.h2.CustomFunction.hi\"";
    // 1、注册uuid函数的SQL语句
    String sql1 = "CREATE ALIAS IF NOT EXISTS my_uuid FOR \"com.wanlong.memoryDB.h2.CustomFunction.my_uuid\"";
    // 2、注册currentTime函数的SQL语句
    String sql2 = "CREATE ALIAS IF NOT EXISTS currentTime FOR \"com.wanlong.memoryDB.h2.CustomFunction.now\"";
    // 3、注册IP函数的SQL语句
    String sql3 = "CREATE ALIAS IF NOT EXISTS IP FOR \"com.wanlong.memoryDB.h2.CustomFunction.getIp\"";
    // 4、注册date_format函数的SQL语句
    String sql4 = "CREATE ALIAS IF NOT EXISTS date_format FOR \"com.wanlong.memoryDB.h2.CustomFunction.date_format\"";

    Statement stmt = null;

    // 获取Statement对象
    stmt = connection.createStatement();
    // 添加要执行的SQL
    stmt.addBatch(sql0);
    stmt.addBatch(sql1);
    stmt.addBatch(sql2);
    stmt.addBatch(sql3);
    stmt.addBatch(sql4);
    // 批量执行
    stmt.executeBatch();
    System.out.println("H2数据库扩展函数注册成功!");
    H2DbUtil.release(connection);
}


@Test
public void testCustomeFuntion() throws Exception {
    Connection connection = H2DbUtil.getConnection();
    Statement stmt = connection.createStatement();
    // 如果存在USERS表就先删除USERS表
    List<Map<String, Object>> execute = H2DbUtil.execute("select my_uuid()", "1");
    for (Map<String, Object> objectMap : execute) {
        System.out.println(objectMap.toString());
    }
    H2DbUtil.release(connection);
}
1.5.2.3 代码运行结果

可以看到,自定义函数执行了

 H2数据库扩展函数注册成功!
{my_uuid=test90899962-93e2-4f5e-be16-d6ca52c3a98c}

2 参考文献:

H2官网
H2参考博客

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
SQLite 作为一个开源的嵌入式数据库产品,具有系统开销小,检索效率高的特性,适用于手机、PDA、机顶盒设备等电器,并且作为嵌入式数据库在可下载的消费类应用程序中运行的很好。这篇文章介绍嵌入式数据库产品SQLite 的技术特点,并着重讨论研究与Java语言之间的接口,并以实例说明如何使用JAVA 开发基于SQLite 的应用程序。 正文: 通常我们采用各种数据库产品来实现对数据的存储、检索等功能,例如,Oracle,SQL Server, MySQL 等等。这些产品除提供基本的查询,删除,添加等功能外,也提供了很多高级特性,如触发器,存储过程,数据备份恢复,全文检索功能等。但实际上,很多的应用,仅仅利用到了这些数据库产品的基本特性而已。而且在一些小型应用上,或者某些特殊场合的应用,比如桌面程序,这些数据库产品就明显有一些臃肿。在这些情况下,嵌入式数据库的优势就特别明显了。 嵌入式数据库无须独立运行的数据库引擎,它是由程序直接调用相应的API 去实现对数据的存取操作。更直白的讲,嵌入式数据库是一种具备了基本数据库特性的数据文件。嵌入式数据库与其它数据库产品的区别是,前者是程序驱动式, 而后者是引擎响应式。嵌入式数据库的一个很重要的特点是它们的体积非常小,编译后的产品也不过几十K。这不但对桌面程序的数据存储方案是一个很好的选择,也使得它们可以应用到一些移动设备上。同时,很多嵌入式数据库在性能上也优于其它数据库,所以在高性能的应用上也常见嵌入式数据库的身影。 下面介绍的是开放源代码的嵌入式数据库SQLite。同时侧重研究如何应用Java 连接SQLite数据库,并开发基于SQLite 的应用程序。 ......
### 回答1: 《SQL必知必会》是一本由Ben Forta所著的经典SQL学习教材,在CSDN上有很高的推荐度和用户评价。 这本书以通俗易懂的语言介绍了SQL(Structured Query Language)的基础概念和使用方法。从最基础的SELECT语句开始,逐渐深入地介绍了SQL的各种操作和技巧,包括过滤、排序、统计、连接、子查询等。 这本书的优点之一是注重实践。作者通过大量的实例和练习题,让读者能够真正动手操作,加深对SQL的理解和熟练度。书中的练习题循序渐进,既能帮助初学者理解知识点,又能挑战更高层次的读者。 同时,本书也非常注重SQL语句的规范和性能优化。作者强调了编写高效SQL语句的重要性,教授了避免冗余查询、选择合适的索引等技巧,帮助读者提高查询速度和数据库的性能。 此外,《SQL必知必会》还介绍了一些数据库管理的基本知识,如创建和管理表、编写存储过程、触发器等。这些知识使读者能够更好地理解数据库系统和整个开发流程。 总的来说,《SQL必知必会》是一本很好的SQL入门教材,适合想要学习SQL的初学者和希望加强SQL技能的读者。它的简洁明了的教学风格和丰富的实例操作,使读者能够迅速入门并掌握SQL的基本知识和技巧。无论是在工作中还是面试中,掌握好SQL技能都是非常重要的,因此《SQL必知必会》是一本不可或缺的参考书籍。 ### 回答2: 《SQL 必知必会》是由Ben Forta所写的一本经典的SQL学习指南。本书以简单易懂的语言、通俗易懂的案例,全面而系统地介绍了SQL的基础知识和常用操作。 本书首先从SQL的起源和发展历程开始,介绍了SQL的作用、应用领域以及其与其他数据库语言的关系。接着,本书详细讲解了SQL的几个基本组成部分,包括数据定义语言(DDL)、数据查询语言(DQL)、数据操作语言(DML)和数据控制语言(DCL)。 在学习SQL语句的过程中,作者通过大量的实例和图表来解释SQL的各种语法和用法。读者可以通过跟着书中的案例一步一步实践,加深对SQL的理解和掌握。此外,书中还介绍了常见的SQL函数、约束、事务等内容,帮助读者进一步提高SQL的应用能力。 《SQL 必知必会》还对数据库的设计和优化进行了一定的介绍,使读者能够更好地理解数据库的运行原理和优化思路。此外,本书还提供了一些常见的数据库错误和解决方案,帮助读者避免常见的问题和错误。 总的来说,本书从入门到进阶都有涉及,内容详实且易懂。不仅适合初学者入门,也适合有一定SQL基础的开发者进一步提高。无论是学习数据库的基础知识,还是使用SQL解决实际问题,本书都是一本不可多得的学习资料。无论是从事数据分析、数据挖掘,还是数据库开发、管理等方面的人员,都应该拥有这本书,作为自己学习和工作中的重要参考书籍。 ### 回答3: 《SQL必知必会》是一本由柯特·戈维(Ben Forta)所著的SQL入门经典教材,已广为程序员数据库爱好者所熟悉。该书系统地介绍了SQL的基本概念、语法和应用,并提供了大量的实例和练习题供读者巩固学习。 《SQL必知必会》主要分为四个部分:查询基础,过滤数据,数据操作和高级特性。首先,它详细介绍了SQL语言的基础知识,包括数据库的概念、表的创建和插入数据等。然后,它讲解了如何使用查询语句来过滤和排序数据,以及使用运算符和函数来处理数据。接着,它介绍了如何更新、插入和删除表中的数据,以及如何创建和修改表结构。最后,它介绍了SQL语言的高级特性,如多表查询、子查询、视图和索引等。 《SQL必知必会》在教学方法上非常简洁明了,注重实践。每个概念和语法都通过实例进行讲解,并提供了大量的练习题供读者巩固学习。此外,书中还提供了SQL语句的常见错误和解决方法,帮助读者更好地理解和应用SQL语言。 总之,《SQL必知必会》作为一本SQL入门经典教材,非常适合初学者学习和掌握SQL语言。无论是作为学习资料还是作为参考手册,它都能帮助读者快速入门并实际应用SQL语言进行数据库操作。读者通过学习本书可以牢固掌握SQL的基础知识,并能够运用SQL语言进行数据库的查询、更新和管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值