diff --git a/hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/YdbDialect.java b/hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/YdbDialect.java index 5434736..44c165d 100644 --- a/hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/YdbDialect.java +++ b/hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/YdbDialect.java @@ -7,6 +7,7 @@ import org.hibernate.boot.model.TypeContributions; import org.hibernate.dialect.Dialect; import org.hibernate.dialect.function.CurrentFunction; +import org.hibernate.dialect.identity.IdentityColumnSupport; import org.hibernate.dialect.pagination.LimitHandler; import org.hibernate.dialect.pagination.LimitOffsetLimitHandler; import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo; @@ -67,6 +68,7 @@ import tech.ydb.hibernate.dialect.hint.PragmaQueryHintHandler; import tech.ydb.hibernate.dialect.hint.QueryHintHandler; import tech.ydb.hibernate.dialect.hint.ScanQueryHintHandler; +import tech.ydb.hibernate.dialect.identity.YdbIdentityColumnSupport; import tech.ydb.hibernate.dialect.translator.YdbSqlAstTranslatorFactory; import tech.ydb.hibernate.dialect.types.BigDecimalJavaType; import tech.ydb.hibernate.dialect.types.DecimalJdbcType; @@ -368,4 +370,19 @@ public Exporter getForeignKeyExporter() { public boolean supportsColumnCheck() { return false; } + + @Override + public IdentityColumnSupport getIdentityColumnSupport() { + return YdbIdentityColumnSupport.INSTANCE; + } + + @Override + public boolean supportsInsertReturning() { + return true; + } + + @Override + public boolean supportsInsertReturningGeneratedKeys() { + return true; + } } diff --git a/hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/identity/YdbIdentityColumnSupport.java b/hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/identity/YdbIdentityColumnSupport.java new file mode 100644 index 0000000..071405a --- /dev/null +++ b/hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/identity/YdbIdentityColumnSupport.java @@ -0,0 +1,49 @@ +package tech.ydb.hibernate.dialect.identity; + +import java.sql.JDBCType; +import org.hibernate.MappingException; +import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.identity.IdentityColumnSupportImpl; +import org.hibernate.id.PostInsertIdentityPersister; +import org.hibernate.id.insert.GetGeneratedKeysDelegate; +import static org.hibernate.type.SqlTypes.BIGINT; +import static org.hibernate.type.SqlTypes.INTEGER; +import static org.hibernate.type.SqlTypes.SMALLINT; +import static org.hibernate.type.SqlTypes.TINYINT; + +/** + * @author Kirill Kurdyukov + */ +public class YdbIdentityColumnSupport extends IdentityColumnSupportImpl { + + public static final YdbIdentityColumnSupport INSTANCE = new YdbIdentityColumnSupport(); + + @Override + public boolean hasDataTypeInIdentityColumn() { + return false; + } + + @Override + public String getIdentityColumnString(int type) throws MappingException { + return switch (type) { + case TINYINT, SMALLINT -> "SmallSerial"; + case INTEGER -> "Serial"; + case BIGINT -> "BigSerial"; + default -> throw new MappingException( + "Ydb does not support identity key generation for sqlType: " + JDBCType.valueOf(type)); + }; + } + + @Override + public boolean supportsIdentityColumns() { + return true; + } + + @Override + public GetGeneratedKeysDelegate buildGetGeneratedKeysDelegate( + PostInsertIdentityPersister persister, + Dialect dialect + ) { + return new GetGeneratedKeysDelegate(persister, dialect, false); + } +} diff --git a/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/GenerationTypeIdentityTest.java b/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/GenerationTypeIdentityTest.java new file mode 100644 index 0000000..b6c472c --- /dev/null +++ b/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/GenerationTypeIdentityTest.java @@ -0,0 +1,56 @@ +package tech.ydb.hibernate.auto_inc; + +import org.hibernate.MappingException; +import org.hibernate.cfg.AvailableSettings; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.extension.RegisterExtension; +import static tech.ydb.hibernate.TestUtils.SESSION_FACTORY; +import static tech.ydb.hibernate.TestUtils.basedConfiguration; +import static tech.ydb.hibernate.TestUtils.jdbcUrl; +import tech.ydb.test.junit5.YdbHelperExtension; + +/** + * @author Kirill Kurdyukov + */ +public class GenerationTypeIdentityTest { + + @RegisterExtension + private static final YdbHelperExtension ydb = new YdbHelperExtension(); + +// @Test + void serialTypesTest() { + /* + create table test_table_int_auto_inc ( + id Serial, + text Text, + primary key (id) + ) + */ + + SESSION_FACTORY = basedConfiguration() + .addAnnotatedClass(TestEntityInt.class) + .addAnnotatedClass(TestEntityLong.class) + .addAnnotatedClass(TestEntityShort.class) + .setProperty(AvailableSettings.URL, jdbcUrl(ydb)) + .buildSessionFactory(); + + Assertions.assertThrows(MappingException.class, () -> basedConfiguration() + .addAnnotatedClass(TestEntityFail.class) + .setProperty(AvailableSettings.URL, jdbcUrl(ydb)) + .buildSessionFactory()); + + SESSION_FACTORY.inTransaction( + session -> { + var intE = new TestEntityInt(); + intE.setText("test"); + session.persist(intE); + var intS = new TestEntityShort(); + intS.setText("test"); + session.persist(intS); + var intL = new TestEntityLong(); + intL.setText("test"); + session.persist(intL); + } + ); + } +} diff --git a/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/TestEntityFail.java b/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/TestEntityFail.java new file mode 100644 index 0000000..d3c5dd4 --- /dev/null +++ b/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/TestEntityFail.java @@ -0,0 +1,29 @@ +package tech.ydb.hibernate.auto_inc; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author Kirill Kurdyukov + */ +@Entity +@Data +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "test_table_int_auto_fail") +public class TestEntityFail { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private String id; + + @Column + private String text; +} diff --git a/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/TestEntityInt.java b/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/TestEntityInt.java new file mode 100644 index 0000000..a55a263 --- /dev/null +++ b/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/TestEntityInt.java @@ -0,0 +1,29 @@ +package tech.ydb.hibernate.auto_inc; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author Kirill Kurdyukov + */ +@Entity +@Data +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "test_table_int_auto_inc") +public class TestEntityInt { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + + @Column + private String text; +} diff --git a/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/TestEntityLong.java b/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/TestEntityLong.java new file mode 100644 index 0000000..c2b66b5 --- /dev/null +++ b/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/TestEntityLong.java @@ -0,0 +1,29 @@ +package tech.ydb.hibernate.auto_inc; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author Kirill Kurdyukov + */ +@Entity +@Data +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "test_table_int_auto_long") +public class TestEntityLong { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private long id; + + @Column + private String text; +} diff --git a/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/TestEntityShort.java b/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/TestEntityShort.java new file mode 100644 index 0000000..a3f88a5 --- /dev/null +++ b/hibernate-dialect/src/test/java/tech/ydb/hibernate/auto_inc/TestEntityShort.java @@ -0,0 +1,29 @@ +package tech.ydb.hibernate.auto_inc; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author Kirill Kurdyukov + */ +@Entity +@Data +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "test_table_int_auto_short") +public class TestEntityShort { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private short id; + + @Column + private String text; +}