diff --git a/src/main/java/net/helenus/core/HelenusSession.java b/src/main/java/net/helenus/core/HelenusSession.java index aeee3c8..c4324c0 100644 --- a/src/main/java/net/helenus/core/HelenusSession.java +++ b/src/main/java/net/helenus/core/HelenusSession.java @@ -235,7 +235,7 @@ public class HelenusSession extends AbstractSessionOperations implements Closeab } } else { value = valueMap.get(prop.getPropertyName()); - binder.setValueForProperty(prop, value.toString()); + if (value != null) binder.setValueForProperty(prop, value.toString()); } } if (binder.isBound()) { diff --git a/src/main/java/net/helenus/core/SchemaUtil.java b/src/main/java/net/helenus/core/SchemaUtil.java index 721e512..0a12d35 100644 --- a/src/main/java/net/helenus/core/SchemaUtil.java +++ b/src/main/java/net/helenus/core/SchemaUtil.java @@ -165,6 +165,14 @@ public final class SchemaUtil { } } + if (p.size() == 0 && c.size() == 0) + return "{" + + properties + .stream() + .map(HelenusProperty::getPropertyName) + .collect(Collectors.joining(", ")) + + "}"; + return "(" + ((p.size() > 1) ? "(" + String.join(", ", p) + ")" : p.get(0)) + ((c.size() > 0) diff --git a/src/main/java/net/helenus/mapping/HelenusMappingEntity.java b/src/main/java/net/helenus/mapping/HelenusMappingEntity.java index cf343bb..9e0a2cc 100644 --- a/src/main/java/net/helenus/mapping/HelenusMappingEntity.java +++ b/src/main/java/net/helenus/mapping/HelenusMappingEntity.java @@ -31,6 +31,7 @@ import net.helenus.mapping.annotation.*; import net.helenus.mapping.validator.DistinctValidator; import net.helenus.support.HelenusMappingException; import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; public final class HelenusMappingEntity implements HelenusEntity { @@ -128,7 +129,24 @@ public final class HelenusMappingEntity implements HelenusEntity { for (ConstraintValidator constraint : MappingUtil.getValidators(prop.getGetterMethod())) { if (constraint.getClass().isAssignableFrom(DistinctValidator.class)) { - UnboundFacet facet = new UnboundFacet(prop); + DistinctValidator validator = (DistinctValidator) constraint; + String[] values = validator.value(); + UnboundFacet facet; + if (values != null && values.length >= 1 && !(StringUtils.isBlank(values[0]))) { + List props = new ArrayList(values.length + 1); + props.add(prop); + for (String value : values) { + for (HelenusProperty p : orderedProps) { + String name = p.getPropertyName(); + if (name.equals(value) && !name.equals(prop.getPropertyName())) { + props.add(p); + } + } + } + facet = new UnboundFacet(props); + } else { + facet = new UnboundFacet(prop); + } facetsBuilder.add(facet); break; } diff --git a/src/main/java/net/helenus/mapping/annotation/Constraints.java b/src/main/java/net/helenus/mapping/annotation/Constraints.java index d1b99d2..22ffbdc 100644 --- a/src/main/java/net/helenus/mapping/annotation/Constraints.java +++ b/src/main/java/net/helenus/mapping/annotation/Constraints.java @@ -228,10 +228,11 @@ public final class Constraints { public @interface Distinct { /** - * User defined Enum to further restrict the items in the set. + * User defined list of properties that combine with this one to result in a distinct + * combination in the table. * * @return Java */ - Class value() default Enum.class; + String[] value() default ""; } } diff --git a/src/main/java/net/helenus/mapping/validator/DistinctValidator.java b/src/main/java/net/helenus/mapping/validator/DistinctValidator.java index 8dc4711..40c30d7 100644 --- a/src/main/java/net/helenus/mapping/validator/DistinctValidator.java +++ b/src/main/java/net/helenus/mapping/validator/DistinctValidator.java @@ -22,13 +22,20 @@ import net.helenus.mapping.annotation.Constraints; public final class DistinctValidator implements ConstraintValidator { + private Constraints.Distinct annotation; + @Override - public void initialize(Constraints.Distinct constraintAnnotation) {} + public void initialize(Constraints.Distinct constraintAnnotation) { + annotation = constraintAnnotation; + } @Override public boolean isValid(CharSequence value, ConstraintValidatorContext context) { - // TODO(gburd): if there is an Enum type supplied, check that value is valid - // Enum.name() + // TODO(gburd): check that the list contains valid property names. return true; } + + public String[] value() { + return annotation == null ? null : annotation.value(); + } } diff --git a/src/test/java/net/helenus/test/integration/core/unitofwork/UnitOfWorkTest.java b/src/test/java/net/helenus/test/integration/core/unitofwork/UnitOfWorkTest.java index 1b5e01f..1c535c1 100644 --- a/src/test/java/net/helenus/test/integration/core/unitofwork/UnitOfWorkTest.java +++ b/src/test/java/net/helenus/test/integration/core/unitofwork/UnitOfWorkTest.java @@ -41,8 +41,13 @@ interface Widget { UUID id(); @Index - @Constraints.Distinct() + @Constraints.Distinct String name(); + + @Constraints.Distinct(value = {"b"}) + String a(); + + String b(); } public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest { @@ -74,6 +79,8 @@ public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest { .insert(widget) .value(widget::id, key) .value(widget::name, RandomString.make(20)) + .value(widget::a, RandomString.make(10)) + .value(widget::b, RandomString.make(10)) .sync(); try (UnitOfWork uow = session.begin()) { @@ -118,6 +125,8 @@ public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest { .insert(widget) .value(widget::id, key1) .value(widget::name, RandomString.make(20)) + .value(widget::a, RandomString.make(10)) + .value(widget::b, RandomString.make(10)) .sync(uow1); try (UnitOfWork uow2 = session.begin(uow1)) { @@ -138,6 +147,8 @@ public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest { .insert(widget) .value(widget::id, key2) .value(widget::name, RandomString.make(20)) + .value(widget::a, RandomString.make(10)) + .value(widget::b, RandomString.make(10)) .sync(uow2); uow2.commit() @@ -151,7 +162,8 @@ public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest { w4 = session .select(widget) - .where(widget::id, eq(key2)) + .where(widget::a, eq(w3.a())) + .and(widget::b, eq(w3.b())) .single() .sync(uow1) .orElse(null); @@ -175,6 +187,8 @@ public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest { .insert(widget) .value(widget::id, key) .value(widget::name, RandomString.make(20)) + .value(widget::a, RandomString.make(10)) + .value(widget::b, RandomString.make(10)) .sync(uow); // This should read from the database and return a Widget. @@ -209,6 +223,8 @@ public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest { .insert(widget) .value(widget::id, key) .value(widget::name, RandomString.make(20)) + .value(widget::a, RandomString.make(10)) + .value(widget::b, RandomString.make(10)) .sync(); try (UnitOfWork uow = session.begin()) { @@ -271,6 +287,8 @@ public class UnitOfWorkTest extends AbstractEmbeddedCassandraTest { .insert(widget) .value(widget::id, key) .value(widget::name, RandomString.make(20)) + .value(widget::a, RandomString.make(10)) + .value(widget::b, RandomString.make(10)) .sync(); try (UnitOfWork uow = session.begin()) {