diff --git a/project/gradle.properties b/project/gradle.properties index c369677d0..476e1de1e 100644 --- a/project/gradle.properties +++ b/project/gradle.properties @@ -1,2 +1,2 @@ group=org.babyfish.jimmer -version=0.9.31 +version=0.9.32 diff --git a/project/jimmer-apt/src/main/java/org/babyfish/jimmer/apt/immutable/meta/ImmutableProp.java b/project/jimmer-apt/src/main/java/org/babyfish/jimmer/apt/immutable/meta/ImmutableProp.java index 165955ff3..c1ff645ad 100644 --- a/project/jimmer-apt/src/main/java/org/babyfish/jimmer/apt/immutable/meta/ImmutableProp.java +++ b/project/jimmer-apt/src/main/java/org/babyfish/jimmer/apt/immutable/meta/ImmutableProp.java @@ -214,10 +214,22 @@ public ImmutableProp( if (executableElement.getAnnotation(Default.class) != null && executableElement.getAnnotation(LogicalDeleted.class) != null) { - throw new MetaException( - executableElement, - "property cannot be decorated by both \"@Default\" and \"@LogicalDeleted\"" - ); + boolean isValid = executableElement.getReturnType().getKind() == TypeKind.INT; + if (!isValid) { + if (executableElement.getReturnType().getKind() == TypeKind.DECLARED) { + DeclaredType declaredType = (DeclaredType) executableElement.getReturnType(); + TypeElement typeElement = (TypeElement) declaredType.asElement(); + TypeElement superTypeElement = (TypeElement) ((DeclaredType)typeElement.getSuperclass()).asElement(); + isValid = superTypeElement.getQualifiedName().toString().equals("java.lang.Enum"); + } + } + if (!isValid) { + throw new MetaException( + executableElement, + "property cannot be decorated by both \"@Default\" and \"@LogicalDeleted\" " + + "unless its type is int or enum" + ); + } } Formula formula = executableElement.getAnnotation(Formula.class); diff --git a/project/jimmer-core/src/main/java/org/babyfish/jimmer/meta/LogicalDeletedInfo.java b/project/jimmer-core/src/main/java/org/babyfish/jimmer/meta/LogicalDeletedInfo.java index 4642be134..c46a14182 100644 --- a/project/jimmer-core/src/main/java/org/babyfish/jimmer/meta/LogicalDeletedInfo.java +++ b/project/jimmer-core/src/main/java/org/babyfish/jimmer/meta/LogicalDeletedInfo.java @@ -270,7 +270,7 @@ public static LogicalDeletedInfo of(ImmutableProp prop) { } } else { Default dft = prop.getAnnotation(Default.class); - if (dft != null && dft.value().isEmpty()) { + if (dft != null && !dft.value().isEmpty()) { initializedText = dft.value(); } } diff --git a/project/jimmer-core/src/main/java/org/babyfish/jimmer/meta/impl/ImmutablePropImpl.java b/project/jimmer-core/src/main/java/org/babyfish/jimmer/meta/impl/ImmutablePropImpl.java index bbf884401..2fb2bc156 100644 --- a/project/jimmer-core/src/main/java/org/babyfish/jimmer/meta/impl/ImmutablePropImpl.java +++ b/project/jimmer-core/src/main/java/org/babyfish/jimmer/meta/impl/ImmutablePropImpl.java @@ -1369,14 +1369,6 @@ public Ref getDefaultValueRef() { if (ref == null) { Version version = getAnnotation(Version.class); Default dft = getAnnotation(Default.class); - if (dft != null && isLogicalDeleted()) { - throw new ModelException( - "Illegal property \"" + - this + - "\", it is logical deleted flag " + - "so it cannot be decorated \"@Default\"" - ); - } if (version != null) { Object value; if (dft == null) { diff --git a/project/jimmer-ksp/src/main/kotlin/org/babyfish/jimmer/ksp/immutable/meta/ImmutableProp.kt b/project/jimmer-ksp/src/main/kotlin/org/babyfish/jimmer/ksp/immutable/meta/ImmutableProp.kt index 9f5263c01..dda361f29 100644 --- a/project/jimmer-ksp/src/main/kotlin/org/babyfish/jimmer/ksp/immutable/meta/ImmutableProp.kt +++ b/project/jimmer-ksp/src/main/kotlin/org/babyfish/jimmer/ksp/immutable/meta/ImmutableProp.kt @@ -74,12 +74,42 @@ class ImmutableProp( "the property whose type is kotlin value class is not supported now" ) } - if (propDeclaration.annotation(Default::class) !== null && - propDeclaration.annotation(LogicalDeleted::class) !== null) { - throw MetaException( - propDeclaration, - "the property cannot be decorated by both \"@Default\" and \"@LogicalDeleted\"" - ) + if (propDeclaration.annotation(LogicalDeleted::class) !== null) { + val declaration = realDeclaration + val typeName = if (declaration is KSClassDeclaration && declaration.modifiers.contains(Modifier.ENUM)) { + "" + } else { + declaration.fullName + } + when (typeName) { + "kotlin.Boolean", "kotlin.Int", "" -> + if (resolvedType.isMarkedNullable) { + throw MetaException( + propDeclaration, + "the property decorated by \"@LogicalDeleted\" cannot be nullable " + + "if its type is boolean, int, or enum" + ) + } + "kotlin.Long", "java.util.UUID", "java.time.LocalDateTime" -> {} + else -> throw MetaException( + propDeclaration, + "the property decorated by \"@LogicalDeleted\" must be " + + "boolean, int, enum, UUID or LocalDateTime" + ) + } + if (propDeclaration.annotation(Default::class) !== null) { + val isValid = when (typeName) { + "kotlin.Int", "" -> true + else -> false + } + if (!isValid) { + throw MetaException( + propDeclaration, + "the property cannot be decorated by both \"@Default\" and \"@LogicalDeleted\" " + + "unless its type is int or enum" + ) + } + } } } diff --git a/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/A.kt b/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/A.kt new file mode 100644 index 000000000..34ebc1b01 --- /dev/null +++ b/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/A.kt @@ -0,0 +1,17 @@ +package org.babyfish.jimmer.sql.kt.model.ld.validation + +import org.babyfish.jimmer.sql.DatabaseValidationIgnore +import org.babyfish.jimmer.sql.Entity +import org.babyfish.jimmer.sql.Id +import org.babyfish.jimmer.sql.LogicalDeleted + +@Entity +@DatabaseValidationIgnore +interface A { + + @Id + val id: Long + + @LogicalDeleted("true") + val deleted: Boolean +} \ No newline at end of file diff --git a/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/B.kt b/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/B.kt new file mode 100644 index 000000000..f9f3f8613 --- /dev/null +++ b/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/B.kt @@ -0,0 +1,17 @@ +package org.babyfish.jimmer.sql.kt.model.ld.validation + +import org.babyfish.jimmer.sql.DatabaseValidationIgnore +import org.babyfish.jimmer.sql.Entity +import org.babyfish.jimmer.sql.Id +import org.babyfish.jimmer.sql.LogicalDeleted + +@Entity +@DatabaseValidationIgnore +interface B { + + @Id + val id: Long + + @LogicalDeleted("false") + val active: Boolean +} \ No newline at end of file diff --git a/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/C.kt b/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/C.kt new file mode 100644 index 000000000..86a67010c --- /dev/null +++ b/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/C.kt @@ -0,0 +1,17 @@ +package org.babyfish.jimmer.sql.kt.model.ld.validation + +import org.babyfish.jimmer.sql.DatabaseValidationIgnore +import org.babyfish.jimmer.sql.Entity +import org.babyfish.jimmer.sql.Id +import org.babyfish.jimmer.sql.LogicalDeleted + +@Entity +@DatabaseValidationIgnore +interface C { + + @Id + val id: Long + + @LogicalDeleted("2") + val state: Int +} \ No newline at end of file diff --git a/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/D.kt b/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/D.kt new file mode 100644 index 000000000..0cc027cff --- /dev/null +++ b/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/D.kt @@ -0,0 +1,15 @@ +package org.babyfish.jimmer.sql.kt.model.ld.validation + +import org.babyfish.jimmer.sql.* + +@Entity +@DatabaseValidationIgnore +interface D { + + @Id + val id: Long + + @Default("1") + @LogicalDeleted("2") + val state: Int +} \ No newline at end of file diff --git a/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/E.kt b/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/E.kt new file mode 100644 index 000000000..5d5eaae47 --- /dev/null +++ b/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/E.kt @@ -0,0 +1,15 @@ +package org.babyfish.jimmer.sql.kt.model.ld.validation + +import org.babyfish.jimmer.sql.* + +@Entity +@DatabaseValidationIgnore +interface E { + + @Id + val id: Long + + @Default("NEW") + @LogicalDeleted("DELETED") + val state: State +} \ No newline at end of file diff --git a/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/State.kt b/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/State.kt new file mode 100644 index 000000000..4d07d2188 --- /dev/null +++ b/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/State.kt @@ -0,0 +1,7 @@ +package org.babyfish.jimmer.sql.kt.model.ld.validation + +enum class State { + NEW, + PROCESSING, + DELETED +} \ No newline at end of file diff --git a/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/ValidationTest.kt b/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/ValidationTest.kt new file mode 100644 index 000000000..2a9dcd093 --- /dev/null +++ b/project/jimmer-sql-kotlin/src/test/kotlin/org/babyfish/jimmer/sql/kt/model/ld/validation/ValidationTest.kt @@ -0,0 +1,88 @@ +package org.babyfish.jimmer.sql.kt.model.ld.validation + +import org.babyfish.jimmer.meta.ImmutableType +import kotlin.test.Test +import kotlin.test.expect + +class ValidationTest { + + @Test + fun testA() { + val type = ImmutableType.get(A::class.java) + val prop = type.getProp("deleted") + val info = type.logicalDeletedInfo ?: error("Impossible") + expect(false) { + prop.defaultValueRef.value + } + expect(false) { + info.allocateInitializedValue() + } + expect(true) { + info.generateValue() + } + } + + @Test + fun testB() { + val type = ImmutableType.get(B::class.java) + val prop = type.getProp("active") + val info = type.logicalDeletedInfo ?: error("Impossible") + expect(true) { + prop.defaultValueRef.value + } + expect(true) { + info.allocateInitializedValue() + } + expect(false) { + info.generateValue() + } + } + + @Test + fun testC() { + val type = ImmutableType.get(C::class.java) + val prop = type.getProp("state") + val info = type.logicalDeletedInfo ?: error("Impossible") + expect(0) { + prop.defaultValueRef.value + } + expect(0) { + info.allocateInitializedValue() + } + expect(2) { + info.generateValue() + } + } + + @Test + fun testD() { + val type = ImmutableType.get(D::class.java) + val prop = type.getProp("state") + val info = type.logicalDeletedInfo ?: error("Impossible") + expect(1) { + prop.defaultValueRef.value + } + expect(1) { + info.allocateInitializedValue() + } + expect(2) { + info.generateValue() + } + } + + @Test + fun testE() { + val type = ImmutableType.get(E::class.java) + val prop = type.getProp("state") + val info = type.logicalDeletedInfo ?: error("Impossible") + expect(State.NEW) { + prop.defaultValueRef.value + } + expect(State.NEW) { + info.allocateInitializedValue() + } + expect(State.DELETED) { + info.generateValue() + } + } +} \ No newline at end of file diff --git a/project/jimmer-sql/src/main/java/org/babyfish/jimmer/sql/ast/impl/render/BatchSqlBuilder.java b/project/jimmer-sql/src/main/java/org/babyfish/jimmer/sql/ast/impl/render/BatchSqlBuilder.java index 714a5d7bb..e7ab5c73a 100644 --- a/project/jimmer-sql/src/main/java/org/babyfish/jimmer/sql/ast/impl/render/BatchSqlBuilder.java +++ b/project/jimmer-sql/src/main/java/org/babyfish/jimmer/sql/ast/impl/render/BatchSqlBuilder.java @@ -23,8 +23,7 @@ public class BatchSqlBuilder extends AbstractSqlBuilder { private final List templateVariables = new ArrayList<>(); - final JSqlClientImplementor sqlClient; - + private final JSqlClientImplementor sqlClient; public BatchSqlBuilder(JSqlClientImplementor sqlClient) { this.sqlClient = sqlClient; diff --git a/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/A.java b/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/A.java new file mode 100644 index 000000000..2713058a7 --- /dev/null +++ b/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/A.java @@ -0,0 +1,17 @@ +package org.babyfish.jimmer.sql.model.ld.validate; + +import org.babyfish.jimmer.sql.DatabaseValidationIgnore; +import org.babyfish.jimmer.sql.Entity; +import org.babyfish.jimmer.sql.Id; +import org.babyfish.jimmer.sql.LogicalDeleted; + +@DatabaseValidationIgnore +@Entity +public interface A { + + @Id + long id(); + + @LogicalDeleted("true") + boolean deleted(); +} diff --git a/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/B.java b/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/B.java new file mode 100644 index 000000000..dec23c8a3 --- /dev/null +++ b/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/B.java @@ -0,0 +1,17 @@ +package org.babyfish.jimmer.sql.model.ld.validate; + +import org.babyfish.jimmer.sql.DatabaseValidationIgnore; +import org.babyfish.jimmer.sql.Entity; +import org.babyfish.jimmer.sql.Id; +import org.babyfish.jimmer.sql.LogicalDeleted; + +@DatabaseValidationIgnore +@Entity +public interface B { + + @Id + long id(); + + @LogicalDeleted("false") + boolean active(); +} diff --git a/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/C.java b/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/C.java new file mode 100644 index 000000000..c58de2ccc --- /dev/null +++ b/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/C.java @@ -0,0 +1,17 @@ +package org.babyfish.jimmer.sql.model.ld.validate; + +import org.babyfish.jimmer.sql.DatabaseValidationIgnore; +import org.babyfish.jimmer.sql.Entity; +import org.babyfish.jimmer.sql.Id; +import org.babyfish.jimmer.sql.LogicalDeleted; + +@DatabaseValidationIgnore +@Entity +public interface C { + + @Id + long id(); + + @LogicalDeleted("2") + int state(); +} diff --git a/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/D.java b/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/D.java new file mode 100644 index 000000000..cf4b786c4 --- /dev/null +++ b/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/D.java @@ -0,0 +1,15 @@ +package org.babyfish.jimmer.sql.model.ld.validate; + +import org.babyfish.jimmer.sql.*; + +@DatabaseValidationIgnore +@Entity +public interface D { + + @Id + long id(); + + @Default("1") + @LogicalDeleted("2") + int state(); +} diff --git a/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/E.java b/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/E.java new file mode 100644 index 000000000..d64d57127 --- /dev/null +++ b/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/E.java @@ -0,0 +1,15 @@ +package org.babyfish.jimmer.sql.model.ld.validate; + +import org.babyfish.jimmer.sql.*; + +@DatabaseValidationIgnore +@Entity +public interface E { + + @Id + long id(); + + @Default("NEW") + @LogicalDeleted("DELETED") + State state(); +} diff --git a/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/State.java b/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/State.java new file mode 100644 index 000000000..fb418b935 --- /dev/null +++ b/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/State.java @@ -0,0 +1,8 @@ +package org.babyfish.jimmer.sql.model.ld.validate; + +public enum State { + + NEW, + PROCESSING, + DELETED +} diff --git a/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/ValidationTest.java b/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/ValidationTest.java new file mode 100644 index 000000000..0dd0c6e53 --- /dev/null +++ b/project/jimmer-sql/src/test/java/org/babyfish/jimmer/sql/model/ld/validate/ValidationTest.java @@ -0,0 +1,105 @@ +package org.babyfish.jimmer.sql.model.ld.validate; + +import org.babyfish.jimmer.meta.ImmutableProp; +import org.babyfish.jimmer.meta.ImmutableType; +import org.babyfish.jimmer.meta.LogicalDeletedInfo; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ValidationTest { + + @Test + public void testA() { + ImmutableType type = ImmutableType.get(A.class); + ImmutableProp prop = type.getProp("deleted"); + LogicalDeletedInfo info = type.getLogicalDeletedInfo(); + Assertions.assertEquals( + false, + prop.getDefaultValueRef().getValue() + ); + Assertions.assertEquals( + false, + info.allocateInitializedValue() + ); + Assertions.assertEquals( + true, + info.generateValue() + ); + } + + @Test + public void testB() { + ImmutableType type = ImmutableType.get(B.class); + ImmutableProp prop = type.getProp("active"); + LogicalDeletedInfo info = type.getLogicalDeletedInfo(); + Assertions.assertEquals( + true, + prop.getDefaultValueRef().getValue() + ); + Assertions.assertEquals( + true, + info.allocateInitializedValue() + ); + Assertions.assertEquals( + false, + info.generateValue() + ); + } + + @Test + public void testC() { + ImmutableType type = ImmutableType.get(C.class); + ImmutableProp prop = type.getProp("state"); + LogicalDeletedInfo info = type.getLogicalDeletedInfo(); + Assertions.assertEquals( + 0, + prop.getDefaultValueRef().getValue() + ); + Assertions.assertEquals( + 0, + info.allocateInitializedValue() + ); + Assertions.assertEquals( + 2, + info.generateValue() + ); + } + + @Test + public void testD() { + ImmutableType type = ImmutableType.get(D.class); + ImmutableProp prop = type.getProp("state"); + LogicalDeletedInfo info = type.getLogicalDeletedInfo(); + Assertions.assertEquals( + 1, + prop.getDefaultValueRef().getValue() + ); + Assertions.assertEquals( + 1, + info.allocateInitializedValue() + ); + Assertions.assertEquals( + 2, + info.generateValue() + ); + } + + @Test + public void testE() { + ImmutableType type = ImmutableType.get(E.class); + ImmutableProp prop = type.getProp("state"); + LogicalDeletedInfo info = type.getLogicalDeletedInfo(); + Assertions.assertEquals( + State.NEW, + prop.getDefaultValueRef().getValue() + ); + Assertions.assertEquals( + State.NEW, + info.allocateInitializedValue() + ); + Assertions.assertEquals( + State.DELETED, + info.generateValue() + ); + } +}