写点什么

无需编程,基于甲骨文 oracle 数据库零代码生成 CRUD 增删改查 RESTful API 接口

作者:crudapi
  • 2022 年 4 月 08 日
  • 本文字数:6789 字

    阅读完需:约 22 分钟

无需编程,基于甲骨文oracle数据库零代码生成CRUD增删改查RESTful API接口

无需编程,基于甲骨文 oracle 数据库零代码生成 CRUD 增删改查 RESTful API 接口

回顾

通过之前一篇文章 无需编程,基于PostgreSQL零代码生成CRUD增删改查RESTful API接口 的介绍,采用抽象工厂设计模式,已经支持了大象数据库 PostgreSQL。之前通过字符串拼接生成 DDL SQL 语句,比较繁琐。本文开始,引入了 FreeMarker 模版引擎,通过配置模版实现创建和修改物理表结构 SQL 语句,简化了大量代码,提高了效率,并且通过配置 oracle 数据库 SQL 模版,基于 oracle 数据库,零代码实现 crud 增删改查。

FreeMarker 简介

FreeMarker 是一款模板引擎: 即一种基于模板和要改变的数据,并用来生成输出文本(HTML 网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个 Java 类库,是一款程序员可以嵌入他们所开发产品的组件。模板编写为 FreeMarker Template Language (FTL)。它是简单的,专用的语言, 不是像 PHP 那样成熟的编程语言。 那就意味着要准备数据在真实编程语言中来显示,比如数据库查询和业务运算,之后模板显示已经准备好的数据。在模板中,你可以专注于如何展现数据,而在模板之外可以专注于要展示什么数据。

UI 界面

通过产品对象为例,无需编程,基于 Oracle 数据库,通过配置零代码实现 CRUD 增删改查 RESTful API 接口和管理 UI。



创建产品



编辑产品数据



产品数据列表



通过 Oracle SQL Developer 查询 Oracle 数据

定义元数据对象模型

元数据表 ca_meta_table


元数据表 ca_meta_table,用于记录表的基本信息。

TableEntity 对象

TableEntity 为“元数据表”对象,和 ca_meta_table 字段对应


public class TableEntity {    private Long id;
private String name;
private String caption;
private String description;
private Timestamp createdDate;
private Timestamp lastModifiedDate;
private String pluralName;
private String tableName;
private EngineEnum engine;
private Boolean createPhysicalTable;
private Boolean reverse;
private Boolean systemable;
private Boolean readOnly;
private List<ColumnEntity> columnEntityList;
private List<IndexEntity> indexEntityList;}
复制代码

元数据列 ca_meta_column


元数据列 ca_meta_column,用于记录表字段信息,比如类型,长度,默认值等。

ColumnEntity 对象

ColumnEntity 为“元数据列”对象,和 ca_meta_column 字段对应


public class ColumnEntity {  private Long id;
private String name;
private String caption;
private String description;
private Timestamp createdDate;
private Timestamp lastModifiedDate;
private Integer displayOrder;
private DataTypeEnum dataType;
private IndexTypeEnum indexType;
private IndexStorageEnum indexStorage;
private String indexName;
private Integer length;
private Integer precision;
private Integer scale;
private String defaultValue;
private Long seqId;
private Boolean unsigned;
private Boolean autoIncrement;
private Boolean nullable;
private Boolean insertable;
private Boolean updatable;
private Boolean queryable;
private Boolean displayable;
private Boolean systemable;
private Long tableId;}
复制代码

元数据索引 ca_meta_index


元数据索引 ca_meta_index,用于记录表联合索引信息,比如索引类型,名称等。

IndexEntity 对象

IndexEntity 为“元数据索引”对象,和 ca_meta_index 字段对应


public class IndexEntity {  private Long id;
private String name;
private String caption;
private String description;
private Timestamp createdDate;
private Timestamp lastModifiedDate;
private IndexTypeEnum indexType;
private IndexStorageEnum indexStorage;
private Long tableId;
private List<IndexLineEntity> indexLineEntityList;}
复制代码

元数据索引行 ca_meta_index_line


元数据索引行 ca_meta_index_line,用于记录表联合索引行信息,一个联合索引可以对应多个联合索引行,表示由多个字段组成。

IndexLineEntity 对象

IndexLineEntity“元数据索行”对象,和 ca_meta_index_line 字段对应


public class IndexLineEntity {  private Long id;
private Long columnId;
private ColumnEntity columnEntity;
private Long indexId;}
复制代码

定义 FreeMarker 模版

创建表 create-table.sql.ftl

CREATE TABLE "${tableName}" (<#list columnEntityList as columnEntity>  <#if columnEntity.dataType == "BOOL">    "${columnEntity.name}" NUMBER(1)<#if columnEntity.defaultValue??> DEFAULT <#if columnEntity.defaultValue == "true">1<#else>0</#if></#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "INT">    "${columnEntity.name}" INT<#if columnEntity.defaultValue??> DEFAULT ${columnEntity.defaultValue}</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity.indexType?? && columnEntity.indexType == "PRIMARY"> PRIMARY KEY</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "BIGINT">    "${columnEntity.name}" INT<#if columnEntity.defaultValue??> DEFAULT ${columnEntity.defaultValue}</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity.indexType?? && columnEntity.indexType == "PRIMARY"> PRIMARY KEY</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "FLOAT">    "${columnEntity.name}" FLOAT<#if columnEntity.defaultValue??> DEFAULT ${columnEntity.defaultValue}</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "DOUBLE">    "${columnEntity.name}" REAL<#if columnEntity.defaultValue??> DEFAULT ${columnEntity.defaultValue}</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "DECIMAL">    "${columnEntity.name}" DECIMAL<#if columnEntity.defaultValue??> DEFAULT ${columnEntity.defaultValue}</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "DATE">    "${columnEntity.name}" DATE<#if columnEntity.defaultValue??> DEFAULT ${columnEntity.defaultValue}</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "TIME">    "${columnEntity.name}" CHAR(8)<#if columnEntity.defaultValue??> DEFAULT ${columnEntity.defaultValue}</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "DATETIME">    "${columnEntity.name}" DATE<#if columnEntity.defaultValue??> DEFAULT ${columnEntity.defaultValue}</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "TIMESTAMP">    "${columnEntity.name}" TIMESTAMP<#if columnEntity.defaultValue??> DEFAULT ${columnEntity.defaultValue}</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "CHAR">    "${columnEntity.name}" CHAR(${columnEntity.length})<#if columnEntity.defaultValue??> DEFAULT '${columnEntity.defaultValue}'</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity.indexType?? && columnEntity.indexType == "PRIMARY"> PRIMARY KEY</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "VARCHAR">    "${columnEntity.name}" VARCHAR(${columnEntity.length})<#if columnEntity.defaultValue??> DEFAULT '${columnEntity.defaultValue}'</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity.indexType?? && columnEntity.indexType == "PRIMARY"> PRIMARY KEY</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "PASSWORD">    "${columnEntity.name}" VARCHAR(200)<#if columnEntity.defaultValue??> DEFAULT '${columnEntity.defaultValue}'</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "ATTACHMENT">    "${columnEntity.name}" VARCHAR(4000)<#if columnEntity.defaultValue??> DEFAULT '${columnEntity.defaultValue}'</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "TEXT">    "${columnEntity.name}" VARCHAR(4000)<#if columnEntity.defaultValue??> DEFAULT '${columnEntity.defaultValue}'</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "LONGTEXT">    "${columnEntity.name}" LONG<#if columnEntity.defaultValue??> DEFAULT ${columnEntity.defaultValue}</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "BLOB">    "${columnEntity.name}" BLOB<#if columnEntity.defaultValue??> DEFAULT ${columnEntity.defaultValue}</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity_has_next>,</#if>  <#elseif columnEntity.dataType == "LONGBLOB">    "${columnEntity.name}" BLOB<#if columnEntity.defaultValue??> DEFAULT ${columnEntity.defaultValue}</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity_has_next>,</#if>  <#else>    "${columnEntity.name}" VARCHAR(200)<#if columnEntity.defaultValue??> DEFAULT ${columnEntity.defaultValue}</#if><#if columnEntity.nullable != true> NOT NULL</#if><#if columnEntity.indexType?? && columnEntity.indexType == "PRIMARY"> PRIMARY KEY</#if><#if columnEntity_has_next>,</#if>  </#if></#list>);
<#list columnEntityList as columnEntity> <#if columnEntity.indexType?? && columnEntity.indexType == "UNIQUE"> ALTER TABLE "${tableName}" ADD CONSTRAINT "${columnEntity.indexName}" UNIQUE("${columnEntity.name}"); </#if>
<#if columnEntity.indexType?? && (columnEntity.indexType == "INDEX" || columnEntity.indexType == "FULLTEXT")> CREATE INDEX "${columnEntity.indexName}" ON "${tableName}" ("${columnEntity.name}"); </#if></#list>
<#if indexEntityList??> <#list indexEntityList as indexEntity> <#if indexEntity.indexType?? && indexEntity.indexType == "UNIQUE"> ALTER TABLE "${tableName}" ADD CONSTRAINT "${indexEntity.name}" UNIQUE(<#list indexEntity.indexLineEntityList as indexLineEntity>"${indexLineEntity.columnEntity.name}"<#if indexLineEntity_has_next>,</#if></#list>); </#if>
<#if indexEntity.indexType?? && (indexEntity.indexType == "INDEX" || indexEntity.indexType == "FULLTEXT")> CREATE INDEX "${indexEntity.name}" ON "${tableName}" (<#list indexEntity.indexLineEntityList as indexLineEntity>"${indexLineEntity.columnEntity.name}"<#if indexLineEntity_has_next>,</#if></#list>); </#if> </#list></#if>
COMMENT ON TABLE "${tableName}" IS '${caption}';
<#list columnEntityList as columnEntity> COMMENT ON COLUMN "${tableName}"."${columnEntity.name}" IS '${columnEntity.caption}';</#list>
复制代码

模版解析 SQL

首先保存元数据信息,下一步传递模版名称和元数据 model,动态解析成创建表 SQL 语句,然后创建物理表,这样元数据和物理表就关联上了。运行时通过解析元数据动态生成 insert,select,update,delete 等 SQL 语句,零代码实现业务数据 crud 功能。


public String processTemplateToString(String database, String templateName, Object dataModel) {    String str = null;    StringWriter stringWriter = new StringWriter();    try {        Configuration config = new Configuration(Configuration.VERSION_2_3_31);        config.setNumberFormat("#");        String templateValue = getTemplate(database, templateName);        if (templateValue == null) {          return str;        }
Template template = new Template(templateName, templateValue, config); template.process(dataModel, stringWriter);
str = stringWriter.getBuffer().toString().trim(); log.info(str); } catch (Exception e) { e.printStackTrace(); throw new BusinessException(ApiErrorCode.DEFAULT_ERROR, e.getMessage()); }
return str;}
public List<String> toCreateTableSql(TableEntity tableEntity) { String createTableSql = processTemplateToString("create-table.sql.ftl", tableEntity);
if (createTableSql == null) { throw new BusinessException(ApiErrorCode.DEFAULT_ERROR, "create-table.sql is empty!"); }
List<String> sqls = new ArrayList<String>(); String[] subSqls = createTableSql.split(";"); for (String t : subSqls) { String subSql = t.trim(); if (!subSql.isEmpty()) { sqls.add(t); } }
return sqls;}
public Long create(TableDTO tableDTO) { TableEntity tableEntity = tableMapper.toEntity(tableDTO); //TODO Long tableId = crudService.create(TABLE_TABLE_NAME, tableEntity); List<String> sqlList = crudService.toCreateTableSql(tableEntity); for (String sql: sqlList) { execute(sql); } //TODO return tableId;}
复制代码

修改表


包括表结构和索引的修改,删除等,和创建表原理类似。

application.properties

需要根据需要配置数据库连接驱动,无需重新发布,就可以切换不同的数据库。


#oraclespring.datasource.url=jdbc:oracle:thin:@//localhost:1521/XEPDB1spring.datasource.driverClassName=oracle.jdbc.OracleDriverspring.datasource.username=crudapispring.datasource.password=crudapispring.datasource.initialization-mode=alwaysspring.datasource.schema=classpath:schema.sql
复制代码

小结

本文主要介绍了 crudapi 支持 oracle 数据库实现原理,并且以产品对象为例,零代码实现了 CRUD 增删改查 RESTful API,后续介绍更多的数据库,比如 MSSQL Server,Mongodb 等。



综上所述,利用 crudapi 系统可以极大地提高工作效率和节约成本,让数据处理变得更简单!

crudapi 简介

crudapi 是 crud+api 组合,表示增删改查接口,是一款零代码可配置的产品。使用 crudapi 可以告别枯燥无味的增删改查代码,让您更加专注业务,节约大量成本,从而提高工作效率。crudapi 的目标是让处理数据变得更简单,所有人都可以免费使用!无需编程,通过配置自动生成 crud 增删改查 RESTful API,提供后台 UI 管理业务数据。基于主流的开源框架,拥有自主知识产权,支持二次开发。

demo 演示

crudapi 属于产品级的零代码平台,不同于自动代码生成器,不需要生成 Controller、Service、Repository、Entity 等业务代码,程序运行起来就可以使用,真正 0 代码,可以覆盖基本的和业务无关的 CRUD RESTful API。


官网地址:https://crudapi.cn


测试地址:https://demo.crudapi.cn/crudapi/login

附源码地址

GitHub 地址

https://github.com/crudapi/crudapi-admin-web

Gitee 地址

https://gitee.com/crudapi/crudapi-admin-web


由于网络原因,GitHub 可能速度慢,改成访问 Gitee 即可,代码同步更新。

发布于: 刚刚阅读数: 2
用户头像

crudapi

关注

crudapi是crud+api组合,表示增删改查接口 2019.06.19 加入

使用crudapi可以告别枯燥无味的增删改查代码,让您更加专注业务,节约大量成本,从而提高工作效率。crudapi的目标是让处理数据变得更简单!官网:https://crudapi.cn 演示:https://demo.crudapi.cn/crudapi/login

评论

发布
暂无评论
无需编程,基于甲骨文oracle数据库零代码生成CRUD增删改查RESTful API接口_oracle_crudapi_InfoQ写作平台