Skip to content

Commit e5f174d

Browse files
authored
JAVA-2902: Consider computed values when validating constructors for immutable entities (apache#1520)
1 parent db803e8 commit e5f174d

4 files changed

Lines changed: 93 additions & 7 deletions

File tree

changelog/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### 4.10.0 (in progress)
66

7+
- [bug] JAVA-2902: Consider computed values when validating constructors for immutable entities
78
- [new feature] JAVA-2899: Re-introduce cross-DC failover in driver 4
89
- [new feature] JAVA-2900: Re-introduce consistency downgrading retries
910
- [new feature] JAVA-2903: BlockHound integration

integration-tests/src/test/java/com/datastax/oss/driver/mapper/ImmutableEntityIT.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.datastax.oss.driver.api.core.CqlSession;
2323
import com.datastax.oss.driver.api.core.cql.SimpleStatement;
2424
import com.datastax.oss.driver.api.mapper.MapperBuilder;
25+
import com.datastax.oss.driver.api.mapper.annotations.Computed;
2526
import com.datastax.oss.driver.api.mapper.annotations.CqlName;
2627
import com.datastax.oss.driver.api.mapper.annotations.Dao;
2728
import com.datastax.oss.driver.api.mapper.annotations.DaoFactory;
@@ -73,7 +74,7 @@ public static void setup() {
7374
@Test
7475
public void should_insert_and_retrieve_immutable_entities() {
7576
ImmutableProduct originalProduct =
76-
new ImmutableProduct(UUID.randomUUID(), "mock description", new Dimensions(1, 2, 3));
77+
new ImmutableProduct(UUID.randomUUID(), "mock description", new Dimensions(1, 2, 3), -1);
7778
dao.save(originalProduct);
7879

7980
ImmutableProduct retrievedProduct = dao.findById(originalProduct.id());
@@ -88,10 +89,14 @@ public static class ImmutableProduct {
8889
private final String description;
8990
private final Dimensions dimensions;
9091

91-
public ImmutableProduct(UUID id, String description, Dimensions dimensions) {
92+
@Computed("writetime(description)")
93+
private final long writetime;
94+
95+
public ImmutableProduct(UUID id, String description, Dimensions dimensions, long writetime) {
9296
this.id = id;
9397
this.description = description;
9498
this.dimensions = dimensions;
99+
this.writetime = writetime;
95100
}
96101

97102
public UUID id() {
@@ -106,6 +111,10 @@ public Dimensions dimensions() {
106111
return dimensions;
107112
}
108113

114+
public long writetime() {
115+
return writetime;
116+
}
117+
109118
@Override
110119
public boolean equals(Object other) {
111120
if (other == this) {

mapper-processor/src/main/java/com/datastax/oss/driver/internal/mapper/processor/entity/DefaultEntityFactory.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -603,7 +603,7 @@ private void validateConstructor(EntityDefinition entity, TypeElement processedC
603603
if (entity.isMutable()) {
604604
validateNoArgConstructor(processedClass);
605605
} else {
606-
validateAllColumnsConstructor(processedClass, entity.getAllColumns());
606+
validateAllValuesConstructor(processedClass, entity.getAllValues());
607607
}
608608
}
609609

@@ -625,7 +625,7 @@ private void validateNoArgConstructor(TypeElement processedClass) {
625625
Entity.class.getSimpleName());
626626
}
627627

628-
private void validateAllColumnsConstructor(
628+
private void validateAllValuesConstructor(
629629
TypeElement processedClass, List<PropertyDefinition> columns) {
630630
for (Element child : processedClass.getEnclosedElements()) {
631631
if (child.getKind() == ElementKind.CONSTRUCTOR) {
@@ -641,15 +641,16 @@ && areAssignable(columns, constructor.getParameters())) {
641641
columns.stream()
642642
.map(
643643
column ->
644-
String.format("%s %s", column.getType().asTypeMirror(), column.getGetterName()))
644+
String.format("%s %s", column.getType().asTypeMirror(), column.getJavaName()))
645645
.collect(Collectors.joining(", "));
646646
context
647647
.getMessager()
648648
.error(
649649
processedClass,
650-
"Immutable @%s-annotated class must have an \"all columns\" constructor. "
651-
+ "Expected signature: (%s).",
650+
"Immutable @%s-annotated class must have an \"all values\" constructor. "
651+
+ "Expected signature: %s(%s).",
652652
Entity.class.getSimpleName(),
653+
processedClass.getSimpleName(),
653654
signature);
654655
}
655656

mapper-processor/src/test/java/com/datastax/oss/driver/internal/mapper/processor/entity/EntityPropertyAnnotationsTest.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@
1919
import com.datastax.oss.driver.api.mapper.annotations.Computed;
2020
import com.datastax.oss.driver.api.mapper.annotations.Entity;
2121
import com.datastax.oss.driver.api.mapper.annotations.PartitionKey;
22+
import com.datastax.oss.driver.api.mapper.annotations.PropertyStrategy;
2223
import com.datastax.oss.driver.api.mapper.annotations.Transient;
2324
import com.datastax.oss.driver.internal.mapper.processor.MapperProcessorTest;
2425
import com.squareup.javapoet.AnnotationSpec;
2526
import com.squareup.javapoet.ClassName;
2627
import com.squareup.javapoet.FieldSpec;
2728
import com.squareup.javapoet.MethodSpec;
29+
import com.squareup.javapoet.ParameterSpec;
2830
import com.squareup.javapoet.TypeSpec;
2931
import com.tngtech.java.junit.dataprovider.DataProvider;
3032
import com.tngtech.java.junit.dataprovider.DataProviderRunner;
@@ -320,6 +322,79 @@ public static Object[][] entitiesWithErrors() {
320322
.build())
321323
.build(),
322324
},
325+
{
326+
"Mutable @Entity-annotated class must have a no-arg constructor.",
327+
TypeSpec.classBuilder(ClassName.get("test", "Product"))
328+
.addAnnotation(Entity.class)
329+
.addField(
330+
FieldSpec.builder(UUID.class, "id", Modifier.PRIVATE)
331+
.addModifiers(Modifier.FINAL)
332+
.addAnnotation(PartitionKey.class)
333+
.build())
334+
.addMethod(
335+
MethodSpec.constructorBuilder()
336+
.addParameter(ParameterSpec.builder(UUID.class, "id").build())
337+
.addModifiers(Modifier.PUBLIC)
338+
.addStatement("this.id = id")
339+
.build())
340+
.addMethod(
341+
MethodSpec.methodBuilder("getId")
342+
.returns(UUID.class)
343+
.addModifiers(Modifier.PUBLIC)
344+
.addStatement("return id")
345+
.build())
346+
.addMethod(
347+
MethodSpec.methodBuilder("setId")
348+
.addParameter(UUID.class, "id")
349+
.addModifiers(Modifier.PUBLIC)
350+
.addStatement("this.id = id")
351+
.build())
352+
.build(),
353+
},
354+
{
355+
"Immutable @Entity-annotated class must have an \"all values\" constructor. "
356+
+ "Expected signature: Product(java.util.UUID id, java.lang.String name, long writetime).",
357+
TypeSpec.classBuilder(ClassName.get("test", "Product"))
358+
.addAnnotation(Entity.class)
359+
.addAnnotation(
360+
AnnotationSpec.builder(PropertyStrategy.class)
361+
.addMember("mutable", "false")
362+
.build())
363+
.addField(
364+
FieldSpec.builder(UUID.class, "id", Modifier.PRIVATE)
365+
.addModifiers(Modifier.FINAL)
366+
.addAnnotation(PartitionKey.class)
367+
.build())
368+
.addField(
369+
FieldSpec.builder(String.class, "name", Modifier.PRIVATE)
370+
.addModifiers(Modifier.FINAL)
371+
.build())
372+
.addField(
373+
FieldSpec.builder(String.class, "writetime", Modifier.PRIVATE)
374+
.addModifiers(Modifier.FINAL)
375+
.addAnnotation(
376+
AnnotationSpec.builder(Computed.class).addMember("value", "$S", "").build())
377+
.build())
378+
.addMethod(
379+
MethodSpec.methodBuilder("getId")
380+
.returns(UUID.class)
381+
.addModifiers(Modifier.PUBLIC)
382+
.addStatement("return id")
383+
.build())
384+
.addMethod(
385+
MethodSpec.methodBuilder("getName")
386+
.returns(String.class)
387+
.addModifiers(Modifier.PUBLIC)
388+
.addStatement("return name")
389+
.build())
390+
.addMethod(
391+
MethodSpec.methodBuilder("getWritetime")
392+
.returns(Long.TYPE)
393+
.addModifiers(Modifier.PUBLIC)
394+
.addStatement("return writetime")
395+
.build())
396+
.build(),
397+
},
323398
};
324399
}
325400
}

0 commit comments

Comments
 (0)