diff --git a/openGaussDialect/.gitignore b/openGaussDialect/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..b83d22266ac8aa2f8df2edef68082c789727841d --- /dev/null +++ b/openGaussDialect/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/openGaussDialect/.idea/.gitignore b/openGaussDialect/.idea/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..13566b81b018ad684f3a35fee301741b2734c8f4 --- /dev/null +++ b/openGaussDialect/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/openGaussDialect/.idea/compiler.xml b/openGaussDialect/.idea/compiler.xml new file mode 100644 index 0000000000000000000000000000000000000000..b60f12a66df622f62aa67332420202bf2179495e --- /dev/null +++ b/openGaussDialect/.idea/compiler.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/openGaussDialect/.idea/encodings.xml b/openGaussDialect/.idea/encodings.xml new file mode 100644 index 0000000000000000000000000000000000000000..aa00ffab7828f4818589659c804ec2cfd99baed3 --- /dev/null +++ b/openGaussDialect/.idea/encodings.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/openGaussDialect/.idea/jarRepositories.xml b/openGaussDialect/.idea/jarRepositories.xml new file mode 100644 index 0000000000000000000000000000000000000000..2661ae018a21e65e2d95a613d0d5fb3891b1f45e --- /dev/null +++ b/openGaussDialect/.idea/jarRepositories.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/openGaussDialect/.idea/misc.xml b/openGaussDialect/.idea/misc.xml new file mode 100644 index 0000000000000000000000000000000000000000..d5cd61439e42be16778fe0d90f206a2b02d987e8 --- /dev/null +++ b/openGaussDialect/.idea/misc.xml @@ -0,0 +1,12 @@ + + + + + + + + \ No newline at end of file diff --git a/openGaussDialect/.idea/modules.xml b/openGaussDialect/.idea/modules.xml new file mode 100644 index 0000000000000000000000000000000000000000..466a0c6d846d334a015eafa5608d02996ead69ec --- /dev/null +++ b/openGaussDialect/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/openGaussDialect/.idea/uiDesigner.xml b/openGaussDialect/.idea/uiDesigner.xml new file mode 100644 index 0000000000000000000000000000000000000000..2b63946d5b31084bbb7dda418ceb3d75eb686373 --- /dev/null +++ b/openGaussDialect/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/openGaussDialect/.idea/vcs.xml b/openGaussDialect/.idea/vcs.xml new file mode 100644 index 0000000000000000000000000000000000000000..6c0b8635858dc7ad44b93df54b762707ce49eefc --- /dev/null +++ b/openGaussDialect/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git "a/openGaussDialect/docs/Hibernate \345\205\263\344\272\216 OpenGauss \346\226\271\350\250\200\345\214\205_\345\274\200\345\217\221\346\211\213\345\206\214.pdf" "b/openGaussDialect/docs/Hibernate \345\205\263\344\272\216 OpenGauss \346\226\271\350\250\200\345\214\205_\345\274\200\345\217\221\346\211\213\345\206\214.pdf" new file mode 100644 index 0000000000000000000000000000000000000000..b6718237576a6991c93a78724ef3bdd24f446f95 Binary files /dev/null and "b/openGaussDialect/docs/Hibernate \345\205\263\344\272\216 OpenGauss \346\226\271\350\250\200\345\214\205_\345\274\200\345\217\221\346\211\213\345\206\214.pdf" differ diff --git "a/openGaussDialect/docs/\346\265\213\350\257\225\346\226\207\346\241\243.md" "b/openGaussDialect/docs/\346\265\213\350\257\225\346\226\207\346\241\243.md" new file mode 100644 index 0000000000000000000000000000000000000000..ac3e124d541157de46c26b2291093a9d72a7db9c --- /dev/null +++ "b/openGaussDialect/docs/\346\265\213\350\257\225\346\226\207\346\241\243.md" @@ -0,0 +1,113 @@ +## 1 测试目标 + +**功能验证**:验证 `OpenGaussDialect` 类的功能,确保其能够正确地与 Hibernate 和 openGauss 数据库协同工作。。 + +**性能验证**:验证分页查询、批量操作等性能是否达到预期。 + +## 2 测试范围 + +**单元测试**:针对 `OpenGaussDialect` 类中的各个方法,进行方法级别的测试,验证其逻辑正确性。 + +**集成测试**:在实际的 Hibernate 环境中,使用 `OpenGaussDialect` 进行数据库操作,验证其功能和兼容性。 + +**性能测试**:对分页查询、批量插入和更新等操作进行性能测试,验证其效率。 + +**异常处理测试**:验证方言对异常的处理是否符合预期,特别是对数据库异常的转换。 + +## 3 测试环境 + +- **操作系统**:Linux(CentOS 7/8) +- **JDK 版本**:jdk1.8.0_202 +- **Hibernate 版本**:5.6.15.Final +- **openGauss 版本**:opengauss:5.0.0 LTS +- **openGauss 数据库驱动**:5.0.0-og + +### 4 测试工具 + +- **测试框架**:JUnit 5 +- **Mock 框架**:Mockito + +## 5 测试内容 + +### 5.1 数据类型映射测试 + +验证 Java 类型与 openGauss 数据库类型之间的映射关系是否正确。 + +### 5.2 SQL 函数支持测试 + +测试注册的 openGauss 特定 SQL 函数是否能够正确使用。 + +### 5.3 分页和限制功能测试 + +验证分页查询的 SQL 生成是否正确,`limit` 和 `offset` 参数是否正确处理。 + +### 5.4 序列和标识支持测试 + +测试序列的创建、使用和删除,以及标识列的支持情况。 + +### 5.5 异常处理测试 + +测试数据库异常是否能够正确地转换为 Hibernate 的异常类型。 + +### 5.6 DDL 生成测试 + +验证生成的 DDL 语句是否符合 openGauss 的语法要求。 + +### 5.7 锁机制支持测试 + +测试数据库锁机制的支持情况,验证生成的 SQL 是否正确。 + +### 5.8 当前时间戳函数支持测试 + +测试获取当前时间戳的函数是否正常工作。 + +### 5.9 命名限定符支持测试 + +验证方言对模式、表等命名限定符的支持。 + +### 5.10 性能测试 + +对分页查询、批量插入和更新操作进行性能测试。 + +## 测试步骤 + +#### 6.1 单元测试 + +- 编写针对 `OpenGaussDialect` 类各个方法的单元测试,使用 JUnit 5 和 Mockito。 +- 覆盖所有重写的方法和新增的功能,确保逻辑正确。 + +#### 6.2 集成测试 + +- 配置 Hibernate,使用 `OpenGaussDialect`,连接到 openGauss 数据库。 +- 编写实体类,进行 CRUD 操作,验证数据库交互的正确性。 +- 测试分页查询、序列生成、锁机制等功能。 + +#### 6.3 性能测试 + +- 在 openGauss 数据库中插入大量数据,测试分页查询的性能。 +- 测试批量插入和更新操作的性能。 + +## 7 测试通过标准 + +- 所有测试用例均通过,无功能性缺陷。 +- 性能测试结果达到预期,性能优化有效。 +- 异常处理符合预期,能够正确捕获和转换数据库异常。 +- 在不同环境下运行正常,无兼容性问题。 + +### 测试结果记录 + +对于每个测试用例,执行后记录实际结果。如果与预期结果一致,则标记为**通过**;如果不一致,则记录问题,分析原因,进行修复后再次测试。 + +### 测试总结 + +- **功能测试**:所有功能测试用例均通过,`OpenGaussDialect` 的功能符合预期。 +- **性能测试**:性能测试结果达到预期,分页查询和批量操作性能良好。 +- **兼容性测试**:在不同版本的 Hibernate 和 openGauss 数据库上测试,均无兼容性问题。 + +### 问题和改进 + +在测试过程中,如果发现任何问题,需要及时修复,并更新测试用例和测试结果。针对发现的问题,分析其根本原因,优化代码,实现改进。 + +## 结论 + +通过本测试计划和测试文档,全面验证了 `OpenGaussDialect` 的功能和性能,确保其能够正确、高效地与 Hibernate 和 openGauss 数据库协同工作。测试结果良好,方言实现满足项目需求,可以在实际项目中使用。建议在后续的开发和维护中,持续完善测试用例,保持对新功能和变更的测试覆盖,确保方言的稳定性和可靠性。 diff --git "a/openGaussDialect/docs/\350\256\276\350\256\241\346\226\207\346\241\243.md" "b/openGaussDialect/docs/\350\256\276\350\256\241\346\226\207\346\241\243.md" new file mode 100644 index 0000000000000000000000000000000000000000..0330b0aaadda4eb57e440403ad8079e1fa246faa --- /dev/null +++ "b/openGaussDialect/docs/\350\256\276\350\256\241\346\226\207\346\241\243.md" @@ -0,0 +1,165 @@ +## 1. 概述 + +本文档旨在为 OpenGauss 数据库编写一个自定义的 Hibernate Dialect 类。该类的作用是为 Hibernate 提供特定于 OpenGauss 数据库的 SQL 方言支持,使其能够与 OpenGauss 数据库进行无缝集成。 + +## 2. 背景 + +Hibernate 是一个广泛使用的对象关系映射(ORM)框架,用于将 Java对象 与关系数据库表进行映射。为了支持多种数据库系统,Hibernate 引入了 Dialect 类,用来处理数据库的特定 SQL 方言。方言(Dialect)在 Hibernate 中的主要作用是**告诉 Hibernate 如何将 HQL 或标准** **SQL** **转换为特定数据库的 SQL 语句**。由于不同数据库的 SQL 语法和数据类型有所不同,Hibernate 通过方言实现对多种数据库的兼容性。每个方言定义了数据类型、函数、特性等,并对数据库的特定行为提供支持。为支持 OpenGauss,需要编写一个符合OpenGauss SQL特性的 Dialect 类。 + +## 3. 目标 + +为了在 Hibernate 和 OpenGauss 之间提供兼容性。通过实现数据库特定的行为、SQL 语法、数据类型和函数,使 Hibernate 能够正确地生成适用于 OpenGauss 的 SQL 语句。 + +## 4. 范围 + +- 分析 OpenGauss 的 SQL 方言特性。 +- 确定需要在 `Dialect` 中重写的方法。 +- 实现 Hibernate 与 OpenGauss 之间的数据类型映射。 +- 支持 OpenGauss 特定的 SQL 函数和关键字。 +- 处理限制、偏移和分页的特定细节。 +- 实现标识、序列和主键生成策略。 +- 解决与事务隔离、锁定和批量更新相关的特殊行为。 + +## 5. 设计概述 + +- **类结构**: + - **类名**:`OpenGaussDialect` + - **包名**:`org.hibernate.dialect` +- **继承关系**:继承自 `org.hibernate.dialect.Dialect` + +## 6. 实现细节 + +### 6.1 数据类型映射 + +**目标**:定义 openGauss 数据类型与 Java 类型之间的映射关系。 + +**实现**:在构造方法中使用 `registerColumnType()` 方法注册数据类型。 + +```java +public class OpenGaussDialect extends Dialect { + public OpenGaussDialect() { + super(); + + // 注册数据类型 + registerColumnType(Types.BIT, "bit"); + registerColumnType(Types.BOOLEAN, "bool"); + // ... 其他数据类型注册 + } +} +``` + +### 6.2 SQL 函数映射 + +**目标**:支持 openGauss 特定的 SQL 函数。 + +**实现**:使用 `registerFunction()` 方法注册函数。 + +```java +registerFunction("abs", new StandardSQLFunction("abs", StandardBasicTypes.DOUBLE)); +registerFunction("cbrt", new StandardSQLFunction("cbrt", StandardBasicTypes.DOUBLE)); +registerFunction("ceil", new StandardSQLFunction("ceil", StandardBasicTypes.DOUBLE)); +... +``` + +### 6.3 限制和分页支持 + +**目标**:实现限制和偏移支持,以处理分页查询。 + +**实现**:实现 `getLimitHandler()`,返回适用于 OpenGauss 的 `LimitHandler` + +```java +@Override +public LimitHandler getLimitHandler() { + return new OpenGaussLimitHandler(); +} + +public class OpenGaussLimitHandler extends AbstractLimitHandler { + // 实现应用 LIMIT 和 OFFSET 子句的方法 +} +``` + +### 6.4 标识和序列支持 + +**目标**:处理主键生成策略,包括标识列和序列。 + +**实现**:确定 OpenGauss 是否支持标识列(`GENERATED AS IDENTITY`),提供一个 `IdentityColumnSupport` 的实现。 + +```java +@Override +public IdentityColumnSupport getIdentityColumnSupport() { + return new OpenGaussIdentityColumnSupport(); +} + +public class OpenGaussIdentityColumnSupport extends IdentityColumnSupportImpl { + // 根据需要重写方法 +} +``` + +### 6.5 保留关键字 + +目标:注册 OpenGauss 的保留关键字,以防止与实体和列名冲突。 + +### 6.6 锁定策略 + +目标:实现 OpenGauss 特定的锁定机制,包括 `FOR UPDATE` 子句。 + +```java +@Override +public String getForUpdateString() { + return " for update"; +} + +@Override +public boolean supportsOuterJoinForUpdate() { + return true; +} +``` + +### 6.7 批量操作 + +目标:优化批量插入、更新和删除操作的性能。 + +```java +@Override +public int getDefaultBatchSize() { + return 50; // 根据 OpenGauss 的性能特性进行调整 +} +``` + +### 6.8 模式支持 + +目标:处理特定于 OpenGauss 的模式创建、修改和删除。 + +```java +@Override +public String[] getCreateSchemaCommand(String schemaName) { + return new String[] {"CREATE SCHEMA IF NOT EXISTS " + schemaName}; +} + +@Override +public boolean canCreateSchema() { + return true; +} +``` + +### 6.9 其他重写 + +... + +## 测试计划 + +- **单元测试**:为每个重写的方法编写单元测试,确保正确的 SQL 生成。 +- **集成测试**:使用 Hibernate 和 OpenGaussDialect 对接 OpenGauss 数据库,测试 CRUD 操作。 +- **性能测试**:对批量操作和分页查询进行基准测试,验证性能优化。 + +## 依赖项 + +- **JDK 版本**:jdk1.8.0_202 +- **Hibernate 版本**:5.6.15.Final +- **openGauss 数据库驱动**:5.0.0-og + +# 参考资料 + +- openGauss 官方文档:https://docs-opengauss.osinfra.cn/zh/docs/5.0.0-lite/docs/GettingStarted/GettingStarted.html +- Hibernate 官方文档:https://hibernate.org/orm/documentation/5.6/ +- JDBC API 文档:https://docs.oracle.com/javase/8/docs/api/java/sql/package-summary.html \ No newline at end of file diff --git a/openGaussDialect/openGaussDialect.iml b/openGaussDialect/openGaussDialect.iml new file mode 100644 index 0000000000000000000000000000000000000000..39cf9e32b4b24a9cc3193ea32113e44c164bd356 --- /dev/null +++ b/openGaussDialect/openGaussDialect.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/openGaussDialect/pom.xml b/openGaussDialect/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..c2984613b3f16c7d602dd785571d66e4de947de8 --- /dev/null +++ b/openGaussDialect/pom.xml @@ -0,0 +1,76 @@ + + 4.0.0 + + org.hibernate.dialect + openGaussDialect + jar + 1.0-SNAPSHOT + openGaussDialect + http://maven.apache.org + + + + central + https://repo.maven.apache.org/maven2 + + + + + 8 + 8 + UTF-8 + 5.0.0-og + 5.6.15.Final + 1.18.34 + 5.10.3 + 2.17.2 + + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson-databind.version} + + + org.opengauss + opengauss-jdbc + ${opengauss-jdbc.version} + + + org.hibernate + hibernate-core + ${hibernate-core.version} + + + org.projectlombok + lombok + ${lombok.version} + + + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + com.jcraft + jsch + 0.1.55 + test + + + org.mockito + mockito-core + 4.11.0 + test + + + diff --git a/openGaussDialect/src/main/java/org/hibernate/dialect/OpenGaussDialect.java b/openGaussDialect/src/main/java/org/hibernate/dialect/OpenGaussDialect.java new file mode 100644 index 0000000000000000000000000000000000000000..8a7bb9f797e71b7d6f2080a4e850799cd79fd323 --- /dev/null +++ b/openGaussDialect/src/main/java/org/hibernate/dialect/OpenGaussDialect.java @@ -0,0 +1,660 @@ +package org.hibernate.dialect; + +import org.hibernate.*; +import org.hibernate.cfg.Environment; +import org.hibernate.dialect.function.*; +import org.hibernate.dialect.identity.IdentityColumnSupport; +import org.hibernate.dialect.identity.OpenGaussIdentityColumnSupport; +import org.hibernate.dialect.pagination.AbstractLimitHandler; +import org.hibernate.dialect.pagination.LimitHandler; +import org.hibernate.dialect.pagination.LimitHelper; +import org.hibernate.engine.jdbc.env.spi.IdentifierCaseStrategy; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelper; +import org.hibernate.engine.jdbc.env.spi.IdentifierHelperBuilder; +import org.hibernate.engine.jdbc.env.spi.NameQualifierSupport; +import org.hibernate.engine.spi.RowSelection; +import org.hibernate.exception.ConstraintViolationException; +import org.hibernate.exception.DataException; +import org.hibernate.exception.JDBCConnectionException; +import org.hibernate.exception.LockAcquisitionException; +import org.hibernate.exception.spi.SQLExceptionConversionDelegate; +import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter; +import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; +import org.hibernate.hql.spi.id.IdTableSupportStandardImpl; +import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy; +import org.hibernate.hql.spi.id.local.AfterUseAction; +import org.hibernate.hql.spi.id.local.LocalTemporaryTableBulkIdStrategy; +import org.hibernate.internal.util.JdbcExceptionHelper; +import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.descriptor.sql.BlobTypeDescriptor; +import org.hibernate.type.descriptor.sql.ClobTypeDescriptor; +import org.hibernate.type.descriptor.sql.SqlTypeDescriptor; + +import java.sql.*; + +public class OpenGaussDialect extends Dialect { + public OpenGaussDialect() { + super(); + registerColumnType(Types.BIT, "bit"); + registerColumnType(Types.BOOLEAN, "bool"); + + registerColumnType(Types.TINYINT, "int1"); + registerColumnType(Types.SMALLINT, "int2"); + registerColumnType(Types.INTEGER, "int4"); + registerColumnType(Types.BIGINT, "int8"); + registerColumnType(Types.REAL, "float4"); + registerColumnType(Types.FLOAT, "float8"); + registerColumnType(Types.DOUBLE, "float8"); + registerColumnType(Types.DECIMAL, "decimal($p,$s)"); + registerColumnType(Types.NUMERIC, "numeric($p,$s)"); + + registerColumnType(Types.CHAR, "char(1)"); + + registerColumnType(Types.VARCHAR, "varchar($l)"); + registerColumnType(Types.LONGVARCHAR, "text"); + registerColumnType(Types.NCHAR, "nchar($l)"); + registerColumnType(Types.NVARCHAR, "nvarchar2($l)"); + + registerColumnType(Types.DATE, "date"); + registerColumnType(Types.TIME, "time"); + registerColumnType(Types.TIMESTAMP, "timestamp"); + registerColumnType(Types.TIME_WITH_TIMEZONE, "timetz"); + registerColumnType(Types.TIMESTAMP_WITH_TIMEZONE, "timestamptz"); + + registerColumnType(Types.BINARY, "bytea"); + registerColumnType(Types.VARBINARY, "bytea"); + registerColumnType(Types.LONGVARBINARY, "bytea"); + + + registerColumnType(Types.BLOB, "blob"); + registerColumnType(Types.CLOB, "clob"); + registerColumnType(Types.NCLOB, "text"); + + registerColumnType(Types.JAVA_OBJECT, "json"); + registerColumnType(Types.SQLXML, "xml"); + registerColumnType(Types.OTHER, "uuid"); + + registerOpenGaussFunctions(); + + getDefaultProperties().setProperty(Environment.STATEMENT_BATCH_SIZE, DEFAULT_BATCH_SIZE); + getDefaultProperties().setProperty(Environment.NON_CONTEXTUAL_LOB_CREATION, "false"); + } + + protected void registerOpenGaussFunctions() { + // https://docs-opengauss.osinfra.cn/zh/docs/5.0.0-lite/docs/BriefTutorial/%E5%87%BD%E6%95%B0.html + // mathematical functions + registerFunction("abs", new StandardSQLFunction("abs", StandardBasicTypes.DOUBLE)); + registerFunction("cbrt", new StandardSQLFunction("cbrt", StandardBasicTypes.DOUBLE)); + registerFunction("ceil", new StandardSQLFunction("ceil", StandardBasicTypes.DOUBLE)); + registerFunction("degrees", new StandardSQLFunction("degrees", StandardBasicTypes.DOUBLE)); + registerFunction("exp", new StandardSQLFunction("exp", StandardBasicTypes.DOUBLE)); + registerFunction("floor", new StandardSQLFunction("floor", StandardBasicTypes.DOUBLE)); + registerFunction("ln", new StandardSQLFunction("ln", StandardBasicTypes.DOUBLE)); + registerFunction("log", new StandardSQLFunction("log", StandardBasicTypes.DOUBLE)); + registerFunction("mod", new StandardSQLFunction("mod", StandardBasicTypes.INTEGER)); + registerFunction("pi", new NoArgSQLFunction("pi", StandardBasicTypes.DOUBLE, true)); + registerFunction("power", new StandardSQLFunction("power", StandardBasicTypes.DOUBLE)); + registerFunction("radians", new StandardSQLFunction("radians", StandardBasicTypes.DOUBLE)); + registerFunction("random", new NoArgSQLFunction("random", StandardBasicTypes.DOUBLE)); + registerFunction("round", new StandardSQLFunction("round", StandardBasicTypes.DOUBLE)); + registerFunction("sign", new StandardSQLFunction("sign", StandardBasicTypes.INTEGER)); + registerFunction("sqrt", new StandardSQLFunction("sqrt", StandardBasicTypes.DOUBLE)); + registerFunction("trunc", new StandardSQLFunction("trunc", StandardBasicTypes.DOUBLE)); + + // trigonometric functions + registerFunction("acos", new StandardSQLFunction("acos", StandardBasicTypes.DOUBLE)); + registerFunction("asin", new StandardSQLFunction("asin", StandardBasicTypes.DOUBLE)); + registerFunction("atan", new StandardSQLFunction("atan", StandardBasicTypes.DOUBLE)); + registerFunction("atan2", new StandardSQLFunction("atan2", StandardBasicTypes.DOUBLE)); + registerFunction("cos", new StandardSQLFunction("cos", StandardBasicTypes.DOUBLE)); + registerFunction("cot", new StandardSQLFunction("cot", StandardBasicTypes.DOUBLE)); + registerFunction("sin", new StandardSQLFunction("sin", StandardBasicTypes.DOUBLE)); + registerFunction("tan", new StandardSQLFunction("tan", StandardBasicTypes.DOUBLE)); + + // string functions + registerFunction("concat", new VarArgsSQLFunction(StandardBasicTypes.STRING, "", "||", "")); + registerFunction("bit_length", new StandardSQLFunction("bit_length", StandardBasicTypes.LONG)); + registerFunction("convert", new SQLFunctionTemplate(StandardBasicTypes.BINARY, "convert(?1, ?2, ?3)")); + registerFunction("lower", new StandardSQLFunction("lower", StandardBasicTypes.STRING)); + registerFunction("octet_length", new StandardSQLFunction("octet_length", StandardBasicTypes.LONG)); + registerFunction("overlay", new SQLFunctionTemplate(StandardBasicTypes.STRING, "overlay(?1 placing ?2 from ?3 for ?4)")); + registerFunction("position", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "position(?1 in ?2)")); + registerFunction("substring", new SubstringFunction()); + registerFunction("trim", new SQLFunctionTemplate(StandardBasicTypes.STRING, "trim(?1 ?2 ?3 ?4)")); + registerFunction("upper", new StandardSQLFunction("upper", StandardBasicTypes.STRING)); + registerFunction("ascii", new StandardSQLFunction("ascii", StandardBasicTypes.INTEGER)); + registerFunction("btrim", new StandardSQLFunction("btrim", StandardBasicTypes.STRING)); + registerFunction("chr", new StandardSQLFunction("chr", StandardBasicTypes.CHARACTER)); + registerFunction("initcap", new StandardSQLFunction("initcap")); + registerFunction("length", new StandardSQLFunction("length", StandardBasicTypes.INTEGER)); + registerFunction("lpad", new StandardSQLFunction("lpad", StandardBasicTypes.STRING)); + registerFunction("ltrim", new StandardSQLFunction("ltrim", StandardBasicTypes.STRING)); + registerFunction("md5", new StandardSQLFunction("md5", StandardBasicTypes.STRING)); + registerFunction("repeat", new StandardSQLFunction("repeat", StandardBasicTypes.STRING)); + registerFunction("replace", new StandardSQLFunction("replace", StandardBasicTypes.STRING)); + registerFunction("rpad", new StandardSQLFunction("rpad", StandardBasicTypes.STRING)); + registerFunction("rtrim", new StandardSQLFunction("rtrim", StandardBasicTypes.STRING)); + registerFunction("split_part", new StandardSQLFunction("split_part", StandardBasicTypes.STRING)); + registerFunction("strpos", new StandardSQLFunction("strpos", StandardBasicTypes.INTEGER)); + registerFunction("to_hex", new StandardSQLFunction("to_hex", StandardBasicTypes.STRING)); + registerFunction("translate", new StandardSQLFunction("translate", StandardBasicTypes.STRING)); + + // type conversion related functions + registerFunction("to_char", new StandardSQLFunction("to_char", StandardBasicTypes.STRING)); + registerFunction("to_date", new StandardSQLFunction("to_date", StandardBasicTypes.DATE)); + registerFunction("to_number", new StandardSQLFunction("to_number", StandardBasicTypes.BIG_DECIMAL)); + registerFunction("to_timestamp", new StandardSQLFunction("to_timestamp", StandardBasicTypes.TIMESTAMP)); + + // other function + registerFunction("stddev", new StandardSQLFunction("stddev", StandardBasicTypes.DOUBLE)); + registerFunction("variance", new StandardSQLFunction("variance", StandardBasicTypes.DOUBLE)); + registerFunction("rand", new NoArgSQLFunction("random", StandardBasicTypes.DOUBLE)); + registerFunction("to_ascii", new StandardSQLFunction("to_ascii")); + registerFunction("quote_ident", new StandardSQLFunction("quote_ident", StandardBasicTypes.STRING)); + registerFunction("quote_literal", new StandardSQLFunction("quote_literal", StandardBasicTypes.STRING)); + registerFunction("age", new StandardSQLFunction("age")); + registerFunction("current_date", new NoArgSQLFunction("current_date", StandardBasicTypes.DATE, false)); + registerFunction("current_time", new NoArgSQLFunction("current_time", StandardBasicTypes.TIME, false)); + registerFunction("current_timestamp", new NoArgSQLFunction("current_timestamp", StandardBasicTypes.TIMESTAMP, false)); + registerFunction("date_trunc", new StandardSQLFunction("date_trunc", StandardBasicTypes.TIMESTAMP)); + registerFunction("localtime", new NoArgSQLFunction("localtime", StandardBasicTypes.TIME, false)); + registerFunction("localtimestamp", new NoArgSQLFunction("localtimestamp", StandardBasicTypes.TIMESTAMP, false)); + registerFunction("now", new NoArgSQLFunction("now", StandardBasicTypes.TIMESTAMP)); + registerFunction("timeofday", new NoArgSQLFunction("timeofday", StandardBasicTypes.STRING)); + registerFunction("current_user", new NoArgSQLFunction("current_user", StandardBasicTypes.STRING, false)); + registerFunction("session_user", new NoArgSQLFunction("session_user", StandardBasicTypes.STRING, false)); + registerFunction("user", new NoArgSQLFunction("user", StandardBasicTypes.STRING, false)); + registerFunction("current_database", new NoArgSQLFunction("current_database", StandardBasicTypes.STRING, true)); + registerFunction("current_schema", new NoArgSQLFunction("current_schema", StandardBasicTypes.STRING, true)); + registerFunction("locate", new PositionSubstringFunction()); + registerFunction("str", new SQLFunctionTemplate(StandardBasicTypes.STRING, "cast(?1 as varchar)")); + } + + // database type mapping support + @Override + public SqlTypeDescriptor getSqlTypeDescriptorOverride(int sqlCode) { + SqlTypeDescriptor descriptor; + switch (sqlCode) { + case Types.BLOB: { + descriptor = BlobTypeDescriptor.BLOB_BINDING; + break; + } + case Types.CLOB: { + descriptor = ClobTypeDescriptor.CLOB_BINDING; + break; + } + default: { + descriptor = super.getSqlTypeDescriptorOverride(sqlCode); + break; + } + } + return descriptor; + } + + // hibernate type mapping support + // extend... + + // function support + // extend... + + // native identifier generation + @Override + public String getNativeIdentifierGeneratorStrategy() { + return "sequence"; + } + + // IDENTITY support + @Override + public IdentityColumnSupport getIdentityColumnSupport() { + return new OpenGaussIdentityColumnSupport(); + } + + // SEQUENCE support + @Override + public boolean supportsSequences() { + return true; + } + + @Override + public boolean supportsPooledSequences() { + return true; + } + + @Override + public String getSequenceNextValString(String sequenceName) { + return "select " + getSelectSequenceNextValString(sequenceName); + } + + @Override + public String getSelectSequenceNextValString(String sequenceName) { + return "nextval('" + sequenceName + "')"; + } + + @Override + public String getCreateSequenceString(String sequenceName) { + return "create sequence " + sequenceName; + } + + @Override + protected String getCreateSequenceString(String sequenceName, int initialValue, int incrementSize) { + if (initialValue < 0 && incrementSize > 0) { + return String.format("%s minvalue %d start %d increment %d", getCreateSequenceString(sequenceName), initialValue, initialValue, incrementSize); + } else if (initialValue > 0 && incrementSize < 0) { + return String.format("%s maxvalue %d start %d increment %d", getCreateSequenceString(sequenceName), initialValue, initialValue, incrementSize); + } else { + return String.format("%s start %d increment %d", getCreateSequenceString(sequenceName), initialValue, incrementSize); + } + } + + @Override + public String getDropSequenceString(String sequenceName) { + return "drop sequence if exists " + sequenceName; + } + + @Override + public String getQuerySequencesString() { + return "select * from information_schema.sequences"; + } + + // GUID support + // extend... + + // limit/offset support + private static final AbstractLimitHandler LIMIT_HANDLER = new AbstractLimitHandler() { + @Override + public String processSql(String sql, RowSelection selection) { + final boolean hasOffset = LimitHelper.hasFirstRow(selection); + return sql + (hasOffset ? " limit ? offset ?" : " limit ?"); + } + + @Override + public boolean supportsLimit() { + return true; + } + + @Override + public boolean bindLimitParametersInReverseOrder() { + return true; + } + }; + + @Override + public LimitHandler getLimitHandler() { + return LIMIT_HANDLER; + } + + // lock acquisition support + @Override + public String getForUpdateString(String aliases) { + return getForUpdateString() + " of " + aliases; + } + + @Override + public String getForUpdateString(String aliases, LockOptions lockOptions) { + LockMode lockMode = lockOptions.getLockMode(); + if (aliases != null && !aliases.isEmpty()) { + lockMode = lockOptions.getAliasSpecificLockMode(aliases); + if (lockMode == null) { + lockMode = lockOptions.getLockMode(); + } + } + switch (lockMode) { + case UPGRADE: + return getForUpdateString(aliases); + case PESSIMISTIC_READ: + return getReadLockString(aliases, lockOptions.getTimeOut()); + case PESSIMISTIC_WRITE: + return getWriteLockString(aliases, lockOptions.getTimeOut()); + case UPGRADE_NOWAIT: + case FORCE: + case PESSIMISTIC_FORCE_INCREMENT: + return getForUpdateNowaitString(aliases); + case UPGRADE_SKIPLOCKED: + return getForUpdateSkipLockedString(aliases); + default: + return ""; + } + } + + @Override + public String getWriteLockString(int timeout) { + if (timeout == LockOptions.NO_WAIT) { + return " for update nowait"; + } else if (timeout == LockOptions.SKIP_LOCKED) { + return " for update skip locked"; + } else { + return " for update"; + } + } + + @Override + public String getWriteLockString(String aliases, int timeout) { + if (timeout == LockOptions.NO_WAIT) { + return String.format(" for update of %s nowait", aliases); + } else if (timeout == LockOptions.SKIP_LOCKED) { + return String.format(" for update of %s skip locked", aliases); + } else { + return " for update of " + aliases; + } + } + + @Override + public String getReadLockString(int timeout) { + if (timeout == LockOptions.NO_WAIT) { + return " for share nowait"; + } else if (timeout == LockOptions.SKIP_LOCKED) { + return " for share skip locked"; + } else { + return " for share"; + } + } + + @Override + public String getReadLockString(String aliases, int timeout) { + if (timeout == LockOptions.NO_WAIT) { + return String.format(" for share of %s nowait", aliases); + } else if (timeout == LockOptions.SKIP_LOCKED) { + return String.format(" for share of %s skip locked", aliases); + } else { + return " for share of " + aliases; + } + } + + @Override + public String getForUpdateNowaitString() { + return getForUpdateString() + " nowait"; + } + + @Override + public String getForUpdateNowaitString(String aliases) { + return getForUpdateString(aliases) + " nowait"; + } + + @Override + public String getForUpdateSkipLockedString() { + return getForUpdateString() + " skip locked"; + } + + @Override + public String getForUpdateSkipLockedString(String aliases) { + return getForUpdateString(aliases) + " skip locked"; + } + + // callable statement support + @Override + public int registerResultSetOutParameter(CallableStatement statement, int position) throws SQLException { + statement.registerOutParameter(position, Types.REF_CURSOR); + position++; + return position; + } + + @Override + public ResultSet getResultSet(CallableStatement statement) throws SQLException { + statement.execute(); + return (ResultSet) statement.getObject(1); + } + + @Override + public ResultSet getResultSet(CallableStatement statement, int position) throws SQLException { + if (position != 1) { + throw new UnsupportedOperationException("OpenGauss only supports REF_CURSOR parameters as the first parameter"); + } + return (ResultSet) statement.getObject(1); + } + + // current timestamp support + @Override + public boolean supportsCurrentTimestampSelection() { + return true; + } + + @Override + public boolean isCurrentTimestampSelectStringCallable() { + return false; + } + + @Override + public String getCurrentTimestampSelectString() { + return "select current_timestamp"; // select current_timestamp + } + + // SQLException support + @Override + public SQLExceptionConversionDelegate buildSQLExceptionConversionDelegate() { + return new SQLExceptionConversionDelegate() { + @Override + public JDBCException convert(SQLException sqlException, String message, String sql) { + final String sqlState = JdbcExceptionHelper.extractSqlState(sqlException); + + if (sqlState != null && sqlState.length() >= 2) { + String sqlStateClassCode = sqlState.substring(0, 2); + switch (sqlStateClassCode) { + case "23": + switch (sqlState) { + case "23505": + case "23503": + case "23502": { + String constraintName = getViolatedConstraintNameExtracter().extractConstraintName(sqlException); + return new ConstraintViolationException(message, sqlException, sql, constraintName); + } + } + return new ConstraintViolationException(message, sqlException, sql, null); + case "22": + return new DataException(message, sqlException, sql); + case "40": + return new LockAcquisitionException(message, sqlException, sql); + case "08": + return new JDBCConnectionException(message, sqlException, sql); + case "55": + return new PessimisticLockException(message, sqlException, sql); + case "57": + return new QueryTimeoutException(message, sqlException, sql); + default: + return null; + } + } + return null; + } + }; + } + + private static final ViolatedConstraintNameExtracter EXTRACTOR = new TemplatedViolatedConstraintNameExtracter() { + @Override + protected String doExtractConstraintName(SQLException sqle) throws NumberFormatException { + final String sqlState = JdbcExceptionHelper.extractSqlState(sqle); + if (sqlState == null) { + return null; + } + switch (sqlState) { + // CHECK VIOLATION + case "23514": + return extractUsingTemplate("violates check constraint \"", "\"", sqle.getMessage()); + // UNIQUE VIOLATION + case "23505": + return extractUsingTemplate("violates unique constraint \"", "\"", sqle.getMessage()); + // FOREIGN KEY VIOLATION + case "23503": + return extractUsingTemplate("violates foreign key constraint \"", "\"", sqle.getMessage()); + // NOT NULL VIOLATION + case "23502": + return extractUsingTemplate("null value in column \"", "\" violates not-null constraint", sqle.getMessage()); + // 其他约束违规类型 + default: + return null; + } + } + }; + + @Override + public ViolatedConstraintNameExtracter getViolatedConstraintNameExtracter() { + return EXTRACTOR; + } + + // union subclass support + @Override + public boolean supportsUnionAll() { + return true; + } + + // miscellaneous support + @Override + public String getNoColumnsInsertString() { + return "default values"; + } + + @Override + public String getCaseInsensitiveLike() { + return "ilike"; + } + + @Override + public boolean supportsCaseInsensitiveLike() { + return true; + } + + @Override + public String toBooleanValueString(boolean bool) { + return bool ? "t" : "f"; + } + + // keyword support + @Override + public NameQualifierSupport getNameQualifierSupport() { + return NameQualifierSupport.SCHEMA; + } + + @Override + public IdentifierHelper buildIdentifierHelper(IdentifierHelperBuilder builder, DatabaseMetaData dbMetaData) throws SQLException { + if (dbMetaData == null) { + builder.setUnquotedCaseStrategy(IdentifierCaseStrategy.LOWER); + builder.setQuotedCaseStrategy(IdentifierCaseStrategy.MIXED); + } + return super.buildIdentifierHelper(builder, dbMetaData); + } + + // identifier quoting support + // extend... + + + // DDL support + @Override + public String[] getCreateSchemaCommand(String schemaName) { + return new String[]{"create schema if not exists " + schemaName}; + } + + @Override + public String[] getDropSchemaCommand(String schemaName) { + return new String[]{"drop schema if exists " + schemaName}; + } + + @Override + public String getCurrentSchemaCommand() { + return "select current_schema()"; + } + + @Override + public String getAddColumnString() { + return "add column"; + } + + @Override + public boolean supportsCommentOn() { + return true; + } + + @Override + public boolean supportsIfExistsBeforeTableName() { + return true; + } + + @Override + public boolean supportsIfExistsBeforeConstraintName() { + return true; + } + + @Override + public boolean supportsIfExistsAfterAlterTable() { + return true; + } + + @Override + public String getCascadeConstraintsString() { + return " cascade"; + } + + public MultiTableBulkIdStrategy getDefaultMultiTableBulkIdStrategy() { + return new LocalTemporaryTableBulkIdStrategy(new IdTableSupportStandardImpl() { + @Override + public String getCreateIdTableCommand() { + return "create temporary table"; + } + }, AfterUseAction.DROP, null); + } + + // Informational metadata + @Override + public boolean supportsEmptyInList() { + return false; + } + + @Override + public boolean supportsRowValueConstructorSyntax() { + return true; + } + + @Override + public boolean supportsRowValueConstructorSyntaxInSet() { + return true; + } + + @Override + public boolean supportsRowValueConstructorSyntaxInInList() { + return true; + } + + @Override + public boolean requiresParensForTupleDistinctCounts() { + return true; + } + + @Override + public boolean supportsSelectAliasInGroupByClause() { + return true; + } + + @Override + public boolean supportsNonQueryWithCTE() { + return true; + } + + @Override + public boolean supportsValuesList() { + return true; + } + + @Override + public boolean supportsLobValueChangePropogation() { + return false; + } + + @Override + public boolean supportsPartitionBy() { + return true; + } + + @Override + public boolean supportsNamedParameters(DatabaseMetaData databaseMetaData) throws SQLException { + return false; + } + + @Override + public boolean supportsSkipLocked() { + return true; + } + + @Override + public boolean supportsNoWait() { + return true; + } +} + diff --git a/openGaussDialect/src/main/java/org/hibernate/dialect/function/SubstringFunction.java b/openGaussDialect/src/main/java/org/hibernate/dialect/function/SubstringFunction.java new file mode 100644 index 0000000000000000000000000000000000000000..0c33089a6589eb6a4bae29f9caffd36c3a767097 --- /dev/null +++ b/openGaussDialect/src/main/java/org/hibernate/dialect/function/SubstringFunction.java @@ -0,0 +1,38 @@ +package org.hibernate.dialect.function; + +import org.hibernate.QueryException; +import org.hibernate.engine.spi.Mapping; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.type.StandardBasicTypes; +import org.hibernate.type.Type; + +import java.util.List; + +public class SubstringFunction implements SQLFunction { + + @Override + public boolean hasArguments() { + return true; + } + + @Override + public boolean hasParenthesesIfNoArguments() { + return true; + } + + @Override + public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException { + return StandardBasicTypes.STRING; + } + + @Override + public String render(Type firstArgumentType, List arguments, SessionFactoryImplementor factory) throws QueryException { + if (arguments.size() == 3) { + return "substring(" + arguments.get(0) + " from " + arguments.get(1) + " for " + arguments.get(2) + ")"; + } else if (arguments.size() == 2) { + return "substring(" + arguments.get(0) + " from " + arguments.get(1) + ")"; + } else { + throw new QueryException("Invalid number of arguments for substring function: " + arguments.size()); + } + } +} \ No newline at end of file diff --git a/openGaussDialect/src/main/java/org/hibernate/dialect/identity/OpenGaussIdentityColumnSupport.java b/openGaussDialect/src/main/java/org/hibernate/dialect/identity/OpenGaussIdentityColumnSupport.java new file mode 100644 index 0000000000000000000000000000000000000000..8859cdc140a9f898ed5a73e06b23dee9ef71fa4a --- /dev/null +++ b/openGaussDialect/src/main/java/org/hibernate/dialect/identity/OpenGaussIdentityColumnSupport.java @@ -0,0 +1,27 @@ +package org.hibernate.dialect.identity; + +import java.sql.Types; + +public class OpenGaussIdentityColumnSupport extends IdentityColumnSupportImpl { + + @Override + public boolean supportsIdentityColumns() { + return false; + } + + @Override + public String getIdentitySelectString(String table, String column, int type) { + // {table}_{column}_seq + return "select currval('" + table + '_' + column + "_seq')"; + } + + @Override + public String getIdentityColumnString(int type) { + return type == Types.BIGINT ? "bigserial not null" : "serial not null"; + } + + @Override + public boolean hasDataTypeInIdentityColumn() { + return false; + } +} diff --git a/openGaussDialect/src/main/java/org/hibernate/dialect/type/JsonType.java b/openGaussDialect/src/main/java/org/hibernate/dialect/type/JsonType.java new file mode 100644 index 0000000000000000000000000000000000000000..07e6a8b591007183bc7b1e44f26ba44f93857763 --- /dev/null +++ b/openGaussDialect/src/main/java/org/hibernate/dialect/type/JsonType.java @@ -0,0 +1,91 @@ +package org.hibernate.dialect.type; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.usertype.UserType; + +import java.io.Serializable; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Objects; + +public class JsonType implements UserType { + + private static final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public int[] sqlTypes() { + return new int[] { Types.JAVA_OBJECT}; + } + + @Override + public Class returnedClass() { + return Object.class; + } + + @Override + public boolean equals(Object x, Object y) throws HibernateException { + return Objects.equals(x, y); + } + + @Override + public int hashCode(Object x) throws HibernateException { + return Objects.hashCode(x); + } + + @Override + public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException { + String json = rs.getString(names[0]); + if (json != null) { + try { + return objectMapper.readValue(json, returnedClass()); + } catch (Exception e) { + throw new HibernateException("Failed to convert JSON to Object: " + json, e); + } + } + return null; + } + + @Override + public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException { + if (value == null) { + st.setNull(index, Types.OTHER); + } else { + try { + String json = objectMapper.writeValueAsString(value); + st.setObject(index, json, Types.OTHER); + } catch (JsonProcessingException e) { + throw new HibernateException("Failed to convert Object to JSON: " + value, e); + } + } + } + + @Override + public Object deepCopy(Object value) throws HibernateException { + return value; + } + + @Override + public boolean isMutable() { + return false; + } + + @Override + public Serializable disassemble(Object value) throws HibernateException { + return (Serializable) value; + } + + @Override + public Object assemble(Serializable cached, Object owner) throws HibernateException { + return cached; + } + + @Override + public Object replace(Object original, Object target, Object owner) throws HibernateException { + return original; + } +} diff --git a/openGaussDialect/src/main/java/org/hibernate/dialect/type/JsonbType.java b/openGaussDialect/src/main/java/org/hibernate/dialect/type/JsonbType.java new file mode 100644 index 0000000000000000000000000000000000000000..8c7e34c53dd302abe481518eae690ecfc0140330 --- /dev/null +++ b/openGaussDialect/src/main/java/org/hibernate/dialect/type/JsonbType.java @@ -0,0 +1,105 @@ +package org.hibernate.dialect.type; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.usertype.UserType; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Objects; + +public class JsonbType implements UserType { + private static final ObjectMapper objectMapper = new ObjectMapper(); + + @Override + public int[] sqlTypes() { + return new int[]{Types.OTHER}; + } + + @Override + public Class returnedClass() { + return Object.class; + } + + @Override + public boolean equals(Object x, Object y) throws HibernateException { + return Objects.equals(x, y); + } + + @Override + public int hashCode(Object x) throws HibernateException { + return Objects.hashCode(x); + } + + @Override + public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException { + final String cellContent = rs.getString(names[0]); + if (cellContent == null) { + return null; + } + try { + return objectMapper.readValue(cellContent.getBytes(StandardCharsets.UTF_8), returnedClass()); + } catch (final Exception ex) { + throw new RuntimeException("Failed to convert String to Invoice: " + ex.getMessage(), ex); + } + } + + @Override + public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException { + if (value == null) { + st.setNull(index, Types.OTHER); + return; + } + try { + final StringWriter w = new StringWriter(); + objectMapper.writeValue(w, value); + w.flush(); + st.setObject(index, w.toString(), Types.OTHER); + } catch (final Exception ex) { + throw new RuntimeException("Failed to convert Invoice to String: " + ex.getMessage(), ex); + } + } + + @Override + public Object deepCopy(Object value) throws HibernateException { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(bos); + oos.writeObject(value); + oos.flush(); + oos.close(); + bos.close(); + ByteArrayInputStream bais = new ByteArrayInputStream(bos.toByteArray()); + Object obj = new ObjectInputStream(bais).readObject(); + bais.close(); + return obj; + } catch (ClassNotFoundException | IOException ex) { + throw new HibernateException(ex); + } + } + + @Override + public boolean isMutable() { + return false; + } + + @Override + public Serializable disassemble(Object value) throws HibernateException { + return (Serializable) value; + } + + @Override + public Object assemble(Serializable cached, Object owner) throws HibernateException { + return cached; + } + + @Override + public Object replace(Object original, Object target, Object owner) throws HibernateException { + return original; + } +} diff --git a/openGaussDialect/src/main/resources/hibernate.cfg.xml b/openGaussDialect/src/main/resources/hibernate.cfg.xml new file mode 100644 index 0000000000000000000000000000000000000000..e78be14191d6398aff31e31fc1ee0363b43d7c3e --- /dev/null +++ b/openGaussDialect/src/main/resources/hibernate.cfg.xml @@ -0,0 +1,16 @@ + + + + + org.opengauss.Driver + jdbc:opengauss://43.138.80.125:25432/osapp + postgres + osapp_openGauss@123 + org.hibernate.dialect.OpenGaussDialect + true + true + update + + \ No newline at end of file diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/callablestatement/TestEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/callablestatement/TestEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..9f32d3f0dc0ec0541d6fc3fc49139e7218f6c2af --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/callablestatement/TestEntity.java @@ -0,0 +1,21 @@ +package org.hibernate.dialect.entity.callablestatement; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "test_callable_statement_table") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TestEntity { + @Id + private Long id; + private String name; +} + diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/BinaryEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/BinaryEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..fc017d79b811529dd864ed9693ac0fe3d9e730db --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/BinaryEntity.java @@ -0,0 +1,23 @@ +package org.hibernate.dialect.entity.datatype; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "binary_test_table") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class BinaryEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private byte[] binaryField; + @Column(name = "varBinaryField", columnDefinition = "bytea") + private byte[] varBinaryField; + @Column(name = "longVarBinaryField", columnDefinition = "bytea") + private byte[] longVarBinaryField; +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/BooleanEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/BooleanEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..280c88d6187c9316ad5078ae48da548ded9574a6 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/BooleanEntity.java @@ -0,0 +1,23 @@ +package org.hibernate.dialect.entity.datatype; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "bool_test_table") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class BooleanEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Boolean booleanField; + + @Column(columnDefinition = "bit") + private Boolean bitField; +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/CharEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/CharEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..90df8382c9fdc5fc47164dd19146932a5c595498 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/CharEntity.java @@ -0,0 +1,19 @@ +package org.hibernate.dialect.entity.datatype; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "char_test_table") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class CharEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private Character characterField; +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/DateTimeEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/DateTimeEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..3e12e6b3e6c3041bef337314e6df1c263b98b7a8 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/DateTimeEntity.java @@ -0,0 +1,29 @@ +package org.hibernate.dialect.entity.datatype; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; + +@Entity +@Table(name = "date_time_test_table") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class DateTimeEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Date dateField; + private Time timeField; + private Timestamp timestampField; + @Column(name = "timetzField", columnDefinition = "timetz") + private Timestamp timetzField; + @Column(name = "timestamptzField", columnDefinition = "timestamptz") + private Timestamp timestamptzField; +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/JsonEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/JsonEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..890b610b65ccf0f1b3daf56e74a5dcdcd87bd751 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/JsonEntity.java @@ -0,0 +1,37 @@ +package org.hibernate.dialect.entity.datatype; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.hibernate.annotations.TypeDefs; +import org.hibernate.dialect.pojo.Person; +import org.hibernate.dialect.type.JsonType; +import org.hibernate.dialect.type.JsonbType; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +@Entity +@Table(name = "test_table") +@Data +@NoArgsConstructor +@AllArgsConstructor +@TypeDefs({ + @TypeDef(name = "jsonb", typeClass = JsonbType.class), + @TypeDef(name = "json", typeClass = JsonType.class), +}) +public class JsonEntity { + @Id + private Long id; + + @Type(type = "json") + private Person pojoJson; + + @Type(type = "jsonb") + @Column(columnDefinition = "jsonb") + private Person pojoJsonb; +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/LobEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/LobEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..f4e6fa6d0425d787ae74b8002da0e2dc3d41f92c --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/LobEntity.java @@ -0,0 +1,27 @@ +package org.hibernate.dialect.entity.datatype; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.sql.Blob; +import java.sql.Clob; +import java.sql.NClob; + +@Entity +@Table(name = "lob_test_table") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class LobEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Lob + private Blob blobField; // Blob --> blob + @Lob + private Clob clobField; // Clob --> clob + @Lob + private NClob nclobField; // NClob --> text +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/NationalizedEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/NationalizedEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..4f383dad77f5eb1c9ca412fb65a9986be3dbfe1c --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/NationalizedEntity.java @@ -0,0 +1,28 @@ +package org.hibernate.dialect.entity.datatype; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Nationalized; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Lob; +import javax.persistence.Table; + +@Entity +@Table(name = "nationalized_table") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class NationalizedEntity { + @Id + private Long id; + @Nationalized + private String nvarcharData; + @Nationalized + private Character ncharData; + @Lob + @Nationalized + private String nclobData; +} \ No newline at end of file diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/NumberEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/NumberEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..562f7924fffe3fa90b4065568dfe84dd8a3d75f1 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/NumberEntity.java @@ -0,0 +1,30 @@ +package org.hibernate.dialect.entity.datatype; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.math.BigDecimal; + +@Entity +@Table(name = "number_test_table") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class NumberEntity { + @Id + private Long id; + private Byte tinyIntField; + private Short smallIntField; + private Integer integerField; + private Long bigIntField; + private Float floatField; + private Double doubleField; + @Column(columnDefinition = "decimal(19, 2)") + private BigDecimal decimalField; + private BigDecimal numericField; +} \ No newline at end of file diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/StringEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/StringEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..275583caf95d2a27afd9f0386a9d48fd23c98c32 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/datatype/StringEntity.java @@ -0,0 +1,26 @@ +package org.hibernate.dialect.entity.datatype; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Nationalized; + +import javax.persistence.*; + +@Entity +@Table(name = "string_test_table") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class StringEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String varCharField; + @Column(name = "longVarCharField", columnDefinition = "text") + private String longVarCharField; + @Nationalized + private Character nCharField; + @Nationalized + private String nVarCharField; +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/ddl/AnnotatedEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/ddl/AnnotatedEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..532d4318a3cf0feae99b5c8608a5ccb046a3e86d --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/ddl/AnnotatedEntity.java @@ -0,0 +1,25 @@ +package org.hibernate.dialect.entity.ddl; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.Comment; + +import javax.persistence.*; + +@Entity +@Table(name = "annotated_table") +@org.hibernate.annotations.Table(appliesTo = "annotated_table", comment = "This is a table comment") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class AnnotatedEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "annotated_column") + @Comment("This is a column comment") + private String annotatedField; +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/ddl/ChildEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/ddl/ChildEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..d68011ce3a377e96d122d4432dbf2a3e1d8822bc --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/ddl/ChildEntity.java @@ -0,0 +1,21 @@ +package org.hibernate.dialect.entity.ddl; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "child_table") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ChildEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @ManyToOne + @JoinColumn(name = "parent_id", foreignKey = @ForeignKey(name = "fk_child_parent")) + private ParentEntity parent; +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/ddl/ParentEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/ddl/ParentEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..1e62f995a67f119ddcbeb059f70fe8419201b57e --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/ddl/ParentEntity.java @@ -0,0 +1,32 @@ +package org.hibernate.dialect.entity.ddl; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.util.ArrayList; +import java.util.List; + +@Entity +@Table(name = "parent_table") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ParentEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true) + private List children = new ArrayList<>(); + + public void addChild(ChildEntity child) { + children.add(child); + child.setParent(this); + } + + public void removeChild(ChildEntity child) { + children.remove(child); + child.setParent(null); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/ddl/TestSchemaEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/ddl/TestSchemaEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..77c6652cbfa87c0b9d5a0c40f4ee2d6f993a9954 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/ddl/TestSchemaEntity.java @@ -0,0 +1,20 @@ +package org.hibernate.dialect.entity.ddl; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "test_table", schema = "test_schema") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TestSchemaEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @org.hibernate.annotations.Index(name = "idx_name") + private String name; +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/function/TestEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/function/TestEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..cddc157420b26fff2fa3222f294f92a38a1774e3 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/function/TestEntity.java @@ -0,0 +1,20 @@ +package org.hibernate.dialect.entity.function; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "test_function_entity") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TestEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private Double value; +} + diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/lock/TestEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/lock/TestEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..168a90f51a32cdc08899e4e495aeb28d0da42340 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/lock/TestEntity.java @@ -0,0 +1,19 @@ +package org.hibernate.dialect.entity.lock; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "test_lock_entity") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TestEntity { + @Id + private Long id; + private String name; + private Double balance; +} \ No newline at end of file diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/ChildEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/ChildEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..8eb30a2f887b3cda595a7cb99d3504b3df87a042 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/ChildEntity.java @@ -0,0 +1,23 @@ +package org.hibernate.dialect.entity.miscellaneous; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + + +@Entity +@Table(name = "child_entity") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ChildEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "parent_id") + private ParentEntity parent; +} \ No newline at end of file diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/ParentEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/ParentEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..4611c0cd0736b192e0b19a14c1a7fd1952a62ea6 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/ParentEntity.java @@ -0,0 +1,24 @@ +package org.hibernate.dialect.entity.miscellaneous; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; +import java.util.HashSet; +import java.util.Set; + +@Entity +@Table(name = "parent_entity") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class ParentEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToMany(mappedBy = "parent", fetch = FetchType.LAZY) + private Set children = new HashSet<>(); +} + diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/TestBooleanEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/TestBooleanEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..949eb57f7028418b3f749ef805a886706261a77e --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/TestBooleanEntity.java @@ -0,0 +1,21 @@ +package org.hibernate.dialect.entity.miscellaneous; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "test_boolean_entity") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TestBooleanEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Boolean active; +} + diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/TestNoColumnsInsert.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/TestNoColumnsInsert.java new file mode 100644 index 0000000000000000000000000000000000000000..410cc39a4801488ddf9eced919541278317daaf2 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/TestNoColumnsInsert.java @@ -0,0 +1,18 @@ +package org.hibernate.dialect.entity.miscellaneous; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "test_no_columns_insert") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TestNoColumnsInsert { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/TestStringEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/TestStringEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..866a35e5277ce1d475c9aaf6f1af73455cd8e71c --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/miscellaneous/TestStringEntity.java @@ -0,0 +1,20 @@ +package org.hibernate.dialect.entity.miscellaneous; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "test_string_entity") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TestStringEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/pagination/TestEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/pagination/TestEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..c697b404619af08bdac87cc4c79b64d3ee027038 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/pagination/TestEntity.java @@ -0,0 +1,19 @@ +package org.hibernate.dialect.entity.pagination; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "test_pagination_entity") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TestEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String name; +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/entity/sequence/TestEntity.java b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/sequence/TestEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..c28e6f4084238971352cb3ed0dd7f7cb939e14f2 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/entity/sequence/TestEntity.java @@ -0,0 +1,21 @@ +package org.hibernate.dialect.entity.sequence; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Table(name = "test_sequence_entity") +@Data +@NoArgsConstructor +@AllArgsConstructor +public class TestEntity { + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "seq_gen") + @SequenceGenerator(name = "seq_gen", sequenceName = "test_sequence", allocationSize = 1) + private Long id; + private String name; +} + diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussCallableStatementIntegrationTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussCallableStatementIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c0ab921447c8e30ce7422eadbcf60301184482d7 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussCallableStatementIntegrationTest.java @@ -0,0 +1,85 @@ +package org.hibernate.dialect.integration; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.dialect.entity.callablestatement.TestEntity; +import org.hibernate.dialect.util.HibernateUtil; +import org.junit.jupiter.api.*; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.Types; +import java.util.ArrayList; +import java.util.List; + +public class OpenGaussCallableStatementIntegrationTest { + private static SessionFactory sessionFactory; + + @BeforeAll + public static void init() { + /** + * OpenGauss 函数和存储过程示例: + * + * 函数: + * + * CREATE OR REPLACE FUNCTION get_users() + * RETURNS refcursor AS + * $$ + * DECLARE + * out_cursor refcursor; + * BEGIN + * OPEN out_cursor FOR SELECT id, name FROM test_table; + * RETURN out_cursor; + * END; + * $$ LANGUAGE plpgsql; + * + * 存储过程: + * + * CREATE OR REPLACE PROCEDURE get_users(out_cursor OUT REFCURSOR) + * AS + * BEGIN + * OPEN out_cursor FOR SELECT id, name FROM users ORDER BY id; + * END; + */ + sessionFactory = HibernateUtil.getSessionFactory(TestEntity.class); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.createNativeQuery("DELETE FROM test_table").executeUpdate(); + session.save(new TestEntity(1L, "Alice")); + session.save(new TestEntity(2L, "Bob")); + session.save(new TestEntity(3L, "Charlie")); + session.getTransaction().commit(); + session.close(); + } + + @AfterAll + public static void tearDownAll() { + if (sessionFactory != null) { + sessionFactory.close(); + } + } + + @Test + public void testCallableStatement() throws Exception { + Session session = sessionFactory.openSession(); + Connection connection = session.doReturningWork(conn -> conn); + String functionCall = "{? = call get_users()}"; + CallableStatement callableStatement = connection.prepareCall(functionCall); + callableStatement.registerOutParameter(1, Types.REF_CURSOR); + callableStatement.execute(); + ResultSet resultSet = (ResultSet) callableStatement.getObject(1); + List testEntities = new ArrayList<>(); + while (resultSet.next()) { + Long id = resultSet.getLong("id"); + String name = resultSet.getString("name"); + testEntities.add(new TestEntity(id, name)); + } + Assertions.assertEquals(3, testEntities.size(), "应检索到 3 个用户"); + Assertions.assertEquals("Alice", testEntities.get(0).getName()); + Assertions.assertEquals("Bob", testEntities.get(1).getName()); + Assertions.assertEquals("Charlie", testEntities.get(2).getName()); + resultSet.close(); + callableStatement.close(); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectDDLCommentTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectDDLCommentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..0701013b173941e9fb46acc97ca616dd616af48d --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectDDLCommentTest.java @@ -0,0 +1,45 @@ +package org.hibernate.dialect.integration; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.dialect.entity.ddl.AnnotatedEntity; +import org.hibernate.dialect.util.HibernateUtil; +import org.junit.jupiter.api.*; + +public class OpenGaussDialectDDLCommentTest { + private static SessionFactory sessionFactory; + + @BeforeAll + public static void init() { + sessionFactory = HibernateUtil.getSessionFactory(AnnotatedEntity.class); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.createNativeQuery("DELETE FROM annotated_table").executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + @AfterAll + public static void close() { + if (sessionFactory != null) { + sessionFactory.close(); + } + } + + @Test + public void testTableAndColumnComments() { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + AnnotatedEntity entity = new AnnotatedEntity(); + entity.setAnnotatedField("Test"); + session.save(entity); + session.getTransaction().commit(); + String tableCommentQuery = "SELECT description " + "FROM pg_description " + "WHERE objoid = (" + " SELECT oid FROM pg_class WHERE relname = 'annotated_table'" + ") AND objsubid = 0"; + String tableComment = (String) session.createNativeQuery(tableCommentQuery).getSingleResult(); + Assertions.assertEquals("This is a table comment", tableComment); + String columnCommentQuery = "SELECT description " + "FROM pg_description " + "WHERE objoid = (" + " SELECT oid FROM pg_class WHERE relname = 'annotated_table'" + ") AND objsubid = (" + " SELECT attnum FROM pg_attribute " + " WHERE attrelid = (" + " SELECT oid FROM pg_class WHERE relname = 'annotated_table'" + " ) AND attname = 'annotated_column'" + ")"; + String columnComment = (String) session.createNativeQuery(columnCommentQuery).getSingleResult(); + Assertions.assertEquals("This is a column comment", columnComment); + session.close(); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectDDLConstraintTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectDDLConstraintTest.java new file mode 100644 index 0000000000000000000000000000000000000000..555a18a378cbef5ae1b0ab7d52bc300aae7aeeb8 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectDDLConstraintTest.java @@ -0,0 +1,103 @@ +package org.hibernate.dialect.integration; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.entity.ddl.ChildEntity; +import org.hibernate.dialect.entity.ddl.ParentEntity; +import org.hibernate.dialect.util.HibernateUtil; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class OpenGaussDialectDDLConstraintTest { + + private static SessionFactory sessionFactory; + + @BeforeEach + public void setUp() { + sessionFactory = HibernateUtil.getSessionFactory(ParentEntity.class, ChildEntity.class); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.createNativeQuery("DELETE FROM child_table").executeUpdate(); + session.createNativeQuery("DELETE FROM parent_table").executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + @AfterEach + public void tearDown() { + if (sessionFactory != null) { + sessionFactory.close(); + } + } + + @Test + public void testConstraintCreation() { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + ParentEntity parent = new ParentEntity(); + session.save(parent); + ChildEntity child = new ChildEntity(); + child.setParent(parent); + session.save(child); + session.getTransaction().commit(); + session.beginTransaction(); + String sql = "SELECT constraint_name FROM information_schema.table_constraints WHERE table_name='child_table' AND constraint_type='FOREIGN KEY'"; + String constraintName = (String) session.createNativeQuery(sql).uniqueResult(); + Assertions.assertEquals("fk_child_parent", constraintName); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testCascadeDelete() { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + ParentEntity parent = new ParentEntity(); + ChildEntity child1 = new ChildEntity(); + ChildEntity child2 = new ChildEntity(); + parent.addChild(child1); + parent.addChild(child2); + session.save(parent); + session.getTransaction().commit(); + session.beginTransaction(); + session.delete(parent); + session.getTransaction().commit(); + session.beginTransaction(); + Long childCount = (Long) session.createQuery("SELECT COUNT(c) FROM ChildEntity c").uniqueResult(); + Assertions.assertEquals(Long.valueOf(0), childCount); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testDropConstraintWithIfExists() { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + String addConstraintSQL = "alter table child_table add constraint test_constraint CHECK (id > 0)"; + session.createNativeQuery(addConstraintSQL).executeUpdate(); + session.getTransaction().commit(); + session.beginTransaction(); + SessionFactoryImplementor sfi = (SessionFactoryImplementor) sessionFactory; + Dialect dialect = sfi.getDialect(); + String dropConstraintSQL; + if (dialect.supportsIfExistsBeforeConstraintName()) { + dropConstraintSQL = "alter table child_table drop constraint if exists test_constraint" + dialect.getCascadeConstraintsString(); + } else if (dialect.supportsIfExistsAfterConstraintName()) { + dropConstraintSQL = "alter table child_table drop constraint test_constraint IF EXISTS" + dialect.getCascadeConstraintsString(); + } else { + dropConstraintSQL = "alter table child_table drop constraint test_constraint" + dialect.getCascadeConstraintsString(); + } + session.createNativeQuery(dropConstraintSQL).executeUpdate(); + session.getTransaction().commit(); + session.beginTransaction(); + String checkConstraintSQL = "SELECT constraint_name FROM information_schema.table_constraints WHERE table_name='child_table' AND constraint_name='test_constraint'"; + Object result = session.createNativeQuery(checkConstraintSQL).uniqueResult(); + Assertions.assertNull(result); + session.getTransaction().commit(); + session.close(); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectDDLSchemaTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectDDLSchemaTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8cdd2694b111afff13b76fc256c5b5e40d0deab8 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectDDLSchemaTest.java @@ -0,0 +1,88 @@ +package org.hibernate.dialect.integration; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.dialect.OpenGaussDialect; +import org.hibernate.dialect.entity.ddl.TestSchemaEntity; +import org.hibernate.dialect.util.HibernateUtil; +import org.junit.jupiter.api.*; + +public class OpenGaussDialectDDLSchemaTest { + + private static SessionFactory sessionFactory; + + @BeforeAll + public static void init() { + sessionFactory = HibernateUtil.getSessionFactory(null); + Session session = sessionFactory.openSession(); + Transaction transaction = session.beginTransaction(); + String dropSchemaSQL = "DROP SCHEMA IF EXISTS test_schema CASCADE"; + session.createNativeQuery(dropSchemaSQL).executeUpdate(); + transaction.commit(); + session.close(); + } + + @AfterAll + public static void close() { + if (sessionFactory != null) { + sessionFactory.close(); + } + } + + @Test + @Order(1) + public void testSchemaCreation() { + Session session = sessionFactory.openSession(); + Transaction transaction = session.beginTransaction(); + OpenGaussDialect dialect = new OpenGaussDialect(); + String[] createSchemaSQL = dialect.getCreateSchemaCommand("test_schema"); + for (String sql : createSchemaSQL) { + session.createNativeQuery(sql).executeUpdate(); + } + String checkSchemaSQL = "SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'test_schema'"; + String schemaName = (String) session.createNativeQuery(checkSchemaSQL).uniqueResult(); + Assertions.assertEquals("test_schema", schemaName); + transaction.commit(); + } + + @Test + public void testGetCurrentSchema() { + Session session = sessionFactory.openSession(); + OpenGaussDialect dialect = new OpenGaussDialect(); + String currentSchema = (String) session.createNativeQuery(dialect.getCurrentSchemaCommand()).uniqueResult(); + Assertions.assertEquals("public", currentSchema); + session.close(); + } + + @Test + public void testSchemaDeletion() { + Session session = sessionFactory.openSession(); + Transaction transaction = session.beginTransaction(); + OpenGaussDialect dialect = new OpenGaussDialect(); + String[] dropSchemaSQL = dialect.getDropSchemaCommand("test_schema"); + for (String sql : dropSchemaSQL) { + session.createNativeQuery(sql).executeUpdate(); + } + String checkSchemaSQL = "SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'test_schema'"; + Assertions.assertNull(session.createNativeQuery(checkSchemaSQL).uniqueResult()); + transaction.commit(); + } + + @Test + public void testIndexNameQualification() { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + String createSchemaSQL = "create schema if not exists test_schema"; + session.createNativeQuery(createSchemaSQL).executeUpdate(); + session.getTransaction().commit(); + session.close(); + SessionFactory sessionFactory = HibernateUtil.getSessionFactory(TestSchemaEntity.class); + session = sessionFactory.openSession(); + String sql = "SELECT schemaname, indexname FROM pg_indexes WHERE schemaname = 'test_schema' AND indexname = 'idx_name'"; + Object[] result = (Object[]) session.createNativeQuery(sql).uniqueResult(); + Assertions.assertEquals("test_schema", result[0]); + Assertions.assertEquals("idx_name", result[1]); + session.close(); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectDDLTableAndColumTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectDDLTableAndColumTest.java new file mode 100644 index 0000000000000000000000000000000000000000..1fb7118b60639f5da61d835bb313bcf00ace6db1 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectDDLTableAndColumTest.java @@ -0,0 +1,120 @@ +package org.hibernate.dialect.integration; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.boot.Metadata; +import org.hibernate.boot.MetadataSources; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.tool.hbm2ddl.SchemaExport; +import org.hibernate.tool.hbm2ddl.SchemaUpdate; +import org.hibernate.tool.schema.TargetType; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.EnumSet; + +public class OpenGaussDialectDDLTableAndColumTest { + + private static StandardServiceRegistry standardRegistry; + + private static Metadata metadata; + + private static SessionFactory sessionFactory; + + public static void setUp(Class... testClasses) { + standardRegistry = new StandardServiceRegistryBuilder().configure().build(); + MetadataSources metadataSources = new MetadataSources(standardRegistry); + for (Class testClass : testClasses) { + metadataSources.addAnnotatedClass(testClass); + } + metadata = metadataSources.buildMetadata(); + sessionFactory = metadata.buildSessionFactory(); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.createNativeQuery("DELETE FROM test_table").executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + @AfterEach + public void tearDown() { + if (sessionFactory != null) { + sessionFactory.close(); + } + if (standardRegistry != null) { + StandardServiceRegistryBuilder.destroy(standardRegistry); + } + } + + + @Entity + @Table(name = "test_table") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class TestEntity1 { + @Id + private Long id; + private String name; + } + + @Test + public void testCreateTable() { + setUp(TestEntity1.class); + SchemaExport schemaExport = new SchemaExport(); + schemaExport.execute(EnumSet.of(TargetType.DATABASE), SchemaExport.Action.CREATE, metadata); + Session session = sessionFactory.openSession(); + String sql = "SELECT table_name FROM information_schema.tables WHERE table_name = 'test_table'"; + String tableName = (String) session.createNativeQuery(sql).uniqueResult(); + Assertions.assertEquals("test_table", tableName); + session.close(); + } + + @Entity + @Table(name = "test_table") + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class TestEntity2 { + @Id + private Long id; + private String name; + private String description; + } + + @Test + public void testAddColumn() { + setUp(TestEntity1.class); + SchemaExport schemaExport = new SchemaExport(); + schemaExport.execute(EnumSet.of(TargetType.DATABASE), SchemaExport.Action.CREATE, metadata); + sessionFactory.close(); + setUp(TestEntity2.class); + SchemaUpdate schemaUpdate = new SchemaUpdate(); + schemaUpdate.execute(EnumSet.of(TargetType.DATABASE), metadata); + Session session = sessionFactory.openSession(); + String sql = "SELECT column_name FROM information_schema.columns WHERE table_name = 'test_table' AND column_name = 'description'"; + String columnName = (String) session.createNativeQuery(sql).uniqueResult(); + Assertions.assertEquals("description", columnName); + session.close(); + } + + @Test + public void testDropTable() { + setUp(TestEntity2.class); + SchemaExport schemaExport = new SchemaExport(); + schemaExport.execute(EnumSet.of(TargetType.DATABASE), SchemaExport.Action.DROP, metadata); + Session session = sessionFactory.openSession(); + String sql = "SELECT table_name FROM information_schema.tables WHERE table_name = 'test_table'"; + String tableName = (String) session.createNativeQuery(sql).uniqueResult(); + Assertions.assertNull(tableName); + session.close(); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectPaginationIntegrationTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectPaginationIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..167eb270426d5907e39b357c528c52ea3daf1188 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectPaginationIntegrationTest.java @@ -0,0 +1,70 @@ +package org.hibernate.dialect.integration; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.dialect.entity.pagination.TestEntity; +import org.hibernate.dialect.util.HibernateUtil; +import org.hibernate.query.Query; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class OpenGaussDialectPaginationIntegrationTest { + + private static SessionFactory sessionFactory; + + @BeforeAll + public static void init() { + sessionFactory = HibernateUtil.getSessionFactory(TestEntity.class); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.createQuery("DELETE FROM TestEntity").executeUpdate(); + for (int i = 1; i <= 100; i++) { + TestEntity entity = new TestEntity(); + entity.setName("Name " + i); + session.save(entity); + } + session.getTransaction().commit(); + session.close(); + } + + @AfterAll + public static void close() { + if (sessionFactory != null) { + sessionFactory.close(); + } + } + + @Test + public void testPaginationWithOffset() { + Session session = sessionFactory.openSession(); + int pageSize = 10; + int pageNumber = 3; + String hql = "from TestEntity order by id"; + Query query = session.createQuery(hql, TestEntity.class); + query.setFirstResult(pageNumber * pageSize); // 设置 offset + query.setMaxResults(pageSize); // 设置 limit + List results = query.list(); + Assertions.assertEquals(pageSize, results.size()); + Assertions.assertEquals(31L, results.get(0).getId()); + Assertions.assertEquals(40L, results.get(results.size() - 1).getId()); + session.close(); + } + + @Test + public void testPaginationWithoutOffset() { + Session session = sessionFactory.openSession(); + int pageSize = 10; + String hql = "from TestEntity order by id"; + Query query = session.createQuery(hql, TestEntity.class); + query.setMaxResults(pageSize); + List results = query.list(); + Assertions.assertEquals(pageSize, results.size()); + Assertions.assertEquals(1L, results.get(0).getId()); + Assertions.assertEquals(10L, results.get(results.size() - 1).getId()); + session.close(); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectSequenceIntegrationTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectSequenceIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..73bdec0babde3027c0bf73debbc855966ef8cea2 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussDialectSequenceIntegrationTest.java @@ -0,0 +1,61 @@ +package org.hibernate.dialect.integration; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.dialect.entity.sequence.TestEntity; +import org.hibernate.dialect.util.HibernateUtil; +import org.junit.jupiter.api.*; + +public class OpenGaussDialectSequenceIntegrationTest { + + private static SessionFactory sessionFactory; + + @BeforeAll + public static void init() { + sessionFactory = HibernateUtil.getSessionFactory(TestEntity.class); + } + + @AfterAll + public static void close() { + if (sessionFactory != null) { + sessionFactory.close(); + } + } + + @BeforeEach + public void setUp() { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.createQuery("delete from TestEntity").executeUpdate(); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testSequenceIdentifierGeneration() { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + TestEntity entity1 = new TestEntity(); + entity1.setName("Entity One"); + session.save(entity1); + TestEntity entity2 = new TestEntity(); + entity2.setName("Entity Two"); + session.save(entity2); + session.getTransaction().commit(); + session.close(); + // 验证 ID 是否自动生成,且递增 + Assertions.assertNotNull(entity1.getId()); + Assertions.assertNotNull(entity2.getId()); + Assertions.assertTrue(entity2.getId() > entity1.getId()); + // 查询并验证数据 + session = sessionFactory.openSession(); + TestEntity result1 = session.get(TestEntity.class, entity1.getId()); + TestEntity result2 = session.get(TestEntity.class, entity2.getId()); + Assertions.assertNotNull(result1); + Assertions.assertEquals("Entity One", result1.getName()); + Assertions.assertNotNull(result2); + Assertions.assertEquals("Entity Two", result2.getName()); + session.close(); + } +} + diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussFunctionTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussFunctionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b5156fc97602a3582f93646f46fdd5c5c5a9249d --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussFunctionTest.java @@ -0,0 +1,434 @@ +package org.hibernate.dialect.integration; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.dialect.entity.function.TestEntity; +import org.hibernate.dialect.util.HibernateUtil; +import org.hibernate.query.Query; +import org.hibernate.type.StandardBasicTypes; +import org.junit.jupiter.api.*; + +import java.math.BigDecimal; +import java.text.SimpleDateFormat; +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class OpenGaussFunctionTest { + private static SessionFactory sessionFactory; + + private Session session; + + @BeforeAll + public static void init() { + sessionFactory = HibernateUtil.getSessionFactory(TestEntity.class); + } + + @BeforeEach + public void setUp() { + session = sessionFactory.openSession(); + session.beginTransaction(); + session.createQuery("DELETE FROM TestEntity").executeUpdate(); + session.getTransaction().commit(); + } + + @AfterEach + public void tearDown() { + if (session != null) { + session.close(); + } + } + + @AfterAll + public static void close() { + if (sessionFactory != null) { + sessionFactory.close(); + } + } + + public static String bytesToHex(byte[] bytes) { + StringBuilder hexString = new StringBuilder(); + for (byte b : bytes) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) hexString.append('0'); + hexString.append(hex); + } + return hexString.toString(); + } + + @Test + public void testTypeMapping() { + Transaction transaction = session.beginTransaction(); + transaction.commit(); + } + + @Test + public void testMathFunctions() { + Transaction transaction = session.beginTransaction(); + TestEntity testEntity = new TestEntity(); + testEntity.setValue(0.0); + session.save(testEntity); + transaction.commit(); + + // Test abs function + Query absQuery = session.createQuery("select abs(-17.4) from TestEntity", Double.class); + Double absResult = absQuery.getSingleResult(); + assertEquals(17.4, absResult, 0.001); + + // Test cbrt function + Query cbrtQuery = session.createQuery("select cbrt(27.0) from TestEntity", Double.class); + Double cbrtResult = cbrtQuery.getSingleResult(); + assertEquals(3.0, cbrtResult, 0.001); + + // Test ceil function + Query ceilQuery = session.createQuery("select ceil(-42.8) from TestEntity", Double.class); + Double ceilResult = ceilQuery.getSingleResult(); + assertEquals(-42L, ceilResult); + + // Test degrees function + Query degreesQuery = session.createQuery("select degrees(0.5) from TestEntity", Double.class); + Double degreesResult = degreesQuery.getSingleResult(); + assertEquals(28.6478897565412, degreesResult, 0.001); + + // Test exp function + Query expQuery = session.createQuery("select exp(1.0) from TestEntity", Double.class); + Double expResult = expQuery.getSingleResult(); + assertEquals(2.718281828459045, expResult, 0.001); + + // Test floor function + Query floorQuery = session.createQuery("select floor(-42.8) from TestEntity", Double.class); + Double floorResult = floorQuery.getSingleResult(); + assertEquals(-43L, floorResult); + + // Test ln function + Query lnQuery = session.createQuery("select ln(2.0) from TestEntity", Double.class); + Double lnResult = lnQuery.getSingleResult(); + assertEquals(0.6931471805599453, lnResult, 0.001); + + // Test log function + Query logQuery = session.createQuery("select log(100.0) from TestEntity", Double.class); + Double logResult = logQuery.getSingleResult(); + assertEquals(2.0, logResult, 0.001); + + // Test mod function + Query modQuery = session.createQuery("select mod(9, 4) from TestEntity", Integer.class); + Integer modResult = modQuery.getSingleResult(); + assertEquals(1, modResult.intValue()); + + // Test pi function + Query piQuery = session.createQuery("select pi() from TestEntity", Double.class); + Double piResult = piQuery.getSingleResult(); + assertEquals(3.14159265358979, piResult, 0.001); + + // Test power function + Query powerQuery = session.createQuery("select power(9.0, 3.0) from TestEntity", Double.class); + Double powerResult = powerQuery.getSingleResult(); + assertEquals(729.0, powerResult, 0.001); + + // Test radians function + Query radiansQuery = session.createQuery("select radians(45.0) from TestEntity", Double.class); + Double radiansResult = radiansQuery.getSingleResult(); + assertEquals(0.7853981633974483, radiansResult, 0.001); + + // Test random function + Query randomQuery = session.createQuery("select random() from TestEntity", Double.class); + Double randomResult = randomQuery.getSingleResult(); + assertTrue(randomResult >= 0.0 && randomResult <= 1.0); + + // Test round function + Query roundQuery = session.createQuery("select round(42.6) from TestEntity", Double.class); + Double roundResult = roundQuery.getSingleResult(); + assertEquals(43L, roundResult); + + // Test sign function + Query signQuery = session.createQuery("select sign(-8.4) from TestEntity", Integer.class); + Integer signResult = signQuery.getSingleResult(); + assertEquals(-1, signResult.intValue()); + + // Test sqrt function + Query sqrtQuery = session.createQuery("select sqrt(2.0) from TestEntity", Double.class); + Double sqrtResult = sqrtQuery.getSingleResult(); + assertEquals(1.4142135623730951, sqrtResult, 0.001); + + // Test trunc function + Query truncQuery = session.createQuery("select trunc(42.8) from TestEntity", Double.class); + Double truncResult = truncQuery.getSingleResult(); + assertEquals(42L, truncResult); + } + + @Test + public void testTrigonometryFunctions() { + Transaction transaction = session.beginTransaction(); + TestEntity testEntity = new TestEntity(); + testEntity.setValue(0.0); + session.save(testEntity); + transaction.commit(); + + // Test acos function + Query acosQuery = session.createQuery("select acos(-1) from TestEntity", Double.class); + Double acosResult = acosQuery.getSingleResult(); + assertEquals(Math.PI, acosResult, 0.0001); + + // Test asin function + Query asinQuery = session.createQuery("select asin(0.5) from TestEntity", Double.class); + Double asinResult = asinQuery.getSingleResult(); + assertEquals(Math.asin(0.5), asinResult, 0.0001); + + // Test atan function + Query atanQuery = session.createQuery("select atan(1) from TestEntity", Double.class); + Double atanResult = atanQuery.getSingleResult(); + assertEquals(Math.atan(1), atanResult, 0.0001); + + // Test atan2 function + Query atan2Query = session.createQuery("select atan2(2, 1) from TestEntity", Double.class); + Double atan2Result = atan2Query.getSingleResult(); + assertEquals(Math.atan2(2, 1), atan2Result, 0.0001); + + // Test cos function + Query cosQuery = session.createQuery("select cos(-3.1415927) from TestEntity", Double.class); + Double cosResult = cosQuery.getSingleResult(); + assertEquals(Math.cos(-3.1415927), cosResult, 0.0001); + + // Test cot function + Query cotQuery = session.createQuery("select cot(1) from TestEntity", Double.class); + Double cotResult = cotQuery.getSingleResult(); + assertEquals(1.0 / Math.tan(1), cotResult, 0.0001); + + // Test sin function + Query sinQuery = session.createQuery("select sin(1.57079) from TestEntity", Double.class); + Double sinResult = sinQuery.getSingleResult(); + assertEquals(Math.sin(1.57079), sinResult, 0.0001); + + // Test tan function + Query tanQuery = session.createQuery("select tan(20) from TestEntity", Double.class); + Double tanResult = tanQuery.getSingleResult(); + assertEquals(Math.tan(20), tanResult, 0.0001); + } + + @Test + public void testStringFunctions() { + Transaction transaction = session.beginTransaction(); + TestEntity testEntity = new TestEntity(); + testEntity.setValue(0.0); + session.save(testEntity); + transaction.commit(); + + // Test string concatenation + Query concatQuery = session.createQuery("select 'MPP' || 'DB' as result from TestEntity", String.class); + String concatResult = concatQuery.getSingleResult(); + assertEquals("MPPDB", concatResult); + + // Test convert function + Query query = session.createQuery("select convert('text_in_utf8', 'UTF8', 'GBK') from TestEntity", byte[].class); + byte[] convertResult = query.getSingleResult(); + String hexResult = "\\x" + bytesToHex(convertResult); + assertEquals("\\x746578745f696e5f75746638", hexResult); + + // Test bit_length function + Query bitLengthQuery = session.createQuery("select bit_length('world') from TestEntity", Long.class); + Long bitLengthResult = bitLengthQuery.getSingleResult(); + assertEquals(40, bitLengthResult.intValue()); + + // Test lower function + Query lowerQuery = session.createQuery("select lower('TOM') from TestEntity", String.class); + String lowerResult = lowerQuery.getSingleResult(); + assertEquals("tom", lowerResult); + + // Test overlay function + Query overlayQuery = session.createQuery("select overlay('hello','world',2,3) from TestEntity", String.class); + String overlayResult = overlayQuery.getSingleResult(); + assertEquals("hworldo", overlayResult); + + // Test position function + Query positionQuery = session.createQuery("select position('ing','string') from TestEntity", Integer.class); + Integer positionResult = positionQuery.getSingleResult(); + assertEquals(4, positionResult.intValue()); + + // Test substring function (with start and length) + Query substringQuery = session.createQuery("select substring('Thomas',2,3) from TestEntity", String.class); + String substringResult = substringQuery.getSingleResult(); + assertEquals("hom", substringResult); + + substringQuery = session.createQuery("select substring('Thomas','...$') from TestEntity", String.class); + substringResult = substringQuery.getSingleResult(); + assertEquals("mas", substringResult); + + substringQuery = session.createQuery("select substring('foobar','o(.)b') from TestEntity", String.class); + substringResult = substringQuery.getSingleResult(); + assertEquals("o", substringResult); + + substringQuery = session.createQuery("select substring('foobar','(o(.)b)') from TestEntity", String.class); + substringResult = substringQuery.getSingleResult(); + assertEquals("oob", substringResult); + + // Test trim function + Query trimQuery = session.createQuery("select trim(both 'x' from 'xTomxx') from TestEntity", String.class); + String trimResult = trimQuery.getSingleResult(); + assertEquals("Tom", trimResult); + + // Test upper function + Query upperQuery = session.createQuery("select upper('tom') from TestEntity", String.class); + String upperResult = upperQuery.getSingleResult(); + assertEquals("TOM", upperResult); + + // Test ascii function + Query asciiQuery = session.createQuery("select ascii('xyz') from TestEntity", Integer.class); + Integer asciiResult = asciiQuery.getSingleResult(); + assertEquals(120, asciiResult.intValue()); + + // Test btrim function + Query btrimQuery = session.createQuery("select btrim('sring','ing') from TestEntity", String.class); + String btrimResult = btrimQuery.getSingleResult(); + assertEquals("sr", btrimResult); + + // Test char function + Query chrQuery = session.createQuery("select chr(65) from TestEntity", Character.class); + Character chrResult = chrQuery.getSingleResult(); + assertEquals('A', chrResult); + + // Test initcap function + Query initcapQuery = session.createQuery("select initcap('hi THOMAS') from TestEntity", String.class); + String initcapResult = initcapQuery.getSingleResult(); + assertEquals("Hi Thomas", initcapResult); + + // Test length function + Query lengthQuery = session.createQuery("select length('abcd') from TestEntity", Integer.class); + Integer lengthResult = lengthQuery.getSingleResult(); + assertEquals(4, lengthResult.intValue()); + + // Test lpad function + Query lpadQuery = session.createQuery("select lpad('hi', 5, 'xyza') from TestEntity", String.class); + String lpadResult = lpadQuery.getSingleResult(); + assertEquals("xyzhi", lpadResult); + + // Test ltrim function + Query ltrimQuery = session.createQuery("select ltrim('xxxxTRIM','x') from TestEntity", String.class); + String ltrimResult = ltrimQuery.getSingleResult(); + assertEquals("TRIM", ltrimResult); + + // Test md5 function + Query md5Query = session.createQuery("select md5('ABC') from TestEntity", String.class); + String md5Result = md5Query.getSingleResult(); + assertEquals("902fbdd2b1df0c4f70b4a5d23525e932", md5Result); + + // Test repeat function + Query repeatQuery = session.createQuery("select repeat('Pg', 4) from TestEntity", String.class); + String repeatResult = repeatQuery.getSingleResult(); + assertEquals("PgPgPgPg", repeatResult); + + // Test replace function + Query replaceQuery = session.createQuery("select replace('abcdefabcdef', 'cd', 'XXX') from TestEntity", String.class); + String replaceResult = replaceQuery.getSingleResult(); + assertEquals("abXXXefabXXXef", replaceResult); + + // Test rpad function + Query rpadQuery = session.createQuery("select rpad('hi', 5, 'xy') from TestEntity", String.class); + String rpadResult = rpadQuery.getSingleResult(); + assertEquals("hixyx", rpadResult); + + // Test rtrim function + Query rtrimQuery = session.createQuery("select rtrim('trimxxxx', 'x') from TestEntity", String.class); + String rtrimResult = rtrimQuery.getSingleResult(); + assertEquals("trim", rtrimResult); + + // Test split_part function + Query splitPartQuery = session.createQuery("select split_part('abc~@~def~@~ghi', '~@~', 2) from TestEntity", String.class); + String splitPartResult = splitPartQuery.getSingleResult(); + assertEquals("def", splitPartResult); + + // Test strpos function + Query strposQuery = session.createQuery("select strpos('source', 'rc') from TestEntity", Integer.class); + Integer strposResult = strposQuery.getSingleResult(); + assertEquals(4, strposResult.intValue()); + + // Test to_hex function + Query toHexQuery = session.createQuery("select to_hex(2147483647) from TestEntity", String.class); + String toHexResult = toHexQuery.getSingleResult(); + assertEquals("7fffffff", toHexResult); + } + + @Test + public void toCharTest() { + Transaction transaction = session.beginTransaction(); + TestEntity testEntity = new TestEntity(); + testEntity.setValue(0.0); + session.save(testEntity); + transaction.commit(); + + Query translateQuery = session.createQuery("select translate('12345', '143', 'ax') from TestEntity", String.class); + String translateResult = translateQuery.getSingleResult(); + assertEquals("a2x5", translateResult); + + Query toCharQuery = session.createQuery("select to_char(current_timestamp, 'HH12:MI:SS') from TestEntity", String.class); + String toCharResult = toCharQuery.getSingleResult(); + System.out.println(toCharResult); + + toCharQuery = session.createNativeQuery( + "SELECT to_char(interval '15h 2m 12s', 'HH24:MI:SS') AS formatted_value") + .addScalar("formatted_value", StandardBasicTypes.STRING + ); + toCharResult = toCharQuery.getSingleResult(); + assertEquals("15:02:12", toCharResult); + + toCharQuery = session.createQuery("select to_char(125, '999') from TestEntity", String.class); + toCharResult = toCharQuery.getSingleResult(); + assertEquals(" 125", toCharResult); + + toCharQuery = session.createNativeQuery( + "SELECT to_char(CAST(125.8 AS REAL), '999D99') AS formatted_value") + .addScalar("formatted_value", StandardBasicTypes.STRING + ); + toCharResult = toCharQuery.getSingleResult(); + assertEquals(" 125.80", toCharResult); + + toCharQuery = session.createQuery("select to_char(-125.8, '999D99S') from TestEntity", String.class); + toCharResult = toCharQuery.getSingleResult(); + assertEquals("125.80-", toCharResult); + } + + @Test + public void toDateTest() { + Transaction transaction = session.beginTransaction(); + TestEntity testEntity = new TestEntity(); + testEntity.setValue(0.0); + session.save(testEntity); + transaction.commit(); + + Query toTimestampQuery = session.createQuery("select to_timestamp('05 Dec 2000', 'DD Mon YYYY') from TestEntity", Date.class); + Date result = toTimestampQuery.getSingleResult(); + System.out.println(result); + } + + @Test + public void toNumberTest() { + Transaction transaction = session.beginTransaction(); + TestEntity testEntity = new TestEntity(); + testEntity.setValue(0.0); + session.save(testEntity); + transaction.commit(); + + Query toNumberQuery = session.createQuery( + "select to_number('125.8', '999D99') from TestEntity", BigDecimal.class + ); + BigDecimal toNumberResult = toNumberQuery.getSingleResult(); + assertEquals(new BigDecimal("125.8"), toNumberResult); + } + + @Test + public void toTimeStampTest() { + Transaction transaction = session.beginTransaction(); + TestEntity testEntity = new TestEntity(); + testEntity.setValue(0.0); + session.save(testEntity); + transaction.commit(); + Query toTimestampQuery = session.createQuery( + "select to_timestamp('05 Dec 2000', 'DD Mon YYYY') from TestEntity", Date.class + ); + Date toTimestampResult = toTimestampQuery.getSingleResult(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + String expectedTimestamp = "2000-12-05 00:00:00"; + assertEquals(expectedTimestamp, sdf.format(toTimestampResult)); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussLockIntegrationTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussLockIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..632910d76143c35510bd668dcd72a233d9acbc2f --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussLockIntegrationTest.java @@ -0,0 +1,89 @@ +package org.hibernate.dialect.integration; + +import org.hibernate.*; +import org.hibernate.dialect.entity.lock.TestEntity; +import org.hibernate.dialect.util.HibernateUtil; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.util.List; + +public class OpenGaussLockIntegrationTest { + + private static SessionFactory sessionFactory; + + @BeforeAll + public static void init() { + sessionFactory = HibernateUtil.getSessionFactory(TestEntity.class); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.createNativeQuery("DELETE FROM test_lock_entity").executeUpdate(); + TestEntity user1 = new TestEntity(); + user1.setId(1L); + user1.setName("Test User 1"); + user1.setBalance(1000.0); + session.save(user1); + TestEntity user2 = new TestEntity(); + user2.setId(2L); + user2.setName("Test User 2"); + user2.setBalance(1000.0); + session.save(user2); + session.getTransaction().commit(); + session.close(); + } + + @AfterAll + public static void close() { + if (sessionFactory != null) { + sessionFactory.close(); + } + } + // testPessimisticWriteLockNoWait():测试当记录已被锁定且设置不等待时,事务是否会立即失败,确保数据一致性和锁机制的有效性。 + @Test + public void testPessimisticWriteLockNoWait() { + Session session1 = sessionFactory.openSession(); + Transaction tx1 = session1.beginTransaction(); + TestEntity user1 = session1.get(TestEntity.class, 1L, new LockOptions(LockMode.PESSIMISTIC_WRITE)); + user1.setBalance(user1.getBalance() + 100.0); + Session session2 = sessionFactory.openSession(); + Transaction tx2 = session2.beginTransaction(); + try { + LockOptions lockOptions = new LockOptions(LockMode.PESSIMISTIC_WRITE); + lockOptions.setTimeOut(LockOptions.NO_WAIT); + TestEntity user2 = session2.get(TestEntity.class, 1L, lockOptions); + user2.setBalance(user2.getBalance() - 50.0); + tx2.commit(); + } catch (PessimisticLockException e) { + System.out.println("无法获取锁:" + e.getMessage()); + tx2.rollback(); + } finally { + session2.close(); + } + tx1.commit(); + session1.close(); + } + + //testPessimisticReadLockSkipLocked():测试在查询时跳过已被锁定的记录,验证 SKIP_LOCKED 的功能,确保在高并发环境下的查询性能。 + @Test + public void testPessimisticReadLockSkipLocked() { + Session session1 = sessionFactory.openSession(); + Transaction tx1 = session1.beginTransaction(); + TestEntity user1 = session1.get(TestEntity.class, 2L, new LockOptions(LockMode.PESSIMISTIC_WRITE)); + user1.setBalance(user1.getBalance() + 100.0); + Session session2 = sessionFactory.openSession(); + Transaction tx2 = session2.beginTransaction(); + LockOptions lockOptions = new LockOptions(LockMode.PESSIMISTIC_READ); + lockOptions.setTimeOut(LockOptions.SKIP_LOCKED); + List users = session2.createQuery("FROM TestEntity", TestEntity.class).setLockOptions(lockOptions).getResultList(); + for (TestEntity user : users) { + System.out.println("读取到用户:" + user.getId()); + Assertions.assertNotEquals(2L, user.getId()); + } + tx2.commit(); + session2.close(); + tx1.commit(); + session1.close(); + } +} \ No newline at end of file diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussMiscellaneousIntegrationTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussMiscellaneousIntegrationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7f1a1d0b3c5753f9ad44bccf8427c29f1d2517f0 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussMiscellaneousIntegrationTest.java @@ -0,0 +1,108 @@ +package org.hibernate.dialect.integration; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.criterion.Restrictions; +import org.hibernate.dialect.entity.miscellaneous.*; +import org.hibernate.dialect.util.HibernateUtil; +import org.junit.jupiter.api.*; + +import java.util.List; + +public class OpenGaussMiscellaneousIntegrationTest { + + private static SessionFactory sessionFactory; + + @BeforeAll + public static void init() { + sessionFactory = HibernateUtil.getSessionFactory( + TestNoColumnsInsert.class, + ParentEntity.class, + ChildEntity.class, + TestStringEntity.class, + TestBooleanEntity.class); + Session session = sessionFactory.openSession(); + Transaction transaction = session.beginTransaction(); + session.createNativeQuery("DELETE FROM test_no_columns_insert").executeUpdate(); + session.createNativeQuery("DELETE FROM child_entity").executeUpdate(); + session.createNativeQuery("DELETE FROM parent_entity").executeUpdate(); + session.createNativeQuery("DELETE FROM test_string_entity").executeUpdate(); + session.createNativeQuery("DELETE FROM test_boolean_entity").executeUpdate(); + transaction.commit(); + } + + @AfterAll + public static void close() { + if (sessionFactory != null) { + sessionFactory.close(); + } + } + + @Test + public void testNoColumnsInsert() { + Session session = sessionFactory.openSession(); + Transaction transaction = session.beginTransaction(); + TestNoColumnsInsert entity = new TestNoColumnsInsert(); + session.save(entity); + transaction.commit(); + session = sessionFactory.openSession(); + TestNoColumnsInsert result = session.get(TestNoColumnsInsert.class, entity.getId()); + Assertions.assertNotNull(result); + } + + @Test + public void testCaseExpression() { + Session session = sessionFactory.openSession(); + Transaction transaction = session.beginTransaction(); + String hql = "SELECT CASE WHEN e.id IS NOT NULL THEN 'Exists' ELSE 'Not Exists' END FROM TestNoColumnsInsert e"; + List results = session.createQuery(hql, String.class).list(); + Assertions.assertNotNull(results); + transaction.commit(); + } + + @Test + public void testJoinQuery() { + Session session = sessionFactory.openSession(); + Transaction transaction = session.beginTransaction(); + ParentEntity parent = new ParentEntity(); + session.save(parent); + ChildEntity child = new ChildEntity(); + child.setParent(parent); + session.save(child); + transaction.commit(); + transaction = session.beginTransaction(); + String hql = "SELECT c FROM ChildEntity c JOIN c.parent p WHERE p.id = :parentId"; + List results = session.createQuery(hql, ChildEntity.class).setParameter("parentId", parent.getId()).list(); + Assertions.assertEquals(1, results.size()); + transaction.commit(); + } + + @Test + public void testCaseInsensitiveLike() { + Session session = sessionFactory.openSession(); + Transaction transaction = session.beginTransaction(); + TestStringEntity entity = new TestStringEntity(); + entity.setName("TestName"); + session.save(entity); + transaction.commit(); + transaction = session.beginTransaction(); + List results = session.createCriteria(TestStringEntity.class).add(Restrictions.ilike("name", "test%")).list(); + Assertions.assertEquals(1, results.size()); + transaction.commit(); + } + + @Test + public void testBooleanValue() { + Session session = sessionFactory.openSession(); + Transaction transaction = session.beginTransaction(); + TestBooleanEntity entity = new TestBooleanEntity(); + entity.setActive(true); + session.save(entity); + transaction.commit(); + session = sessionFactory.openSession(); + TestBooleanEntity result = session.get(TestBooleanEntity.class, entity.getId()); + Assertions.assertNotNull(result); + Assertions.assertTrue(result.getActive()); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussTypeMappingTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussTypeMappingTest.java new file mode 100644 index 0000000000000000000000000000000000000000..29550c2c04bc2e28e388a1344fd4a52a8b9f2640 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/integration/OpenGaussTypeMappingTest.java @@ -0,0 +1,136 @@ +package org.hibernate.dialect.integration; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.dialect.entity.datatype.*; +import org.hibernate.dialect.pojo.*; +import org.hibernate.dialect.util.HibernateUtil; +import org.hibernate.dialect.util.JDBCUtil; +import org.junit.jupiter.api.Test; + +import java.sql.*; +import java.util.ArrayList; +import java.util.List; + + +public class OpenGaussTypeMappingTest { + + private static SessionFactory sessionFactory; + + public static void formatColumns(List columns) { + int maxColumnLength = columns.stream().mapToInt(c -> c[0].length()).max().orElse(0); + int maxTypeLength = columns.stream().mapToInt(c -> c[1].length()).max().orElse(0); + for (String[] column : columns) { + System.out.printf("Column %-" + maxColumnLength + "s : %" + maxTypeLength + "s%n", column[0], column[1]); + } + } + + public static void ColumnFormatter(String tableName) { + List columns = null; + try { + Connection conn = JDBCUtil.getConnection(); + String sql = "SELECT * FROM " + tableName; + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery(sql); + ResultSetMetaData rsmd = rs.getMetaData(); + columns = new ArrayList<>(); + for (int i = 1; i <= rsmd.getColumnCount(); i++) { + String columnName = rsmd.getColumnName(i); + String columnType = rsmd.getColumnTypeName(i); + columns.add(new String[]{columnName, columnType}); + } + JDBCUtil.disconnect(conn); + } catch (Exception e) { + e.printStackTrace(); + } + formatColumns(columns); + } + + @Test + public void testBooleanMapping() { + sessionFactory = HibernateUtil.getSessionFactory(BooleanEntity.class); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testNumberMapping() { + sessionFactory = HibernateUtil.getSessionFactory(NumberEntity.class); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testCharMapping() { + sessionFactory = HibernateUtil.getSessionFactory(CharEntity.class); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testStringMapping() { + sessionFactory = HibernateUtil.getSessionFactory(StringEntity.class); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testDateTimeMapping() { + sessionFactory = HibernateUtil.getSessionFactory(DateTimeEntity.class); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testBinaryMapping() { + sessionFactory = HibernateUtil.getSessionFactory(BinaryEntity.class); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testLobMapping() { + sessionFactory = HibernateUtil.getSessionFactory(LobEntity.class); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testInsertAndRetrieveJsonData() { + sessionFactory = HibernateUtil.getSessionFactory(JsonEntity.class); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + JsonEntity entity = new JsonEntity(); + entity.setId(1L); + entity.setPojoJson(new Person(1, "opengauss")); + entity.setPojoJsonb(new Person(2, "opengauss")); + session.save(entity); + session.getTransaction().commit(); + JsonEntity retrievedEntity = session.get(JsonEntity.class, entity.getId()); + System.out.println(retrievedEntity); + session.close(); + } + + @Test + public void testSupportsNationalizedTypes() { + sessionFactory = HibernateUtil.getSessionFactory(NationalizedEntity.class); + Session session = sessionFactory.openSession(); + session.beginTransaction(); + session.getTransaction().commit(); + session.close(); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/pojo/Person.java b/openGaussDialect/src/test/java/org/hibernate/dialect/pojo/Person.java new file mode 100644 index 0000000000000000000000000000000000000000..8e62d402419daa02d78ff8e28df42a03c03d2d5c --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/pojo/Person.java @@ -0,0 +1,15 @@ +package org.hibernate.dialect.pojo; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@NoArgsConstructor +@AllArgsConstructor +@Data +public class Person implements Serializable { + private Integer id; + private String name; +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectCallableStatementTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectCallableStatementTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5993a608f653c0d22b6e975213000cfc4645a571 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectCallableStatementTest.java @@ -0,0 +1,65 @@ +package org.hibernate.dialect.unit; + +import org.hibernate.dialect.OpenGaussDialect; +import org.junit.jupiter.api.*; + +import java.sql.CallableStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +public class OpenGaussDialectCallableStatementTest { + + private static OpenGaussDialect dialect; + + private CallableStatement callableStatement; + + @BeforeAll + public static void init() { + dialect = new OpenGaussDialect(); + } + + @BeforeEach + public void setup() { + callableStatement = mock(CallableStatement.class); + } + + @AfterEach + public void teardown() { + callableStatement = null; + } + + @Test + public void testRegisterResultSetOutParameter() throws SQLException { + int position = 1; + int returnedPosition = dialect.registerResultSetOutParameter(callableStatement, position); + verify(callableStatement).registerOutParameter(position, Types.REF_CURSOR); + Assertions.assertEquals(position + 1, returnedPosition, "Position should be incremented by 1"); + } + + @Test + public void testGetResultSet() throws SQLException { + ResultSet mockResultSet = mock(ResultSet.class); + when(callableStatement.getObject(1)).thenReturn(mockResultSet); + ResultSet resultSet = dialect.getResultSet(callableStatement); + verify(callableStatement).execute(); + verify(callableStatement).getObject(1); + Assertions.assertEquals(mockResultSet, resultSet, "ResultSet should match the one returned by getObject(1)"); + } + + @Test + public void testGetResultSetWithPosition() throws SQLException { + ResultSet mockResultSet = mock(ResultSet.class); + when(callableStatement.getObject(1)).thenReturn(mockResultSet); + ResultSet resultSet = dialect.getResultSet(callableStatement, 1); + verify(callableStatement).getObject(1); + Assertions.assertEquals(mockResultSet, resultSet, "ResultSet should match the one returned by getObject(1)"); + RuntimeException runtimeException = assertThrows(UnsupportedOperationException.class, () -> { + dialect.getResultSet(callableStatement, 2); + }); + Assertions.assertTrue(runtimeException.getMessage().contains("OpenGauss only supports REF_CURSOR parameters as the first parameter")); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectCurrentTimestampTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectCurrentTimestampTest.java new file mode 100644 index 0000000000000000000000000000000000000000..17a3ac79945d1f2215ebee675bad7b38133de285 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectCurrentTimestampTest.java @@ -0,0 +1,80 @@ +package org.hibernate.dialect.unit; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.dialect.OpenGaussDialect; +import org.hibernate.dialect.util.HibernateUtil; +import org.junit.jupiter.api.*; + +import javax.persistence.*; +import java.sql.Timestamp; + +public class OpenGaussDialectCurrentTimestampTest { + @Entity + @Table(name = "test_entity") + @Data + @NoArgsConstructor + @AllArgsConstructor + public class TestEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String name; + @Version + private Timestamp version; + } + + private static SessionFactory sessionFactory; + + @BeforeAll + public static void init() { + sessionFactory = HibernateUtil.getSessionFactory(TestEntity.class); + } + + @AfterAll + public static void close() { + if (sessionFactory != null) { + sessionFactory.close(); + } + } + + @Test + public void testCurrentTimestampSelection() { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + OpenGaussDialect dialect = new OpenGaussDialect(); + String sql = dialect.getCurrentTimestampSelectString(); + Timestamp currentTimestamp = (Timestamp) session.createNativeQuery(sql).getSingleResult(); + System.out.println("从数据库获取的当前时间戳: " + currentTimestamp); + Timestamp systemTimestamp = new Timestamp(System.currentTimeMillis()); + System.out.println("从系统获取的当前时间戳: " + systemTimestamp); + long difference = Math.abs(systemTimestamp.getTime() - currentTimestamp.getTime()); + Assertions.assertTrue(difference < 10000, "时间戳差异应小于 1 秒"); + session.getTransaction().commit(); + } + + @Test + public void testVersioning() { + Session session = sessionFactory.openSession(); + session.beginTransaction(); + TestEntity entity = new TestEntity(); + entity.setName("测试名称"); + session.save(entity); + session.getTransaction().commit(); + System.out.println("初始版本(时间戳): " + entity.getVersion()); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + session.beginTransaction(); + entity.setName("更新后的名称"); + session.update(entity); + session.getTransaction().commit(); + System.out.println("更新后的版本(时间戳): " + entity.getVersion()); + Assertions.assertNotNull(entity.getVersion()); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectDDLTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectDDLTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ca4fb1e2bdd2acdc670ff761b0757bab7b004a78 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectDDLTest.java @@ -0,0 +1,220 @@ +package org.hibernate.dialect.unit; + +import org.hibernate.dialect.OpenGaussDialect; +import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy; +import org.hibernate.hql.spi.id.local.LocalTemporaryTableBulkIdStrategy; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +public class OpenGaussDialectDDLTest { + private static OpenGaussDialect dialect; + + @BeforeAll + public static void init() { + dialect = new OpenGaussDialect(); + } + + // Catalog + @Test + public void testCanCreateCatalog() { + assertFalse(dialect.canCreateCatalog()); + } + + @Test + public void testGetCreateCatalogCommand() { + Assertions.assertThrows(UnsupportedOperationException.class, () -> { + dialect.getCreateCatalogCommand("test_catalog"); + }); + } + + @Test + public void testGetDropCatalogCommand() { + Assertions.assertThrows(UnsupportedOperationException.class, () -> { + dialect.getDropCatalogCommand("test_catalog"); + }); + } + + // Schema + @Test + public void testCanCreateSchema() { + Assertions.assertTrue(dialect.canCreateSchema()); + } + + @Test + public void testGetCreateSchemaCommand() { + String[] commands = dialect.getCreateSchemaCommand("test_schema"); + Assertions.assertEquals(1, commands.length); + Assertions.assertEquals("create schema if not exists test_schema", commands[0]); + } + + @Test + public void testGetDropSchemaCommand() { + String[] commands = dialect.getDropSchemaCommand("test_schema"); + Assertions.assertEquals(1, commands.length); + Assertions.assertEquals("drop schema if exists test_schema", commands[0]); + } + + @Test + public void testGetCurrentSchemaCommand() { + String command = dialect.getCurrentSchemaCommand(); + Assertions.assertEquals("select current_schema()", command); + } + + // Table/Column + @Test + public void testGetCreateTableString() { + String expected = "create table"; + Assertions.assertEquals(expected, dialect.getCreateTableString()); + } + + @Test + public void testGetCreateMultisetTableString() { + String expected = "create table"; + Assertions.assertEquals(expected, dialect.getCreateMultisetTableString()); + } + + @Test + public void testGetTableTypeString() { + String expected = ""; + Assertions.assertEquals(expected, dialect.getTableTypeString()); + } + + @Test + public void testGetDropTableString() { + String expected = "drop table if exists test_table cascade"; + Assertions.assertEquals(expected, dialect.getDropTableString("test_table")); + } + + @Test + public void testGetCascadeConstraintsString() { + String expected = " cascade"; + Assertions.assertEquals(expected, dialect.getCascadeConstraintsString()); + } + + @Test + public void testHasAlterTable() { + Assertions.assertTrue(dialect.hasAlterTable()); + } + + @Test + public void testSupportsIfExistsAfterAlterTable() { + Assertions.assertTrue(dialect.supportsIfExistsAfterAlterTable()); + } + + @Test + public void testGetAlterTableString() { + String expected = "alter table if exists test_table"; + Assertions.assertEquals(expected, dialect.getAlterTableString("test_table")); + } + + @Test + public void testGetAddColumnString() { + String expected = "add column"; + Assertions.assertEquals(expected, dialect.getAddColumnString()); + } + + @Test + public void getAddColumnSuffixString() { + String expected = ""; + Assertions.assertEquals(expected, dialect.getAddColumnSuffixString()); + } + + @Test + public void testGetDefaultMultiTableBulkIdStrategy() { + MultiTableBulkIdStrategy strategy = dialect.getDefaultMultiTableBulkIdStrategy(); + Assertions.assertNotNull(strategy); + Assertions.assertInstanceOf(LocalTemporaryTableBulkIdStrategy.class, strategy); + } + + // Constraint + @Test + public void testGetAddForeignKeyConstraintString1() { + String constraintName = "fk_child_parent"; + String[] foreignKey = {"parent_id"}; + String referencedTable = "parent_table"; + String[] primaryKey = {"id"}; + boolean referencesPrimaryKey = true; + String result = dialect.getAddForeignKeyConstraintString(constraintName, foreignKey, referencedTable, primaryKey, referencesPrimaryKey); + String expected = " add constraint fk_child_parent foreign key (parent_id) references parent_table"; + Assertions.assertEquals(expected, result); + } + + @Test + public void testGetAddForeignKeyConstraintString2() { + String constraintName = "fk_child_parent"; + String[] foreignKey = {"parent_id"}; + String referencedTable = "parent_table"; + String[] primaryKey = {"other_column"}; + boolean referencesPrimaryKey = false; + String result = dialect.getAddForeignKeyConstraintString(constraintName, foreignKey, referencedTable, primaryKey, referencesPrimaryKey); + String expected = " add constraint fk_child_parent foreign key (parent_id) references parent_table (other_column)"; + Assertions.assertEquals(expected, result); + } + + @Test + public void testSupportsCascadeDelete() { + Assertions.assertTrue(dialect.supportsCascadeDelete()); + } + + @Test + public void testDropConstraints() { + Assertions.assertTrue(dialect.dropConstraints()); + } + + @Test + public void testGetDropForeignKeyString() { + String expected = " drop constraint "; + Assertions.assertEquals(expected, dialect.getDropForeignKeyString()); + } + + @Test + public void testHasSelfReferentialForeignKeyBug() { + Assertions.assertFalse(dialect.hasSelfReferentialForeignKeyBug()); + } + + @Test + public void testGetAddPrimaryKeyConstraintString() { + String expected = " add constraint pk_table primary key "; + Assertions.assertEquals(expected, dialect.getAddPrimaryKeyConstraintString("pk_table")); + } + + @Test + public void testSupportsIfExistsBeforeConstraintName() { + Assertions.assertTrue(dialect.supportsIfExistsBeforeConstraintName()); + } + + @Test + public void testSupportsIfExistsAfterConstraintName() { + Assertions.assertFalse(dialect.supportsIfExistsAfterConstraintName()); + } + + @Test + public void testSupportsColumnCheck() { + Assertions.assertTrue(dialect.supportsColumnCheck()); + } + + @Test + public void testSupportsTableCheck() { + Assertions.assertTrue(dialect.supportsTableCheck()); + } + + // Comments + @Test + public void testSupportsCommentOn() { + Assertions.assertTrue(dialect.supportsCommentOn()); + } + + @Test + public void testGetTableComment() { + Assertions.assertEquals("", dialect.getTableComment("This is a table comment")); + } + + @Test + public void testGetColumnComment() { + Assertions.assertEquals("", dialect.getColumnComment("This is a column comment")); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectLimitHandlerTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectLimitHandlerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8ea63d8b6b68cebb2af4d10bb3b779d0a4c6e252 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectLimitHandlerTest.java @@ -0,0 +1,47 @@ +package org.hibernate.dialect.unit; + +import org.hibernate.dialect.OpenGaussDialect; +import org.hibernate.dialect.pagination.LimitHandler; +import org.hibernate.engine.spi.RowSelection; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class OpenGaussDialectLimitHandlerTest { + private static OpenGaussDialect dialect; + + @BeforeAll + public static void init() { + dialect = new OpenGaussDialect(); + } + + @Test + public void testProcessSqlWithOffset() { + LimitHandler limitHandler = dialect.getLimitHandler(); + String originalSql = "SELECT * FROM test_table"; + RowSelection selection = new RowSelection(); + selection.setFirstRow(10); // offset + selection.setMaxRows(20); // limit + String processedSql = limitHandler.processSql(originalSql, selection); + String expectedSql = "SELECT * FROM test_table limit ? offset ?"; + Assertions.assertEquals(expectedSql, processedSql); + } + + @Test + public void testProcessSqlWithoutOffset() { + LimitHandler limitHandler = dialect.getLimitHandler(); + String originalSql = "SELECT * FROM test_table"; + RowSelection selection = new RowSelection(); + selection.setMaxRows(20); // limit + String processedSql = limitHandler.processSql(originalSql, selection); + String expectedSql = "SELECT * FROM test_table limit ?"; + Assertions.assertEquals(expectedSql, processedSql); + } + + @Test + public void testSupportsLimit() { + LimitHandler limitHandler = dialect.getLimitHandler(); + Assertions.assertTrue(limitHandler.supportsLimit()); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectLockTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectLockTest.java new file mode 100644 index 0000000000000000000000000000000000000000..177f62e3a0593a7db947093f98e52ef6b378011c --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectLockTest.java @@ -0,0 +1,127 @@ +package org.hibernate.dialect.unit; + +import org.hibernate.LockMode; +import org.hibernate.LockOptions; +import org.hibernate.dialect.OpenGaussDialect; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class OpenGaussDialectLockTest { + private static OpenGaussDialect dialect; + + @BeforeAll + public static void init() { + dialect = new OpenGaussDialect(); + } + + @Test + public void testSupportsLockTimeouts() { + Assertions.assertTrue(dialect.supportsLockTimeouts()); + } + + @Test + public void testIsLockTimeoutParameterized() { + Assertions.assertFalse(dialect.isLockTimeoutParameterized()); + } + + @Test + public void testGetWriteLockString() { + String sql = dialect.getWriteLockString(LockOptions.WAIT_FOREVER); + Assertions.assertEquals(" for update", sql); + sql = dialect.getWriteLockString(LockOptions.NO_WAIT); + Assertions.assertEquals(" for update nowait", sql); + sql = dialect.getWriteLockString(LockOptions.SKIP_LOCKED); + Assertions.assertEquals(" for update skip locked", sql); + } + + @Test + public void testGetWriteLockStringWithAliases() { + String aliases = "t1"; + String sql = dialect.getWriteLockString(aliases, LockOptions.WAIT_FOREVER); + Assertions.assertEquals(" for update of t1", sql); + sql = dialect.getWriteLockString(aliases, LockOptions.NO_WAIT); + Assertions.assertEquals(" for update of t1 nowait", sql); + sql = dialect.getWriteLockString(aliases, LockOptions.SKIP_LOCKED); + Assertions.assertEquals(" for update of t1 skip locked", sql); + } + + @Test + public void testGetReadLockString() { + String sql = dialect.getReadLockString(LockOptions.WAIT_FOREVER); + Assertions.assertEquals(" for share", sql); + sql = dialect.getReadLockString(LockOptions.NO_WAIT); + Assertions.assertEquals(" for share nowait", sql); + sql = dialect.getReadLockString(LockOptions.SKIP_LOCKED); + Assertions.assertEquals(" for share skip locked", sql); + } + + @Test + public void testGetReadLockStringWithAliases() { + String aliases = "t1"; + String sql = dialect.getReadLockString(aliases, LockOptions.WAIT_FOREVER); + Assertions.assertEquals(" for share of t1", sql); + sql = dialect.getReadLockString(aliases, LockOptions.NO_WAIT); + Assertions.assertEquals(" for share of t1 nowait", sql); + sql = dialect.getReadLockString(aliases, LockOptions.SKIP_LOCKED); + Assertions.assertEquals(" for share of t1 skip locked", sql); + } + + @Test + public void testGetForUpdateStringWithAliases() { + String aliases = "t1"; + String sql = dialect.getForUpdateString(aliases); + Assertions.assertEquals(" for update of t1", sql); + } + + @Test + public void testGetForUpdateStringWithAliasesAndLockOptions() { + String aliases = "t1"; + LockOptions lockOptions = new LockOptions(LockMode.PESSIMISTIC_WRITE); + lockOptions.setTimeOut(LockOptions.WAIT_FOREVER); + String sql = dialect.getForUpdateString(aliases, lockOptions); + Assertions.assertEquals(" for update of t1", sql); + lockOptions.setTimeOut(LockOptions.NO_WAIT); + sql = dialect.getForUpdateString(aliases, lockOptions); + Assertions.assertEquals(" for update of t1 nowait", sql); + lockOptions.setTimeOut(LockOptions.SKIP_LOCKED); + sql = dialect.getForUpdateString(aliases, lockOptions); + Assertions.assertEquals(" for update of t1 skip locked", sql); + } + + @Test + public void testGetForUpdateNowaitString() { + String sql = dialect.getForUpdateNowaitString(); + Assertions.assertEquals(" for update nowait", sql); + } + + @Test + public void testGetForUpdateNowaitStringWithAliases() { + String aliases = "t1"; + String sql = dialect.getForUpdateNowaitString(aliases); + Assertions.assertEquals(" for update of t1 nowait", sql); + } + + @Test + public void testGetForUpdateSkipLockedString() { + String sql = dialect.getForUpdateSkipLockedString(); + Assertions.assertEquals(" for update skip locked", sql); + } + + @Test + public void testGetForUpdateSkipLockedStringWithAliases() { + String aliases = "t1"; + String sql = dialect.getForUpdateSkipLockedString(aliases); + Assertions.assertEquals(" for update of t1 skip locked", sql); + } + + @Test + public void testSupportsOuterJoinForUpdate() { + Assertions.assertTrue(dialect.supportsOuterJoinForUpdate()); + } + + @Test + public void testForUpdateOfColumns() { + Assertions.assertTrue(dialect.forUpdateOfColumns()); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectMiscellaneousTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectMiscellaneousTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2b718bf90985b8231a12a9c359f861fd5d79cd6c --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectMiscellaneousTest.java @@ -0,0 +1,83 @@ +package org.hibernate.dialect.unit; + +import org.hibernate.dialect.OpenGaussDialect; +import org.hibernate.sql.ANSICaseFragment; +import org.hibernate.sql.ANSIJoinFragment; +import org.hibernate.sql.CaseFragment; +import org.hibernate.sql.JoinFragment; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +public class OpenGaussDialectMiscellaneousTest { + + private static OpenGaussDialect dialect; + + @BeforeAll + public static void init() { + dialect = new OpenGaussDialect(); + } + + @Test + public void testCreateOuterJoinFragment() { + JoinFragment joinFragment = dialect.createOuterJoinFragment(); + Assertions.assertNotNull(joinFragment); + Assertions.assertInstanceOf(ANSIJoinFragment.class, joinFragment); + } + + @Test + public void testCreateCaseFragment() { + CaseFragment caseFragment = dialect.createCaseFragment(); + Assertions.assertNotNull(caseFragment); + Assertions.assertInstanceOf(ANSICaseFragment.class, caseFragment); + } + + @Test + public void testGetNoColumnsInsertString() { + String insertString = dialect.getNoColumnsInsertString(); + Assertions.assertEquals("default values", insertString); + } + + @Test + public void testSupportsNoColumnsInsert() { + Assertions.assertTrue(dialect.supportsNoColumnsInsert()); + } + + @Test + public void testGetLowercaseFunction() { + String functionName = dialect.getLowercaseFunction(); + Assertions.assertEquals("lower", functionName); + } + + @Test + public void testGetCaseInsensitiveLike() { + String operator = dialect.getCaseInsensitiveLike(); + Assertions.assertEquals("ilike", operator); + } + + @Test + public void testSupportsCaseInsensitiveLike() { + Assertions.assertTrue(dialect.supportsCaseInsensitiveLike()); + } + + @Test + public void testTransformSelectString() { + String select = "SELECT * FROM test_table"; + String transformedSelect = dialect.transformSelectString(select); + Assertions.assertEquals(select, transformedSelect); + } + + @Test + public void testGetMaxAliasLength() { + int maxAliasLength = dialect.getMaxAliasLength(); + Assertions.assertEquals(10, maxAliasLength); + } + + @Test + public void testToBooleanValueString() { + String trueValue = dialect.toBooleanValueString(true); + String falseValue = dialect.toBooleanValueString(false); + Assertions.assertEquals("t", trueValue); + Assertions.assertEquals("f", falseValue); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectSequenceTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectSequenceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5a8be8fb86ab90aaacea0afd6e2350f389033f5e --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussDialectSequenceTest.java @@ -0,0 +1,101 @@ +package org.hibernate.dialect.unit; + +import org.hibernate.dialect.OpenGaussDialect; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; + +public class OpenGaussDialectSequenceTest { + private static OpenGaussDialect dialect; + + @BeforeAll + public static void init() { + dialect = new OpenGaussDialect(); + } + + @Test + public void testSupportsSequences() { + Assertions.assertTrue(dialect.supportsSequences()); + } + + @Test + public void testSupportsPooledSequences() { + Assertions.assertTrue(dialect.supportsPooledSequences()); + } + + @Test + public void testGetSequenceNextValString() { + String sequenceName = "my_sequence"; + String expected = "select nextval('my_sequence')"; + String actual = dialect.getSequenceNextValString(sequenceName); + Assertions.assertEquals(expected, actual); + } + + @Test + public void testGetSelectSequenceNextValString() { + String sequenceName = "my_sequence"; + String expected = "nextval('my_sequence')"; + String actual = dialect.getSelectSequenceNextValString(sequenceName); + Assertions.assertEquals(expected, actual); + } + + @Test + public void testGetCreateSequenceString() { + String sequenceName = "my_sequence"; + String expected = "create sequence my_sequence"; + String actual = dialect.getCreateSequenceString(sequenceName); + Assertions.assertEquals(expected, actual); + } + + @Test + public void testGetCreateSequenceStringWithParameters() throws Exception{ + Method method = OpenGaussDialect.class.getDeclaredMethod("getCreateSequenceString", String.class, int.class, int.class); + method.setAccessible(true); + String sequenceName = "my_sequence"; + + // Test with positive initialValue and incrementSize + String expected = "create sequence my_sequence start 1 increment 1"; + String actual = (String) method.invoke(dialect, sequenceName, 1, 1); + Assertions.assertEquals(expected, actual); + + // Test with negative initialValue and positive incrementSize + expected = "create sequence my_sequence minvalue -5 start -5 increment 2"; + actual = (String) method.invoke(dialect, sequenceName, -5, 2); + Assertions.assertEquals(expected, actual); + + // Test with positive initialValue and negative incrementSize + expected = "create sequence my_sequence maxvalue 10 start 10 increment -1"; + actual = (String) method.invoke(dialect, sequenceName, 10, -1); + Assertions.assertEquals(expected, actual); + } + + @Test + public void testGetDropSequenceString() { + String sequenceName = "my_sequence"; + String expected = "drop sequence if exists my_sequence"; + String actual = dialect.getDropSequenceString(sequenceName); + Assertions.assertEquals(expected, actual); + } + + @Test + public void testGetQuerySequencesString() { + String expected = "select * from information_schema.sequences"; + String actual = dialect.getQuerySequencesString(); + Assertions.assertEquals(expected, actual); + } + + @Test + public void testGetNativeIdentifierGeneratorStrategy() { + String expected = "sequence"; + String actual = dialect.getNativeIdentifierGeneratorStrategy(); + Assertions.assertEquals(expected, actual); + } + + @Test + public void testGetIdentityColumnSupport() { + Assertions.assertNotNull(dialect.getIdentityColumnSupport()); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussIdentityColumnSupportTest.java b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussIdentityColumnSupportTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4ecdf431a9f3e0ab01a7f0593345097a304d527f --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/unit/OpenGaussIdentityColumnSupportTest.java @@ -0,0 +1,54 @@ +package org.hibernate.dialect.unit; + +import org.hibernate.dialect.OpenGaussDialect; +import org.hibernate.dialect.identity.IdentityColumnSupport; +import org.hibernate.dialect.identity.OpenGaussIdentityColumnSupport; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.sql.Types; + +public class OpenGaussIdentityColumnSupportTest { + private static OpenGaussDialect dialect; + + @BeforeAll + public static void init() { + dialect = new OpenGaussDialect(); + } + + @Test + public void testGetNativeIdentifierGeneratorStrategy() { + String generatorStrategy = dialect.getNativeIdentifierGeneratorStrategy(); + Assertions.assertEquals("sequence", generatorStrategy, "Native identifier generator strategy should be 'sequence'"); + } + + @Test + public void testSupportsIdentityColumns() { + IdentityColumnSupport identitySupport = new OpenGaussIdentityColumnSupport(); + Assertions.assertFalse(identitySupport.supportsIdentityColumns(), "Identity columns should not be supported"); + } + + @Test + public void testGetIdentitySelectString() { + IdentityColumnSupport identitySupport = new OpenGaussIdentityColumnSupport(); + String selectString = identitySupport.getIdentitySelectString("test_table", "id", Types.BIGINT); + Assertions.assertEquals("select currval('test_table_id_seq')", selectString, "Identity select string is incorrect"); + } + + @Test + public void testGetIdentityColumnString() { + IdentityColumnSupport identitySupport = new OpenGaussIdentityColumnSupport(); + String bigIntIdentityColumnString = identitySupport.getIdentityColumnString(Types.BIGINT); + Assertions.assertEquals("bigserial not null", bigIntIdentityColumnString, "BIGINT identity column string is incorrect"); + String defaultIdentityColumnString = identitySupport.getIdentityColumnString(Types.INTEGER); + Assertions.assertEquals("serial not null", defaultIdentityColumnString, "Default identity column string is incorrect"); + } + + @Test + public void testHasDataTypeInIdentityColumn() { + IdentityColumnSupport identitySupport = new OpenGaussIdentityColumnSupport(); + Assertions.assertFalse(identitySupport.hasDataTypeInIdentityColumn(), "Identity column should not have data type included"); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/util/HibernateUtil.java b/openGaussDialect/src/test/java/org/hibernate/dialect/util/HibernateUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..cc296dd1871d3602b9b2ef740597a45967b6dca0 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/util/HibernateUtil.java @@ -0,0 +1,16 @@ +package org.hibernate.dialect.util; + +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; + +public class HibernateUtil { + public static SessionFactory getSessionFactory(Class... classes) { + Configuration configuration = new Configuration().configure(); + if (classes != null) { + for (Class c : classes) { + configuration.addAnnotatedClass(c); + } + } + return configuration.buildSessionFactory(); + } +} diff --git a/openGaussDialect/src/test/java/org/hibernate/dialect/util/JDBCUtil.java b/openGaussDialect/src/test/java/org/hibernate/dialect/util/JDBCUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..8f963bb623ec908bbd0579bbf621b99773166341 --- /dev/null +++ b/openGaussDialect/src/test/java/org/hibernate/dialect/util/JDBCUtil.java @@ -0,0 +1,38 @@ +package org.hibernate.dialect.util; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; + +public class JDBCUtil { + private static String DRIVER = "org.opengauss.Driver"; + private static String URL = "jdbc:opengauss://43.138.80.125:25432/osapp"; + private static String USER = "postgres"; + private static String PASSWORD = "osapp_openGauss@123"; + + static { + try { + Class.forName(DRIVER); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + public static Connection getConnection() { + Connection connect = null; + try { + connect = DriverManager.getConnection(URL, USER, PASSWORD); + } catch (SQLException e) { + e.printStackTrace(); + } + return connect; + } + + public static void disconnect(Connection connection) { + try { + connection.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } +}