This repository has been archived by the owner on Nov 23, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 302
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #617 from hohwille/feature-id-ref
IdRef feature for typesafe and expressive entity references
- Loading branch information
Showing
53 changed files
with
1,379 additions
and
214 deletions.
There are no files selected for viewing
60 changes: 60 additions & 0 deletions
60
modules/basic/src/main/java/io/oasp/module/basic/common/api/reference/GenericIdRef.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package io.oasp.module.basic.common.api.reference; | ||
|
||
import java.util.Objects; | ||
|
||
/** | ||
* Generic implementation of {@link Ref}. | ||
* | ||
* @param <ID> generic type of {@link #getId() ID}. | ||
* @param <E> generic type of the referenced {@link net.sf.mmm.util.entity.api.Entity}. | ||
*/ | ||
public class GenericIdRef<ID, E> implements Ref<ID, E> { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
private final ID id; | ||
|
||
/** | ||
* The constructor. | ||
* | ||
* @param id the {@link #getId() ID}. | ||
*/ | ||
public GenericIdRef(ID id) { | ||
|
||
super(); | ||
Objects.requireNonNull(id, "id"); | ||
this.id = id; | ||
} | ||
|
||
@Override | ||
public ID getId() { | ||
|
||
return this.id; | ||
} | ||
|
||
@Override | ||
public int hashCode() { | ||
|
||
return this.id.hashCode(); | ||
} | ||
|
||
@Override | ||
public boolean equals(Object obj) { | ||
|
||
if (this == obj) { | ||
return true; | ||
} | ||
if ((obj == null) || (getClass() != obj.getClass())) { | ||
return false; | ||
} | ||
GenericIdRef<?, ?> other = (GenericIdRef<?, ?>) obj; | ||
return Objects.equals(this.id, other.id); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
|
||
return this.id.toString(); | ||
} | ||
|
||
} |
62 changes: 62 additions & 0 deletions
62
modules/basic/src/main/java/io/oasp/module/basic/common/api/reference/IdRef.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package io.oasp.module.basic.common.api.reference; | ||
|
||
import net.sf.mmm.util.entity.api.GenericEntity; | ||
|
||
/** | ||
* A {@link Ref} using {@link Long} values as {@link #getId() ID}. | ||
* | ||
* @param <E> generic type of the referenced {@link net.sf.mmm.util.entity.api.Entity}. | ||
*/ | ||
public class IdRef<E> extends GenericIdRef<Long, E> { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
/** | ||
* The constructor. | ||
* | ||
* @param id the {@link #getId() ID}. | ||
*/ | ||
public IdRef(Long id) { | ||
|
||
super(id); | ||
} | ||
|
||
/** | ||
* @param <E> generic type of the referenced {@link GenericEntity}. | ||
* @param entity the {@link GenericEntity} to reference. | ||
* @return the {@link IdRef} pointing to the given {@link GenericEntity} or {@code null} if the {@link GenericEntity} | ||
* or its {@link GenericEntity#getId() ID} is {@code null}. | ||
*/ | ||
public static <E extends GenericEntity<Long>> IdRef<E> of(E entity) { | ||
|
||
if (entity == null) { | ||
return null; | ||
} | ||
return of(entity.getId()); | ||
} | ||
|
||
/** | ||
* @param <E> generic type of the referenced {@link GenericEntity}. | ||
* @param id the {@link #getId() ID} to wrap. | ||
* @return the {@link IdRef} pointing to an entity with the specified {@link #getId() ID} or {@code null} if the given | ||
* {@code ID} was {@code null}. | ||
*/ | ||
public static <E> IdRef<E> of(Long id) { | ||
|
||
if (id == null) { | ||
return null; | ||
} | ||
return new IdRef<>(id); | ||
} | ||
|
||
/** | ||
* @param <E> generic type of the referenced {@link GenericEntity}. | ||
* @param id the {@link #getId() ID} to wrap. | ||
* @return the {@link IdRef} pointing to an entity with the specified {@link #getId() ID}. | ||
*/ | ||
public static <E> IdRef<E> of(long id) { | ||
|
||
return new IdRef<>(Long.valueOf(id)); | ||
} | ||
|
||
} |
22 changes: 22 additions & 0 deletions
22
modules/basic/src/main/java/io/oasp/module/basic/common/api/reference/Ref.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
package io.oasp.module.basic.common.api.reference; | ||
|
||
import net.sf.mmm.util.lang.api.Datatype; | ||
|
||
/** | ||
* Interface for a reference to an {@link net.sf.mmm.util.entity.api.GenericEntity entity} via its {@link #getId() ID}. | ||
* In most cases you want to use {@link IdRef}. | ||
* | ||
* @param <ID> generic type of {@link #getId() ID}. | ||
* @param <E> generic type of the referenced {@link net.sf.mmm.util.entity.api.Entity}. For flexibility not technically | ||
* bound to {@link net.sf.mmm.util.entity.api.Entity} so it can also be used for an external entity not | ||
* satisfying any requirements. | ||
*/ | ||
public interface Ref<ID, E> extends Datatype { | ||
|
||
/** | ||
* @return the ({@link net.sf.mmm.util.entity.api.GenericEntity#getId() ID} of the referenced | ||
* {@link net.sf.mmm.util.entity.api.Entity}. | ||
*/ | ||
ID getId(); | ||
|
||
} |
79 changes: 79 additions & 0 deletions
79
modules/basic/src/test/java/io/oasp/module/basic/common/api/reference/IdRefTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package io.oasp.module.basic.common.api.reference; | ||
|
||
import net.sf.mmm.util.entity.api.MutableGenericEntity; | ||
|
||
import org.junit.Test; | ||
|
||
import io.oasp.module.basic.common.api.to.AbstractEto; | ||
import io.oasp.module.test.common.base.ModuleTest; | ||
|
||
/** | ||
* Test of {@link IdRef}. | ||
*/ | ||
public class IdRefTest extends ModuleTest { | ||
|
||
/** Test of {@link IdRef#of(net.sf.mmm.util.entity.api.GenericEntity)} */ | ||
@Test | ||
public void testOfEntity() { | ||
|
||
// given | ||
long id = 4711L; | ||
FooEto foo = new FooEto(); | ||
foo.setId(id); | ||
|
||
// when | ||
IdRef<Foo> fooId = IdRef.<Foo> of(foo); // with Java8 type-inference the additional <Foo> is not required | ||
|
||
// then | ||
assertThat(fooId).isNotNull(); | ||
assertThat(fooId.getId()).isEqualTo(foo.getId()).isEqualTo(id); | ||
assertThat(fooId.toString()).isEqualTo(Long.toString(id)); | ||
assertThat(IdRef.of((Foo) null)).isNull(); | ||
|
||
Bar bar = new Bar(); | ||
bar.setFooId(fooId); // just a syntax/compilation check. | ||
// bar.setBarId(fooId); // will produce compiler error what is desired in such case | ||
IdRef<Bar> barId = IdRef.of(1234L); | ||
bar.setBarId(barId); // this again will compile | ||
} | ||
|
||
/** Test of {@link IdRef#of(Long)} */ | ||
@Test | ||
public void testOfLong() { | ||
|
||
// given | ||
long id = 4711L; | ||
|
||
// when | ||
IdRef<Foo> fooId = IdRef.of(id); // not type-safe but required in some cases | ||
|
||
// then | ||
assertThat(fooId).isNotNull(); | ||
assertThat(fooId.getId()).isEqualTo(id); | ||
assertThat(fooId.toString()).isEqualTo(Long.toString(id)); | ||
assertThat(IdRef.of(Long.valueOf(id))).isEqualTo(fooId).isNotSameAs(fooId); | ||
assertThat(IdRef.of((Long) null)).isNull(); | ||
} | ||
|
||
private interface Foo extends MutableGenericEntity<Long> { | ||
|
||
} | ||
|
||
private class FooEto extends AbstractEto implements Foo { | ||
|
||
private static final long serialVersionUID = 1L; | ||
|
||
} | ||
|
||
private class Bar { | ||
|
||
void setFooId(IdRef<Foo> fooId) { | ||
|
||
} | ||
|
||
void setBarId(IdRef<Bar> fooId) { | ||
|
||
} | ||
} | ||
|
||
} |
43 changes: 43 additions & 0 deletions
43
...les/jpa-basic/src/main/java/io/oasp/module/jpa/dataaccess/api/JpaEntityManagerAccess.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package io.oasp.module.jpa.dataaccess.api; | ||
|
||
import javax.persistence.EntityManager; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
/** | ||
* Internal access to {@link EntityManager}. | ||
*/ | ||
class JpaEntityManagerAccess { | ||
|
||
private static final Logger LOG = LoggerFactory.getLogger(JpaEntityManagerAccess.class); | ||
|
||
private static EntityManager entityManager; | ||
|
||
static void setEntityManager(EntityManager entityManager, boolean check) { | ||
|
||
if ((JpaEntityManagerAccess.entityManager != null) && (JpaEntityManagerAccess.entityManager != entityManager)) { | ||
if (check) { | ||
throw new IllegalStateException("EntityManager has already been initialized!"); | ||
} else { | ||
LOG.debug("EntityManager conflict: {} has been replaced with {}. This may only happen during tests.", | ||
JpaEntityManagerAccess.entityManager, entityManager); | ||
} | ||
} | ||
JpaEntityManagerAccess.entityManager = entityManager; | ||
} | ||
|
||
static boolean hasEntityManager() { | ||
|
||
return (entityManager != null); | ||
} | ||
|
||
static EntityManager getEntityManager() { | ||
|
||
if (entityManager == null) { | ||
throw new IllegalStateException("EntityManager has not yet been initialized!"); | ||
} | ||
return entityManager; | ||
} | ||
|
||
} |
78 changes: 78 additions & 0 deletions
78
modules/jpa-basic/src/main/java/io/oasp/module/jpa/dataaccess/api/JpaHelper.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
package io.oasp.module.jpa.dataaccess.api; | ||
|
||
import java.util.Collection; | ||
|
||
import net.sf.mmm.util.entity.api.GenericEntity; | ||
|
||
import io.oasp.module.basic.common.api.reference.GenericIdRef; | ||
import io.oasp.module.basic.common.api.reference.Ref; | ||
|
||
/** | ||
* Helper class for generic handling of {@link net.sf.mmm.util.entity.api.PersistenceEntity persistence entities} (based | ||
* on {@link javax.persistence.EntityManager}). In some cases it is required to access JPA features in a static way. | ||
* E.g. a common case is a setter in your {@link net.sf.mmm.util.entity.api.PersistenceEntity} for a | ||
* {@link io.oasp.module.basic.common.api.reference.Ref reference} from an | ||
* {@link io.oasp.module.basic.common.api.to.AbstractEto ETO} that can be archieved via the following code: | ||
* | ||
* <pre> | ||
* @Entity | ||
* @Table("Foo") | ||
* public class FooEntity extends ApplicationPersistenceEntity implements Foo { | ||
* @OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) | ||
* @JoinColumn(name = "bar") | ||
* private BarEntity bar; | ||
* ... | ||
* @Override | ||
* public void setBarId({@link io.oasp.module.basic.common.api.reference.IdRef}{@literal <Bar>} barId) { | ||
* this.bar = {@link JpaHelper}.{@link JpaHelper#asEntity(Ref, Class) asEntity}(barId, BarEntity.class); | ||
* } | ||
* } | ||
* </pre> | ||
*/ | ||
public class JpaHelper { | ||
|
||
/** | ||
* @param <E> generic type of the {@link net.sf.mmm.util.entity.api.PersistenceEntity entity} | ||
* @param reference the {@link Ref} or {@code null}. Typically an | ||
* {@link io.oasp.module.basic.common.api.reference.IdRef}. | ||
* @param entityClass the {@link net.sf.mmm.util.entity.api.PersistenceEntity entity} {@link Class}. | ||
* @return the {@link net.sf.mmm.util.entity.api.PersistenceEntity entity} of the specified {@link Class} with the | ||
* {@link Ref#getId() ID} from the given {@link GenericIdRef} or {@code null} if the given {@link Ref} is | ||
* {@code null}. | ||
*/ | ||
public static <E> E asEntity(Ref<?, ? super E> reference, Class<E> entityClass) { | ||
|
||
if (reference == null) { | ||
return null; | ||
} else { | ||
return JpaEntityManagerAccess.getEntityManager().getReference(entityClass, reference.getId()); | ||
} | ||
} | ||
|
||
/** | ||
* @param <E> generic type of the input {@link GenericEntity entities} (most commonly the entity interface). | ||
* @param <P> generic type of the output {@link net.sf.mmm.util.entity.api.PersistenceEntity persistence entities}. | ||
* @param input the {@link Collection} of {@link GenericEntity entities} (e.g. | ||
* {@link io.oasp.module.basic.common.api.to.AbstractEto ETOs}) to use as input. | ||
* @param entityClass the {@link Class} reflecting the {@link net.sf.mmm.util.entity.api.PersistenceEntity}. | ||
* @param output die {@link Collection} where to {@link Collection#add(Object) add} the | ||
* {@link net.sf.mmm.util.entity.api.PersistenceEntity persistent entities} corresponding to the input | ||
* {@link GenericEntity entities}. Most probably {@link Collection#isEmpty() empty} but may also already | ||
* contain entities so this method will add additional entities. | ||
*/ | ||
@SuppressWarnings("unchecked") | ||
public static <E extends GenericEntity<?>, P extends E> void asEntities(Collection<? extends E> input, | ||
Class<P> entityClass, Collection<P> output) { | ||
|
||
for (E eto : input) { | ||
P entity; | ||
if (entityClass.isInstance(eto)) { | ||
entity = (P) eto; | ||
} else { | ||
entity = JpaEntityManagerAccess.getEntityManager().getReference(entityClass, eto.getId()); | ||
} | ||
output.add(entity); | ||
} | ||
} | ||
|
||
} |
29 changes: 29 additions & 0 deletions
29
modules/jpa-basic/src/main/java/io/oasp/module/jpa/dataaccess/api/JpaInitializer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package io.oasp.module.jpa.dataaccess.api; | ||
|
||
import javax.persistence.EntityManager; | ||
import javax.persistence.PersistenceContext; | ||
|
||
/** | ||
* Initializer bean for {@link EntityManager}. Will be auto configured via {@code oasp4j-starter-jpa}. | ||
*/ | ||
public class JpaInitializer { | ||
|
||
/** | ||
* @param entityManager the {@link EntityManager} to inject. | ||
*/ | ||
@PersistenceContext | ||
protected void setEntityManager(EntityManager entityManager) { | ||
|
||
JpaEntityManagerAccess.setEntityManager(entityManager, true); | ||
} | ||
|
||
/** | ||
* @param entityManager the {@link EntityManager} to set. | ||
* @param check - {@code true} to check that the {@link EntityManager} does not change on-the-fly (desired in | ||
* productive code), {@code false} otherwise (may be desired in test-code). | ||
*/ | ||
protected void setEntityManager(EntityManager entityManager, boolean check) { | ||
|
||
JpaEntityManagerAccess.setEntityManager(entityManager, check); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.