java动态数据源(多种数据库)中自定义表(实体)的创建及增查删改实现

我正在做一个开源的低代码平台:https://gitee.com/IElwin/ezlcp-java,了解代代码平台的都知道,会有自定义数据源、自定义表(实体)、自定义表单、自定义报表等功能。

关于自定义数据源,如果只支持一种数据库的话,开发起来就会容易些。目前很多平台是支持多种数据库的,如:MySQLOraclePostgreSQLSQL Server,还有的会支持国产的数据库,如:达梦人大金仓等。

如果表结构是固定的,我们可以使用MyBatisHibernateORM工具类库来操作表;但是需求中表结构是用户自定义的,可能随时变动,使用ORM工具就不适合了。那怎么实现这些用户自定义动态表的创建、修改表结构、查询、插入等操作呢?接下来比较一下各实现方案:

方法一:使用JdbcTemplate全手写SQL

当然直接用jdbc来实现也是可以的,但用spring的JdbcTemplate会更优雅一些,可以少写一些代码。我看过一个产品的源码就是用这种方式来实现的。使用的是策略设计模式,定义一个数据操作的接口,每一种数据库就写一个实现该接口的操作类。当进行数据库操作时,会根据DruidDataSource数据源的getDbType方法来获取数据库类型,找到相应的操作类Bean,再执行相应的方法。这是最基本也是最灵活的方案,但是会有很多代码量,也需要花大量的时间来测试。之前用过的那个产品在MySQL数据库下运行正常,切换到Oracle数据源时一大堆问题出现。我就想有没有这方面开源组件,节省开发时间和测试时间。

方法二:使用AnyLine

AnyLine是国内的开源java组件,官网地址:http://doc.anyline.org/,它底层的实现方式类似于第一种方法。使用时,如果要支持某一种数据,就引用该数据库的相关的Jar包,官方文档中写的支持多种数据库,还有些国产数据库也支持。完全开源免费的,也可以切换数据源,所以我就试用了一下,测试主要代码如下:

        service.setDataSource(dsName);
        Table table = service.metadata().table(null, null, tableName);
        if (null != table) {
            service.ddl().drop(table);
        }
        table = new Table(tableName);
        table.setComment("测试创建的表");
        table.addColumn("id", "varchar(64)").setPrimaryKey(true).setComment("主键");
        table.addColumn("name", "varchar(50)").setComment("姓名");
        table.addColumn("age", "int").setComment("年龄");
        table.addColumn("create_time", "datetime").setComment("创建时间");
        table.addColumn("create_by", "varchar(64)").setComment("创建人");
        service.ddl().create(table);
        //插入数据
        DataRow row = new DataRow();
        row.put("id","101");
        row.put("name","张三");
        String now= DateUtils.getTime();
        row.put("age",60);
        row.put("create_time",now);
        service.insert(tableName, row);
        //查询数据
        DataSet set = service.querys(tableName);
        service.setDefaultDataSource();

pom.xml文件相关内容如下:

     <!-- 每一种数据库要引用一个类库,还要引入该数据库的jdbc驱动程序 -->
        <dependency>
            <groupId>org.anyline</groupId>
            <artifactId>anyline-data-jdbc-postgresql</artifactId>
            <version>8.6.3-20230625</version>
        </dependency>
        <dependency>
            <groupId>org.anyline</groupId>
            <artifactId>anyline-data-jdbc-oracle</artifactId>
            <version>8.6.3-20230625</version>
        </dependency>
       <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.6.0</version>
        </dependency>
        <dependency>
            <groupId>com.oracle.database.jdbc</groupId>
            <artifactId>ojdbc8</artifactId>
            <version>23.2.0.0</version>
        </dependency>

就是先切换到指定的数据源,然后创建表,插入一条数据,再查询该表,最后切换回默认数据源。日期类型在MySQL数据库下,直接传new Date() 或者时间字符串都是可以的;但切换到PostgreSQL两种类型值都会报错了。说明AnyLine也不是很稳定,还需要测试以及对某种数据库特殊处理才可以。

方法三: 使用jDialects

jDialects项目地址:https://gitee.com/drinkjava2/jdialects,也是国内的免费开源组件,并不是很出名,但非常轻量极。官方的介绍:jDialects是一个Java数据库方言工具,支持80多种数据库方言,具有分页、函数变换、类型变换、DDL生成、JPA注解解析等功能,它通常与JDBC工具组合使用。
pom.xml引入:

<dependency>
    <groupId>com.github.drinkjava2</groupId>
    <artifactId>jdialects</artifactId>
    <version>5.0.13.jre8</version>
</dependency>

因为这个类库只负责生成SQL,至于SQL的执行还是需要引入相关的JDBC驱动程序的。测试代码如下:

        var ds = DruidUtils.getDataSource(dsId);
        var template = new JdbcTemplate(ds);
        TableModel t = new TableModel(tableName);
        t.comment("测试创建的表");
        t.column("id").VARCHAR(64).pkey().comment("主键");
        t.column("name").VARCHAR(50).comment("姓名");
        t.column("age").INTEGER().comment("年龄");
        t.column("create_time").TIMESTAMP().comment("创建时间");
        t.column("create_by").VARCHAR(64).comment("创建人");
        Dialect dialect = Dialect.guessDialect(ds);
        String[] ddlArray = dialect.toDropAndCreateDDL(t);
        for (String ddl : ddlArray) {
            template.execute(ddl);
        }
        String now = DateUtils.getTime();
        String sql = "insert into " + tableName + " (id,name,age,create_time) values('101','李明',18,'" + now + "')";
        template.execute(sql);

        sql = "select * from " + tableName;
        JsonResult result = JsonResult.getSuccessResult("common.handleSuccess");
        var data = template.queryForList(sql);

测试了MySQLPostgreSQL两种数据库都是可以正常运行的,所以就决定是你了。

方法四: 使用jOOQ

jOOQ官网:https://www.jooq.org/,看了教程觉得用起来很简单的;但是它是一个商用软件,虽然也有开源版本。开源版本只支持MySQLPostgreSQL还有一些小众数据库,而且只支持特定的版本(比如MySQL只支持8.0.31版,其他旧版本不支持),觉得开源版本太鸡肋,决定放弃试用。如果你不在乎钱的话可以使用,商业版也可以免费试用30天。

总结

最终的决定采用的方案是:先定义统一的操作接口(方法一、二中都有接口),然后建立一个默认的实现类(调用jDialects,应该可以兼容大多数常用的数据库);对于jDialects未实现的或者出错的数据库,再新建一个实现类(可继承默认实现类)来处理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容