CRUD CRUD CRUDRESTfulAPIインターフェースを生成するためのOracleデータベースゼロコードに基づくプログラミングは必要ありません
レビュー
前回の記事の紹介により、CRUDを生成するPostgreSQLゼロコードに基づくプログラミングなし、RESTful APIインターフェイス抽象ファクトリデザインパターンが採用され、エレファントデータベースPostgreSQLがサポートされました。以前は、文字列の連結によってDDLSQLステートメントを生成するのは面倒でした。この記事の冒頭で、FreeMarkerテンプレートエンジンを紹介し、テンプレートを構成して多くのコードを簡素化し、効率を向上させ、Oracleデータベースのゼロコードに基づいてOracleデータベースのSQLテンプレートを構成することにより、物理テーブル構造のSQLステートメントを作成および変更します。 Crudの追加、削除、変更、およびチェックを実行します。
FreeMarkerの紹介
FreeMarkerはテンプレートエンジンです。テンプレートと変更するデータに基づいて出力テキスト(HTML Webページ、電子メール、構成ファイル、ソースコードなど)を生成するための汎用ツールです。エンドユーザー向けではなく、プログラマーが開発する製品に組み込むことができるコンポーネントであるJavaクラスライブラリを対象としています。テンプレートはFreeMarkerテンプレート言語(FTL)で書かれています。これは、PHPのような本格的なプログラミング言語ではなく、単純で特殊な言語です。つまり、データベースクエリやビジネスオペレーションなどの実際のプログラミング言語で表示するデータを準備すると、テンプレートに準備されたデータが表示されます。テンプレートでは、データの表示方法に焦点を当てることができ、テンプレートの外側では、表示するデータに焦点を当てることができます。
UIインターフェース
製品オブジェクトを例にとると、プログラミングなしで、Oracleデータベースに基づいて、CRUDの追加、削除、変更、クエリのRESTful APIインターフェイスと管理UIを、ゼロコードを構成することで実現できます。
製品を作成する
製品データの編集
製品データリスト
OracleSQLDeveloperを介した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
需要根据需要配置数据库连接驱动,无需重新发布,就可以切换不同的数据库。
#oracle
spring.datasource.url=jdbc:oracle:thin:@//localhost:1521/XEPDB1
spring.datasource.driverClassName=oracle.jdbc.OracleDriver
spring.datasource.username=crudapi
spring.datasource.password=crudapi
spring.datasource.initialization-mode=always
spring.datasource.schema=classpath:schema.sql
复制代码
小结
本文主要介绍了crudapi支持oracle数据库实现原理,并且以产品对象为例,零代码实现了CRUD增删改查RESTful API,后续介绍更多的数据库,比如MSSQL Server,Mongodb等。
实现方式 | 代码量 | 时间 | 稳定性 |
---|---|---|---|
传统开发 | 1000行左右 | 2天/人 | 5个bug左右 |
crudapi系统 | 0行 | 1分钟 | 基本为0 |
综上所述,利用crudapi系统可以极大地提高工作效率和节约成本,让数据处理变得更简单!
crudapi简介
Crudapiは、インターフェイスの追加、削除、変更、およびチェックを意味するcrud + apiの組み合わせであり、ゼロコードで構成可能な製品です。crudapiを使用すると、退屈なコードの追加、削除、変更、およびチェックに別れを告げることができ、ビジネスに集中し、多くのコストを節約し、作業効率を向上させることができます。crudapiの目標は、すべての人がデータを簡単かつ無料で操作できるようにすることです。プログラミングなしで、構成を通じてRESTful APIを自動的に生成、追加、削除、変更、チェックし、ビジネスデータを管理するためのバックグラウンドUIを提供できます。主流のオープンソースフレームワークに基づいて、独立した知的財産権を持ち、二次開発をサポートします。
demo演示
Crudapiは、製品レベルのゼロコードプラットフォームです。自動コードジェネレーターとは異なり、crudapiは、コントローラー、サービス、リポジトリ、エンティティなどのビジネスコードを生成する必要はありません。プログラムは、実行中に使用できます。真にゼロです。コードを記述し、ビジネスに依存しない基本的なCRUD。RESTfulAPIをカバーできます。
公式ウェブサイトアドレス:crudapi.cn
テストアドレス:demo.crudapi.cn/crudapi/log…
ソースコードアドレス付き
GitHubアドレス
Giteeの住所
ネットワーク上の理由により、GitHubは低速である可能性があり、Giteeにアクセスするように変更でき、コードは同期的に更新されます。