From 333d15f142d714b81444a2bab3e70912d0ff1d7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Hohwiller?= Date: Fri, 12 Jul 2024 20:43:05 +0200 Subject: [PATCH] next ID refactoring, reaching perfection --- .../mmm/entity/bean/AdvancedEntityBean.java | 222 +++--- .../io/github/mmm/entity/bean/EntityBean.java | 4 +- .../mmm/entity/bean/SimpleEntityBean.java | 5 +- .../builder/EntityPropertyBuilders.java | 4 +- .../mmm/entity/property/id/IdProperty.java | 208 +++--- .../entity/property/link/LinkProperty.java | 680 +++++++++--------- .../property/id/PropertyFactoryFkTest.java | 9 +- .../property/link/LinkPropertyTest.java | 47 +- .../link/PropertyFactoryLinkTest.java | 6 +- .../io/github/mmm/entity/id/AbstractId.java | 30 +- .../mmm/entity/id/AbstractRevisionlessId.java | 62 -- .../io/github/mmm/entity/id/FkMapper.java | 2 +- .../io/github/mmm/entity/id/GenericId.java | 128 +--- .../mmm/entity/id/GenericIdFactory.java | 53 +- .../main/java/io/github/mmm/entity/id/Id.java | 88 +-- .../io/github/mmm/entity/id/IdFactory.java | 8 +- .../github/mmm/entity/id/IdMarshalling.java | 32 +- .../java/io/github/mmm/entity/id/LongId.java | 131 ---- .../github/mmm/entity/id/LongInstantId.java | 111 --- .../mmm/entity/id/LongRevisionlessId.java | 110 --- .../github/mmm/entity/id/LongVersionId.java | 123 ---- .../io/github/mmm/entity/id/NoRevision.java | 20 - .../java/io/github/mmm/entity/id/PkId.java | 210 ++++++ .../io/github/mmm/entity/id/PkIdLong.java | 116 +++ .../io/github/mmm/entity/id/PkIdString.java | 105 +++ .../io/github/mmm/entity/id/PkIdUuid.java | 115 +++ .../io/github/mmm/entity/id/RevisionedId.java | 147 ++++ ...nstantId.java => RevisionedIdInstant.java} | 39 +- ...ersionId.java => RevisionedIdVersion.java} | 31 +- .../io/github/mmm/entity/id/StringId.java | 119 --- .../github/mmm/entity/id/StringInstantId.java | 111 --- .../mmm/entity/id/StringRevisionlessId.java | 111 --- .../github/mmm/entity/id/StringVersionId.java | 109 --- .../java/io/github/mmm/entity/id/UuidId.java | 129 ---- .../github/mmm/entity/id/UuidInstantId.java | 111 --- .../mmm/entity/id/UuidRevisionlessId.java | 110 --- .../github/mmm/entity/id/UuidVersionId.java | 110 --- .../entity/id/generator/LongIdGenerator.java | 49 -- .../id/generator/SequenceIdGenerator.java | 40 ++ .../entity/id/generator/UuidIdGenerator.java | 64 +- .../mmm/entity/id/sequence/IdSequence.java | 5 +- .../mmm/entity/link/AbstractIdLink.java | 15 +- .../io/github/mmm/entity/link/EntityLink.java | 2 +- .../link/EntityLinkWithoutRevision.java | 2 +- .../io/github/mmm/entity/link/IdLink.java | 2 +- .../entity/link/IdLinkWithoutRevision.java | 2 +- .../io/github/mmm/entity/AbstractEntity.java | 5 +- .../mmm/entity/id/IdMarshallingTest.java | 43 +- .../io/github/mmm/entity/link/LinkTest.java | 11 +- 49 files changed, 1573 insertions(+), 2423 deletions(-) delete mode 100644 core/src/main/java/io/github/mmm/entity/id/AbstractRevisionlessId.java delete mode 100644 core/src/main/java/io/github/mmm/entity/id/LongId.java delete mode 100644 core/src/main/java/io/github/mmm/entity/id/LongInstantId.java delete mode 100644 core/src/main/java/io/github/mmm/entity/id/LongRevisionlessId.java delete mode 100644 core/src/main/java/io/github/mmm/entity/id/LongVersionId.java delete mode 100644 core/src/main/java/io/github/mmm/entity/id/NoRevision.java create mode 100644 core/src/main/java/io/github/mmm/entity/id/PkId.java create mode 100644 core/src/main/java/io/github/mmm/entity/id/PkIdLong.java create mode 100644 core/src/main/java/io/github/mmm/entity/id/PkIdString.java create mode 100644 core/src/main/java/io/github/mmm/entity/id/PkIdUuid.java create mode 100644 core/src/main/java/io/github/mmm/entity/id/RevisionedId.java rename core/src/main/java/io/github/mmm/entity/id/{AbstractInstantId.java => RevisionedIdInstant.java} (50%) rename core/src/main/java/io/github/mmm/entity/id/{AbstractVersionId.java => RevisionedIdVersion.java} (53%) delete mode 100644 core/src/main/java/io/github/mmm/entity/id/StringId.java delete mode 100644 core/src/main/java/io/github/mmm/entity/id/StringInstantId.java delete mode 100644 core/src/main/java/io/github/mmm/entity/id/StringRevisionlessId.java delete mode 100644 core/src/main/java/io/github/mmm/entity/id/StringVersionId.java delete mode 100644 core/src/main/java/io/github/mmm/entity/id/UuidId.java delete mode 100644 core/src/main/java/io/github/mmm/entity/id/UuidInstantId.java delete mode 100644 core/src/main/java/io/github/mmm/entity/id/UuidRevisionlessId.java delete mode 100644 core/src/main/java/io/github/mmm/entity/id/UuidVersionId.java delete mode 100644 core/src/main/java/io/github/mmm/entity/id/generator/LongIdGenerator.java create mode 100644 core/src/main/java/io/github/mmm/entity/id/generator/SequenceIdGenerator.java diff --git a/bean/src/main/java/io/github/mmm/entity/bean/AdvancedEntityBean.java b/bean/src/main/java/io/github/mmm/entity/bean/AdvancedEntityBean.java index f37ccd6..cac3939 100644 --- a/bean/src/main/java/io/github/mmm/entity/bean/AdvancedEntityBean.java +++ b/bean/src/main/java/io/github/mmm/entity/bean/AdvancedEntityBean.java @@ -1,111 +1,111 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.bean; - -import io.github.mmm.bean.AdvancedBean; -import io.github.mmm.bean.Bean; -import io.github.mmm.bean.BeanClass; -import io.github.mmm.bean.StandardPropertyBuilders; -import io.github.mmm.bean.WritableBean; -import io.github.mmm.entity.bean.impl.EntityPropertyBuildersImpl; -import io.github.mmm.entity.id.Id; -import io.github.mmm.entity.id.LongVersionId; -import io.github.mmm.entity.property.builder.EntityPropertyBuilders; -import io.github.mmm.entity.property.id.IdProperty; -import io.github.mmm.entity.property.id.PkProperty; -import io.github.mmm.property.PropertyMetadata; - -/** - * Implementation of {@link EntityBean} as simple {@link Bean}. - * - * @since 1.0.0 - */ -public class AdvancedEntityBean extends AdvancedBean implements EntityBean { - - /** The {@link IdProperty property} with the {@link Id primary key}. */ - public final PkProperty Id; - - /** - * The constructor. - */ - public AdvancedEntityBean() { - - this(null, null, null); - } - - /** - * The constructor. - * - * @param writable the {@link WritableBean} to wrap as {@link #isReadOnly() read-only} bean or {@code null} to create - * a mutable bean. - */ - public AdvancedEntityBean(WritableBean writable) { - - this(writable, null, null); - } - - /** - * The constructor. - * - * @param pkProperty the {@link #Id() ID property}. - */ - public AdvancedEntityBean(PkProperty pkProperty) { - - this(null, null, pkProperty); - } - - /** - * The constructor. - * - * @param type the {@link #getType() type}. - */ - public AdvancedEntityBean(BeanClass type) { - - this(null, type, null); - } - - /** - * The constructor. - * - * @param writable the {@link WritableBean} to wrap as {@link #isReadOnly() read-only} bean or {@code null} to create - * a mutable bean. - * @param type the {@link #getType() type}. - * @param pkProperty the {@link #Id() ID property}. - */ - private AdvancedEntityBean(WritableBean writable, BeanClass type, PkProperty pkProperty) { - - super(writable, type); - if (pkProperty == null) { - if (writable == null) { - // default - Id id = LongVersionId.getEmpty().withEntityType(getClass()); - pkProperty = new PkProperty(id, PropertyMetadata.of(this)); - } else { - pkProperty = ((AdvancedEntityBean) writable).Id; - } - } else { - assert pkProperty.getName().equals(PkProperty.NAME); - assert (writable == null); - } - this.Id = add(pkProperty); - } - - @Override - public PkProperty Id() { - - return this.Id; - } - - @Override - protected EntityPropertyBuilders add() { - - return (EntityPropertyBuilders) super.add(); - } - - @Override - protected StandardPropertyBuilders createPropertyBuilders() { - - return new EntityPropertyBuildersImpl(this); - } - -} +/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 */ +package io.github.mmm.entity.bean; + +import io.github.mmm.bean.AdvancedBean; +import io.github.mmm.bean.Bean; +import io.github.mmm.bean.BeanClass; +import io.github.mmm.bean.StandardPropertyBuilders; +import io.github.mmm.bean.WritableBean; +import io.github.mmm.entity.bean.impl.EntityPropertyBuildersImpl; +import io.github.mmm.entity.id.Id; +import io.github.mmm.entity.id.RevisionedIdVersion; +import io.github.mmm.entity.property.builder.EntityPropertyBuilders; +import io.github.mmm.entity.property.id.IdProperty; +import io.github.mmm.entity.property.id.PkProperty; +import io.github.mmm.property.PropertyMetadata; + +/** + * Implementation of {@link EntityBean} as simple {@link Bean}. + * + * @since 1.0.0 + */ +public class AdvancedEntityBean extends AdvancedBean implements EntityBean { + + /** The {@link IdProperty property} with the {@link Id primary key}. */ + public final PkProperty Id; + + /** + * The constructor. + */ + public AdvancedEntityBean() { + + this(null, null, null); + } + + /** + * The constructor. + * + * @param writable the {@link WritableBean} to wrap as {@link #isReadOnly() read-only} bean or {@code null} to create + * a mutable bean. + */ + public AdvancedEntityBean(WritableBean writable) { + + this(writable, null, null); + } + + /** + * The constructor. + * + * @param pkProperty the {@link #Id() ID property}. + */ + public AdvancedEntityBean(PkProperty pkProperty) { + + this(null, null, pkProperty); + } + + /** + * The constructor. + * + * @param type the {@link #getType() type}. + */ + public AdvancedEntityBean(BeanClass type) { + + this(null, type, null); + } + + /** + * The constructor. + * + * @param writable the {@link WritableBean} to wrap as {@link #isReadOnly() read-only} bean or {@code null} to create + * a mutable bean. + * @param type the {@link #getType() type}. + * @param pkProperty the {@link #Id() ID property}. + */ + private AdvancedEntityBean(WritableBean writable, BeanClass type, PkProperty pkProperty) { + + super(writable, type); + if (pkProperty == null) { + if (writable == null) { + // default + Id id = RevisionedIdVersion.DEFAULT.withEntityTypeGeneric(getClass()); + pkProperty = new PkProperty(id, PropertyMetadata.of(this)); + } else { + pkProperty = ((AdvancedEntityBean) writable).Id; + } + } else { + assert pkProperty.getName().equals(PkProperty.NAME); + assert (writable == null); + } + this.Id = add(pkProperty); + } + + @Override + public PkProperty Id() { + + return this.Id; + } + + @Override + protected EntityPropertyBuilders add() { + + return (EntityPropertyBuilders) super.add(); + } + + @Override + protected StandardPropertyBuilders createPropertyBuilders() { + + return new EntityPropertyBuildersImpl(this); + } + +} diff --git a/bean/src/main/java/io/github/mmm/entity/bean/EntityBean.java b/bean/src/main/java/io/github/mmm/entity/bean/EntityBean.java index 1ad2dc1..0441d6f 100644 --- a/bean/src/main/java/io/github/mmm/entity/bean/EntityBean.java +++ b/bean/src/main/java/io/github/mmm/entity/bean/EntityBean.java @@ -6,7 +6,7 @@ import io.github.mmm.bean.WritableBean; import io.github.mmm.entity.Entity; import io.github.mmm.entity.id.Id; -import io.github.mmm.entity.id.LongVersionId; +import io.github.mmm.entity.id.RevisionedIdVersion; import io.github.mmm.entity.property.id.IdProperty; import io.github.mmm.entity.property.id.PkProperty; @@ -55,7 +55,7 @@ public interface EntityBean extends WritableBean, Entity { */ default PkProperty Id() { - return new PkProperty(LongVersionId.getEmpty().withEntityType(getType().getJavaClass())); + return new PkProperty(RevisionedIdVersion.DEFAULT.withEntityTypeGeneric(getType().getJavaClass())); } @Override diff --git a/bean/src/main/java/io/github/mmm/entity/bean/SimpleEntityBean.java b/bean/src/main/java/io/github/mmm/entity/bean/SimpleEntityBean.java index 668a15e..03c2b71 100644 --- a/bean/src/main/java/io/github/mmm/entity/bean/SimpleEntityBean.java +++ b/bean/src/main/java/io/github/mmm/entity/bean/SimpleEntityBean.java @@ -6,7 +6,7 @@ import io.github.mmm.bean.StandardPropertyBuilders; import io.github.mmm.entity.bean.impl.EntityPropertyBuildersImpl; import io.github.mmm.entity.id.Id; -import io.github.mmm.entity.id.LongVersionId; +import io.github.mmm.entity.id.RevisionedIdVersion; import io.github.mmm.entity.property.builder.EntityPropertyBuilders; import io.github.mmm.entity.property.id.IdProperty; import io.github.mmm.entity.property.id.PkProperty; @@ -39,7 +39,8 @@ public SimpleEntityBean(PkProperty idProperty) { super(); if (idProperty == null) { - idProperty = new PkProperty(LongVersionId.getEmpty().withEntityType(getClass()), PropertyMetadata.of(this)); + idProperty = new PkProperty(RevisionedIdVersion.DEFAULT.withEntityTypeGeneric(getClass()), + PropertyMetadata.of(this)); } this.Id = add(idProperty); } diff --git a/bean/src/main/java/io/github/mmm/entity/property/builder/EntityPropertyBuilders.java b/bean/src/main/java/io/github/mmm/entity/property/builder/EntityPropertyBuilders.java index a1f5314..47bece4 100644 --- a/bean/src/main/java/io/github/mmm/entity/property/builder/EntityPropertyBuilders.java +++ b/bean/src/main/java/io/github/mmm/entity/property/builder/EntityPropertyBuilders.java @@ -12,7 +12,7 @@ import io.github.mmm.entity.bean.EntityBean; import io.github.mmm.entity.id.GenericId; import io.github.mmm.entity.id.Id; -import io.github.mmm.entity.id.LongVersionId; +import io.github.mmm.entity.id.RevisionedIdVersion; import io.github.mmm.entity.link.IdLink; import io.github.mmm.entity.property.id.FkProperty; import io.github.mmm.entity.property.id.FkPropertyBuilder; @@ -95,7 +95,7 @@ default FkProperty newFk(String name, Id idTemplate private Id safeId(Id id) { if (id == null) { - id = LongVersionId.getEmpty(); + id = (Id) RevisionedIdVersion.DEFAULT; } AttributeReadOnly lock = getLock(); if (lock instanceof EntityBean) { diff --git a/bean/src/main/java/io/github/mmm/entity/property/id/IdProperty.java b/bean/src/main/java/io/github/mmm/entity/property/id/IdProperty.java index b0f53ea..73e91cd 100644 --- a/bean/src/main/java/io/github/mmm/entity/property/id/IdProperty.java +++ b/bean/src/main/java/io/github/mmm/entity/property/id/IdProperty.java @@ -1,104 +1,104 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.property.id; - -import io.github.mmm.entity.id.GenericId; -import io.github.mmm.entity.id.Id; -import io.github.mmm.entity.id.LongVersionId; -import io.github.mmm.marshall.StructuredReader; -import io.github.mmm.marshall.StructuredWriter; -import io.github.mmm.property.PropertyMetadata; -import io.github.mmm.property.object.SimpleProperty; - -/** - * {@link SimpleProperty} with {@link Id} {@link #get() value} pointing to an entity. - * - * @param type of the {@link #get() value}. - * @since 1.0.0 - */ -public abstract class IdProperty> extends SimpleProperty { - - private V value; - - private Class entityClass; - - /** - * The constructor. - * - * @param name the {@link #getName() name}. - * @param id the initial {@link #get() value}. - * @param metadata the {@link #getMetadata() metadata}. - */ - @SuppressWarnings("unchecked") - public IdProperty(String name, V id, PropertyMetadata metadata) { - - super(name, metadata); - if (id == null) { - this.value = (V) LongVersionId.getEmpty(); - } else { - doSet(id); - } - } - - @Override - protected V doGet() { - - return this.value; - } - - @Override - @SuppressWarnings("unchecked") - protected void doSet(V newValue) { - - if (newValue != null) { - Class newEntityClass = newValue.getEntityClass(); - if (this.entityClass == null) { - this.entityClass = newEntityClass; - } else { - if (newEntityClass == null) { - newValue = (V) ((GenericId) newValue).withEntityType(this.entityClass); - } else if (!this.entityClass.isAssignableFrom(newEntityClass)) { - throw new IllegalArgumentException("Cannot set ID of type " + newEntityClass.getName() + " to " - + getClass().getSimpleName() + " " + getName() + " with incompatible type " + this.entityClass.getName()); - } - } - } - this.value = newValue; - } - - @SuppressWarnings("unchecked") - @Override - public V getFallbackSafeValue() { - - return (V) LongVersionId.getEmpty(); - } - - @SuppressWarnings({ "rawtypes", "unchecked" }) - @Override - public Class getValueClass() { - - return (Class) Id.class; - } - - @Override - @SuppressWarnings("unchecked") - public V parse(String valueAsString) { - - return (V) ((GenericId) this.value).create(valueAsString); - } - - @Override - @SuppressWarnings("unchecked") - protected void readValue(StructuredReader reader) { - - V id = (V) ((GenericId) this.value).readObject(reader); - set(id); - } - - @Override - public void write(StructuredWriter writer) { - - ((GenericId) this.value).write(writer); - } - -} +/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 */ +package io.github.mmm.entity.property.id; + +import io.github.mmm.entity.id.GenericId; +import io.github.mmm.entity.id.Id; +import io.github.mmm.entity.id.RevisionedIdVersion; +import io.github.mmm.marshall.StructuredReader; +import io.github.mmm.marshall.StructuredWriter; +import io.github.mmm.property.PropertyMetadata; +import io.github.mmm.property.object.SimpleProperty; + +/** + * {@link SimpleProperty} with {@link Id} {@link #get() value} pointing to an entity. + * + * @param type of the {@link #get() value}. + * @since 1.0.0 + */ +public abstract class IdProperty> extends SimpleProperty { + + private V value; + + private Class entityClass; + + /** + * The constructor. + * + * @param name the {@link #getName() name}. + * @param id the initial {@link #get() value}. + * @param metadata the {@link #getMetadata() metadata}. + */ + @SuppressWarnings("unchecked") + public IdProperty(String name, V id, PropertyMetadata metadata) { + + super(name, metadata); + if (id == null) { + this.value = (V) RevisionedIdVersion.DEFAULT; + } else { + doSet(id); + } + } + + @Override + protected V doGet() { + + return this.value; + } + + @Override + @SuppressWarnings("unchecked") + protected void doSet(V newValue) { + + if (newValue != null) { + Class newEntityClass = newValue.getEntityClass(); + if (this.entityClass == null) { + this.entityClass = newEntityClass; + } else { + if (newEntityClass == null) { + newValue = (V) ((GenericId) newValue).withEntityTypeGeneric(this.entityClass); + } else if (!this.entityClass.isAssignableFrom(newEntityClass)) { + throw new IllegalArgumentException("Cannot set ID of type " + newEntityClass.getName() + " to " + + getClass().getSimpleName() + " " + getName() + " with incompatible type " + this.entityClass.getName()); + } + } + } + this.value = newValue; + } + + @SuppressWarnings("unchecked") + @Override + public V getFallbackSafeValue() { + + return (V) RevisionedIdVersion.DEFAULT; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public Class getValueClass() { + + return (Class) Id.class; + } + + @Override + @SuppressWarnings("unchecked") + public V parse(String valueAsString) { + + return (V) ((GenericId) this.value).create(valueAsString); + } + + @Override + @SuppressWarnings("unchecked") + protected void readValue(StructuredReader reader) { + + V id = (V) ((GenericId) this.value).readObject(reader); + set(id); + } + + @Override + public void write(StructuredWriter writer) { + + ((GenericId) this.value).write(writer); + } + +} diff --git a/bean/src/main/java/io/github/mmm/entity/property/link/LinkProperty.java b/bean/src/main/java/io/github/mmm/entity/property/link/LinkProperty.java index c60bef6..d904128 100644 --- a/bean/src/main/java/io/github/mmm/entity/property/link/LinkProperty.java +++ b/bean/src/main/java/io/github/mmm/entity/property/link/LinkProperty.java @@ -1,340 +1,340 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.property.link; - -import java.util.function.Function; -import java.util.function.Supplier; - -import io.github.mmm.bean.WritableBean; -import io.github.mmm.entity.Entity; -import io.github.mmm.entity.bean.EntityBean; -import io.github.mmm.entity.id.Id; -import io.github.mmm.entity.id.IdMarshalling; -import io.github.mmm.entity.link.AbstractLink; -import io.github.mmm.entity.link.IdLink; -import io.github.mmm.entity.link.Link; -import io.github.mmm.entity.link.LinkMapper; -import io.github.mmm.entity.property.id.IdProperty; -import io.github.mmm.marshall.StructuredReader; -import io.github.mmm.marshall.StructuredWriter; -import io.github.mmm.property.PropertyMetadata; -import io.github.mmm.property.ReadableProperty; -import io.github.mmm.property.criteria.CriteriaPredicate; -import io.github.mmm.property.criteria.PredicateOperator; -import io.github.mmm.property.object.ObjectProperty; -import io.github.mmm.value.PropertyPath; -import io.github.mmm.value.ReadableValue; -import io.github.mmm.value.converter.TypeMapper; - -/** - * {@link ObjectProperty} with {@link Link} {@link #getValue() value} {@link Link#getTarget() pointing to} an - * {@link io.github.mmm.entity.bean.EntityBean entity}. - * - * @param the generic type of the {@link Link#getTarget() linked} {@link io.github.mmm.entity.bean.EntityBean - * entity}. - * - * @since 1.0.0 - */ -public class LinkProperty extends ObjectProperty> { - - private Class entityClass; - - private Function, E> resolver; - - private LinkMapper typeMapper; - - /** - * The constructor. - * - * @param name the {@link #getName() name}. - * @param entityClass the optional {@link Class} of the linked entity. - * @param metadata the {@link #getMetadata() metadata}. - */ - public LinkProperty(String name, Class entityClass, PropertyMetadata> metadata) { - - this(name, entityClass, metadata, null); - } - - /** - * The constructor. - * - * @param name the {@link #getName() name}. - * @param entityClass the optional {@link Class} of the linked entity. - * @param metadata the {@link #getMetadata() metadata}. - * @param resolver the optional {@link IdLink#of(Id, Function) resolver function}. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public LinkProperty(String name, Class entityClass, PropertyMetadata> metadata, - Function, E> resolver) { - - super(name, (Class) Link.class, metadata); - this.entityClass = entityClass; - this.resolver = resolver; - } - - /** - * The constructor. - * - * @param name the {@link #getName() name}. - * @param value the (initial) {@link #get() value}. - * @param metadata the {@link #getMetadata() metadata}. - */ - public LinkProperty(String name, Link value, PropertyMetadata> metadata) { - - this(name, value, metadata, null); - } - - /** - * The constructor. - * - * @param name the {@link #getName() name}. - * @param value the (initial) {@link #get() value}. - * @param metadata the {@link #getMetadata() metadata}. - * @param resolver the optional {@link IdLink#of(Id, Function) resolver function}. - */ - public LinkProperty(String name, Link value, PropertyMetadata> metadata, Function, E> resolver) { - - super(name, value, metadata); - this.entityClass = value.getId().getEntityClass(); - this.resolver = resolver; - } - - @Override - protected void doSet(Link newValue) { - - if (newValue != null) { - Id id = newValue.getId(); - if (id != null) { - if (this.entityClass == null) { - this.entityClass = id.getEntityClass(); - } else { - Class idEntityType = id.getEntityClass(); - if (idEntityType == null) { - if (newValue instanceof IdLink) { - newValue = ((IdLink) newValue).withType(this.entityClass); - } - } else if (!this.entityClass.isAssignableFrom(idEntityType)) { - throw new IllegalArgumentException("Cannot set link of type " + idEntityType.getName() + " to property " - + getName() + " with incompatible entity type " + this.entityClass.getName()); - } - } - } - } - super.doSet(newValue); - } - - /** - * @return the linked {@link EntityBean entity}. - * @see Link#getTarget() - */ - public E getEntity() { - - Link link = get(); - if (link == null) { - return null; - } - return link.getTarget(); - } - - /** - * @return the {@link Id#getEntityClass() entity class}. - */ - @SuppressWarnings({ "unchecked", "rawtypes" }) - public Class getEntityClass() { - - if (this.entityClass == null) { - Link link = get(); - if (link != null) { - Id id = link.getId(); - if (id != null) { - this.entityClass = id.getEntityClass(); - } else if (link.isResolved()) { - E target = link.getTarget(); - this.entityClass = (Class) ((EntityBean) target).getType().getJavaClass(); - } - } - } - return this.entityClass; - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public TypeMapper, Id> getTypeMapper() { - - if (this.typeMapper == null) { - this.typeMapper = new LinkMapper(this.resolver); - } - return (TypeMapper) this.typeMapper; - } - - @Override - public boolean isValueMutable() { - - return true; // IdLink without resolver function is actually immutable but lets keep it simple - } - - @Override - protected Supplier> createReadOnlyExpression() { - - final ReadOnlyLink readOnlyLink = new ReadOnlyLink(); - return () -> { - Link link = get(); - if (link == null) { - return null; - } - return readOnlyLink; - }; - } - - @Override - protected void readValue(StructuredReader reader) { - - Id id = IdMarshalling.get().readObject(reader, this.entityClass); - IdLink link = IdLink.of(id, this.resolver); - setValue(link); - } - - @Override - public void write(StructuredWriter writer) { - - Id id = null; - Link link = getValue(); - if (link != null) { - id = link.getId(); - } - IdMarshalling.get().writeObject(writer, id); - } - - /** - * @see #eq(Object) - * @param other the literal {@link Id} to compare with using {@link PredicateOperator#EQ = (equal)}. - * @return the resulting {@link CriteriaPredicate}. - */ - public CriteriaPredicate eq(Id other) { - - return eq(Link.of(other)); - } - - /** - * @see #eq(Object) - * @param other the {@link IdProperty} to compare with using {@link PredicateOperator#EQ = (equal)}. - * @return the resulting {@link CriteriaPredicate}. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public CriteriaPredicate eq(IdProperty other) { - - return predicate((ReadableProperty) this, PredicateOperator.EQ, (PropertyPath) other); - } - - /** - * @see #eq(Object) - * @param other the literal {@link Entity} whose {@link Entity#getId() ID} to compare with using - * {@link PredicateOperator#EQ = (equal)}. - * @return the resulting {@link CriteriaPredicate}. - */ - public CriteriaPredicate eq(E other) { - - if ((other != null) && (other.getId() == null)) { - return eq(other.Id()); - } - return eq(Link.of(other)); - } - - /** - * @see #neq(Object) - * @param other the literal {@link Id} to compare with using {@link PredicateOperator#NEQ != (not-equal)}. - * @return the resulting {@link CriteriaPredicate}. - */ - public CriteriaPredicate neq(Id other) { - - return neq(Link.of(other)); - } - - /** - * @see #neq(Object) - * @param other the literal {@link Entity} whose {@link Entity#getId() ID} to compare with using - * {@link PredicateOperator#NEQ != (not-equal)}. - * @return the resulting {@link CriteriaPredicate}. - */ - public CriteriaPredicate neq(E other) { - - if ((other != null) && (other.getId() == null)) { - return neq(other.Id()); - } - return neq(Link.of(other)); - } - - /** - * @see #neq(Object) - * @param other the {@link IdProperty} to compare with using {@link PredicateOperator#NEQ != (not-equal)}. - * @return the resulting {@link CriteriaPredicate}. - */ - @SuppressWarnings({ "rawtypes", "unchecked" }) - public CriteriaPredicate neq(IdProperty other) { - - return predicate((ReadableProperty) this, PredicateOperator.NEQ, (PropertyPath) other); - } - - private CriteriaPredicate predicate(ReadableProperty property1, PredicateOperator op, - PropertyPath property2) { - - return CriteriaPredicate.of(property1, op, property2); - } - - /** - * ATTENTION:
- * This is an internal method for framework code. - * - * @param resolver new resolver {@link Function}. - * @see IdLink#setResolver(Function) - */ - @SuppressWarnings("unchecked") - public void setResolver(Function, E> resolver) { - - // TODO: checks? - this.resolver = resolver; - this.typeMapper = null; - Link link = doGet(); - if ((link != null) && !link.isResolved()) { - if (link instanceof IdLink idLink) { - idLink.setResolver(resolver); - } - } - } - - @Override - public void copyValue(ReadableValue> other) { - - Link link = other.get(); - if (link != null) { - link = Link.of(link.getId()); - } - set(link); - } - - private class ReadOnlyLink extends AbstractLink { - @Override - public Id getId() { - - return get().getId(); - } - - @Override - public E getTarget() { - - E target = get().getTarget(); - if (target != null) { - target = WritableBean.getReadOnly(target); - } - return target; - } - - @Override - public boolean isResolved() { - - return get().isResolved(); - } - - } - -} +/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 */ +package io.github.mmm.entity.property.link; + +import java.util.function.Function; +import java.util.function.Supplier; + +import io.github.mmm.bean.WritableBean; +import io.github.mmm.entity.Entity; +import io.github.mmm.entity.bean.EntityBean; +import io.github.mmm.entity.id.Id; +import io.github.mmm.entity.id.IdMarshalling; +import io.github.mmm.entity.link.AbstractLink; +import io.github.mmm.entity.link.IdLink; +import io.github.mmm.entity.link.Link; +import io.github.mmm.entity.link.LinkMapper; +import io.github.mmm.entity.property.id.IdProperty; +import io.github.mmm.marshall.StructuredReader; +import io.github.mmm.marshall.StructuredWriter; +import io.github.mmm.property.PropertyMetadata; +import io.github.mmm.property.ReadableProperty; +import io.github.mmm.property.criteria.CriteriaPredicate; +import io.github.mmm.property.criteria.PredicateOperator; +import io.github.mmm.property.object.ObjectProperty; +import io.github.mmm.value.PropertyPath; +import io.github.mmm.value.ReadableValue; +import io.github.mmm.value.converter.TypeMapper; + +/** + * {@link ObjectProperty} with {@link Link} {@link #getValue() value} {@link Link#getTarget() pointing to} an + * {@link io.github.mmm.entity.bean.EntityBean entity}. + * + * @param the generic type of the {@link Link#getTarget() linked} {@link io.github.mmm.entity.bean.EntityBean + * entity}. + * + * @since 1.0.0 + */ +public class LinkProperty extends ObjectProperty> { + + private Class entityClass; + + private Function, E> resolver; + + private LinkMapper typeMapper; + + /** + * The constructor. + * + * @param name the {@link #getName() name}. + * @param entityClass the optional {@link Class} of the linked entity. + * @param metadata the {@link #getMetadata() metadata}. + */ + public LinkProperty(String name, Class entityClass, PropertyMetadata> metadata) { + + this(name, entityClass, metadata, null); + } + + /** + * The constructor. + * + * @param name the {@link #getName() name}. + * @param entityClass the optional {@link Class} of the linked entity. + * @param metadata the {@link #getMetadata() metadata}. + * @param resolver the optional {@link IdLink#of(Id, Function) resolver function}. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public LinkProperty(String name, Class entityClass, PropertyMetadata> metadata, + Function, E> resolver) { + + super(name, (Class) Link.class, metadata); + this.entityClass = entityClass; + this.resolver = resolver; + } + + /** + * The constructor. + * + * @param name the {@link #getName() name}. + * @param value the (initial) {@link #get() value}. + * @param metadata the {@link #getMetadata() metadata}. + */ + public LinkProperty(String name, Link value, PropertyMetadata> metadata) { + + this(name, value, metadata, null); + } + + /** + * The constructor. + * + * @param name the {@link #getName() name}. + * @param value the (initial) {@link #get() value}. + * @param metadata the {@link #getMetadata() metadata}. + * @param resolver the optional {@link IdLink#of(Id, Function) resolver function}. + */ + public LinkProperty(String name, Link value, PropertyMetadata> metadata, Function, E> resolver) { + + super(name, value, metadata); + this.entityClass = value.getId().getEntityClass(); + this.resolver = resolver; + } + + @Override + protected void doSet(Link newValue) { + + if (newValue != null) { + Id id = newValue.getId(); + if (id != null) { + if (this.entityClass == null) { + this.entityClass = id.getEntityClass(); + } else { + Class idEntityType = id.getEntityClass(); + if (idEntityType == null) { + if (newValue instanceof IdLink) { + newValue = ((IdLink) newValue).withType(this.entityClass); + } + } else if (!this.entityClass.isAssignableFrom(idEntityType)) { + throw new IllegalArgumentException("Cannot set link of type " + idEntityType.getName() + " to property " + + getName() + " with incompatible entity type " + this.entityClass.getName()); + } + } + } + } + super.doSet(newValue); + } + + /** + * @return the linked {@link EntityBean entity}. + * @see Link#getTarget() + */ + public E getEntity() { + + Link link = get(); + if (link == null) { + return null; + } + return link.getTarget(); + } + + /** + * @return the {@link Id#getEntityClass() entity class}. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public Class getEntityClass() { + + if (this.entityClass == null) { + Link link = get(); + if (link != null) { + Id id = link.getId(); + if (id != null) { + this.entityClass = id.getEntityClass(); + } else if (link.isResolved()) { + E target = link.getTarget(); + this.entityClass = (Class) ((EntityBean) target).getType().getJavaClass(); + } + } + } + return this.entityClass; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public TypeMapper, Id> getTypeMapper() { + + if (this.typeMapper == null) { + this.typeMapper = new LinkMapper(this.resolver); + } + return (TypeMapper) this.typeMapper; + } + + @Override + public boolean isValueMutable() { + + return true; // IdLink without resolver function is actually immutable but lets keep it simple + } + + @Override + protected Supplier> createReadOnlyExpression() { + + final ReadOnlyLink readOnlyLink = new ReadOnlyLink(); + return () -> { + Link link = get(); + if (link == null) { + return null; + } + return readOnlyLink; + }; + } + + @Override + protected void readValue(StructuredReader reader) { + + Id id = IdMarshalling.get().readObject(reader, this.entityClass); + IdLink link = IdLink.of(id, this.resolver); + setValue(link); + } + + @Override + public void write(StructuredWriter writer) { + + Id id = null; + Link link = getValue(); + if (link != null) { + id = link.getId(); + } + IdMarshalling.get().writeObject(writer, id); + } + + /** + * @see #eq(Object) + * @param other the literal {@link Id} to compare with using {@link PredicateOperator#EQ = (equal)}. + * @return the resulting {@link CriteriaPredicate}. + */ + public CriteriaPredicate eq(Id other) { + + return eq(Link.of(other)); + } + + /** + * @see #eq(Object) + * @param other the {@link IdProperty} to compare with using {@link PredicateOperator#EQ = (equal)}. + * @return the resulting {@link CriteriaPredicate}. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public CriteriaPredicate eq(IdProperty other) { + + return predicate((ReadableProperty) this, PredicateOperator.EQ, (PropertyPath) other); + } + + /** + * @see #eq(Object) + * @param other the literal {@link Entity} whose {@link Entity#getId() ID} to compare with using + * {@link PredicateOperator#EQ = (equal)}. + * @return the resulting {@link CriteriaPredicate}. + */ + public CriteriaPredicate eq(E other) { + + if ((other != null) && (other.getId() == null)) { + return eq(other.Id()); + } + return eq(Link.of(other)); + } + + /** + * @see #neq(Object) + * @param other the literal {@link Id} to compare with using {@link PredicateOperator#NEQ != (not-equal)}. + * @return the resulting {@link CriteriaPredicate}. + */ + public CriteriaPredicate neq(Id other) { + + return neq(Link.of(other)); + } + + /** + * @see #neq(Object) + * @param other the literal {@link Entity} whose {@link Entity#getId() ID} to compare with using + * {@link PredicateOperator#NEQ != (not-equal)}. + * @return the resulting {@link CriteriaPredicate}. + */ + public CriteriaPredicate neq(E other) { + + if ((other != null) && (other.getId() == null)) { + return neq(other.Id()); + } + return neq(Link.of(other)); + } + + /** + * @see #neq(Object) + * @param other the {@link IdProperty} to compare with using {@link PredicateOperator#NEQ != (not-equal)}. + * @return the resulting {@link CriteriaPredicate}. + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public CriteriaPredicate neq(IdProperty other) { + + return predicate((ReadableProperty) this, PredicateOperator.NEQ, (PropertyPath) other); + } + + private CriteriaPredicate predicate(ReadableProperty property1, PredicateOperator op, + PropertyPath property2) { + + return CriteriaPredicate.of(property1, op, property2); + } + + /** + * ATTENTION:
+ * This is an internal method for framework code. + * + * @param resolver new resolver {@link Function}. + * @see IdLink#setResolver(Function) + */ + @SuppressWarnings("unchecked") + public void setResolver(Function, E> resolver) { + + // TODO: checks? + this.resolver = resolver; + this.typeMapper = null; + Link link = doGet(); + if ((link != null) && !link.isResolved()) { + if (link instanceof IdLink idLink) { + idLink.setResolver(resolver); + } + } + } + + @Override + public void copyValue(ReadableValue> other) { + + Link link = other.get(); + if (link != null) { + link = Link.of(link.getId()); + } + set(link); + } + + private class ReadOnlyLink extends AbstractLink { + @Override + public Id getId() { + + return get().getId(); + } + + @Override + public E getTarget() { + + E target = get().getTarget(); + if (target != null) { + target = WritableBean.getReadOnly(target); + } + return target; + } + + @Override + public boolean isResolved() { + + return get().isResolved(); + } + + } + +} diff --git a/bean/src/test/java/io/github/mmm/entity/property/id/PropertyFactoryFkTest.java b/bean/src/test/java/io/github/mmm/entity/property/id/PropertyFactoryFkTest.java index 9dae28b..a3f19ab 100644 --- a/bean/src/test/java/io/github/mmm/entity/property/id/PropertyFactoryFkTest.java +++ b/bean/src/test/java/io/github/mmm/entity/property/id/PropertyFactoryFkTest.java @@ -4,8 +4,10 @@ import org.junit.jupiter.api.Test; import io.github.mmm.bean.BeanFactory; +import io.github.mmm.entity.id.GenericId; import io.github.mmm.entity.id.Id; -import io.github.mmm.entity.id.LongVersionId; +import io.github.mmm.entity.id.PkIdLong; +import io.github.mmm.entity.id.RevisionedIdVersion; import io.github.mmm.entity.link.Link; import io.github.mmm.entity.property.link.LinkProperty; @@ -39,9 +41,10 @@ public void testSetFkOfWrongTypeFails() { // arrange Source source = BeanFactory.get().create(Source.class); + GenericId sourceId = RevisionedIdVersion.DEFAULT.create(Source.class, 4711L, 1L); try { // act - source.Target().set(new LongVersionId(Source.class, 4711L, 1L)); // doing evil things should fail + source.Target().set((Id) sourceId); // doing evil things should fail // assert failBecauseExceptionWasNotThrown(IllegalArgumentException.class); } catch (IllegalArgumentException e) { @@ -55,7 +58,7 @@ public void testSetFkOfWrongTypeFails() { public void testSetLinkWithCorrectTypeSucceeds() { // arrange - LongVersionId id = new LongVersionId<>(Target.class, 4711L, 1L); + RevisionedIdVersion id = new RevisionedIdVersion<>(new PkIdLong<>(Target.class, 4711L), 1L); Source source = BeanFactory.get().create(Source.class); // act source.Target().set(id); diff --git a/bean/src/test/java/io/github/mmm/entity/property/link/LinkPropertyTest.java b/bean/src/test/java/io/github/mmm/entity/property/link/LinkPropertyTest.java index 8bd9d89..06911c6 100644 --- a/bean/src/test/java/io/github/mmm/entity/property/link/LinkPropertyTest.java +++ b/bean/src/test/java/io/github/mmm/entity/property/link/LinkPropertyTest.java @@ -11,15 +11,11 @@ import io.github.mmm.bean.BeanFactory; import io.github.mmm.entity.bean.PropertyTest; import io.github.mmm.entity.id.Id; -import io.github.mmm.entity.id.LongInstantId; -import io.github.mmm.entity.id.LongRevisionlessId; -import io.github.mmm.entity.id.LongVersionId; -import io.github.mmm.entity.id.StringInstantId; -import io.github.mmm.entity.id.StringRevisionlessId; -import io.github.mmm.entity.id.StringVersionId; -import io.github.mmm.entity.id.UuidInstantId; -import io.github.mmm.entity.id.UuidRevisionlessId; -import io.github.mmm.entity.id.UuidVersionId; +import io.github.mmm.entity.id.PkIdLong; +import io.github.mmm.entity.id.PkIdString; +import io.github.mmm.entity.id.PkIdUuid; +import io.github.mmm.entity.id.RevisionedIdInstant; +import io.github.mmm.entity.id.RevisionedIdVersion; import io.github.mmm.entity.link.Link; import io.github.mmm.marshall.MarshallingConfig; import io.github.mmm.marshall.StandardFormat; @@ -33,19 +29,15 @@ */ public class LinkPropertyTest extends PropertyTest, LinkProperty> { - /** - * - */ - private static final String INSTANT = "1999-12-31T23:59:59.123456789Z"; + private static final String TEST_INSTANT = "1999-12-31T23:59:59.123456789Z"; - private static final Id ID; + private static final Id ID = new RevisionedIdVersion<>(new PkIdLong<>(Target.class, 4711L), 1L); private static final Target TARGET; private static final Link LINK; static { - ID = new LongVersionId<>(Target.class, 4711L, 1L); TARGET = BeanFactory.get().create(Target.class); TARGET.Id().set(ID); LINK = Link.of(TARGET); @@ -63,7 +55,6 @@ public void testReadOnly() { // arrange LinkProperty linkProperty = new LinkProperty<>("target", Target.class, null); - LongVersionId id = new LongVersionId<>(Target.class, 4712L, 1L); // act LinkProperty readOnlyLinkProperty = WritableProperty.getReadOnly(linkProperty); // assert @@ -71,7 +62,7 @@ public void testReadOnly() { linkProperty.set(LINK); Link readOnlyLink = readOnlyLinkProperty.get(); assertThrows(ReadOnlyException.class, () -> { - readOnlyLink.getTarget().setId(id); + readOnlyLink.getTarget().setId(ID); }); assertThat(readOnlyLink).isNotNull().isNotSameAs(LINK); } @@ -79,24 +70,26 @@ public void testReadOnly() { @Test public void testMapJson() { - Instant instant = Instant.parse(INSTANT); + Instant instant = Instant.parse(TEST_INSTANT); // LongId variants - checkMapJson("4711", 4711L, null, LongRevisionlessId.class); - checkMapJson("{\"l\":4711,\"v\":42}", 4711L, 42L, LongVersionId.class); - checkMapJson("{\"l\":4711,\"t\":\"" + INSTANT + "\"}", 4711L, instant, LongInstantId.class); + checkMapJson("4711", 4711L, null, PkIdLong.class); + checkMapJson("{\"l\":4711,\"v\":42}", 4711L, 42L, RevisionedIdVersion.class); + checkMapJson("{\"l\":4711,\"t\":\"" + TEST_INSTANT + "\"}", 4711L, instant, RevisionedIdInstant.class); // UuidId variants UUID uuid = UUID.randomUUID(); String uuidString = uuid.toString(); - checkMapJson("\"" + uuidString + "\"", uuid, null, UuidRevisionlessId.class); - checkMapJson("{\"u\":\"" + uuidString + "\",\"v\":43}", uuid, 43L, UuidVersionId.class); - checkMapJson("{\"u\":\"" + uuidString + "\",\"t\":\"" + INSTANT + "\"}", uuid, instant, UuidInstantId.class); + checkMapJson("\"" + uuidString + "\"", uuid, null, PkIdUuid.class); + checkMapJson("{\"u\":\"" + uuidString + "\",\"v\":43}", uuid, 43L, RevisionedIdVersion.class); + checkMapJson("{\"u\":\"" + uuidString + "\",\"t\":\"" + TEST_INSTANT + "\"}", uuid, instant, + RevisionedIdInstant.class); // String variants String pk = "primary-key"; - checkMapJson("\"" + pk + "\"", pk, null, StringRevisionlessId.class); - checkMapJson("{\"s\":\"" + pk + "\",\"v\":44}", pk, 44L, StringVersionId.class); - checkMapJson("{\"s\":\"" + pk + "\",\"t\":\"" + INSTANT + "\"}", pk, instant, StringInstantId.class); + checkMapJson("\"" + pk + "\"", pk, null, PkIdString.class); + checkMapJson("{\"s\":\"" + pk + "\",\"v\":44}", pk, 44L, RevisionedIdVersion.class); + checkMapJson("{\"s\":\"" + pk + "\",\"t\":\"" + TEST_INSTANT + "\"}", pk, instant, RevisionedIdInstant.class); } + @SuppressWarnings("rawtypes") private void checkMapJson(String json, Object pk, Object revision, Class idClass) { // arrange diff --git a/bean/src/test/java/io/github/mmm/entity/property/link/PropertyFactoryLinkTest.java b/bean/src/test/java/io/github/mmm/entity/property/link/PropertyFactoryLinkTest.java index d60d8b8..f3b163d 100644 --- a/bean/src/test/java/io/github/mmm/entity/property/link/PropertyFactoryLinkTest.java +++ b/bean/src/test/java/io/github/mmm/entity/property/link/PropertyFactoryLinkTest.java @@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test; import io.github.mmm.bean.BeanFactory; -import io.github.mmm.entity.id.LongVersionId; +import io.github.mmm.entity.id.Id; import io.github.mmm.entity.link.Link; /** @@ -52,13 +52,13 @@ public void testSetLinkWithCorrectTypeSucceeds() { // arrange Target target = BeanFactory.get().create(Target.class); - LongVersionId id = LongVersionId.of(4711L, 1L, Target.class); + Id id = Id.of(Target.class, 4711L); target.setId(id); Source source = BeanFactory.get().create(Source.class); // act source.Target().set(Link.of(target)); // assert - assertThat(source.Target().get().getId()).isEqualTo(id.withoutRevision()); + assertThat(source.Target().get().getId()).isEqualTo(id); assertThat(source.Target().get().getTarget()).isSameAs(target); } diff --git a/core/src/main/java/io/github/mmm/entity/id/AbstractId.java b/core/src/main/java/io/github/mmm/entity/id/AbstractId.java index 3475e64..ec5e5b1 100644 --- a/core/src/main/java/io/github/mmm/entity/id/AbstractId.java +++ b/core/src/main/java/io/github/mmm/entity/id/AbstractId.java @@ -10,28 +10,27 @@ * @param type of the identified entity. * @param

type of the {@link #getPk() primary key}. * @param type of the {@link #getRevision() revision}. + * @param type of this class itself for fluent API calls. * @since 1.0.0 */ -public abstract class AbstractId> implements GenericId { - - /** @see #getEntityClass() */ - protected final Class entityClass; +public abstract class AbstractId, SELF extends AbstractId> + implements GenericId { /** * The constructor. - * - * @param entityType - see {@link #getEntityClass()}. */ - public AbstractId(Class entityType) { + protected AbstractId() { super(); - this.entityClass = entityType; } - @Override - public final Class getEntityClass() { + /** + * @return this instance itself as {@code }. + */ + @SuppressWarnings("unchecked") + protected SELF self() { - return this.entityClass; + return (SELF) this; } @Override @@ -49,14 +48,15 @@ public final boolean equals(Object obj) { if ((obj == null) || !(obj instanceof AbstractId)) { return false; } - AbstractId other = (AbstractId) obj; + AbstractId other = (AbstractId) obj; if (!Objects.equals(getPk(), other.getPk())) { return false; - } - if ((this.entityClass != null) && (other.entityClass != null) && !this.entityClass.equals(other.entityClass)) { + } else if (!Objects.equals(getRevision(), other.getRevision())) { return false; } - if (!Objects.equals(getRevision(), other.getRevision())) { + Class entityClass = getEntityClass(); + Class otherEntityClass = other.getEntityClass(); + if ((entityClass != null) && (otherEntityClass != null) && !entityClass.equals(otherEntityClass)) { return false; } return true; diff --git a/core/src/main/java/io/github/mmm/entity/id/AbstractRevisionlessId.java b/core/src/main/java/io/github/mmm/entity/id/AbstractRevisionlessId.java deleted file mode 100644 index d3b7afc..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/AbstractRevisionlessId.java +++ /dev/null @@ -1,62 +0,0 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id; - -/** - * An abstract base implementation of {@link Id} that has no {@link #getRevision() revision} and will neither support - * optimistic locking nor reading audit trails. - * - * @param type of the identified entity. - * @param

type of the {@link #getPk() primary key}. - * - * @since 1.0.0 - */ -public abstract class AbstractRevisionlessId extends AbstractId { - - /** - * The constructor. - * - * @param type - see {@link #getEntityClass()}. - */ - public AbstractRevisionlessId(Class type) { - - super(type); - } - - @Override - public NoRevision getRevision() { - - return null; - } - - @Override - public boolean hasRevisionField() { - - return false; - } - - @Override - public Class getRevisionType() { - - return NoRevision.class; - } - - @Override - public NoRevision parseRevision(String revisionString) { - - return null; - } - - @Override - public NoRevision updateRevision(NoRevision currentRevision) { - - return null; - } - - @Override - public String getMarshalPropertyRevision() { - - return null; - } - -} diff --git a/core/src/main/java/io/github/mmm/entity/id/FkMapper.java b/core/src/main/java/io/github/mmm/entity/id/FkMapper.java index 16a7a8e..dae88a1 100644 --- a/core/src/main/java/io/github/mmm/entity/id/FkMapper.java +++ b/core/src/main/java/io/github/mmm/entity/id/FkMapper.java @@ -13,7 +13,7 @@ @SuppressWarnings({ "unchecked", "rawtypes" }) public class FkMapper extends AtomicTypeMapper { - private static final FkMapper DEFAULT = new FkMapper(LongVersionId.getEmpty()); + private static final FkMapper DEFAULT = new FkMapper(RevisionedIdVersion.DEFAULT); private final GenericId idTemplate; diff --git a/core/src/main/java/io/github/mmm/entity/id/GenericId.java b/core/src/main/java/io/github/mmm/entity/id/GenericId.java index 83efec5..f1f76ed 100644 --- a/core/src/main/java/io/github/mmm/entity/id/GenericId.java +++ b/core/src/main/java/io/github/mmm/entity/id/GenericId.java @@ -1,7 +1,5 @@ package io.github.mmm.entity.id; -import java.util.Objects; - import io.github.mmm.marshall.MarshallableObject; import io.github.mmm.marshall.StructuredReader; import io.github.mmm.marshall.StructuredWriter; @@ -16,54 +14,48 @@ * @param type of the identified {@link io.github.mmm.entity.Entity}. * @param

type of the {@link #getPk() primary key}. * @param type of the {@link #getRevision() revision}. + * @param type of this class itself for fluent API calls. * @since 1.0.0 * @see AbstractId */ -public interface GenericId> - extends Id, IdFactory, MarshallableObject, Unmarshaller>, StructuredIdMappingObject { +public interface GenericId, SELF extends GenericId> + extends Id, IdFactory, MarshallableObject, Unmarshaller, StructuredIdMappingObject { /** * Name of the {@link #getPk() ID} property (e.g. for JSON or XML) in case of a {@link Long}. * - * @see LongInstantId - * @see LongVersionId + * @see PkIdLong */ - String PROPERTY_LONG_ID = "l"; + String PROPERTY_PK_LONG = "l"; /** * Name of the {@link #getPk() ID} property (e.g. for JSON or XML) in case of a {@link String}. * - * @see StringInstantId - * @see StringVersionId + * @see PkIdString */ - String PROPERTY_STRING_ID = "s"; + String PROPERTY_PK_STRING = "s"; /** * Name of the {@link #getPk() ID} property (e.g. for JSON or XML) in case of a {@link java.util.UUID}. * - * @see UuidInstantId - * @see UuidVersionId + * @see PkIdUuid */ - String PROPERTY_UUID = "u"; + String PROPERTY_PK_UUID = "u"; /** * Name of the {@link #getRevision() revision} property (e.g. for JSON or XML) in case of a {@link Long}. * - * @see LongVersionId - * @see UuidVersionId - * @see StringVersionId + * @see RevisionedIdVersion */ - String PROPERTY_LONG_REVISION = "v"; + String PROPERTY_REVISION_VERSION = "v"; /** * Name of the {@link #getRevision() revision} property (e.g. for JSON or XML) in case of an {@link java.time.Instant} * ("t" for "timestamp"). * - * @see LongInstantId - * @see UuidInstantId - * @see StringInstantId + * @see RevisionedIdInstant */ - String PROPERTY_INSTANT_REVISION = "t"; + String PROPERTY_REVISION_INSTANT = "t"; @Override P getPk(); @@ -76,7 +68,7 @@ public interface GenericId> /** * @return {@code true} if this {@link Id} implementation has a field to store the {@link #getRevision() revision}, - * {@code false} otherwise (see {@link NoRevision} and {@link AbstractRevisionlessId}). + * {@code false} otherwise (see {@link PkId} and {@link #withoutRevision()}). */ boolean hasRevisionField(); @@ -90,42 +82,27 @@ public interface GenericId> * @param valueString the {@link #toString() string representation} of this {@link GenericId}. * @return the parsed {@link GenericId}. */ - default GenericId create(String valueString) { + default GenericId create(String valueString) { return create(getEntityClass(), valueString); } @Override - default GenericId withoutRevision() { - - return withRevision(null); - } + PkId withoutRevision(); /** * @param newRevision the new value of {@link #getRevision() revision}. * @return a copy of this {@link Id} with the given {@link #getRevision() revision} or this {@link Id} itself if * already satisfying. */ - default GenericId withRevision(R newRevision) { - - if (Objects.equals(getRevision(), newRevision)) { - return this; - } - return create(getEntityClass(), getPk(), newRevision); - } + SELF withRevision(R newRevision); /** * @param newPk the new value of {@link #getPk() primary key}. * @return a copy of this {@link Id} with the given {@link #getPk() primary key} or this {@link Id} itself if already * satisfying. */ - default GenericId withPk(P newPk) { - - if (Objects.equals(getPk(), newPk)) { - return this; - } - return create(getEntityClass(), newPk, getRevision()); - } + SELF withPk(P newPk); /** * @param newPk the new value of {@link #getPk() primary key}. @@ -133,45 +110,13 @@ default GenericId withPk(P newPk) { * @return a copy of this {@link Id} with the given {@link #getPk() primary key} and {@link #getRevision() revision} * or this {@link Id} itself if already satisfying. */ - default GenericId withPkAndRevision(P newPk, R newRevision) { - - if (Objects.equals(getRevision(), newRevision) && Objects.equals(getPk(), newPk)) { - return this; - } - return create(getEntityClass(), newPk, newRevision); - } + SELF withPkAndRevision(P newPk, R newRevision); - @SuppressWarnings("unchecked") @Override - default GenericId withEntityType(Class newEntityType) { - - Class entityType = getEntityClass(); - if (entityType == null) { - return create(newEntityType, getPk(), getRevision()); - } else if (entityType != newEntityType) { - throw new IllegalArgumentException( - "Illegal type " + newEntityType + " - already typed to " + entityType.getName() + " at " + toString()); - } - return (GenericId) this; - } - - /** - * ATTENTION: This method is designed to ensure and verify the expected {@link #getEntityClass() type}. It will - * fail if a different type is already assigned. - * - * @param newEntityType the new value of {@link #getEntityClass()}. Exact type should actually be - * {@link Class}{@literal } but this prevents simple generic usage. As the {@link #getEntityClass() type} - * can not actually be changed with this method, this should be fine. - * @return a copy of this {@link Id} with the given {@link #getEntityClass() type} or this {@link Id} itself if - * already satisfying. - * @throws IllegalArgumentException if this {@link Id} already has a different {@link #getEntityClass() type} - * assigned. - */ - @SuppressWarnings("unchecked") - default GenericId withEntityTypeGeneric(Class newEntityType) { + GenericId withEntityTypeGeneric(Class newEntityType); - return (GenericId) withEntityType(newEntityType); - } + @Override + SELF withEntityType(Class newEntityType); /** * @param currentRevision the current {@link #getRevision() revision}. @@ -183,7 +128,7 @@ default GenericId withEntityTypeGeneric(Class newEntityType) { * @return a new {@link GenericId} with an {@link #updateRevision(Comparable) updated} {@link #getRevision() * revision}. */ - default GenericId updateRevision() { + default SELF updateRevision() { R newRevision = updateRevision(getRevision()); return withRevision(newRevision); @@ -191,16 +136,16 @@ default GenericId updateRevision() { /** * @return the property name of the {@link #getPk() id} for marshalling. - * @see #PROPERTY_LONG_ID - * @see #PROPERTY_UUID - * @see #PROPERTY_STRING_ID + * @see #PROPERTY_PK_LONG + * @see #PROPERTY_PK_UUID + * @see #PROPERTY_PK_STRING */ String getMarshalPropertyId(); /** * @return the property name of the {@link #getRevision() revision} for marshalling. - * @see #PROPERTY_LONG_REVISION - * @see #PROPERTY_INSTANT_REVISION + * @see #PROPERTY_REVISION_VERSION + * @see #PROPERTY_REVISION_INSTANT */ String getMarshalPropertyRevision(); @@ -222,10 +167,11 @@ default void write(StructuredWriter writer) { } } + @SuppressWarnings("unchecked") @Override - default GenericId readObject(StructuredReader reader) { + default SELF readObject(StructuredReader reader) { - return IdMarshalling.readObject(reader, this, getEntityClass()); + return (SELF) IdMarshalling.readObject(reader, this, getEntityClass()); } /** @@ -235,7 +181,7 @@ default GenericId readObject(StructuredReader reader) { */ public static Id updateRevision(Id id) { - return ((GenericId) id).updateRevision(); + return ((GenericId) id).updateRevision(); } @Override @@ -243,12 +189,12 @@ default StructuredIdMapping defineIdMapping() { StructuredIdMappingMap map = StructuredIdMappingMap.of(10); // primary keys - map.put(1, PROPERTY_LONG_ID); - map.put(2, PROPERTY_UUID); - map.put(3, PROPERTY_STRING_ID); + map.put(1, PROPERTY_PK_LONG); + map.put(2, PROPERTY_PK_UUID); + map.put(3, PROPERTY_PK_STRING); // revisions - map.put(8, PROPERTY_LONG_REVISION); - map.put(9, PROPERTY_INSTANT_REVISION); + map.put(8, PROPERTY_REVISION_VERSION); + map.put(9, PROPERTY_REVISION_INSTANT); return map; } diff --git a/core/src/main/java/io/github/mmm/entity/id/GenericIdFactory.java b/core/src/main/java/io/github/mmm/entity/id/GenericIdFactory.java index da0b642..f87d845 100644 --- a/core/src/main/java/io/github/mmm/entity/id/GenericIdFactory.java +++ b/core/src/main/java/io/github/mmm/entity/id/GenericIdFactory.java @@ -19,25 +19,15 @@ final class GenericIdFactory implements IdFactory> { private static final Map, GenericId> EMPTY_ID_MAP = new HashMap<>(); static { - EMPTY_ID_MAP.put(null, LongVersionId.getEmpty()); - EMPTY_ID_MAP.put(Id.class, LongVersionId.getEmpty()); - EMPTY_ID_MAP.put(GenericId.class, LongVersionId.getEmpty()); - EMPTY_ID_MAP.put(AbstractId.class, LongVersionId.getEmpty()); - EMPTY_ID_MAP.put(LongId.class, LongVersionId.getEmpty()); - EMPTY_ID_MAP.put(AbstractVersionId.class, LongVersionId.getEmpty()); - EMPTY_ID_MAP.put(AbstractRevisionlessId.class, LongRevisionlessId.getEmpty()); - EMPTY_ID_MAP.put(LongRevisionlessId.class, LongRevisionlessId.getEmpty()); - EMPTY_ID_MAP.put(LongVersionId.class, LongVersionId.getEmpty()); - EMPTY_ID_MAP.put(AbstractInstantId.class, LongInstantId.getEmpty()); - EMPTY_ID_MAP.put(LongInstantId.class, LongInstantId.getEmpty()); - EMPTY_ID_MAP.put(StringId.class, StringVersionId.getEmpty()); - EMPTY_ID_MAP.put(StringVersionId.class, StringVersionId.getEmpty()); - EMPTY_ID_MAP.put(StringInstantId.class, StringInstantId.getEmpty()); - EMPTY_ID_MAP.put(StringRevisionlessId.class, StringRevisionlessId.getEmpty()); - EMPTY_ID_MAP.put(UuidId.class, UuidVersionId.getEmpty()); - EMPTY_ID_MAP.put(UuidVersionId.class, UuidVersionId.getEmpty()); - EMPTY_ID_MAP.put(UuidInstantId.class, UuidInstantId.getEmpty()); - EMPTY_ID_MAP.put(UuidRevisionlessId.class, UuidRevisionlessId.getEmpty()); + EMPTY_ID_MAP.put(null, PkIdLong.getEmpty()); + EMPTY_ID_MAP.put(Id.class, PkIdLong.getEmpty()); + EMPTY_ID_MAP.put(GenericId.class, PkIdLong.getEmpty()); + EMPTY_ID_MAP.put(AbstractId.class, PkIdLong.getEmpty()); + EMPTY_ID_MAP.put(PkIdLong.class, PkIdLong.getEmpty()); + EMPTY_ID_MAP.put(PkId.class, PkIdLong.getEmpty()); + EMPTY_ID_MAP.put(PkIdLong.class, PkIdLong.getEmpty()); + EMPTY_ID_MAP.put(PkIdString.class, PkIdString.getEmpty()); + EMPTY_ID_MAP.put(PkIdUuid.class, PkIdUuid.getEmpty()); } static final GenericIdFactory INSTANCE = new GenericIdFactory(); @@ -48,27 +38,17 @@ private GenericIdFactory() { @SuppressWarnings({ "unchecked", "rawtypes" }) @Override - public GenericId> create(Class entityType, Object pk, Comparable revision) { + public GenericId, ?> create(Class entityClass, Object pk, Comparable revision) { - GenericId result; if (revision instanceof Integer i) { revision = Long.valueOf(i.longValue()); } - if (pk == null) { + PkId pkId = PkId.of(entityClass, pk); + if (pkId == null) { assert (revision == null); return null; - } else if (pk instanceof Long l) { - result = LongId.of(l, entityType, revision); - } else if (pk instanceof UUID uuid) { - result = UuidId.of(uuid, entityType, revision); - } else if (pk instanceof String string) { - result = StringId.of(string, entityType, revision); - } else if (pk instanceof Integer i) { - result = LongId.of(Long.valueOf(i.longValue()), entityType, revision); - } else { - throw new IllegalStateException("Unsupported ID type: " + pk.getClass().getName()); } - return result; + return pkId.withRevisionGeneric(revision); } @Override @@ -93,12 +73,11 @@ public Comparable parseRevision(String revisionString) { } @SuppressWarnings("rawtypes") - static GenericId empty(Class entityType, Class pkClass) { + static GenericId empty(Class entityType, Class idClass) { - GenericId empty = EMPTY_ID_MAP.get(pkClass); + GenericId empty = EMPTY_ID_MAP.get(idClass); if (empty == null) { - assert false : "" + pkClass; - empty = EMPTY_ID_MAP.get(pkClass); + throw new IllegalStateException(idClass.getName()); } return empty.withEntityType(entityType); } diff --git a/core/src/main/java/io/github/mmm/entity/id/Id.java b/core/src/main/java/io/github/mmm/entity/id/Id.java index 8ff42a3..c48d852 100644 --- a/core/src/main/java/io/github/mmm/entity/id/Id.java +++ b/core/src/main/java/io/github/mmm/entity/id/Id.java @@ -2,7 +2,7 @@ * http://www.apache.org/licenses/LICENSE-2.0 */ package io.github.mmm.entity.id; -import java.util.UUID; +import java.time.Instant; import java.util.function.Supplier; import io.github.mmm.entity.Entity; @@ -12,17 +12,19 @@ * {@link #getEntityClass() type} ({@code }).
* An {@link Id} has the following properties: *

    - *
  • {@link #getPk() primary-key} - the primary key that identifies the entity and is unique for a specific - * {@link #getEntityClass() type}. As a best practice it is recommended to make the primary-key even unique for all - * entities of a database.
  • - *
  • {@link #getRevision() revision} - the optional revision of the entity.
  • - *
  • {@link #getEntityClass() type} - is the type of the identified entity.
  • + *
  • {@link #getPk() primary-key} - identifies the entity and is unique for a specific {@link #getEntityClass() entity + * class}. As a best practice it is recommended to make the primary-key even unique for all entities of a database. We + * provide out-of-the-box support for the types {@link Long}, {@link java.util.UUID}, and {@link String} as + * {@link #getPk() primary-key}.
  • + *
  • {@link #getRevision() revision} - optional revision of the entity. We support out-of-the-box support for the + * types {@link Long} (sequential modification counter) and {@link Instant} (modification timestamp).
  • + *
  • {@link #getEntityClass() entity class} - reflects the identified entity.
  • *
- * Just like the {@link #getPk() primary key} the {@link #getEntityClass() type} of an object does not change. In an - * {@link Entity} is updated its {@link #getRevision() revision} may change. In this case a new {@link Id} instance is - * created and assigned since {@link Id} is designed as an immutable datatype. To allow using {@link Id} as globally - * unique identifier for its corresponding {@link Entity}, regular methods (e.g. {@code EntityRepository.findById} will - * ignore the {@link #getRevision() revision}).
+ * Just like the {@link #getPk() primary key} the {@link #getEntityClass() entity class} of an object does not change. + * If an {@link Entity} is updated its {@link #getRevision() revision} may change. In this case a new {@link Id} + * instance is created and assigned since {@link Id} is designed as an immutable datatype. To allow using {@link Id} as + * globally unique identifier for its corresponding {@link Entity}, regular methods (e.g. + * {@code EntityRepository.findById} will ignore the {@link #getRevision() revision}).
* Unlike regular JPA design this {@link Id} approach gives you a lot of simplicity without loosing any flexibility. So * your {@code EntityBean} and {@code EntityRepository} does not need any generic type for the primary key and * refactoring the type of the primary key or revision will have minimal impact on your entire code-base.
@@ -37,7 +39,7 @@ * complexity and generic types for frameworks. Regular API users only need to use this {@link Id} interface that hides * some complexity.
* In case you need to create an {@link Id} from its primary key e.g. in test-code, you may want to use static factory - * methods such as {@link LongId#of(Long)}, {@link UuidId#of(UUID)}, {@link StringId#of(String)}, etc. + * methods such as {@link #of(Class, Object)}. * * @param the {@link #getEntityClass() entity type}. * @@ -46,8 +48,8 @@ */ public interface Id extends Supplier { - /** Marshalling property name of the {@link #getPk() id}. */ - String PROPERTY_ID = "id"; + /** Marshalling property name of the {@link #getPk() primary-key}. */ + String PROPERTY_PK = "pk"; /** Marshalling property name of the {@link #getRevision() revision}. */ String PROPERTY_REVISION = "rev"; @@ -65,12 +67,13 @@ public interface Id extends Supplier { char REVISION_SEPARATOR = '@'; /** - * @see LongVersionId - * @see UuidVersionId - * @see StringVersionId + * @see PkIdLong + * @see PkIdUuid + * @see PkIdString * * @return the primary key of the identified {@link Entity} as {@link Object} value. It may only be unique - * for a particular {@link #getEntityClass() type} of an entity (unless {@link UUID} is used). + * for a particular {@link #getEntityClass() entity class}. However for {@link java.util.UUID} it should + * always be globally unique. */ Object getPk(); @@ -122,11 +125,15 @@ default boolean isEmpty() { /** * @return the {@code revision} of this entity. Whenever the {@link io.github.mmm.entity.Entity} gets updated (a - * modification is saved and the transaction is committed), this revision is increased. Typically the revision - * is a {@link Number} starting with {@link AbstractVersionId#INSERT_REVISION 0} for a new - * {@link io.github.mmm.entity.Entity} that is increased whenever a modification is committed. However, it may - * also be an {@link java.time.Instant}. The revision acts as a modification sequence for optimistic locking. - * On each update it will be verified that the revision has not been increased already by another transaction. + * modification is saved and the transaction is committed), this revision is increased. It then acts as a + * modification sequence for {@link OptimisicLockException optimistic locking}. On each update it will be + * verified that the revision has not been increased already by another transaction.
+ *
+ * By default the revision is a {@link Long} starting with {@link RevisionedIdVersion#INSERT_REVISION 0} for a + * new {@link io.github.mmm.entity.Entity} and is increased whenever a modification is committed. However, it + * may also be an {@link java.time.Instant} set to the {@link Instant#now() current timestamp} on insert or + * update.
+ *
* When linking an {@link io.github.mmm.entity.Entity} ({@link Id} used as foreign key) the revision can act * as version identifier for auditing. If it is {@code null} it points to the latest revision of the * {@link io.github.mmm.entity.Entity}. Otherwise it points to a specific historic revision of the @@ -151,6 +158,18 @@ default String getRevisionAsString() { return revision.toString(); } + /** + * ATTENTION: This method is designed to ensure and verify the expected {@link #getEntityClass() entity class}. + * It will fail if a different type is already assigned. + * + * @param entityClass the new value of {@link #getEntityClass()}. + * @return a copy of this {@link Id} with the given {@link #getEntityClass() entity class} or this {@link Id} itself + * if already satisfying. + * @throws IllegalArgumentException if this {@link Id} already has a different {@link #getEntityClass() entity class} + * assigned. + */ + Id withEntityType(Class entityClass); + /** * ATTENTION: This method is designed to ensure and verify the expected {@link #getEntityClass() type}. It will * fail if a different type is already assigned. It shall be used to cast from {@code Id} to an {@link Id} with a @@ -171,15 +190,14 @@ default String getRevisionAsString() { * @throws IllegalArgumentException if this {@link Id} already has a different {@link #getEntityClass() type} * assigned. */ - Id withEntityType(Class newEntityType); + Id withEntityTypeGeneric(Class newEntityType); /** * @return {@code true} if this {@link Id} is transient if used as {@link Entity#getId() primary key}, {@code false} * otherwise. Here transient means that the {@link Entity#getId() owning} {@link Entity} has never been saved * to a persistent store yet. Otherwise the {@link Entity} is persistent and was received from a persistent * store. Please note that the existence of its {@link #getPk() primary key} is not sufficient as it may be - * assigned already in transient state (e.g. for {@link UuidId} or also for {@link LongId} using negative TX - * local values). + * assigned already in transient state (e.g. for {@link java.util.UUID}). */ default boolean isTransient() { @@ -211,7 +229,7 @@ static Id from(E entity) { } Id id = (Id) entity.getId(); if (id.getEntityClass() == null) { - id = (Id) id.withEntityType(entity.getJavaClass()); + id = (Id) id.withEntityTypeGeneric(entity.getJavaClass()); } return id; } @@ -224,21 +242,11 @@ static Id from(E entity) { * @param type the {@link #getEntityClass() entityClass} * @param pk the {@link #getPk() primary key}. * @return the {@link Id#withoutRevision() revision-less} {@link Id} for the given arguments. + * @see PkId#of(Class, Object) */ - static Id of(Class type, Object pk) { + static Id of(Class type, Object pk) { - if (pk == null) { - return null; - } - if (pk instanceof Long l) { - return new LongRevisionlessId<>(type, l); - } else if (pk instanceof UUID u) { - return new UuidRevisionlessId<>(type, u); - } else if (pk instanceof String s) { - return new StringRevisionlessId<>(type, s); - } else { - throw new IllegalArgumentException("Unsupported primary key type " + pk.getClass().getName()); - } + return PkId.of(type, pk); } } diff --git a/core/src/main/java/io/github/mmm/entity/id/IdFactory.java b/core/src/main/java/io/github/mmm/entity/id/IdFactory.java index 299ab29..2b4ebd0 100644 --- a/core/src/main/java/io/github/mmm/entity/id/IdFactory.java +++ b/core/src/main/java/io/github/mmm/entity/id/IdFactory.java @@ -19,7 +19,7 @@ public interface IdFactory> { * @param revision the {@link Id#getRevision() revision}. May be {@code null}. * @return the {@link Id} for the given values. */ - GenericId create(Class entityType, P pk, R revision); + GenericId create(Class entityType, P pk, R revision); /** * @param type of the identified entity. @@ -27,7 +27,7 @@ public interface IdFactory> { * @param pkString the {@link Id#getAsString() primary key as string}. * @return the parsed {@link Id}. */ - default GenericId create(Class entityType, String pkString) { + default GenericId create(Class entityType, String pkString) { String id = null; String revision = null; @@ -51,7 +51,7 @@ default GenericId create(Class entityType, String pkString) { * @return the new {@link AbstractId} for the given values. */ @SuppressWarnings("unchecked") - default GenericId createGeneric(Class entityType, Object pk, Object revision) { + default GenericId createGeneric(Class entityType, Object pk, Object revision) { if (pk instanceof String) { pk = parsePk((String) pk); @@ -82,7 +82,7 @@ default GenericId createGeneric(Class entityType, Object pk, Obj * @return the {@link Id#isEmpty() empty} {@link GenericId} instance. */ @SuppressWarnings("rawtypes") - default GenericId createEmpty(Class entityType, Class pkClass) { + default GenericId createEmpty(Class entityType, Class pkClass) { return GenericIdFactory.empty(entityType, pkClass); } diff --git a/core/src/main/java/io/github/mmm/entity/id/IdMarshalling.java b/core/src/main/java/io/github/mmm/entity/id/IdMarshalling.java index 18800d3..8941297 100644 --- a/core/src/main/java/io/github/mmm/entity/id/IdMarshalling.java +++ b/core/src/main/java/io/github/mmm/entity/id/IdMarshalling.java @@ -21,7 +21,7 @@ default void writeObject(StructuredWriter writer, Id id) { writer.writeValueAsNull(); return; } - ((GenericId) id).write(writer); + ((GenericId) id).write(writer); } @Override @@ -50,24 +50,24 @@ default Id readObject(StructuredReader reader, Class type) { * @param entityType the {@link GenericId#getEntityClass() entity type}. * @return the unmarshalled {@link GenericId}. */ - static > GenericId readObject(StructuredReader reader, IdFactory factory, - Class entityType) { + static > GenericId readObject(StructuredReader reader, + IdFactory factory, Class entityType) { - Object id = null; + Object pk = null; Object revision = null; try { - if (reader.readStartObject(LongVersionId.getEmpty())) { + if (reader.readStartObject(RevisionedIdVersion.DEFAULT)) { while (!reader.readEnd()) { String name = reader.readName(); - if (GenericId.PROPERTY_LONG_ID.equals(name)) { - id = update(id, reader.readValueAsLong(), Id.PROPERTY_ID); - } else if (GenericId.PROPERTY_UUID.equals(name)) { - id = update(id, UuidParser.get().parse(reader.readValueAsString()), Id.PROPERTY_ID); - } else if (GenericId.PROPERTY_STRING_ID.equals(name)) { - id = update(id, reader.readValueAsString(), Id.PROPERTY_ID); - } else if (GenericId.PROPERTY_LONG_REVISION.equals(name)) { + if (GenericId.PROPERTY_PK_LONG.equals(name)) { + pk = update(pk, reader.readValueAsLong(), Id.PROPERTY_PK); + } else if (GenericId.PROPERTY_PK_UUID.equals(name)) { + pk = update(pk, UuidParser.get().parse(reader.readValueAsString()), Id.PROPERTY_PK); + } else if (GenericId.PROPERTY_PK_STRING.equals(name)) { + pk = update(pk, reader.readValueAsString(), Id.PROPERTY_PK); + } else if (GenericId.PROPERTY_REVISION_VERSION.equals(name)) { revision = update(revision, reader.readValueAsLong(), Id.PROPERTY_REVISION); - } else if (GenericId.PROPERTY_INSTANT_REVISION.equals(name)) { + } else if (GenericId.PROPERTY_REVISION_INSTANT.equals(name)) { revision = update(revision, reader.readValueAsInstant(), Id.PROPERTY_REVISION); } else { // ignore unknown property for compatibility and future extensions... @@ -75,15 +75,15 @@ static > GenericId readObject(StructuredR } } else { if (reader.isStringValue()) { - id = reader.readValueAsString(); + pk = reader.readValueAsString(); } else { - id = reader.readValueAsLong(); + pk = reader.readValueAsLong(); } } } catch (Exception e) { throw new IllegalArgumentException("Failed to parse Id.", e); } - return factory.createGeneric(entityType, id, revision); + return factory.createGeneric(entityType, pk, revision); } private static Object update(Object oldValue, Object newValue, String key) { diff --git a/core/src/main/java/io/github/mmm/entity/id/LongId.java b/core/src/main/java/io/github/mmm/entity/id/LongId.java deleted file mode 100644 index d34dbb7..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/LongId.java +++ /dev/null @@ -1,131 +0,0 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id; - -import java.time.Instant; - -/** - * {@link Id} using {@link Long} as {@link #getPk() primary key (ID)}. - * - * @param type of the identified entity. - * @param type of the {@link #getRevision() revision}. - * @since 1.0.0 - */ -public interface LongId> extends GenericId { - - @Override - Long getPk(); - - @Override - default Class getPkClass() { - - return Long.class; - } - - /** - * @return the {@link #getPk() primary key} as primitve {@code long} value. - */ - default long getPkAsLong() { - - Long id = getPk(); - if (id == null) { - return -1; - } - return id.longValue(); - } - - @Override - default Long parsePk(String idString) { - - return Long.valueOf(idString); - } - - @Override - default String getMarshalPropertyId() { - - return PROPERTY_LONG_ID; - } - - @Override - default LongId withEntityType(Class newEntityType) { - - return (LongId) GenericId.super.withEntityType(newEntityType); - } - - @Override - default LongId withEntityTypeGeneric(Class newEntityType) { - - return (LongId) GenericId.super.withEntityTypeGeneric(newEntityType); - } - - @Override - default LongId withPkAndRevision(Long newId, V newRevision) { - - return (LongId) GenericId.super.withPkAndRevision(newId, newRevision); - } - - @Override - default LongId withRevision(V newRevision) { - - return (LongId) GenericId.super.withRevision(newRevision); - } - - @Override - default LongId withoutRevision() { - - return new LongRevisionlessId<>(getEntityClass(), getPk()); - } - - @Override - default LongId updateRevision() { - - return (LongId) GenericId.super.updateRevision(); - } - - /** - * @param type of the referenced entity. - * @param pk the actual {@link #getPk() primary key}. - * @return the {@link LongId}. - */ - static LongId of(Long pk) { - - return of(pk, null); - } - - /** - * @param type of the referenced entity. - * @param pk the actual {@link #getPk() primary key}. - * @param type the {@link #getEntityClass() entity type}. - * @return the {@link LongId}. - */ - static LongId of(Long pk, Class type) { - - if (pk == null) { - return null; - } - return new LongRevisionlessId<>(type, pk); - } - - /** - * @param the generic type of the identified entity. - * @param pk the actual {@link #getPk() primary key}. - * @param type the {@link #getEntityClass() entity type}. - * @param revision the optional {@link #getRevision() revision}. - * @return the new {@link LongId}. - */ - static LongId of(Long pk, Class type, Object revision) { - - if (pk == null) { - return null; - } - if (revision == null) { - return new LongRevisionlessId<>(type, pk); - } else if (revision instanceof Long l) { - return new LongVersionId<>(type, pk, l); - } else if (revision instanceof Instant i) { - return new LongInstantId<>(type, pk, i); - } - throw new IllegalStateException("Unsupported revision type: " + revision.getClass().getName()); - } - -} diff --git a/core/src/main/java/io/github/mmm/entity/id/LongInstantId.java b/core/src/main/java/io/github/mmm/entity/id/LongInstantId.java deleted file mode 100644 index c6d5c72..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/LongInstantId.java +++ /dev/null @@ -1,111 +0,0 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id; - -import java.time.Instant; -import java.util.Objects; - -/** - * Implementation of {@link AbstractInstantId} as {@link LongId}. - * - * @param type of the identified entity. - * @since 1.0.0 - */ -public final class LongInstantId extends AbstractInstantId implements LongId { - - @SuppressWarnings("rawtypes") - private static final LongInstantId EMPTY = new LongInstantId<>(null, null, null); - - private final Long pk; - - /** - * The constructor. - * - * @param type the {@link #getEntityClass() type}. - * @param pk the {@link #getPk() primary key}. See {@link #getPkAsLong()}. - * @param revision the {@link #getRevision() revision}. - */ - public LongInstantId(Class type, Long pk, Instant revision) { - - super(type, revision); - this.pk = pk; - } - - @Override - public Long getPk() { - - return this.pk; - } - - @Override - public LongInstantId create(Class newEntityType, Long newId, Instant newRevision) { - - return new LongInstantId<>(newEntityType, newId, newRevision); - } - - @Override - public LongInstantId withPk(Long newPk) { - - if (Objects.equals(this.pk, newPk)) { - return this; - } - return create(this.entityClass, newPk, this.revision); - } - - @Override - public LongInstantId withPkAndRevision(Long newPk, Instant newRevision) { - - if (Objects.equals(this.revision, newRevision) && Objects.equals(this.pk, newPk)) { - return this; - } - return create(this.entityClass, newPk, newRevision); - } - - @Override - public LongInstantId withRevision(Instant newRevision) { - - if (Objects.equals(this.revision, newRevision)) { - return this; - } - return create(this.entityClass, this.pk, newRevision); - } - - @Override - public LongInstantId withEntityType(Class newEntityType) { - - return (LongInstantId) super.withEntityType(newEntityType); - } - - @Override - public LongInstantId withEntityTypeGeneric(Class newEntityType) { - - return (LongInstantId) super.withEntityTypeGeneric(newEntityType); - } - - @Override - public LongInstantId updateRevision() { - - Instant newRevision = updateRevision(this.revision); - return withRevision(newRevision); - } - - /** - * @param type of the identified entity. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static LongInstantId getEmpty() { - - return EMPTY; - } - - /** - * @param type of the identified entity. - * @param entityType the {@link #getEntityClass() entity type}. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static LongInstantId getEmpty(Class entityType) { - - return getEmpty().withEntityType(entityType); - } - -} diff --git a/core/src/main/java/io/github/mmm/entity/id/LongRevisionlessId.java b/core/src/main/java/io/github/mmm/entity/id/LongRevisionlessId.java deleted file mode 100644 index f4d67a4..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/LongRevisionlessId.java +++ /dev/null @@ -1,110 +0,0 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id; - -import java.util.Objects; - -/** - * Implementation of {@link AbstractRevisionlessId} using {@link Long} as {@link #getPk() primary key}. - * - * @param type of the identified entity. - * @since 1.0.0 - */ -public final class LongRevisionlessId extends AbstractRevisionlessId implements LongId { - - @SuppressWarnings("rawtypes") - private static final LongRevisionlessId EMPTY = new LongRevisionlessId<>(null, null); - - private final Long pk; - - /** - * The constructor. - * - * @param type the {@link #getEntityClass() type}. - * @param pk the {@link #getPk() primary key}. See {@link #getPkAsLong()}. - */ - public LongRevisionlessId(Class type, Long pk) { - - super(type); - this.pk = pk; - } - - @Override - public Long getPk() { - - return this.pk; - } - - @Override - public LongRevisionlessId create(Class newEntityType, Long newId, NoRevision newRevision) { - - return new LongRevisionlessId<>(newEntityType, newId); - } - - @Override - public LongRevisionlessId withPk(Long newPk) { - - if (Objects.equals(this.pk, newPk)) { - return this; - } - return create(this.entityClass, newPk, null); - } - - @Override - public LongRevisionlessId withPkAndRevision(Long newPk, NoRevision newRevision) { - - assert (newRevision == null); - return withPk(newPk); - } - - @Override - public LongRevisionlessId withRevision(NoRevision newRevision) { - - assert (newRevision == null); - return this; - } - - @Override - public LongRevisionlessId withoutRevision() { - - return this; - } - - @Override - public LongRevisionlessId withEntityType(Class newEntityType) { - - return (LongRevisionlessId) super.withEntityType(newEntityType); - } - - @Override - public LongRevisionlessId withEntityTypeGeneric(Class newEntityType) { - - return (LongRevisionlessId) super.withEntityTypeGeneric(newEntityType); - } - - @Override - public LongRevisionlessId updateRevision() { - - return this; - } - - /** - * @param type of the identified entity. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static LongRevisionlessId getEmpty() { - - return EMPTY; - } - - /** - * @param type of the identified entity. - * @param entityType the {@link #getEntityClass() entity type}. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static LongRevisionlessId getEmpty(Class entityType) { - - return getEmpty().withEntityType(entityType); - } - -} diff --git a/core/src/main/java/io/github/mmm/entity/id/LongVersionId.java b/core/src/main/java/io/github/mmm/entity/id/LongVersionId.java deleted file mode 100644 index e852e05..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/LongVersionId.java +++ /dev/null @@ -1,123 +0,0 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id; - -import java.util.Objects; - -/** - * Implementation of {@link AbstractVersionId} as {@link LongId}. - * - * @param the generic type of the identified entity. - * - * @since 1.0.0 - */ -public final class LongVersionId extends AbstractVersionId implements LongId { - - @SuppressWarnings("rawtypes") - private static final LongVersionId EMPTY = new LongVersionId<>(null, null, null); - - private final Long pk; - - /** - * The constructor. - * - * @param type the {@link #getEntityClass() type}. - * @param pk the {@link #getPk() primary key}. See {@link #getPkAsLong()}. - * @param revision the {@link #getRevision() revision}. - */ - public LongVersionId(Class type, Long pk, Long revision) { - - super(type, revision); - this.pk = pk; - } - - @Override - public Long getPk() { - - return this.pk; - } - - @Override - public LongVersionId create(Class newEntityClass, Long newPk, Long newRevision) { - - return new LongVersionId<>(newEntityClass, newPk, newRevision); - } - - @Override - public LongVersionId withPk(Long newPk) { - - if (Objects.equals(this.pk, newPk)) { - return this; - } - return create(this.entityClass, newPk, this.revision); - } - - @Override - public LongVersionId withPkAndRevision(Long newPk, Long newRevision) { - - if (Objects.equals(this.revision, newRevision) && Objects.equals(this.pk, newPk)) { - return this; - } - return create(this.entityClass, newPk, newRevision); - } - - @Override - public LongVersionId withRevision(Long newRevision) { - - if (Objects.equals(this.revision, newRevision)) { - return this; - } - return create(this.entityClass, this.pk, newRevision); - } - - @Override - public LongVersionId withEntityType(Class newEntityType) { - - return (LongVersionId) super.withEntityType(newEntityType); - } - - @Override - public LongVersionId withEntityTypeGeneric(Class newEntityType) { - - return (LongVersionId) super.withEntityTypeGeneric(newEntityType); - } - - @Override - public LongVersionId updateRevision() { - - Long newRevision = updateRevision(this.revision); - return withRevision(newRevision); - } - - /** - * @param type of the identified entity. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static LongVersionId getEmpty() { - - return EMPTY; - } - - /** - * @param type of the identified entity. - * @param type the {@link #getEntityClass() entity type}. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static LongVersionId getEmpty(Class type) { - - return getEmpty().withEntityType(type); - } - - /** - * @param type of the identified entity. - * @param id the {@link #getPk() primary key}. - * @param revision the {@link #getRevision() revision}. - * @param type the {@link #getEntityClass() entity type}. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static LongVersionId of(Long id, Long revision, Class type) { - - return new LongVersionId<>(type, id, revision); - } - -} diff --git a/core/src/main/java/io/github/mmm/entity/id/NoRevision.java b/core/src/main/java/io/github/mmm/entity/id/NoRevision.java deleted file mode 100644 index 9ae6b10..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/NoRevision.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.github.mmm.entity.id; - -/** - * Placeholder for {@link AbstractRevisionlessId#getRevision() revision} of a - * {@link AbstractRevisionlessId#getRevision() revisionless ID}. Since {@link Void} does not implement - * {@link Comparable} it cannot be bound here and we use this class as placeholder instead. - */ -public final class NoRevision implements Comparable { - - private NoRevision() { - - } - - @Override - public int compareTo(NoRevision o) { - - return 0; - } - -} diff --git a/core/src/main/java/io/github/mmm/entity/id/PkId.java b/core/src/main/java/io/github/mmm/entity/id/PkId.java new file mode 100644 index 0000000..1035da1 --- /dev/null +++ b/core/src/main/java/io/github/mmm/entity/id/PkId.java @@ -0,0 +1,210 @@ +/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 */ +package io.github.mmm.entity.id; + +import java.time.Instant; +import java.util.Objects; +import java.util.UUID; + +import io.github.mmm.entity.Entity; + +/** + * An abstract base implementation of {@link Id} that has no {@link #getRevision() revision} and will neither support + * optimistic locking nor reading audit trails. + * + * @param type of the identified entity. + * @param

type of the {@link #getPk() primary key}. + * @param type of this class itself for fluent API calls. + * + * @since 1.0.0 + */ +public abstract class PkId> extends AbstractId, SELF> { + + /** @see #getEntityClass() */ + protected final Class entityClass; + + /** + * The constructor. + * + * @param entityClass - see {@link #getEntityClass()}. + */ + protected PkId(Class entityClass) { + + super(); + this.entityClass = entityClass; + } + + @Override + public final Class getEntityClass() { + + return this.entityClass; + } + + @Override + public Comparable getRevision() { + + return null; + } + + @Override + public boolean hasRevisionField() { + + return false; + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + @Override + public Class> getRevisionType() { + + return (Class) Comparable.class; + } + + @Override + public Comparable parseRevision(String revisionString) { + + return null; + } + + @Override + public Comparable updateRevision(Comparable currentRevision) { + + return null; + } + + @Override + public String getMarshalPropertyRevision() { + + return null; + } + + /** + * @param newEntityClass the {@link #getEntityClass() entity class}. + * @param newPk the {@link #getPk() primary key}. + * @return a new instance of this {@link PkId} implementation class with the given parameters. + */ + protected abstract SELF newId(Class newEntityClass, P newPk); + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public GenericId, ?> create(Class entityType, P pk, Comparable revision) { + + PkId id = newId((Class) entityType, pk); + return id.withRevisionGeneric(revision); + } + + @Override + public SELF withoutRevision() { + + return self(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public PkId withEntityTypeGeneric(Class newEntityClass) { + + return withEntityType((Class) newEntityClass); + } + + @Override + public SELF withEntityType(Class newEntityClass) { + + if (this.entityClass == newEntityClass) { + return self(); + } else if (this.entityClass == null) { + return newId(newEntityClass, getPk()); + } else { + throw new IllegalArgumentException("Illegal type " + newEntityClass.getName() + " - already typed to " + + this.entityClass.getName() + " at " + toString()); + } + } + + @Override + public SELF withPk(P newPk) { + + if (Objects.equals(getPk(), newPk)) { + return self(); + } + return newId(getEntityClass(), newPk); + } + + @Override + public SELF withPkAndRevision(P newPk, Comparable newRevision) { + + if (newRevision != null) { + throw new IllegalArgumentException( + "Cannot change revision type to " + newRevision.getClass().getName() + " - use withRevisionGeneric instead!"); + } + if (Objects.equals(getPk(), newPk)) { + return self(); + } + return newId(getEntityClass(), newPk); + } + + /** + * @deprecated use {@link #withRevisionGeneric(Comparable)} instead. As the return type can change this method would + * break the generic contract. + */ + @Deprecated + @Override + public SELF withRevision(Comparable newRevision) { + + if (newRevision != null) { + throw new IllegalArgumentException( + "You must use withRevisionGeneric instead to avoid violating generic contract."); + } + return self(); + } + + /** + * @param type of the new {@link #getRevision() revision}. + * @param newRevision the new {@link #getRevision() revision}. + * @return a {@link GenericId} that has the given {@link #getRevision() revision} but other attributes from this + * {@link PkId}. Therefore the returned object may be of a different class and not be an instance of + * {@link PkId}. + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public > GenericId withRevisionGeneric(R newRevision) { + + GenericId result; + if (newRevision == null) { + result = this; + } else if (newRevision instanceof Long rev) { + result = new RevisionedIdVersion<>(this, rev); + } else if (newRevision instanceof Instant rev) { + result = new RevisionedIdInstant<>(this, rev); + } else if (newRevision instanceof Integer rev) { + result = new RevisionedIdVersion<>(this, Long.valueOf(rev.longValue())); + } else { + throw new IllegalArgumentException("Unsupported revision type " + newRevision.getClass().getName() + "!"); + } + return result; + } + + /** + * This is a generic convenience method to create a {@link Id#withoutRevision() revision-less} {@link Id} back from + * its {@link #getPk() primary key}. + * + * @param type of {@link Entity}. + * @param type the {@link #getEntityClass() entityClass} + * @param pk the {@link #getPk() primary key}. + * @return the {@link Id#withoutRevision() revision-less} {@link Id} for the given arguments. + */ + public static PkId of(Class type, Object pk) { + + if (pk == null) { + return null; + } + if (pk instanceof Long l) { + return new PkIdLong<>(type, l); + } else if (pk instanceof UUID u) { + return new PkIdUuid<>(type, u); + } else if (pk instanceof String s) { + return new PkIdString<>(type, s); + } else if (pk instanceof Integer i) { + return new PkIdLong<>(type, Long.valueOf(i.longValue())); + } else { + throw new IllegalArgumentException("Unsupported primary key type " + pk.getClass().getName()); + } + } + +} diff --git a/core/src/main/java/io/github/mmm/entity/id/PkIdLong.java b/core/src/main/java/io/github/mmm/entity/id/PkIdLong.java new file mode 100644 index 0000000..b97e6c3 --- /dev/null +++ b/core/src/main/java/io/github/mmm/entity/id/PkIdLong.java @@ -0,0 +1,116 @@ +/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 */ +package io.github.mmm.entity.id; + +/** + * Implementation of {@link PkId} using {@link Long} as type for the {@link #getPk() primary key}. + * + * @param type of the identified entity. + * @since 1.0.0 + */ +public final class PkIdLong extends PkId> { + + @SuppressWarnings("rawtypes") + private static final PkIdLong EMPTY = new PkIdLong<>(null, null); + + private final Long pk; + + /** + * The constructor. + * + * @param type the {@link #getEntityClass() type}. + * @param pk the {@link #getPk() primary key}. See {@link #getPkAsLong()}. + */ + public PkIdLong(Class type, Long pk) { + + super(type); + this.pk = pk; + } + + @Override + public Long getPk() { + + return this.pk; + } + + @Override + protected PkIdLong newId(Class newEntityClass, Long newPk) { + + return new PkIdLong<>(newEntityClass, newPk); + } + + @Override + public Class getPkClass() { + + return Long.class; + } + + /** + * @return the {@link #getPk() primary key} as primitve {@code long} value. + */ + public long getPkAsLong() { + + Long id = getPk(); + if (id == null) { + return -1; + } + return id.longValue(); + } + + @Override + public Long parsePk(String idString) { + + return Long.valueOf(idString); + } + + @Override + public String getMarshalPropertyId() { + + return PROPERTY_PK_LONG; + } + + /** + * @param type of the identified entity. + * @return the {@link #isEmpty() empty} template of this class. + */ + public static PkIdLong getEmpty() { + + return EMPTY; + } + + /** + * @param type of the identified entity. + * @param entityType the {@link #getEntityClass() entity type}. + * @return the {@link #isEmpty() empty} template of this class. + */ + public static PkIdLong getEmpty(Class entityType) { + + PkIdLong empty = getEmpty(); + return empty.withEntityType(entityType); + } + + /** + * @param type of the referenced entity. + * @param pk the actual {@link #getPk() primary key}. + * @return the {@link PkIdLong}. + */ + static PkIdLong of(Long pk) { + + return of(pk, null); + } + + /** + * @param type of the referenced entity. + * @param pk the actual {@link #getPk() primary key}. + * @param entityClass the {@link #getEntityClass() entity class}. + * @return the {@link PkIdLong}. + */ + public static PkIdLong of(Long pk, Class entityClass) { + + if (pk == null) { + return null; + } + return new PkIdLong<>(entityClass, pk); + } + +} diff --git a/core/src/main/java/io/github/mmm/entity/id/PkIdString.java b/core/src/main/java/io/github/mmm/entity/id/PkIdString.java new file mode 100644 index 0000000..2982999 --- /dev/null +++ b/core/src/main/java/io/github/mmm/entity/id/PkIdString.java @@ -0,0 +1,105 @@ +/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 */ +package io.github.mmm.entity.id; + +/** + * Implementation of {@link PkId} using {@link #getPk() primary key}s of type {@link String}. + * + * @param the generic type of the identified entity. + * + * @since 1.0.0 + */ +public final class PkIdString extends PkId> { + + @SuppressWarnings("rawtypes") + private static final PkIdString EMPTY = new PkIdString<>(null, null); + + private final String pk; + + /** + * The constructor. + * + * @param type the {@link #getEntityClass() type}. + * @param pk the {@link #getPk() primary key}. + */ + public PkIdString(Class type, String pk) { + + super(type); + this.pk = pk; + } + + @Override + public String getPk() { + + return this.pk; + } + + @Override + protected PkIdString newId(Class newEntityClass, String newPk) { + + return new PkIdString<>(newEntityClass, newPk); + } + + @Override + public Class getPkClass() { + + return String.class; + } + + @Override + public String parsePk(String idString) { + + return idString; + } + + @Override + public String getMarshalPropertyId() { + + return PROPERTY_PK_STRING; + } + + /** + * @param type of the identified entity. + * @return the {@link #isEmpty() empty} template of this class. + */ + public static PkIdString getEmpty() { + + return EMPTY; + } + + /** + * @param type of the identified entity. + * @param entityType the {@link #getEntityClass() entity type}. + * @return the {@link #isEmpty() empty} template of this class. + */ + public static PkIdString getEmpty(Class entityType) { + + PkIdString empty = getEmpty(); + return empty.withEntityType(entityType); + } + + /** + * @param type of the referenced entity. + * @param pk the actual {@link #getPk() primary key}. + * @return the new {@link PkIdString}. + */ + static PkIdString of(String pk) { + + return of(pk, null); + } + + /** + * @param type of the referenced entity. + * @param pk the actual {@link #getPk() primary key}. + * @param entityClass the {@link #getEntityClass() entity class}. + * @return the new {@link PkIdString}. + */ + public static PkIdString of(String pk, Class entityClass) { + + if (pk == null) { + return null; + } + return new PkIdString<>(entityClass, pk); + } + +} diff --git a/core/src/main/java/io/github/mmm/entity/id/PkIdUuid.java b/core/src/main/java/io/github/mmm/entity/id/PkIdUuid.java new file mode 100644 index 0000000..7b2b2ad --- /dev/null +++ b/core/src/main/java/io/github/mmm/entity/id/PkIdUuid.java @@ -0,0 +1,115 @@ +/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 */ +package io.github.mmm.entity.id; + +import java.util.UUID; + +import io.github.mmm.base.uuid.UuidParser; + +/** + * Implementation of {@link PkId} using {@link UUID} as type for the {@link #getPk() primary key}. + * + * @param type of the identified entity. + * @since 1.0.0 + */ +public final class PkIdUuid extends PkId> { + + @SuppressWarnings("rawtypes") + private static final PkIdUuid EMPTY = new PkIdUuid<>(null, null); + + private final UUID pk; + + /** + * The constructor. + * + * @param entityClass the {@link #getEntityClass() type}. + * @param pk the {@link #getPk() primary key}. + */ + public PkIdUuid(Class entityClass, UUID pk) { + + super(entityClass); + this.pk = pk; + } + + @Override + public UUID getPk() { + + return this.pk; + } + + @Override + protected PkIdUuid newId(Class newEntityClass, UUID newPk) { + + return new PkIdUuid<>(newEntityClass, newPk); + } + + @Override + public Class getPkClass() { + + return UUID.class; + } + + @Override + public UUID parsePk(String idString) { + + if (idString == null) { + return null; + } + UUID uuid = UuidParser.get().parse(idString); + if (uuid == null) { + throw new IllegalArgumentException(idString); + } + return uuid; + } + + @Override + public String getMarshalPropertyId() { + + return PROPERTY_PK_UUID; + } + + /** + * @param type of the identified entity. + * @return the {@link #isEmpty() empty} template of this class. + */ + public static PkIdUuid getEmpty() { + + return EMPTY; + } + + /** + * @param type of the identified entity. + * @param entityType the {@link #getEntityClass() entity type}. + * @return the {@link #isEmpty() empty} template of this class. + */ + public static PkIdUuid getEmpty(Class entityType) { + + PkIdUuid empty = getEmpty(); + return empty.withEntityType(entityType); + } + + /** + * @param type of the referenced entity. + * @param pk the actual {@link #getPk() primary key}. + * @return the new {@link PkIdUuid}. + */ + public static PkIdUuid of(UUID pk) { + + return of(pk, null); + } + + /** + * @param type of the referenced entity. + * @param pk the actual {@link #getPk() primary key}. + * @param type the {@link #getEntityClass() entity type}. + * @return the new {@link PkIdUuid}. + */ + public static PkIdUuid of(UUID pk, Class type) { + + if (pk == null) { + return null; + } + return new PkIdUuid<>(type, pk); + } + +} diff --git a/core/src/main/java/io/github/mmm/entity/id/RevisionedId.java b/core/src/main/java/io/github/mmm/entity/id/RevisionedId.java new file mode 100644 index 0000000..b334f8a --- /dev/null +++ b/core/src/main/java/io/github/mmm/entity/id/RevisionedId.java @@ -0,0 +1,147 @@ +/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 */ +package io.github.mmm.entity.id; + +import java.util.Objects; + +/** + * Implementation of {@link AbstractId} that has a {@link #getRevisionType() typed} {@link #getRevision() revision}. It + * combines a {@link PkId} with the actual {@link #getRevision() revision} for better flexibility. + * + * @param type of the identified entity. + * @param

type of the {@link #getPk() primary key}. + * @param type of the {@link #getRevision() revision}. + * @param type of this class itself for fluent API calls. + * @since 1.0.0 + */ +public abstract class RevisionedId, SELF extends RevisionedId> + extends AbstractId { + + /** @see #withoutRevision() */ + protected final PkId id; + + /** + * The constructor. + * + * @param id the wrapped {@link PkId} containing {@link #getEntityClass() entity class} and {@link #getPk() primary + * key}. + */ + protected RevisionedId(PkId id) { + + super(); + this.id = id; + } + + @Override + public final boolean hasRevisionField() { + + return true; + } + + @Override + public final P getPk() { + + return this.id.getPk(); + } + + @Override + public final Class

getPkClass() { + + return this.id.getPkClass(); + } + + @Override + public P parsePk(String idString) { + + return this.id.parsePk(idString); + } + + @Override + public final Class getEntityClass() { + + return this.id.getEntityClass(); + } + + @Override + public final String getMarshalPropertyId() { + + return this.id.getMarshalPropertyId(); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public GenericId create(Class entityType, P pk, R revision) { + + PkId newId = this.id.withEntityTypeGeneric(entityType).withPk(pk); + return newId(newId, revision); + } + + /** + * Internal factory method. + * + * @param newId the new internal {@link PkId}. + * @param newRevision the new {@link #getRevision() revision}. + * @return a new instance of this type with the given arguments. + */ + protected abstract SELF newId(PkId newId, R newRevision); + + @Override + public SELF withPk(P newPk) { + + PkId newId = this.id.withPk(newPk); + if (newId == this.id) { + return self(); + } + return newId(newId, getRevision()); + } + + @Override + public SELF withPkAndRevision(P newPk, R newRevision) { + + PkId newId = this.id.withPk(newPk); + if (newId == this.id && Objects.equals(getRevision(), newRevision)) { + return self(); + } + return newId(newId, newRevision); + } + + @Override + public SELF withRevision(R newRevision) { + + if (Objects.equals(getRevision(), newRevision)) { + return self(); + } + return newId(this.id, newRevision); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public RevisionedId withEntityTypeGeneric(Class newEntityType) { + + return withEntityType((Class) newEntityType); + } + + @Override + public SELF withEntityType(Class newEntityType) { + + PkId newId = this.id.withEntityType(newEntityType); + if (newId == this.id) { + return self(); + } + return newId(newId, getRevision()); + } + + @Override + public SELF updateRevision() { + + R newRevision = updateRevision(getRevision()); + return withRevision(newRevision); + } + + @Override + public final PkId withoutRevision() { + + return this.id; + } + +} diff --git a/core/src/main/java/io/github/mmm/entity/id/AbstractInstantId.java b/core/src/main/java/io/github/mmm/entity/id/RevisionedIdInstant.java similarity index 50% rename from core/src/main/java/io/github/mmm/entity/id/AbstractInstantId.java rename to core/src/main/java/io/github/mmm/entity/id/RevisionedIdInstant.java index 041f534..546cdd7 100644 --- a/core/src/main/java/io/github/mmm/entity/id/AbstractInstantId.java +++ b/core/src/main/java/io/github/mmm/entity/id/RevisionedIdInstant.java @@ -5,27 +5,28 @@ import java.time.Instant; /** - * An abstract base implementation of {@link Id} using {@link Instant} as type for the {@link #getRevision() revision}. + * Implementation of {@link RevisionedId} with {@link Instant} as {@link #getRevisionType() revision type}. On + * {@link #updateRevision() update} the {@link Instant#now() current timestamp} is used as {@link #getRevision() + * revision}. * * @param type of the identified entity. * @param

type of the {@link #getPk() primary key}. - * * @since 1.0.0 */ -public abstract class AbstractInstantId extends AbstractId { +public final class RevisionedIdInstant extends RevisionedId> { - /** @see #getRevision() */ - protected final Instant revision; + private final Instant revision; /** * The constructor. * - * @param type - see {@link #getEntityClass()}. - * @param revision - see {@link #getRevision()}. + * @param id the wrapped {@link PkId} containing {@link #getEntityClass() entity class} and {@link #getPk() primary + * key}. + * @param revision the {@link #getRevision() revision}. */ - public AbstractInstantId(Class type, Instant revision) { + public RevisionedIdInstant(PkId id, Instant revision) { - super(type); + super(id); this.revision = revision; } @@ -36,33 +37,33 @@ public Instant getRevision() { } @Override - public boolean hasRevisionField() { + public Class getRevisionType() { - return true; + return Instant.class; } @Override - public Class getRevisionType() { + public String getMarshalPropertyRevision() { - return Instant.class; + return PROPERTY_REVISION_INSTANT; } @Override - public Instant parseRevision(String revisionString) { + protected RevisionedIdInstant newId(PkId newId, Instant newRevision) { - return Instant.parse(revisionString); + return new RevisionedIdInstant<>(newId, newRevision); } @Override - public Instant updateRevision(Instant currentRevision) { + public Instant parseRevision(String revisionString) { - return Instant.now(); + return Instant.parse(revisionString); } @Override - public String getMarshalPropertyRevision() { + public Instant updateRevision(Instant currentRevision) { - return PROPERTY_INSTANT_REVISION; + return Instant.now(); } } diff --git a/core/src/main/java/io/github/mmm/entity/id/AbstractVersionId.java b/core/src/main/java/io/github/mmm/entity/id/RevisionedIdVersion.java similarity index 53% rename from core/src/main/java/io/github/mmm/entity/id/AbstractVersionId.java rename to core/src/main/java/io/github/mmm/entity/id/RevisionedIdVersion.java index 9d76bcb..457fb9c 100644 --- a/core/src/main/java/io/github/mmm/entity/id/AbstractVersionId.java +++ b/core/src/main/java/io/github/mmm/entity/id/RevisionedIdVersion.java @@ -3,43 +3,46 @@ package io.github.mmm.entity.id; /** - * An abstract base implementation of {@link Id} using {@link Long} as type for the {@link #getRevision() revision}. + * Implementation of {@link RevisionedId} with {@link Long} as {@link #getRevisionType() revision type}. With every + * {@link #updateRevision() update} the revision is incremented. * * @param type of the identified entity. * @param

type of the {@link #getPk() primary key}. - * * @since 1.0.0 */ -public abstract class AbstractVersionId extends AbstractId { +public final class RevisionedIdVersion extends RevisionedId> { /** {@link #getRevision() Revision} of a newly inserted {@link io.github.mmm.entity.Entity entity}. */ public static final Long INSERT_REVISION = Long.valueOf(1); - /** @see #getRevision() */ - protected final Long revision; + /** The empty default instance using {@link PkIdLong}. */ + public static final RevisionedIdVersion DEFAULT = new RevisionedIdVersion<>(PkIdLong.getEmpty(), null); + + private final Long revision; /** * The constructor. * - * @param type - see {@link #getEntityClass()}. - * @param revision - see {@link #getRevision()}. + * @param id the wrapped {@link PkId} containing {@link #getEntityClass() entity class} and {@link #getPk() primary + * key}. + * @param revision the {@link #getRevision() revision}. */ - public AbstractVersionId(Class type, Long revision) { + public RevisionedIdVersion(PkId id, Long revision) { - super(type); + super(id); this.revision = revision; } @Override - public Long getRevision() { + protected RevisionedIdVersion newId(PkId newId, Long newRevision) { - return this.revision; + return new RevisionedIdVersion<>(newId, newRevision); } @Override - public boolean hasRevisionField() { + public Long getRevision() { - return true; + return this.revision; } @Override @@ -67,7 +70,7 @@ public Long updateRevision(Long currentRevision) { @Override public String getMarshalPropertyRevision() { - return PROPERTY_LONG_REVISION; + return PROPERTY_REVISION_VERSION; } } diff --git a/core/src/main/java/io/github/mmm/entity/id/StringId.java b/core/src/main/java/io/github/mmm/entity/id/StringId.java deleted file mode 100644 index 467c661..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/StringId.java +++ /dev/null @@ -1,119 +0,0 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id; - -import java.time.Instant; - -/** - * {@link GenericId} using {@link String} as {@link #getPk() primary key (ID)}. {@link String} is obviously the most - * generic type of {@link #getPk() primary key}. However {@link LongId} or {@link UuidId} will be much more efficient. - * - * @param type of the identified entity. - * @param type of the {@link #getRevision() revision}. - * @since 1.0.0 - */ -public interface StringId> extends GenericId { - - @Override - String getPk(); - - @Override - default Class getPkClass() { - - return String.class; - } - - @Override - default String parsePk(String idString) { - - return idString; - } - - @Override - default String getMarshalPropertyId() { - - return PROPERTY_STRING_ID; - } - - @Override - default StringId withEntityType(Class newEntityType) { - - return (StringId) GenericId.super.withEntityType(newEntityType); - } - - @Override - default StringId withEntityTypeGeneric(Class newEntityType) { - - return (StringId) GenericId.super.withEntityTypeGeneric(newEntityType); - } - - @Override - default StringId withPkAndRevision(String newId, V newRevision) { - - return (StringId) GenericId.super.withPkAndRevision(newId, newRevision); - } - - @Override - default StringId withRevision(V newRevision) { - - return (StringId) GenericId.super.withRevision(newRevision); - } - - @Override - default StringId withoutRevision() { - - return new StringRevisionlessId<>(getEntityClass(), getPk()); - } - - @Override - default StringId updateRevision() { - - return (StringId) GenericId.super.updateRevision(); - } - - /** - * @param type of the referenced entity. - * @param pk the actual {@link #getPk() primary key}. - * @return the new {@link StringId}. - */ - static StringId of(String pk) { - - return of(pk, null); - } - - /** - * @param type of the referenced entity. - * @param pk the actual {@link #getPk() primary key}. - * @param type the {@link #getEntityClass() entity type}. - * @return the new {@link StringId}. - */ - static StringId of(String pk, Class type) { - - if (pk == null) { - return null; - } - return new StringRevisionlessId<>(type, pk); - } - - /** - * @param the generic type of the identified entity. - * @param type the {@link #getEntityClass() type}. - * @param pk the {@link #getPk() primary key}. - * @param revision the optional {@link #getRevision() revision}. - * @return the new {@link StringId}. - */ - static StringId of(String pk, Class type, Object revision) { - - if (pk == null) { - return null; - } - if (revision == null) { - return new StringRevisionlessId<>(type, pk); - } else if (revision instanceof Long l) { - return new StringVersionId<>(type, pk, l); - } else if (revision instanceof Instant i) { - return new StringInstantId<>(type, pk, i); - } - throw new IllegalStateException("Unsupported revision type: " + revision.getClass().getName()); - } -} diff --git a/core/src/main/java/io/github/mmm/entity/id/StringInstantId.java b/core/src/main/java/io/github/mmm/entity/id/StringInstantId.java deleted file mode 100644 index ef1a6db..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/StringInstantId.java +++ /dev/null @@ -1,111 +0,0 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id; - -import java.time.Instant; -import java.util.Objects; - -/** - * Implementation of {@link AbstractInstantId} as {@link StringId}. - * - * @param the generic type of the identified entity. - * - * @since 1.0.0 - */ -public final class StringInstantId extends AbstractInstantId implements StringId { - - @SuppressWarnings("rawtypes") - private static final StringInstantId EMPTY = new StringInstantId<>(null, null, null); - - private final String pk; - - /** - * The constructor. - * - * @param type the {@link #getEntityClass() type}. - * @param pk the {@link #getPk() primary key}. - * @param revision the {@link #getRevision() revision}. - */ - public StringInstantId(Class type, String pk, Instant revision) { - - super(type, revision); - this.pk = pk; - } - - @Override - public String getPk() { - - return this.pk; - } - - @Override - public StringInstantId create(Class newEntityType, String newId, Instant newRevision) { - - return new StringInstantId<>(newEntityType, newId, newRevision); - } - - @Override - public StringInstantId withPk(String newPk) { - - if (Objects.equals(this.pk, newPk)) { - return this; - } - return create(this.entityClass, newPk, this.revision); - } - - @Override - public StringInstantId withPkAndRevision(String newPk, Instant newRevision) { - - if (Objects.equals(this.revision, newRevision) && Objects.equals(this.pk, newPk)) { - return this; - } - return create(this.entityClass, newPk, newRevision); - } - - @Override - public StringInstantId withRevision(Instant newRevision) { - - if (Objects.equals(this.revision, newRevision)) { - return this; - } - return create(this.entityClass, this.pk, newRevision); - } - - @Override - public StringInstantId withEntityType(Class newEntityType) { - - return (StringInstantId) super.withEntityType(newEntityType); - } - - @Override - public StringInstantId withEntityTypeGeneric(Class newEntityType) { - - return (StringInstantId) super.withEntityTypeGeneric(newEntityType); - } - - @Override - public StringInstantId updateRevision() { - - Instant newRevision = updateRevision(this.revision); - return withRevision(newRevision); - } - - /** - * @param type of the identified entity. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static StringInstantId getEmpty() { - - return EMPTY; - } - - /** - * @param type of the identified entity. - * @param entityType the {@link #getEntityClass() entity type}. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static StringInstantId getEmpty(Class entityType) { - - return getEmpty().withEntityType(entityType); - } -} diff --git a/core/src/main/java/io/github/mmm/entity/id/StringRevisionlessId.java b/core/src/main/java/io/github/mmm/entity/id/StringRevisionlessId.java deleted file mode 100644 index d82da37..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/StringRevisionlessId.java +++ /dev/null @@ -1,111 +0,0 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id; - -import java.util.Objects; - -/** - * Implementation of {@link AbstractRevisionlessId} as {@link StringId}. - * - * @param the generic type of the identified entity. - * - * @since 1.0.0 - */ -public final class StringRevisionlessId extends AbstractRevisionlessId - implements StringId { - - @SuppressWarnings("rawtypes") - private static final StringRevisionlessId EMPTY = new StringRevisionlessId<>(null, null); - - private final String pk; - - /** - * The constructor. - * - * @param type the {@link #getEntityClass() type}. - * @param pk the {@link #getPk() primary key}. - */ - public StringRevisionlessId(Class type, String pk) { - - super(type); - this.pk = pk; - } - - @Override - public String getPk() { - - return this.pk; - } - - @Override - public StringRevisionlessId create(Class newEntityType, String newPk, NoRevision newRevision) { - - return new StringRevisionlessId<>(newEntityType, newPk); - } - - @Override - public StringRevisionlessId withPk(String newPk) { - - if (Objects.equals(this.pk, newPk)) { - return this; - } - return create(this.entityClass, newPk, null); - } - - @Override - public StringRevisionlessId withPkAndRevision(String newPk, NoRevision newRevision) { - - assert (newRevision == null); - return withPk(newPk); - } - - @Override - public StringRevisionlessId withRevision(NoRevision newRevision) { - - assert (newRevision == null); - return this; - } - - @Override - public StringRevisionlessId withoutRevision() { - - return this; - } - - @Override - public StringRevisionlessId withEntityType(Class newEntityType) { - - return (StringRevisionlessId) super.withEntityType(newEntityType); - } - - @Override - public StringRevisionlessId withEntityTypeGeneric(Class newEntityType) { - - return (StringRevisionlessId) super.withEntityTypeGeneric(newEntityType); - } - - @Override - public StringRevisionlessId updateRevision() { - - return this; - } - - /** - * @param type of the identified entity. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static StringRevisionlessId getEmpty() { - - return EMPTY; - } - - /** - * @param type of the identified entity. - * @param entityType the {@link #getEntityClass() entity type}. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static StringRevisionlessId getEmpty(Class entityType) { - - return getEmpty().withEntityType(entityType); - } -} diff --git a/core/src/main/java/io/github/mmm/entity/id/StringVersionId.java b/core/src/main/java/io/github/mmm/entity/id/StringVersionId.java deleted file mode 100644 index 97bc3b0..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/StringVersionId.java +++ /dev/null @@ -1,109 +0,0 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id; - -import java.util.Objects; - -/** - * Implementation of {@link AbstractVersionId} as {@link StringId}. - * - * @param type of the identified entity. - * @since 1.0.0 - */ -public final class StringVersionId extends AbstractVersionId implements StringId { - - @SuppressWarnings("rawtypes") - private static final StringVersionId EMPTY = new StringVersionId<>(null, null, null); - - private final String pk; - - /** - * The constructor. - * - * @param type the {@link #getEntityClass() type}. - * @param pk the {@link #getPk() primary key}. - * @param revision the {@link #getRevision() revision}. - */ - public StringVersionId(Class type, String pk, Long revision) { - - super(type, revision); - this.pk = pk; - } - - @Override - public String getPk() { - - return this.pk; - } - - @Override - public StringVersionId create(Class newEntityType, String newPk, Long newRevision) { - - return new StringVersionId<>(newEntityType, newPk, newRevision); - } - - @Override - public StringVersionId withPk(String newPk) { - - if (Objects.equals(this.pk, newPk)) { - return this; - } - return create(this.entityClass, newPk, this.revision); - } - - @Override - public StringVersionId withPkAndRevision(String newPk, Long newRevision) { - - if (Objects.equals(this.revision, newRevision) && Objects.equals(this.pk, newPk)) { - return this; - } - return create(this.entityClass, newPk, newRevision); - } - - @Override - public StringVersionId withRevision(Long newRevision) { - - if (Objects.equals(this.revision, newRevision)) { - return this; - } - return create(this.entityClass, this.pk, newRevision); - } - - @Override - public StringVersionId withEntityType(Class newEntityType) { - - return (StringVersionId) super.withEntityType(newEntityType); - } - - @Override - public StringVersionId withEntityTypeGeneric(Class newEntityType) { - - return (StringVersionId) super.withEntityTypeGeneric(newEntityType); - } - - @Override - public StringVersionId updateRevision() { - - Long newRevision = updateRevision(this.revision); - return withRevision(newRevision); - } - - /** - * @param type of the identified entity. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static StringVersionId getEmpty() { - - return EMPTY; - } - - /** - * @param type of the identified entity. - * @param entityType the {@link #getEntityClass() entity type}. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static StringVersionId getEmpty(Class entityType) { - - return getEmpty().withEntityType(entityType); - } -} diff --git a/core/src/main/java/io/github/mmm/entity/id/UuidId.java b/core/src/main/java/io/github/mmm/entity/id/UuidId.java deleted file mode 100644 index 45bd8bc..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/UuidId.java +++ /dev/null @@ -1,129 +0,0 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id; - -import java.time.Instant; -import java.util.UUID; - -import io.github.mmm.base.uuid.UuidParser; - -/** - * {@link Id} using {@link UUID} as {@link #getPk() primary key (ID)}. (e.g. apache cassandra supports this natively). - * - * @param type of the identified entity. - * @param type of the {@link #getRevision() revision}. - * @since 1.0.0 - */ -public interface UuidId> extends GenericId { - - @Override - UUID getPk(); - - @Override - default Class getPkClass() { - - return UUID.class; - } - - @Override - default UUID parsePk(String idString) { - - if (idString == null) { - return null; - } - UUID uuid = UuidParser.get().parse(idString); - if (uuid == null) { - throw new IllegalArgumentException(idString); - } - return uuid; - } - - @Override - default String getMarshalPropertyId() { - - return PROPERTY_UUID; - } - - @Override - default UuidId withEntityType(Class newEntityType) { - - return (UuidId) GenericId.super.withEntityType(newEntityType); - } - - @Override - default UuidId withEntityTypeGeneric(Class newEntityType) { - - return (UuidId) GenericId.super.withEntityTypeGeneric(newEntityType); - } - - @Override - default UuidId withPkAndRevision(UUID newId, V newRevision) { - - return (UuidId) GenericId.super.withPkAndRevision(newId, newRevision); - } - - @Override - default UuidId withRevision(V newRevision) { - - return (UuidId) GenericId.super.withRevision(newRevision); - } - - @Override - default UuidId withoutRevision() { - - return new UuidRevisionlessId<>(getEntityClass(), getPk()); - } - - @Override - default UuidId updateRevision() { - - return (UuidId) GenericId.super.updateRevision(); - } - - /** - * @param type of the referenced entity. - * @param pk the actual {@link #getPk() primary key}. - * @return the new {@link UuidId}. - */ - static UuidId of(UUID pk) { - - return of(pk, null); - } - - /** - * @param type of the referenced entity. - * @param pk the actual {@link #getPk() primary key}. - * @param type the {@link #getEntityClass() entity type}. - * @return the new {@link UuidId}. - */ - static UuidId of(UUID pk, Class type) { - - if (pk == null) { - return null; - } - return new UuidRevisionlessId<>(type, pk); - } - - /** - * @param the generic type of the identified entity. - * @param type the {@link #getEntityClass() type}. - * @param pk the {@link #getPk() primary key}. - * @param revision the optional {@link #getRevision() revision}. - * @return the new {@link UuidId}. - */ - static UuidId of(UUID pk, Class type, Object revision) { - - if (pk == null) { - return null; - } - if (revision == null) { - return new UuidRevisionlessId<>(type, pk); - } else if (revision instanceof Long l) { - return new UuidVersionId<>(type, pk, l); - } else if (revision instanceof Instant i) { - return new UuidInstantId<>(type, pk, i); - } - throw new IllegalStateException("Unsupported revision type: " + revision.getClass().getName()); - } - -} diff --git a/core/src/main/java/io/github/mmm/entity/id/UuidInstantId.java b/core/src/main/java/io/github/mmm/entity/id/UuidInstantId.java deleted file mode 100644 index 5ed0c17..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/UuidInstantId.java +++ /dev/null @@ -1,111 +0,0 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id; - -import java.time.Instant; -import java.util.Objects; -import java.util.UUID; - -/** - * Implementation of {@link AbstractInstantId} as {@link UuidId}. - * - * @param type of the identified entity. - * @since 1.0.0 - */ -public final class UuidInstantId extends AbstractInstantId implements UuidId { - - @SuppressWarnings("rawtypes") - private static final UuidInstantId EMPTY = new UuidInstantId<>(null, null, null); - - private final UUID pk; - - /** - * The constructor. - * - * @param type the {@link #getEntityClass() type}. - * @param pk the {@link #getPk() primary key}. - * @param revision the {@link #getRevision() revision}. - */ - public UuidInstantId(Class type, UUID pk, Instant revision) { - - super(type, revision); - this.pk = pk; - } - - @Override - public UUID getPk() { - - return this.pk; - } - - @Override - public UuidInstantId create(Class newEntityType, UUID newPk, Instant newRevision) { - - return new UuidInstantId<>(newEntityType, newPk, newRevision); - } - - @Override - public UuidInstantId withPk(UUID newPk) { - - if (Objects.equals(this.pk, newPk)) { - return this; - } - return create(getEntityClass(), newPk, this.revision); - } - - @Override - public UuidInstantId withPkAndRevision(UUID newPk, Instant newRevision) { - - if (Objects.equals(this.revision, newRevision) && Objects.equals(this.pk, newPk)) { - return this; - } - return create(this.entityClass, newPk, newRevision); - } - - @Override - public UuidInstantId withRevision(Instant newRevision) { - - if (Objects.equals(this.revision, newRevision)) { - return this; - } - return create(this.entityClass, this.pk, newRevision); - } - - @Override - public UuidInstantId withEntityType(Class newEntityType) { - - return (UuidInstantId) super.withEntityType(newEntityType); - } - - @Override - public UuidInstantId withEntityTypeGeneric(Class newEntityType) { - - return (UuidInstantId) super.withEntityTypeGeneric(newEntityType); - } - - @Override - public UuidInstantId updateRevision() { - - Instant newRevision = updateRevision(this.revision); - return withRevision(newRevision); - } - - /** - * @param type of the identified entity. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static UuidInstantId getEmpty() { - - return EMPTY; - } - - /** - * @param type of the identified entity. - * @param entityType the {@link #getEntityClass() entity type}. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static UuidInstantId getEmpty(Class entityType) { - - return getEmpty().withEntityType(entityType); - } -} diff --git a/core/src/main/java/io/github/mmm/entity/id/UuidRevisionlessId.java b/core/src/main/java/io/github/mmm/entity/id/UuidRevisionlessId.java deleted file mode 100644 index ded07a4..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/UuidRevisionlessId.java +++ /dev/null @@ -1,110 +0,0 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id; - -import java.util.Objects; -import java.util.UUID; - -/** - * Implementation of {@link AbstractRevisionlessId} as {@link UuidId}. - * - * @param type of the identified entity. - * @since 1.0.0 - */ -public final class UuidRevisionlessId extends AbstractRevisionlessId implements UuidId { - - @SuppressWarnings("rawtypes") - private static final UuidRevisionlessId EMPTY = new UuidRevisionlessId<>(null, null); - - private final UUID pk; - - /** - * The constructor. - * - * @param type the {@link #getEntityClass() type}. - * @param pk the {@link #getPk() primary key}. - */ - public UuidRevisionlessId(Class type, UUID pk) { - - super(type); - this.pk = pk; - } - - @Override - public UUID getPk() { - - return this.pk; - } - - @Override - public UuidRevisionlessId create(Class newEntityType, UUID newPk, NoRevision newRevision) { - - return new UuidRevisionlessId<>(newEntityType, newPk); - } - - @Override - public UuidRevisionlessId withPk(UUID newPk) { - - if (Objects.equals(getPk(), newPk)) { - return this; - } - return create(getEntityClass(), newPk, null); - } - - @Override - public UuidRevisionlessId withPkAndRevision(UUID newPk, NoRevision newRevision) { - - assert (newRevision == null); - return withPk(newPk); - } - - @Override - public UuidRevisionlessId withRevision(NoRevision newRevision) { - - assert (newRevision == null); - return this; - } - - @Override - public UuidRevisionlessId withoutRevision() { - - return this; - } - - @Override - public UuidRevisionlessId withEntityType(Class newEntityType) { - - return (UuidRevisionlessId) super.withEntityType(newEntityType); - } - - @Override - public UuidRevisionlessId withEntityTypeGeneric(Class newEntityType) { - - return (UuidRevisionlessId) super.withEntityTypeGeneric(newEntityType); - } - - @Override - public UuidRevisionlessId updateRevision() { - - return this; - } - - /** - * @param type of the identified entity. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static UuidRevisionlessId getEmpty() { - - return EMPTY; - } - - /** - * @param type of the identified entity. - * @param entityType the {@link #getEntityClass() entity type}. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static UuidRevisionlessId getEmpty(Class entityType) { - - return getEmpty().withEntityType(entityType); - } -} diff --git a/core/src/main/java/io/github/mmm/entity/id/UuidVersionId.java b/core/src/main/java/io/github/mmm/entity/id/UuidVersionId.java deleted file mode 100644 index 8d4a485..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/UuidVersionId.java +++ /dev/null @@ -1,110 +0,0 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id; - -import java.util.Objects; -import java.util.UUID; - -/** - * Implementation of {@link AbstractVersionId} as {@link UuidId}. - * - * @param type of the identified entity. - * @since 1.0.0 - */ -public final class UuidVersionId extends AbstractVersionId implements UuidId { - - @SuppressWarnings("rawtypes") - private static final UuidVersionId EMPTY = new UuidVersionId<>(null, null, null); - - private final UUID pk; - - /** - * The constructor. - * - * @param type the {@link #getEntityClass() type}. - * @param pk the {@link #getPk() primary key}. - * @param revision the {@link #getRevision() revision}. - */ - public UuidVersionId(Class type, UUID pk, Long revision) { - - super(type, revision); - this.pk = pk; - } - - @Override - public UUID getPk() { - - return this.pk; - } - - @Override - public UuidVersionId create(Class newEntityType, UUID newPk, Long newRevision) { - - return new UuidVersionId<>(newEntityType, newPk, newRevision); - } - - @Override - public UuidVersionId withPk(UUID newPk) { - - if (Objects.equals(this.pk, newPk)) { - return this; - } - return create(this.entityClass, newPk, this.revision); - } - - @Override - public UuidVersionId withPkAndRevision(UUID newPk, Long newRevision) { - - if (Objects.equals(this.revision, newRevision) && Objects.equals(this.pk, newPk)) { - return this; - } - return create(this.entityClass, newPk, newRevision); - } - - @Override - public UuidVersionId withRevision(Long newRevision) { - - if (Objects.equals(this.revision, newRevision)) { - return this; - } - return create(this.entityClass, this.pk, newRevision); - } - - @Override - public UuidVersionId withEntityType(Class newEntityType) { - - return (UuidVersionId) super.withEntityType(newEntityType); - } - - @Override - public UuidVersionId withEntityTypeGeneric(Class newEntityType) { - - return (UuidVersionId) super.withEntityTypeGeneric(newEntityType); - } - - @Override - public UuidVersionId updateRevision() { - - Long newRevision = updateRevision(this.revision); - return withRevision(newRevision); - } - - /** - * @param type of the identified entity. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static UuidVersionId getEmpty() { - - return EMPTY; - } - - /** - * @param type of the identified entity. - * @param entityType the {@link #getEntityClass() entity type}. - * @return the {@link #isEmpty() empty} template of this class. - */ - public static UuidVersionId getEmpty(Class entityType) { - - return getEmpty().withEntityType(entityType); - } -} diff --git a/core/src/main/java/io/github/mmm/entity/id/generator/LongIdGenerator.java b/core/src/main/java/io/github/mmm/entity/id/generator/LongIdGenerator.java deleted file mode 100644 index bf98fb4..0000000 --- a/core/src/main/java/io/github/mmm/entity/id/generator/LongIdGenerator.java +++ /dev/null @@ -1,49 +0,0 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id.generator; - -import java.time.Instant; - -import io.github.mmm.entity.id.AbstractVersionId; -import io.github.mmm.entity.id.Id; -import io.github.mmm.entity.id.LongId; -import io.github.mmm.entity.id.LongInstantId; -import io.github.mmm.entity.id.LongVersionId; -import io.github.mmm.entity.id.sequence.IdSequence; - -/** - * {@link IdGenerator} for {@link LongId}. - * - * @since 1.0.0 - */ -public class LongIdGenerator implements IdGenerator { - - private final IdSequence sequence; - - /** - * The constructor. - * - * @param sequence the {@link IdSequence}. - */ - public LongIdGenerator(IdSequence sequence) { - - super(); - this.sequence = sequence; - } - - @Override - public LongId generate(Id template) { - - Class entityType = template.getEntityClass(); - Long pk = Long.valueOf(this.sequence.next(template)); - Class> revisionType = template.getRevisionType(); - if (revisionType == Long.class) { - return new LongVersionId<>(entityType, pk, AbstractVersionId.INSERT_REVISION); - } else if (revisionType == Instant.class) { - return new LongInstantId<>(entityType, pk, Instant.now()); - } else { - throw new IllegalArgumentException("Unsupported revision type " + revisionType + "(" + template.getClass() + ")"); - } - } - -} diff --git a/core/src/main/java/io/github/mmm/entity/id/generator/SequenceIdGenerator.java b/core/src/main/java/io/github/mmm/entity/id/generator/SequenceIdGenerator.java new file mode 100644 index 0000000..a2c3f81 --- /dev/null +++ b/core/src/main/java/io/github/mmm/entity/id/generator/SequenceIdGenerator.java @@ -0,0 +1,40 @@ +/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 */ +package io.github.mmm.entity.id.generator; + +import io.github.mmm.entity.id.GenericId; +import io.github.mmm.entity.id.Id; +import io.github.mmm.entity.id.PkIdLong; +import io.github.mmm.entity.id.sequence.IdSequence; + +/** + * {@link IdGenerator} based on an {@link IdSequence} for {@link PkIdLong}. + * + * @since 1.0.0 + */ +public class SequenceIdGenerator implements IdGenerator { + + private final IdSequence sequence; + + /** + * The constructor. + * + * @param sequence the {@link IdSequence}. + */ + public SequenceIdGenerator(IdSequence sequence) { + + super(); + this.sequence = sequence; + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public Id generate(Id template) { + + Class entityType = template.getEntityClass(); + Long pk = Long.valueOf(this.sequence.next(template)); + GenericId gid = (GenericId) template; + return gid.create(entityType, pk, null).updateRevision(); + } + +} diff --git a/core/src/main/java/io/github/mmm/entity/id/generator/UuidIdGenerator.java b/core/src/main/java/io/github/mmm/entity/id/generator/UuidIdGenerator.java index fe72535..6047dfb 100644 --- a/core/src/main/java/io/github/mmm/entity/id/generator/UuidIdGenerator.java +++ b/core/src/main/java/io/github/mmm/entity/id/generator/UuidIdGenerator.java @@ -1,36 +1,28 @@ -/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 - * http://www.apache.org/licenses/LICENSE-2.0 */ -package io.github.mmm.entity.id.generator; - -import java.time.Instant; -import java.util.UUID; - -import io.github.mmm.entity.id.AbstractVersionId; -import io.github.mmm.entity.id.Id; -import io.github.mmm.entity.id.UuidId; -import io.github.mmm.entity.id.UuidInstantId; -import io.github.mmm.entity.id.UuidVersionId; - -/** - * {@link IdGenerator} for {@link UuidId}. - * - * @since 1.0.0 - */ -public class UuidIdGenerator implements IdGenerator { - - @Override - public UuidId generate(Id template) { - - Class entityType = template.getEntityClass(); - UUID pk = UUID.randomUUID(); - Class> revisionType = template.getRevisionType(); - if (revisionType == Long.class) { - return new UuidVersionId<>(entityType, pk, AbstractVersionId.INSERT_REVISION); - } else if (revisionType == Instant.class) { - return new UuidInstantId<>(entityType, pk, Instant.now()); - } else { - throw new IllegalArgumentException("Unsupported revision type " + revisionType + "(" + template.getClass() + ")"); - } - } - -} +/* Copyright (c) The m-m-m Team, Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 */ +package io.github.mmm.entity.id.generator; + +import java.util.UUID; + +import io.github.mmm.entity.id.GenericId; +import io.github.mmm.entity.id.Id; +import io.github.mmm.entity.id.PkIdUuid; + +/** + * {@link IdGenerator} based on {@link UUID} for {@link PkIdUuid}. + * + * @since 1.0.0 + */ +public class UuidIdGenerator implements IdGenerator { + + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Override + public Id generate(Id template) { + + Class entityType = template.getEntityClass(); + UUID pk = UUID.randomUUID(); + GenericId gid = (GenericId) template; + return gid.create(entityType, pk, null).updateRevision(); + } + +} diff --git a/core/src/main/java/io/github/mmm/entity/id/sequence/IdSequence.java b/core/src/main/java/io/github/mmm/entity/id/sequence/IdSequence.java index 1eabaa1..e4d0b75 100644 --- a/core/src/main/java/io/github/mmm/entity/id/sequence/IdSequence.java +++ b/core/src/main/java/io/github/mmm/entity/id/sequence/IdSequence.java @@ -3,12 +3,11 @@ package io.github.mmm.entity.id.sequence; import io.github.mmm.entity.id.Id; -import io.github.mmm.entity.id.LongId; import io.github.mmm.entity.id.generator.IdGenerator; /** * Interface for a sequence that can generate the next unique {@link Id#getPk() primary key} of type {@link Long} for a - * new {@link LongId}. + * new {@link io.github.mmm.entity.id.PkIdLong}. * * @see IdGenerator#generate(Id) */ @@ -16,7 +15,7 @@ public interface IdSequence { /** * @param template the {@link Id#isTransient() transient} {@link Id} of the {@link io.github.mmm.entity.Entity entity} - * to insert. + * to insert. May happily be ignored by implementations. * @return the next long value guaranteed to be unique for this sequence. Proper implementations delegate to a * database sequence. */ diff --git a/core/src/main/java/io/github/mmm/entity/link/AbstractIdLink.java b/core/src/main/java/io/github/mmm/entity/link/AbstractIdLink.java index 1152c20..2a7c5e1 100644 --- a/core/src/main/java/io/github/mmm/entity/link/AbstractIdLink.java +++ b/core/src/main/java/io/github/mmm/entity/link/AbstractIdLink.java @@ -17,7 +17,7 @@ */ public abstract class AbstractIdLink extends AbstractLink { - private /* final */ GenericId id; + private /* final */ GenericId id; /** @see #getTarget() */ private E target; @@ -32,7 +32,7 @@ public abstract class AbstractIdLink extends AbstractLink { protected AbstractIdLink(Id id, E target) { super(); - GenericId gid = (GenericId) id; + GenericId gid = (GenericId) id; if (target == null) { if (gid == null) { throw new IllegalArgumentException("Cannot create link with neither ID nor target entity!"); @@ -41,12 +41,12 @@ protected AbstractIdLink(Id id, E target) { throw new IllegalArgumentException("Cannot create link for empty ID - primary key must be present!"); } } else if (gid == null) { - gid = (GenericId) Id.from((Entity) this.target); + gid = (GenericId) Id.from((Entity) this.target); } if ((gid != null) && isRemoveRevision()) { gid = gid.withoutRevision(); } - this.id = (GenericId) id; + this.id = gid; this.target = target; } @@ -63,7 +63,7 @@ public Id getId() { private synchronized Id updateId() { if (this.id == null) { - GenericId gid = (GenericId) Id.from((Entity) this.target); + GenericId gid = (GenericId) Id.from((Entity) this.target); if ((gid != null) && isRemoveRevision()) { gid = gid.withoutRevision(); } @@ -113,9 +113,10 @@ protected synchronized E updateTarget(Function, E> resolver) { * already satisfying. * @see GenericId#withEntityType(Class) */ + @SuppressWarnings({ "rawtypes", "unchecked" }) public AbstractIdLink withType(Class type) { - GenericId newId = this.id.withEntityTypeGeneric(type); + GenericId newId = this.id.withEntityTypeGeneric(type); if (newId == this.id) { return this; } @@ -126,5 +127,5 @@ public AbstractIdLink withType(Class type) { * @param newId the new {@link #getId()}. * @return a copy of this link with the given {@link Id}. */ - protected abstract AbstractIdLink withId(GenericId newId); + protected abstract AbstractIdLink withId(GenericId newId); } diff --git a/core/src/main/java/io/github/mmm/entity/link/EntityLink.java b/core/src/main/java/io/github/mmm/entity/link/EntityLink.java index e81e84b..8232945 100644 --- a/core/src/main/java/io/github/mmm/entity/link/EntityLink.java +++ b/core/src/main/java/io/github/mmm/entity/link/EntityLink.java @@ -31,7 +31,7 @@ protected EntityLink(E target) { } @Override - protected AbstractIdLink withId(GenericId newId) { + protected AbstractIdLink withId(GenericId newId) { // should never happen... return new EntityLink<>(newId, getTarget()); diff --git a/core/src/main/java/io/github/mmm/entity/link/EntityLinkWithoutRevision.java b/core/src/main/java/io/github/mmm/entity/link/EntityLinkWithoutRevision.java index 681607b..14317d5 100644 --- a/core/src/main/java/io/github/mmm/entity/link/EntityLinkWithoutRevision.java +++ b/core/src/main/java/io/github/mmm/entity/link/EntityLinkWithoutRevision.java @@ -37,7 +37,7 @@ protected boolean isRemoveRevision() { } @Override - protected AbstractIdLink withId(GenericId newId) { + protected AbstractIdLink withId(GenericId newId) { // should never happen... return new EntityLinkWithoutRevision<>(newId, getTarget()); diff --git a/core/src/main/java/io/github/mmm/entity/link/IdLink.java b/core/src/main/java/io/github/mmm/entity/link/IdLink.java index c4dda41..1207c0e 100644 --- a/core/src/main/java/io/github/mmm/entity/link/IdLink.java +++ b/core/src/main/java/io/github/mmm/entity/link/IdLink.java @@ -52,7 +52,7 @@ public E getTarget() { } @Override - protected IdLink withId(GenericId newId) { + protected IdLink withId(GenericId newId) { return new IdLink<>(newId, super.getTarget(), this.resolver); } diff --git a/core/src/main/java/io/github/mmm/entity/link/IdLinkWithoutRevision.java b/core/src/main/java/io/github/mmm/entity/link/IdLinkWithoutRevision.java index e7703e5..39e1617 100644 --- a/core/src/main/java/io/github/mmm/entity/link/IdLinkWithoutRevision.java +++ b/core/src/main/java/io/github/mmm/entity/link/IdLinkWithoutRevision.java @@ -39,7 +39,7 @@ protected boolean isRemoveRevision() { } @Override - protected IdLinkWithoutRevision withId(GenericId newId) { + protected IdLinkWithoutRevision withId(GenericId newId) { return new IdLinkWithoutRevision<>(newId, super.getTarget(), this.resolver); } diff --git a/core/src/test/java/io/github/mmm/entity/AbstractEntity.java b/core/src/test/java/io/github/mmm/entity/AbstractEntity.java index 03f37e6..04e7128 100644 --- a/core/src/test/java/io/github/mmm/entity/AbstractEntity.java +++ b/core/src/test/java/io/github/mmm/entity/AbstractEntity.java @@ -1,7 +1,8 @@ package io.github.mmm.entity; import io.github.mmm.entity.id.Id; -import io.github.mmm.entity.id.LongVersionId; +import io.github.mmm.entity.id.PkIdLong; +import io.github.mmm.entity.id.RevisionedIdVersion; /** * Abstract base implementation of {@link Entity}. Intentionally not in {@code src/main/java} as we want to propagate @@ -17,7 +18,7 @@ public abstract class AbstractEntity implements Entity { public AbstractEntity() { super(); - this.id = LongVersionId.getEmpty(getClass()); + this.id = new RevisionedIdVersion<>(new PkIdLong<>(getClass(), null), null); } @Override diff --git a/core/src/test/java/io/github/mmm/entity/id/IdMarshallingTest.java b/core/src/test/java/io/github/mmm/entity/id/IdMarshallingTest.java index 8b55eeb..486c2eb 100644 --- a/core/src/test/java/io/github/mmm/entity/id/IdMarshallingTest.java +++ b/core/src/test/java/io/github/mmm/entity/id/IdMarshallingTest.java @@ -25,19 +25,21 @@ public void testAllIdTypes() { UUID uuid = UUID.randomUUID(); // test version IDs - check(new LongVersionId<>(Entity.class, 42L, 5L), "{\"l\":42,\"v\":5}"); - check(new StringVersionId<>(Entity.class, "MyId", 5L), "{\"s\":\"MyId\",\"v\":5}"); - check(new UuidVersionId<>(Entity.class, uuid, 5L), "{\"u\":\"" + uuid + "\",\"v\":5}"); + check(new RevisionedIdVersion<>(new PkIdLong<>(Entity.class, 42L), 5L), "{\"l\":42,\"v\":5}"); + check(new RevisionedIdVersion<>(new PkIdString<>(Entity.class, "MyId"), 5L), "{\"s\":\"MyId\",\"v\":5}"); + check(new RevisionedIdVersion<>(new PkIdUuid<>(Entity.class, uuid), 5L), "{\"u\":\"" + uuid + "\",\"v\":5}"); // test timestamp IDs Instant ts = Instant.parse("1999-12-31T23:59:59.123456789Z"); - check(new LongInstantId<>(Entity.class, 42L, ts), "{\"l\":42,\"t\":\"1999-12-31T23:59:59.123456789Z\"}"); - check(new StringInstantId<>(Entity.class, "MyId", ts), "{\"s\":\"MyId\",\"t\":\"1999-12-31T23:59:59.123456789Z\"}"); - check(new UuidInstantId<>(Entity.class, uuid, ts), + check(new RevisionedIdInstant<>(new PkIdLong<>(Entity.class, 42L), ts), + "{\"l\":42,\"t\":\"1999-12-31T23:59:59.123456789Z\"}"); + check(new RevisionedIdInstant<>(new PkIdString<>(Entity.class, "MyId"), ts), + "{\"s\":\"MyId\",\"t\":\"1999-12-31T23:59:59.123456789Z\"}"); + check(new RevisionedIdInstant<>(new PkIdUuid<>(Entity.class, uuid), ts), "{\"u\":\"" + uuid + "\",\"t\":\"1999-12-31T23:59:59.123456789Z\"}"); // test flat IDs - check(new LongRevisionlessId<>(Entity.class, 42L), "42"); - check(new StringRevisionlessId<>(Entity.class, "MyId"), "\"MyId\""); - check(new UuidRevisionlessId<>(Entity.class, uuid), "\"" + uuid + "\""); + check(new PkIdLong<>(Entity.class, 42L), "42"); + check(new PkIdString<>(Entity.class, "MyId"), "\"MyId\""); + check(new PkIdUuid<>(Entity.class, uuid), "\"" + uuid + "\""); } /** @@ -48,9 +50,12 @@ public void testAllIdTypes() { public void testWriteWithZeroVersion() { UUID uuid = UUID.randomUUID(); - assertThat(writeJson(new LongVersionId<>(Entity.class, 42L, 0L))).isEqualTo("{\"l\":42,\"v\":0}"); - assertThat(writeJson(new StringVersionId<>(Entity.class, "MyId", 0L))).isEqualTo("{\"s\":\"MyId\",\"v\":0}"); - assertThat(writeJson(new UuidVersionId<>(Entity.class, uuid, 0L))).isEqualTo("{\"u\":\"" + uuid + "\",\"v\":0}"); + assertThat(writeJson(new RevisionedIdVersion<>(new PkIdLong<>(Entity.class, 42L), 0L))) + .isEqualTo("{\"l\":42,\"v\":0}"); + assertThat(writeJson(new RevisionedIdVersion<>(new PkIdString<>(Entity.class, "MyId"), 0L))) + .isEqualTo("{\"s\":\"MyId\",\"v\":0}"); + assertThat(writeJson(new RevisionedIdVersion<>(new PkIdUuid<>(Entity.class, uuid), 0L))) + .isEqualTo("{\"u\":\"" + uuid + "\",\"v\":0}"); } /** @@ -61,29 +66,29 @@ public void testWriteWithZeroVersion() { public void testWriteWithoutRevision() { UUID uuid = UUID.randomUUID(); - assertThat(writeJson(new LongRevisionlessId<>(Entity.class, 42L))).isEqualTo("42"); - assertThat(writeJson(new StringRevisionlessId<>(Entity.class, "MyId"))).isEqualTo("\"MyId\""); - assertThat(writeJson(new UuidRevisionlessId<>(Entity.class, uuid))).isEqualTo("\"" + uuid + '"'); + assertThat(writeJson(new PkIdLong<>(Entity.class, 42L))).isEqualTo("42"); + assertThat(writeJson(new PkIdString<>(Entity.class, "MyId"))).isEqualTo("\"MyId\""); + assertThat(writeJson(new PkIdUuid<>(Entity.class, uuid))).isEqualTo("\"" + uuid + '"'); } - public static void check(GenericId id, String json) { + public static void check(GenericId id, String json) { assertThat(id.getEntityClass()).isSameAs(Entity.class); assertThat(writeJson(id)).isEqualTo(json); assertThat(readJson(id.withPkAndRevision(null, null), json)).isEqualTo(id); } - public static String writeJson(GenericId id) { + public static String writeJson(GenericId id) { StringBuilder sb = new StringBuilder(); id.write(StandardFormat.json(MarshallingConfig.NO_INDENTATION).writer(sb)); return sb.toString(); } - public static GenericId readJson(GenericId id, String json) { + public static GenericId readJson(GenericId id, String json) { StructuredReader reader = StandardFormat.json().reader(json); - GenericId newId = id.readObject(reader); + GenericId newId = id.readObject(reader); assertThat(newId.getEntityClass()).isSameAs(Entity.class); return newId; } diff --git a/core/src/test/java/io/github/mmm/entity/link/LinkTest.java b/core/src/test/java/io/github/mmm/entity/link/LinkTest.java index 22b816d..56dbf53 100644 --- a/core/src/test/java/io/github/mmm/entity/link/LinkTest.java +++ b/core/src/test/java/io/github/mmm/entity/link/LinkTest.java @@ -5,7 +5,8 @@ import io.github.mmm.entity.DummyEntity; import io.github.mmm.entity.id.Id; -import io.github.mmm.entity.id.LongVersionId; +import io.github.mmm.entity.id.PkIdLong; +import io.github.mmm.entity.id.RevisionedIdVersion; /** * Test of {@link Link}. @@ -17,7 +18,7 @@ public class LinkTest extends Assertions { public void testCreateLinkWithEmptyIdFails() { // arrange - Id id = LongVersionId.getEmpty(DummyEntity.class); + Id id = PkIdLong.getEmpty(DummyEntity.class); // act try { Link link = Link.of(id); @@ -33,11 +34,11 @@ public void testCreateLinkWithEmptyIdFails() { public void testCreateLinkWithValidIdSucceeds() { // arrange - Id id = LongVersionId.getEmpty(DummyEntity.class).withPkAndRevision(4711L, 1L); + Id id = RevisionedIdVersion.DEFAULT.create(DummyEntity.class, 4711L, 1L); // act Link link = Link.of(id); // assert - assertThat(link.getId()).isSameAs(id); + assertThat(link.getId()).isSameAs(id.withoutRevision()); } /** Test of {@link Link#isResolved()}. */ @@ -45,7 +46,7 @@ public void testCreateLinkWithValidIdSucceeds() { public void testIsResolved() { // arrange - Id id = LongVersionId.getEmpty(DummyEntity.class).withPkAndRevision(4711L, 1L); + Id id = RevisionedIdVersion.DEFAULT.create(DummyEntity.class, 4711L, 1L); DummyEntity entity = new DummyEntity(); entity.setId(id); // act