Java-低代码平台使用H2内存数据库

news2024/11/24 0:58:40

一、引言

        作者目前在做的平台使用到了H2,这里介绍下使用场景、使用方式,出于以下两个原因会使用H2:

        1、平台化的项目一般是用户使用脚本或者sql进行通用的执行,这样可以实现低代码平台,不需要管理类之间的引入、依赖、编译,页面上点点点和输入就可以了,所以很多时候需要把数据放入H2进行sql解析实现跨库、跨实例、跨服务的数据分析

        2、在数据集合写起来非常复杂的时候,举个例子:集合a、b,ab的对象有字段c是一一对应的,然后集合a的其他字段和b元素下面挂着的集合又是对应的,这个组合写起来就很麻烦,但是如果存入H2用sql进行关联组合,形成一张大宽表,那么接下来的逻辑和分析验证就会好做。

二、H2介绍

        H2是一个用Java编写的开源关系型数据库管理系统(RDBMS)。它被设计为一个嵌入式数据库,可以作为应用程序的一部分直接嵌入到Java应用程序中使用,也可以作为独立的数据库服务器运行。

        H2数据库具有以下特点:

1. 嵌入式数据库:H2数据库可以作为一个库文件嵌入到Java应用程序中,这样应用程序可以直接访问和管理数据库,而无需额外的数据库服务器。
2. 支持多种模式:H2数据库支持多种模式,包括内存模式、磁盘模式和混合模式。内存模式可以用于临时数据存储,磁盘模式可以持久化数据到磁盘,混合模式可以将数据存储在内存和磁盘上,提供更高的性能和可靠性。
3. 支持多种数据库引擎:H2数据库支持多种数据库引擎,包括嵌入式模式、服务器模式和集群模式。嵌入式模式适用于单个应用程序,服务器模式适用于多个应用程序共享数据库,集群模式适用于高可用性和负载均衡的场景。
4. 支持标准SQL语法:H2数据库支持标准的SQL语法,包括DDL(数据定义语言)、DML(数据操作语言)和DQL(数据查询语言)。它还支持事务、索引、触发器、存储过程和用户定义函数等高级特性。
5. 轻量级和高性能:H2数据库是一个轻量级的数据库管理系统,具有快速的启动时间和低内存消耗。它采用了高效的数据存储和索引算法,提供了优秀的读写性能。
6. 跨平台支持:H2数据库可以在多个平台上运行,包括Windows、Linux和Mac OS等。

三、使用

1、Pom

        要引入H2的依赖,还有链接池有已经封装好的,毕竟不能用一下h2就开一个链接,这个消耗的句柄和socket就可怕了。链接复用是必须的。


            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
                <version>2.2.220</version>
            </dependency>
            <dependency>
                <groupId>com.zaxxer</groupId>
                <artifactId>HikariCP</artifactId>
                <version>4.0.3</version>
            </dependency>

2、链接

        这里只要引入H2的依赖包就会自动在运行时创建

        url = "jdbc:h2:mem:testdb";

        username = "sa";
        password = "";

        的内存数据库,所以要是不用很多包是不能随便引入的,你不知道这些包都自己做了什么事。

public class H2ConnectionPool {
    private static final String url = "jdbc:h2:mem:testdb";
    private static final String username = "sa";
    private static final String password = "";
    private static final HikariConfig config = new HikariConfig();
    private static final HikariDataSource dataSource;
    static {
        config.setJdbcUrl(url);
        config.setUsername(username);
        config.setPassword(password);
        // 设置连接池大小,默认为10
        config.setMaximumPoolSize(10);
        dataSource = new HikariDataSource(config);
    }
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    public static void close() {
        dataSource.close();
    }
}

3、建表

        实时动态的建表就需要知道表名称,字段名称和类型,有两种情况,一种是从数据库查出来的ResultSet,经过转化之后会变成List<HashMap<String,Object>>,hashmap是表的字段名和值,每行数据形成了一个map,做过底层通用转化的应该都知道,不清楚的同学也没关系,可以私聊作者。

        这种情况就可以根据map生成建表语句。

public static boolean createTable(String tableName, HashMap map) throws SQLException {
        if (StringUtilsExt.isBlank(tableName)) {
            return false;
        }
        if (map == null) {
            return false;
        }

        Connection conn = null;
        Statement stmt = null;
        try {
            // 连接到H2数据库
            conn = H2ConnectionPool.getConnection();
            // 创建Statement对象
            stmt = conn.createStatement();

            // 创建表的SQL语句
            String sql = getCreateSql(tableName, map);
            // 执行SQL语句
            stmt.executeUpdate(sql);
        } finally {
            if (conn != null) {
                conn.close();
            }
            if (stmt != null) {
                stmt.close();
            }
        }
        return true;
    }

    private static String getCreateSql(String tableName, HashMap map) {
        StringBuilder builder = new StringBuilder("CREATE TABLE " + tableName + " (");
        map.forEach((key, value) -> {
            builder.append(key);
            builder.append(" ");
            if (value instanceof Long) {
                builder.append("bigint");
            } else if (value instanceof Integer) {
                builder.append("int");
            } else if (value instanceof String) {
                builder.append("varchar");
            } else if (value instanceof BigDecimal) {
                builder.append("decimal");
            } else if (value instanceof Boolean) {
                builder.append("int");
            } else if (value instanceof Timestamp) {
                builder.append("datetime");
            }
            builder.append(",");
        });
        builder.deleteCharAt(builder.length() - 1);
        // 创建表的SQL语句
        return builder + ")";
    }

        第二种情况是调用接口或者查其他一些数据的时候,得到的是List<T>,那就要根据反射获取T的字段名称和值了。

public boolean createTableByT(String tableName, T t) throws SQLException {
        if (StringUtilsExt.isBlank(tableName)) {
            return false;
        }
        if (t == null) {
            return false;
        }

        Connection conn = null;
        Statement stmt = null;
        try {
            // 连接到H2数据库
            conn = H2ConnectionPool.getConnection();
            // 创建Statement对象
            stmt = conn.createStatement();

            // 创建表的SQL语句
            String sql = getCreateSqlByT(tableName, t);
            // 执行SQL语句
            stmt.executeUpdate(sql);
        } finally {
            if (conn != null) {
                conn.close();
            }
            if (stmt != null) {
                stmt.close();
            }
        }
        return true;
    }

    private String getCreateSqlByT(String tableName, T t) {
        StringBuilder builder = new StringBuilder("CREATE TABLE " + tableName + " (");
        Class<?> clazz = t.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String fieldName = field.getName();
            Object fieldValue = null;
            try {
                fieldValue = field.get(t);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            builder.append(fieldName);
            builder.append(" ");
            if (fieldValue instanceof Long) {
                builder.append("bigint");
            } else if (fieldValue instanceof Integer) {
                builder.append("int");
            } else if (fieldValue instanceof String) {
                builder.append("varchar");
            } else if (fieldValue instanceof BigDecimal) {
                builder.append("decimal");
            } else if (fieldValue instanceof Boolean) {
                builder.append("int");
            } else if (fieldValue instanceof Timestamp) {
                builder.append("datetime");
            }
        }
        builder.deleteCharAt(builder.length() - 1);
        // 创建表的SQL语句
        return builder + ")";
    }

4、插数据

        第一种是根据数据库转化的map插入

public static boolean insertByHashMap(String tableName, List<HashMap> listMap) throws SQLException {

        Connection connection = null;
        PreparedStatement statement = null;
        try {
            // 连接到H2数据库
            connection = H2ConnectionPool.getConnection();
            HashMap<String, Integer> indexMap = new HashMap<>(listMap.get(0).size());
            String sql = getInsertSql(tableName, listMap.get(0), indexMap);
            statement = connection.prepareStatement(sql);

            setValue(listMap, indexMap, statement);
            // 执行批处理
            int[] rowsInserted = statement.executeBatch();
            if (rowsInserted.length != listMap.size()) {
                return false;
            }
        } finally {
            if (connection != null) {
                connection.close();
            }
            if (statement != null) {
                statement.close();
            }
        }

        return true;
    }

    private static void setValue(List<HashMap> listMap, HashMap<String, Integer> indexMap,
            PreparedStatement statement) throws SQLException {
        // 批量插入数据
        for (HashMap m : listMap) {
            m.forEach((key, value) -> {
                if (value instanceof Long) {
                    try {
                        statement.setLong(indexMap.get(key), (Long)value);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                } else if (value instanceof Integer) {
                    try {
                        statement.setInt(indexMap.get(key), (Integer)value);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                } else if (value instanceof String) {
                    try {
                        statement.setString(indexMap.get(key), (String)value);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                } else if (value instanceof BigDecimal) {
                    try {
                        statement.setBigDecimal(indexMap.get(key), (BigDecimal)value);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                } else if (value instanceof Boolean) {
                    int flag = (Boolean) value ? 1 : 0;
                    try {
                        statement.setInt(indexMap.get(key), flag);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                } else if (value instanceof Byte) {
                    try {
                        statement.setByte(indexMap.get(key), (Byte) value);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                }
            });
            statement.addBatch();
        }
    }

    private static String getInsertSql(String tableName, HashMap map, HashMap<String, Integer> indexMap) {
        StringBuilder builder = new StringBuilder("INSERT INTO " + tableName + " (");
        StringBuilder builder2 = new StringBuilder(" VALUES (");
        AtomicInteger index = new AtomicInteger(1);
        map.forEach((key, value) -> {
            builder.append(key);
            builder.append(" ");
            builder.append(",");
            builder2.append("?");
            builder2.append(" ");
            builder2.append(",");
            indexMap.put((String)key, index.get());
            index.getAndIncrement();
        });
        builder.deleteCharAt(builder.length() - 1);
        builder2.deleteCharAt(builder2.length() - 1);
        // 插入表的SQL语句
        return builder + ")" + builder2 + ")";
    }

        第二种就是根据list插入数据 


    public boolean insertByList(String tableName, List<T> list) throws SQLException {

        Connection connection = null;
        PreparedStatement statement = null;
        try {
            // 连接到H2数据库
            connection = H2ConnectionPool.getConnection();
            HashMap<String, Integer> indexMap = new HashMap<>();
            String sql = getInsertSqlByList(tableName, list.get(0), indexMap);
            statement = connection.prepareStatement(sql);

            setValueByList(list, indexMap, statement);
            // 执行批处理
            int[] rowsInserted = statement.executeBatch();
            if (rowsInserted.length != list.size()) {
                return false;
            }
        } finally {
            if (connection != null) {
                connection.close();
            }
            if (statement != null) {
                statement.close();
            }
        }

        return true;
    }

    private void setValueByList(List<T> list, HashMap<String, Integer> indexMap,
                                 PreparedStatement statement) throws SQLException {
        // 批量插入数据
        for (T t : list) {
            Class<?> clazz = t.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                field.setAccessible(true);
                String key = field.getName();
                Object value = null;
                try {
                    value = field.get(t);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
                if (value instanceof Long) {
                    try {
                        statement.setLong(indexMap.get(key), (Long)value);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                } else if (value instanceof Integer) {
                    try {
                        statement.setInt(indexMap.get(key), (Integer)value);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                } else if (value instanceof String) {
                    try {
                        statement.setString(indexMap.get(key), (String)value);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                } else if (value instanceof BigDecimal) {
                    try {
                        statement.setBigDecimal(indexMap.get(key), (BigDecimal)value);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                } else if (value instanceof Boolean) {
                    int flag = (Boolean) value ? 1 : 0;
                    try {
                        statement.setInt(indexMap.get(key), flag);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                } else if (value instanceof Byte) {
                    try {
                        statement.setByte(indexMap.get(key), (Byte) value);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            statement.addBatch();
        }
    }

    private String getInsertSqlByList(String tableName, T t, HashMap<String, Integer> indexMap) {
        StringBuilder builder = new StringBuilder("INSERT INTO " + tableName + " (");
        StringBuilder builder2 = new StringBuilder(" VALUES (");
        AtomicInteger index = new AtomicInteger(1);
        Class<?> clazz = t.getClass();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            String fieldName = field.getName();
            builder.append(fieldName);
            builder.append(" ");
            builder.append(",");
            builder2.append("?");
            builder2.append(" ");
            builder2.append(",");
            indexMap.put(fieldName, index.get());
            index.getAndIncrement();
        }
        builder.deleteCharAt(builder.length() - 1);
        builder2.deleteCharAt(builder2.length() - 1);
        // 插入表的SQL语句
        return builder + ")" + builder2 + ")";
    }

5、查询

        查询没什么,执行sql就好,这里就能看到数据库查询出来的ResultSet被通用的转化

public static List<HashMap> query(String sql) throws SQLException {
        // 创建List对象来存储查询结果
        List<HashMap> resultList = new ArrayList<>();
        Connection connection = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            connection = H2ConnectionPool.getConnection();
            // 执行查询
            stmt = connection.createStatement();
            rs = stmt.executeQuery(sql);

            // 获取结果集的元数据
            ResultSetMetaData metaData = rs.getMetaData();
            int columnCount = metaData.getColumnCount();

            // 处理查询结果
            while (rs.next()) {
                HashMap<String, Object> map = new HashMap<>(columnCount);
                for (int i = 1; i <= columnCount; i++) {
                    map.put(metaData.getColumnName(i), rs.getObject(i));
                }
                resultList.add(map);
            }

        } finally {
            if (connection != null) {
                connection.close();
            }
            if (stmt != null) {
                stmt.close();
            }
            if (rs != null) {
                rs.close();
            }
        }
        return resultList;
    }

6、删表

public static boolean dropTable(String tableName) throws SQLException {
        if (StringUtilsExt.isBlank(tableName)) {
            return false;
        }

        Connection conn = null;
        Statement stmt = null;
        try {
            // 连接到H2数据库
            conn = H2ConnectionPool.getConnection();
            // 创建Statement对象
            stmt = conn.createStatement();

            String sql = "DROP TABLE " + tableName;
            stmt.executeUpdate(sql);
        } finally {
            if (conn != null) {
                conn.close();
            }
            if (stmt != null) {
                stmt.close();
            }
        }
        return true;
    }

四、总结

        平台化的建设要考虑低代码和通用,不然源码处处改就很完蛋,这里作者介绍了H2做通用化跨库、跨实例、跨服务数据分析和逻辑,有兴趣的同学欢迎讨论!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/866139.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4个简化IT服务台任务的ChatGPT功能

最近几个月&#xff0c;ChatGPT 风靡全球&#xff0c;这是一个 AI 聊天机器人&#xff0c;使用户能够生成脚本、文章、锻炼图表等。这项技术在各行各业都有无穷无尽的应用&#xff0c;在本文中&#xff0c;我们将研究这种现代技术如何帮助服务台团队增强服务交付和客户体验。 什…

AD7151

AD7151采用ADI公司的电容-数字转换器&#xff08;CDC&#xff09;技术,这种技术汇集了与实际传感器接口过程中起着重要作用的众多特性于一身,如高输入灵敏度,较高的输入寄生接地电容和泄漏电流容限。 集成自适应式阈值算法可对因环境因素&#xff08;如湿度和温度&#xff09;…

AI:02-基于深度学习的动物图像检索算法的研究

文章目录 一、算法原理二、代码实现三、实验结果四、总结深度学习在计算机视觉领域中的应用越来越广泛,其中动物图像检索算法是一个重要的应用场景。本文将介绍一种基于深度学习的动物图像检索算法,并提供相应的代码实现。 一、算法原理 本算法采用卷积神经网络(Convolutio…

Attention is all you need ---Transformer

大语言模型已经在很多领域大显身手&#xff0c;其应用包括只能写作、音乐创作、知识问答、聊天、客服、广告文案、论文、新闻、小说创作、润色、会议/文章摘要等等领域。在商业上模型即产品、服务即产品、插件即产品&#xff0c;任何形态的用户可触及的都可以是产品&#xff0c…

docker实现Nginx

文章目录 1.docker 安装2.docker环境实现Nginx 1.docker 安装 1.使用环境为红帽8.1,添加源 yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo2.安装 yum install docker-ce docker-ce-cli containerd.io显示出错 Docker C…

强化学习-信任区域策略优化和近端策略优化(第7章)

来源书籍&#xff1a; TENSORFLOW REINFORCEMENT LEARNING QUICK START GUIDE 《TensorFlow强化学习快速入门指南-使用Python动手搭建自学习的智能体》 著者&#xff1a;[美]考希克巴拉克里希南&#xff08;Kaushik Balakrishnan&#xff09; 译者&#xff1a;赵卫东 出版…

JSP的文件扩展名必须是.jsp吗

并不是 打开Tomcat目录下conf目录下的web.xml 也就是说,映射路径为xxx.jsp或者xxx.jspx就会访问jsp 可以修改

前后端分离------后端创建笔记(上)

本文章转载于【SpringBootVue】全网最简单但实用的前后端分离项目实战笔记 - 前端_大菜007的博客-CSDN博客 仅用于学习和讨论&#xff0c;如有侵权请联系 源码&#xff1a;https://gitee.com/green_vegetables/x-admin-project.git 素材&#xff1a;https://pan.baidu.com/s/…

Mysql:Access denied for user ‘root‘@‘localhost‘ (using password:YES)解决方案

最近在配置Maven以及Mybatis时&#xff0c;连接localhost数据库时出现无法连接&#xff0c;用cmd测试时报错&#xff1a;Access denied for user ‘ODBC’‘localhost’ (using password: NO)&#xff0c;这个意思就是不允许远程访问&#xff0c;一开始笔者进入mysql试了一下是…

计算机网络:网络字节序

目录 一、字节序1.字节序概念2.字节序的理解&#xff08;1&#xff09;大端模式存储数据&#xff08;2&#xff09;小端模式存储数据 二、网络字节序 一、字节序 1.字节序概念 字节序&#xff1a;内存中存储多字节数据的顺序。 难道存储数据还要看顺序吗&#xff1f; yes。内…

maven是什么?安装+配置

目录 1.什么是maven&#xff1f; 1.2.maven的核心功能是什么&#xff1f; 2.Maven安装配置 2.1Maven的安装 2.2Maven环境配置 1.配置 MAVEN_HOME &#xff0c;变量值就是你的 maven 安装的路径&#xff08;bin 目录之前一级目录&#xff09; 2.将MAVEN_HOME 添加到Path系…

路由导航守卫中document.title = to.meta.title的作用以及路由跳转修改页面title

目录 &#x1f53d; document.title to.meta.title的作用 &#x1f53d; Vue路由跳转时如何更改页面title &#x1f53d; document.title to.meta.title的作用 路由导航守卫如下&#xff1a; router.beforeEach(async (to, from, next) > {document.title to.meta.ti…

“一日之际在于晨”,欢迎莅临WAVE SUMMIT上午场:Arm 虚拟硬件早餐交流会

8月16日&#xff0c;盛夏的北京将迎来第九届WAVE SUMMIT深度学习开发者大会。在峰会主论坛正式开启前&#xff0c;让我们先用一份精美的元气早餐&#xff0c;和一场“Arm虚拟硬件交流会”&#xff0c;唤醒各位开发小伙伴的开发魂&#xff01; 8月16日&#xff0c;WAVE SUMMIT大…

湘大 XTU OJ 1097 排序 题解:c++ 函数库的使用 快速排序 归并排序 冒泡排序

一、链接 1097 排序 二、题目 Description N个整数&#xff0c;将其排序输出。 输入 第一行是一个整数K&#xff08;1<K<20&#xff09;&#xff0c;表示有多少个样例&#xff0c;每个样例的第一行是一个整数N&#xff08;1<N<1,000&#xff09;和一个字符X&…

136.只出现一次的数字+26.删除有序数组中的重复项

目录 一、136.只出现一次的数字 二、代码 三、26删除有序数组中的重复项 四、代码 一、136.只出现一次的数字 136. 只出现一次的数字 - 力扣&#xff08;LeetCode&#xff09; 二、代码 交换律&#xff1a;a ^ b ^ c <> a ^ c ^ b 任何数与0异或为任何数 0 ^ n >…

第三篇|金融人数据来源有哪些

数据对于金融行业真的很重要&#xff0c;那么金融人有哪些途径查数据呢&#xff1f; 国内&#xff1a; 1. 国家统计局 这个应该是无论什么行业都使用最频繁的网站&#xff0c;每个月都会固定发上个月资产投资数据 、工业增加值和利润数据等常规数据&#xff0c;其他数据也会…

日常BUG—— maven编译报错

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;日常BUG、BUG、问题分析☀️每日 一言 &#xff1a;存在错误说明你在进步&#xff01; 一、问题描述 一个maven项目在由于在代码中书写了如下代码&#xff1a; public static ConcurrentMap<…

若依如何使用(基本环境的配置)

本文章转载于公众号&#xff1a;王清江唷,仅用于学习和讨论&#xff0c;如有侵权请联系 QQ交流群&#xff1a;298405437 本人QQ&#xff1a;4206359 具体视频地址:8 跑后端_哔哩哔哩_bilibili 1、Ruoyi-Vue是什么&#xff1f; 借用官网原话来解释&#xff1a; 一直想做一款…

28.Netty源码之缓存一致性协议

Mpsc Queue 基础知识 Mpsc 的全称是 Multi Producer Single Consumer&#xff0c;多生产者单消费者。Mpsc Queue 可以保证多个生产者同时访问队列是线程安全的&#xff0c;而且同一时刻只允许一个消费者从队列中读取数据。 Netty Reactor 线程中任务队列 taskQueue 必须满足多个…

【input】关于input 元素的type类型及相关作用

传统类型&#xff1a; text&#xff1a;用于输入单行文本。 <input type"text" name"username">password&#xff1a;用于输入密码&#xff0c;输入的内容会被隐藏。 <input type"password" name"password">checkbox&a…