diff --git a/ebean-api/pom.xml b/ebean-api/pom.xml
index ec4a2cddb..0ad522236 100644
--- a/ebean-api/pom.xml
+++ b/ebean-api/pom.xml
@@ -49,7 +49,7 @@
io.ebean
ebean-annotation
- 7.3
+ 7.4-SNAPSHOT
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
index 1cdabc9f8..0fca00f82 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/core/DefaultServer.java
@@ -47,7 +47,14 @@ import io.ebean.bean.PersistenceContext.WithOption;
import io.ebean.bean.SingleBeanLoader;
import io.ebean.cache.ServerCacheManager;
import io.ebean.common.CopyOnFirstWriteList;
-import io.ebean.config.*;
+import io.ebean.config.CurrentTenantProvider;
+import io.ebean.config.DatabaseConfig;
+import io.ebean.config.EncryptKeyManager;
+import io.ebean.config.QueryPlanCapture;
+import io.ebean.config.QueryPlanListener;
+import io.ebean.config.SlowQueryEvent;
+import io.ebean.config.SlowQueryListener;
+import io.ebean.config.TenantMode;
import io.ebean.config.dbplatform.DatabasePlatform;
import io.ebean.event.BeanPersistController;
import io.ebean.event.ShutdownManager;
@@ -649,7 +656,11 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer {
*/
@Override
public T createEntityBean(Class type) {
- return descriptor(type).createBean();
+ final BeanDescriptor desc = descriptor(type);
+ if (desc == null) {
+ throw new IllegalArgumentException("No bean type " + type.getName() + " registered");
+ }
+ return desc.createBean();
}
/**
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java
index eee73e4c0..3c949b6e1 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptorManager.java
@@ -87,10 +87,11 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
private final BootupClasses bootupClasses;
private final String serverName;
private final List> elementDescriptors = new ArrayList<>();
- private final Map, BeanTable> beanTableMap = new HashMap<>();
+ private final Map beanTableMap = new HashMap<>();
private final Map> descMap = new HashMap<>();
private final Map> descQueueMap = new HashMap<>();
private final Map> beanManagerMap = new HashMap<>();
+ private final Map descAliases = new HashMap<>();
private final Map>> tableToDescMap = new HashMap<>();
private final Map>> tableToViewDescMap = new HashMap<>();
private final DbIdentity dbIdentity;
@@ -116,7 +117,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
private final Map draftTableMap = new HashMap<>();
// temporary collections used during startup and then cleared
- private Map, DeployBeanInfo>> deployInfoMap = new HashMap<>();
+ private Map> deployInfoMap = new HashMap<>();
private Set> embeddedIdTypes = new HashSet<>();
private List> embeddedBeans = new ArrayList<>();
@@ -234,14 +235,22 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
}
@Override
- @SuppressWarnings("unchecked")
public BeanDescriptor descriptor(Class entityType) {
- return (BeanDescriptor) descMap.get(entityType.getName());
+ BeanDescriptor ret = descriptorByClassName(entityType.getName());
+ if (ret == null) {
+ for (Class> iface : entityType.getInterfaces()) {
+ ret = descriptorByClassName(iface.getName());
+ if (ret != null) {
+ return ret;
+ }
+ }
+ }
+ return ret;
}
@SuppressWarnings("unchecked")
public BeanDescriptor descriptorByClassName(String entityClassName) {
- return (BeanDescriptor) descMap.get(entityClassName);
+ return (BeanDescriptor) descMap.get(resolveAlias(entityClassName));
}
@Override
@@ -287,6 +296,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
try {
createListeners();
readEntityDeploymentInitial();
+ readOverridesAndAliases();
readXmlMapping(mappings);
readEntityBeanTable();
readEntityDeploymentAssociations();
@@ -336,7 +346,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
return;
}
- DeployBeanInfo> info = deployInfoMap.get(entityClass);
+ DeployBeanInfo> info = deploy(entityClass);
if (info == null) {
log.error("No entity bean for ebean.xml entry " + entityClassName);
@@ -550,7 +560,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
}
public BeanTable beanTable(Class> type) {
- return beanTableMap.get(type);
+ return beanTableMap.get(resolveAlias(type.getName()));
}
/**
@@ -566,7 +576,12 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
}
private BeanManager> beanManager(String beanClassName) {
- return beanManagerMap.get(beanClassName);
+ return beanManagerMap.get(resolveAlias(beanClassName));
+ }
+
+ private String resolveAlias(String name) {
+ String altName = descAliases.get(name);
+ return altName == null ? name : altName;
}
/**
@@ -591,7 +606,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
*/
@SuppressWarnings("unchecked")
public DeployBeanInfo deploy(Class cls) {
- return (DeployBeanInfo) deployInfoMap.get(cls);
+ return (DeployBeanInfo) deployInfoMap.get(resolveAlias(cls.getName()));
}
private void registerDescriptor(DeployBeanInfo> info) {
@@ -616,7 +631,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
private void readEntityDeploymentInitial() {
for (Class> entityClass : bootupClasses.getEntities()) {
DeployBeanInfo> info = createDeployBeanInfo(entityClass);
- deployInfoMap.put(entityClass, info);
+ deployInfoMap.put(entityClass.getName(), info);
Class> embeddedIdType = info.getEmbeddedIdType();
if (embeddedIdType != null) {
embeddedIdTypes.add(embeddedIdType);
@@ -624,7 +639,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
}
for (Class> entityClass : bootupClasses.getEmbeddables()) {
DeployBeanInfo> info = createDeployBeanInfo(entityClass);
- deployInfoMap.put(entityClass, info);
+ deployInfoMap.put(entityClass.getName(), info);
if (embeddedIdTypes.contains(entityClass)) {
// register embeddedId types early - scalar properties only
// and needed for creating BeanTables (id properties)
@@ -650,7 +665,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
private void readEntityBeanTable() {
for (DeployBeanInfo> info : deployInfoMap.values()) {
BeanTable beanTable = createBeanTable(info);
- beanTableMap.put(beanTable.getBeanType(), beanTable);
+ beanTableMap.put(beanTable.getBeanType().getName(), beanTable);
}
// register non-id embedded beans (after bean tables are created)
for (DeployBeanInfo> info : embeddedBeans) {
@@ -674,7 +689,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
DeployBeanDescriptor> descriptor = info.getDescriptor();
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null && !inheritInfo.isRoot()) {
- DeployBeanInfo> rootBeanInfo = deployInfoMap.get(inheritInfo.getRoot().getType());
+ DeployBeanInfo> rootBeanInfo = deploy(inheritInfo.getRoot().getType());
PlatformIdGenerator rootIdGen = rootBeanInfo.getDescriptor().getIdGenerator();
if (rootIdGen != null) {
descriptor.setIdGenerator(rootIdGen);
@@ -715,13 +730,79 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
}
}
+ /**
+ * Finds the overrides by priority. Computes a map with the base class as key
+ * and the descriptor, that shoud replace that class. Modifies the deployInfoMap
+ * and descAliases accordingly.
+ */
+ private void readOverridesAndAliases() {
+ Map, DeployBeanInfo>> overrides = new HashMap<>();
+
+ for (DeployBeanInfo> info : deployInfoMap.values()) {
+ DeployBeanDescriptor> desc = info.getDescriptor();
+ Integer prio = desc.getOverridePriority();
+ if (prio != null) {
+ Class> base = desc.getBaseBeanType();
+ Class> child = desc.getBeanType();
+ log.debug("{} overridden by {} with priority {}", base.getName(), child.getName(), prio);
+
+ // check, if we already have found an override for this base and take the one
+ // with
+ // lowest prio
+ DeployBeanInfo> override = overrides.get(base);
+ if (override == null) {
+ overrides.put(base, info);
+ } else {
+ int delta = override.getDescriptor().getOverridePriority().compareTo(prio);
+ if (delta > 0) {
+ overrides.put(base, info);
+ } else if (delta == 0) {
+ throw new IllegalStateException("There are two or more implementations for " + base.getName()
+ + " with priority " + prio + ". Conflicting entity: " + desc.getBeanType().getName() + " - "
+ + override.getDescriptor().getBeanType().getName());
+ }
+ }
+ }
+ }
+
+ overrides.forEach((key, value) -> {
+ deployInfoMap.remove(key.getName()); // remove the original descriptor
+
+ DeployBeanDescriptor> newDesc = value.getDescriptor();
+ newDesc.setOverridePriority(null); // clear priority (so no discard will occur)
+
+ deployInfoMap.put(newDesc.getBeanType().getName(), value);
+ descAliases.put(key.getName(), newDesc.getBeanType().getName());
+ });
+
+
+ // remove all left overrides classes (they are discarded)
+ deployInfoMap.values().removeIf(info -> info.getDescriptor().getOverridePriority() != null);
+
+ for (DeployBeanInfo> info : deployInfoMap.values()) {
+ for (Class> iface : info.getDescriptor().getInterfaces()) {
+ String conflict = descAliases.put(iface.getName(), info.getDescriptor().getBeanType().getName());
+ if (conflict != null) {
+ throw new IllegalStateException("There are two or more implementations for " + iface
+ + ". Conflicting entity: " + conflict + " - " + info.getDescriptor().getBeanType().getName());
+ }
+ }
+ }
+
+ // output alias map
+ if (log.isInfoEnabled()) {
+ descAliases.forEach((key, value) -> log.info("{} alias for {}", key, value));
+ }
+ }
+
+
/**
* Sets the inheritance info.
*/
private void setInheritanceInfo(DeployBeanInfo> info) {
for (DeployBeanPropertyAssocOne> oneProp : info.getDescriptor().propertiesAssocOne()) {
if (!oneProp.isTransient()) {
- DeployBeanInfo> assoc = deployInfoMap.get(oneProp.getTargetType());
+ DeployBeanInfo> assoc = deploy(oneProp.getTargetType());
if (assoc != null) {
oneProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
}
@@ -729,7 +810,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
}
for (DeployBeanPropertyAssocMany> manyProp : info.getDescriptor().propertiesAssocMany()) {
if (!manyProp.isTransient()) {
- DeployBeanInfo> assoc = deployInfoMap.get(manyProp.getTargetType());
+ DeployBeanInfo> assoc = deploy(manyProp.getTargetType());
if (assoc != null) {
manyProp.getTableJoin().setInheritInfo(assoc.getDescriptor().getInheritInfo());
}
@@ -786,7 +867,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
private DeployBeanDescriptor> targetDescriptor(DeployBeanPropertyAssoc> prop) {
Class> targetType = prop.getTargetType();
- DeployBeanInfo> info = deployInfoMap.get(targetType);
+ DeployBeanInfo> info = deploy(targetType);
if (info == null) {
throw new PersistenceException("Can not find descriptor [" + targetType + "] for " + prop.getFullBeanName());
}
@@ -878,7 +959,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
final InheritInfo targetInheritInfo = targetDesc.getInheritInfo();
if (targetInheritInfo != null) {
for (InheritInfo child : targetInheritInfo.getChildren()) {
- final DeployBeanDescriptor> childDescriptor = deployInfoMap.get(child.getType()).getDescriptor();
+ final DeployBeanDescriptor> childDescriptor = deploy(child.getType()).getDescriptor();
childDescriptor.setOrderColumn(orderProperty);
}
}
@@ -1143,11 +1224,12 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
// set bean controller, finder and listener
setBeanControllerFinderListener(desc);
deplyInherit.process(desc);
- desc.checkInheritanceMapping();
createProperties.createProperties(desc);
DeployBeanInfo info = new DeployBeanInfo<>(deployUtil, desc);
readAnnotations.readInitial(info);
+
+ desc.checkInheritanceMapping();
return info;
}
@@ -1424,7 +1506,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
String baseTable = prop.getDesc().getBaseTable();
DeployTableJoin inverse = prop.getTableJoin().createInverse(baseTable);
TableJoin inverseJoin = new TableJoin(inverse, prop.getForeignKey());
- DeployBeanInfo> target = deployInfoMap.get(prop.getTargetType());
+ DeployBeanInfo> target = deploy(prop.getTargetType());
target.setPrimaryKeyJoin(inverseJoin);
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java
index e670dd136..f6282db2d 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/meta/DeployBeanDescriptor.java
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.deploy.meta;
import io.ebean.annotation.Cache;
import io.ebean.annotation.DocStore;
import io.ebean.annotation.DocStoreMode;
+import io.ebean.annotation.EntityOverride;
import io.ebean.annotation.Identity;
import io.ebean.config.DatabaseConfig;
import io.ebean.config.TableName;
@@ -16,6 +17,7 @@ import io.ebean.event.BeanPostLoad;
import io.ebean.event.BeanQueryAdapter;
import io.ebean.event.changelog.ChangeLogFilter;
import io.ebean.text.PathProperties;
+import io.ebean.util.AnnotationUtil;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.ConcurrencyMode;
import io.ebeaninternal.server.core.CacheOptions;
@@ -44,9 +46,11 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Describes Beans including their deployment information.
@@ -152,6 +156,10 @@ public class DeployBeanDescriptor {
private TableJoin primaryKeyJoin;
private Object jacksonAnnotatedClass;
+ private final Set> interfaces = new HashSet<>();
+
+ private Integer overridePriority;
+
/**
* Construct the BeanDescriptor.
*/
@@ -957,7 +965,7 @@ public class DeployBeanDescriptor {
* Check the mapping for class inheritance
*/
public void checkInheritanceMapping() {
- if (inheritInfo == null) {
+ if (inheritInfo == null && overridePriority == null) {
checkInheritance(getBeanType());
}
}
@@ -1130,4 +1138,46 @@ public class DeployBeanDescriptor {
}
return jacksonAnnotatedClass;
}
+
+ /**
+ * Adds a concrete interface, that will be registered as alias for a concrete implementation.
+ */
+ public void addInterface(Class> iface) {
+ if (!iface.isAssignableFrom(beanType)) {
+ throw new ClassCastException("Cannot cast " + iface.getName() + " to " + beanType.getName());
+ }
+ interfaces.add(iface);
+ }
+
+ /**
+ * Returns the interfaces.
+ */
+ public Set> getInterfaces() {
+ return interfaces;
+ }
+
+ /**
+ * Sets the override priority.
+ */
+ public void setOverridePriority(Integer overridePriority) {
+ this.overridePriority = overridePriority;
+ }
+
+ /**
+ * returns the override priority, or null if this is a base entity.
+ */
+ public Integer getOverridePriority() {
+ return overridePriority;
+ }
+
+ /**
+ * Returns the base bean type, if {@link EntityOverride} was used.
+ */
+ public Class> getBaseBeanType() {
+ Class> base = getBeanType();
+ while (AnnotationUtil.has(base, EntityOverride.class)) {
+ base = base.getSuperclass();
+ }
+ return base;
+ }
}
diff --git a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java
index cd015da92..d9061e9e9 100644
--- a/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java
+++ b/ebean-core/src/main/java/io/ebeaninternal/server/deploy/parse/AnnotationClass.java
@@ -6,6 +6,8 @@ import io.ebean.annotation.DbPartition;
import io.ebean.annotation.DocStore;
import io.ebean.annotation.Draftable;
import io.ebean.annotation.DraftableElement;
+import io.ebean.annotation.EntityImplements;
+import io.ebean.annotation.EntityOverride;
import io.ebean.annotation.History;
import io.ebean.annotation.Identity;
import io.ebean.annotation.Index;
@@ -14,6 +16,7 @@ import io.ebean.annotation.ReadAudit;
import io.ebean.annotation.StorageEngine;
import io.ebean.annotation.View;
import io.ebean.config.TableName;
+import io.ebean.util.AnnotationUtil;
import io.ebeaninternal.api.CoreLog;
import io.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import io.ebeaninternal.server.deploy.IndexDefinition;
@@ -29,6 +32,7 @@ import javax.persistence.IdClass;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
+import java.util.Set;
import static io.ebean.util.AnnotationUtil.typeGet;
@@ -84,7 +88,7 @@ final class AnnotationClass extends AnnotationParser {
*/
private void setTableName() {
if (descriptor.isBaseTableType()) {
- Class> beanType = descriptor.getBeanType();
+ Class> beanType = descriptor.getBaseBeanType();
InheritInfo inheritInfo = descriptor.getInheritInfo();
if (inheritInfo != null) {
beanType = inheritInfo.getRoot().getType();
@@ -188,6 +192,18 @@ final class AnnotationClass extends AnnotationParser {
for (NamedQuery namedQuery : annotationClassNamedQuery(cls)) {
descriptor.addNamedQuery(namedQuery.name(), namedQuery.query());
}
+
+ Set entityImplements = AnnotationUtil.typeGetAll(cls, EntityImplements.class);
+ for (EntityImplements ann : entityImplements) {
+ for (Class> iface : ann.value()) {
+ descriptor.addInterface(iface);
+ }
+ }
+
+ EntityOverride entityOverride = AnnotationUtil.typeGet(cls, EntityOverride.class);
+ if (entityOverride != null) {
+ descriptor.setOverridePriority(entityOverride.priority());
+ }
}
}
diff --git a/ebean-test/src/test/java/io/ebean/model/interfaces/ExtPerson1.java b/ebean-test/src/test/java/io/ebean/model/interfaces/ExtPerson1.java
new file mode 100644
index 000000000..ffe781ecf
--- /dev/null
+++ b/ebean-test/src/test/java/io/ebean/model/interfaces/ExtPerson1.java
@@ -0,0 +1,25 @@
+package org.tests.model.interfaces;
+
+import io.ebean.annotation.EntityImplements;
+import io.ebean.annotation.EntityOverride;
+
+import javax.persistence.Entity;
+
+@Entity()
+@EntityImplements(IExtPerson1.class)
+@EntityOverride(priority = 30)
+public class ExtPerson1 extends Person implements IExtPerson1 {
+
+ private int myField1;
+
+ @Override
+ public int getMyField1() {
+ return myField1;
+ }
+
+ @Override
+ public void setMyField1(int myField1) {
+ this.myField1 = myField1;
+ }
+
+}
diff --git a/ebean-test/src/test/java/io/ebean/model/interfaces/ExtPerson1and2.java b/ebean-test/src/test/java/io/ebean/model/interfaces/ExtPerson1and2.java
new file mode 100644
index 000000000..edc7486f2
--- /dev/null
+++ b/ebean-test/src/test/java/io/ebean/model/interfaces/ExtPerson1and2.java
@@ -0,0 +1,26 @@
+package org.tests.model.interfaces;
+
+import io.ebean.annotation.EntityImplements;
+import io.ebean.annotation.EntityOverride;
+
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+@Entity()
+@Table(name = "person")
+@EntityImplements(IExtPerson2.class)
+@EntityOverride(priority = -30)
+public class ExtPerson1and2 extends ExtPerson1 implements IExtPerson2 {
+
+ private int myField2;
+
+ @Override
+ public int getMyField2() {
+ return myField2;
+ }
+
+ @Override
+ public void setMyField2(int myField2) {
+ this.myField2 = myField2;
+ }
+}
diff --git a/ebean-test/src/test/java/io/ebean/model/interfaces/ExtPerson2.java b/ebean-test/src/test/java/io/ebean/model/interfaces/ExtPerson2.java
new file mode 100644
index 000000000..d1866ba4a
--- /dev/null
+++ b/ebean-test/src/test/java/io/ebean/model/interfaces/ExtPerson2.java
@@ -0,0 +1,27 @@
+package org.tests.model.interfaces;
+
+import io.ebean.annotation.EntityImplements;
+import io.ebean.annotation.EntityOverride;
+
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+@Entity
+@EntityOverride(priority = 20)
+@EntityImplements(IExtPerson2.class)
+@Table(name = "person")
+public class ExtPerson2 extends Person implements IExtPerson2 {
+
+ private int myField2;
+
+ @Override
+ public int getMyField2() {
+ return myField2;
+ }
+
+ @Override
+ public void setMyField2(int myField2) {
+ this.myField2 = myField2;
+ }
+
+}
diff --git a/ebean-test/src/test/java/io/ebean/model/interfaces/IExtPerson1.java b/ebean-test/src/test/java/io/ebean/model/interfaces/IExtPerson1.java
new file mode 100644
index 000000000..b81a5266d
--- /dev/null
+++ b/ebean-test/src/test/java/io/ebean/model/interfaces/IExtPerson1.java
@@ -0,0 +1,7 @@
+package org.tests.model.interfaces;
+
+public interface IExtPerson1 extends IPerson {
+ int getMyField1();
+
+ void setMyField1(int myField1);
+}
diff --git a/ebean-test/src/test/java/io/ebean/model/interfaces/IExtPerson2.java b/ebean-test/src/test/java/io/ebean/model/interfaces/IExtPerson2.java
new file mode 100644
index 000000000..49bf60133
--- /dev/null
+++ b/ebean-test/src/test/java/io/ebean/model/interfaces/IExtPerson2.java
@@ -0,0 +1,7 @@
+package org.tests.model.interfaces;
+
+public interface IExtPerson2 extends IPerson {
+ int getMyField2();
+
+ void setMyField2(int myField2);
+}
diff --git a/ebean-test/src/test/java/org/tests/basic/TestManyOneInterface.java b/ebean-test/src/test/java/org/tests/basic/TestManyOneInterface.java
index 7f601523a..eab5cf6ae 100644
--- a/ebean-test/src/test/java/org/tests/basic/TestManyOneInterface.java
+++ b/ebean-test/src/test/java/org/tests/basic/TestManyOneInterface.java
@@ -5,27 +5,51 @@ import io.ebean.DB;
import org.junit.jupiter.api.Test;
import org.tests.model.basic.ResetBasicData;
import org.tests.model.interfaces.Address;
+import org.tests.model.interfaces.ExtPerson1and2;
import org.tests.model.interfaces.IAddress;
+import org.tests.model.interfaces.IExtPerson1;
+import org.tests.model.interfaces.IExtPerson2;
import org.tests.model.interfaces.IPerson;
import org.tests.model.interfaces.Person;
+import static org.assertj.core.api.Assertions.assertThat;
+
public class TestManyOneInterface extends BaseTestCase {
@Test
- public void test() {
-
+ public void testEntityOverrideEntityImplements() {
ResetBasicData.reset();
- IAddress a = new Address("hello");
+ IAddress a = DB.getDefault().createEntityBean(IAddress.class);
+ assertThat(a).isInstanceOf(Address.class);
- IPerson p = new Person();
+ IPerson p = DB.getDefault().createEntityBean(Person.class);
+ assertThat(p).isInstanceOf(ExtPerson1and2.class);
+ p = DB.getDefault().getPluginApi().createEntityBean(IPerson.class);
+ assertThat(p).isInstanceOf(ExtPerson1and2.class);
p.setDefaultAddress(a);
- DB.save(a);
+ IAddress ea1 = DB.getDefault().createEntityBean(IAddress.class);
+ IAddress ea2 = DB.getDefault().createEntityBean(IAddress.class);
+
+ p.getExtraAddresses().add(ea1);
+ p.getExtraAddresses().add(ea2);
+
DB.save(p);
- //Assert.assertTrue();
+ IAddress a2 = DB.find(IAddress.class, a.getOid());
+ IPerson p2 = DB.find(IPerson.class, p.getOid());
+
+ assertThat(a2).isInstanceOf(Address.class);
+ assertThat(p2).isNotNull().isInstanceOf(ExtPerson1and2.class);
+ assertThat(p2.getDefaultAddress()).isInstanceOf(Address.class);
+
+ // some more checks
+ IExtPerson1 pe1 = DB.getDefault().getPluginApi().createEntityBean(IExtPerson1.class);
+ IExtPerson2 pe2 = DB.getDefault().getPluginApi().createEntityBean(IExtPerson2.class);
+ assertThat(pe1).isInstanceOf(ExtPerson1and2.class);
+ assertThat(pe2).isInstanceOf(ExtPerson1and2.class);
}
}
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/Address.java b/ebean-test/src/test/java/org/tests/model/interfaces/Address.java
index 052ef63f8..0e5f7a32e 100644
--- a/ebean-test/src/test/java/org/tests/model/interfaces/Address.java
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/Address.java
@@ -1,10 +1,14 @@
package org.tests.model.interfaces;
+import io.ebean.annotation.EntityImplements;
+
import javax.persistence.Entity;
import javax.persistence.Id;
+import javax.persistence.ManyToOne;
import javax.persistence.Version;
@Entity
+@EntityImplements(IAddress.class)
public class Address implements IAddress {
@Id
@@ -15,6 +19,9 @@ public class Address implements IAddress {
private String street;
+ @ManyToOne
+ private Person extraAddress;
+
public Address(String street) {
this.street = street;
}
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/IAddress.java b/ebean-test/src/test/java/org/tests/model/interfaces/IAddress.java
index 78b6d2005..9097d23d7 100644
--- a/ebean-test/src/test/java/org/tests/model/interfaces/IAddress.java
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/IAddress.java
@@ -1,6 +1,9 @@
package org.tests.model.interfaces;
public interface IAddress {
+
+ long getOid();
+
String getStreet();
void setStreet(String s);
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/IPerson.java b/ebean-test/src/test/java/org/tests/model/interfaces/IPerson.java
index 0d07ecc6b..3c1366185 100644
--- a/ebean-test/src/test/java/org/tests/model/interfaces/IPerson.java
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/IPerson.java
@@ -1,7 +1,16 @@
package org.tests.model.interfaces;
+import java.util.List;
+
public interface IPerson {
+
+ long getOid();
+
IAddress getDefaultAddress();
void setDefaultAddress(IAddress address);
+
+ List getExtraAddresses();
+
+ List getAddressLinks();
}
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/Person.java b/ebean-test/src/test/java/org/tests/model/interfaces/Person.java
index 5edbb68c7..50d6b9330 100644
--- a/ebean-test/src/test/java/org/tests/model/interfaces/Person.java
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/Person.java
@@ -1,11 +1,19 @@
package org.tests.model.interfaces;
+import io.ebean.annotation.EntityImplements;
+
+import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.Id;
+import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
+import javax.persistence.OneToMany;
import javax.persistence.Version;
+import java.util.ArrayList;
+import java.util.List;
@Entity
+@EntityImplements(IPerson.class)
public class Person implements IPerson {
@Id
private long oid;
@@ -13,9 +21,15 @@ public class Person implements IPerson {
@Version
private int version;
- @ManyToOne(targetEntity = Address.class)
+ @ManyToOne(cascade = CascadeType.PERSIST)
private IAddress defaultAddress;
+ @OneToMany(cascade = CascadeType.PERSIST, orphanRemoval = true)
+ private List extraAddresses = new ArrayList<>();
+
+ @ManyToMany(cascade = CascadeType.PERSIST)
+ private List addressLinks = new ArrayList<>();
+
@Override
public IAddress getDefaultAddress() {
return defaultAddress;
@@ -42,4 +56,14 @@ public class Person implements IPerson {
this.version = version;
}
+ @Override
+ public List getExtraAddresses() {
+ return extraAddresses;
+ }
+
+ @Override
+ public List getAddressLinks() {
+ return addressLinks;
+ }
+
}
diff --git a/ebean-test/src/test/java/org/tests/model/interfaces/TestTargetEntity.java b/ebean-test/src/test/java/org/tests/model/interfaces/TestTargetEntity.java
index 974d7aeef..d27280ac8 100644
--- a/ebean-test/src/test/java/org/tests/model/interfaces/TestTargetEntity.java
+++ b/ebean-test/src/test/java/org/tests/model/interfaces/TestTargetEntity.java
@@ -30,7 +30,7 @@ public class TestTargetEntity extends BaseTestCase {
private Person setup() {
Address address = new Address("street");
DB.save(address);
- Person person = new Person();
+ Person person = DB.getDefault().createEntityBean(Person.class);
person.setDefaultAddress(address);
DB.save(person);
return person;