Investigate allowing an entity to extend entity ( ~"Customization" )

This commit is contained in:
Rob Bygrave
2022-05-17 13:47:08 +12:00
parent 4f3c9adf6f
commit 5f6c93cc2a
6 changed files with 149 additions and 35 deletions
@@ -119,6 +119,7 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
private Map<Class<?>, DeployBeanInfo<?>> deployInfoMap = new HashMap<>();
private Set<Class<?>> embeddedIdTypes = new HashSet<>();
private List<DeployBeanInfo<?>> embeddedBeans = new ArrayList<>();
private final List<CustomPair> customizedEntities = new ArrayList<>();
/**
* Create for a given database dbConfig.
@@ -511,6 +512,11 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
checkForValidEmbeddedId(d);
}
}
//for (CustomPair pair : customizedEntities) {
//BeanManager<?> newDesc = beanManagerMap.get(pair.overrideType.getName());
//BeanManager<?> oldDesc = beanManagerMap.get(pair.originalType.getName());
// System.out.println("replaced " + oldDesc + " with " + newDesc);
//}
}
private void checkForValidEmbeddedId(BeanDescriptor<?> d) {
@@ -618,7 +624,16 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
private void registerDescriptor(DeployBeanInfo<?> info) {
BeanDescriptor<?> desc = new BeanDescriptor<>(this, info.getDescriptor());
if (info.isCustomized()) {
// register for persisting via BeanManager which will use this descriptor
descMap.put("customized:" + desc.type().getName(), desc);
return;
}
descMap.put(desc.type().getName(), desc);
Class<?> originalType = info.getOriginalType();
if (originalType != null) {
descMap.put(originalType.getName(), desc);
}
if (desc.isDocStoreMapped()) {
descQueueMap.put(desc.docStoreQueueId(), desc);
}
@@ -657,6 +672,10 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
embeddedBeans.add(info);
}
}
for (CustomPair pair : customizedEntities) {
DeployBeanInfo<?> info = deployInfoMap.get(pair.originalType);
info.markAsCustomized();
}
}
private void registerEmbeddedBean(DeployBeanInfo<?> info) {
@@ -671,8 +690,14 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
*/
private void readEntityBeanTable() {
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
BeanTable beanTable = createBeanTable(info);
beanTableMap.put(beanTable.getBeanType(), beanTable);
if (!info.isCustomized()) {
BeanTable beanTable = createBeanTable(info);
beanTableMap.put(beanTable.getBeanType(), beanTable);
Class<?> originalType = info.getOriginalType();
if (originalType != null) {
beanTableMap.put(originalType, beanTable);
}
}
}
// register non-id embedded beans (after bean tables are created)
for (DeployBeanInfo<?> info : embeddedBeans) {
@@ -1506,6 +1531,13 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
return list;
}
/**
* Register that entity overrideType extends/customizes the originalType entity.
*/
public void addCustomizedEntity(Class<?> overrideType, Class<?> originalType) {
customizedEntities.add(new CustomPair(overrideType, originalType));
}
/**
* Comparator to sort the BeanDescriptors by name.
*/
@@ -1518,4 +1550,15 @@ public final class BeanDescriptorManager implements BeanDescriptorMap, SpiBeanTy
return o1.name().compareTo(o2.name());
}
}
private static final class CustomPair {
final Class<?> overrideType;
final Class<?> originalType;
CustomPair(Class<?> overrideType, Class<?> originalType) {
this.overrideType = overrideType;
this.originalType = originalType;
}
}
}
@@ -8,31 +8,14 @@ import io.ebean.config.DatabaseConfig;
import io.ebean.config.TableName;
import io.ebean.config.dbplatform.IdType;
import io.ebean.config.dbplatform.PlatformIdGenerator;
import io.ebean.event.BeanFindController;
import io.ebean.event.BeanPersistController;
import io.ebean.event.BeanPersistListener;
import io.ebean.event.BeanPostConstructListener;
import io.ebean.event.BeanPostLoad;
import io.ebean.event.BeanQueryAdapter;
import io.ebean.event.*;
import io.ebean.event.changelog.ChangeLogFilter;
import io.ebean.text.PathProperties;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.ConcurrencyMode;
import io.ebeaninternal.server.core.CacheOptions;
import io.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
import io.ebeaninternal.server.deploy.ChainedBeanPersistController;
import io.ebeaninternal.server.deploy.ChainedBeanPersistListener;
import io.ebeaninternal.server.deploy.ChainedBeanPostConstructListener;
import io.ebeaninternal.server.deploy.ChainedBeanPostLoad;
import io.ebeaninternal.server.deploy.ChainedBeanQueryAdapter;
import io.ebeaninternal.server.deploy.DeployPropertyParserMap;
import io.ebeaninternal.server.deploy.IdentityMode;
import io.ebeaninternal.server.deploy.IndexDefinition;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.PartitionMeta;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.TablespaceMeta;
import io.ebeaninternal.server.deploy.*;
import io.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import io.ebeaninternal.server.idgen.UuidV1IdGenerator;
import io.ebeaninternal.server.idgen.UuidV1RndIdGenerator;
@@ -41,13 +24,7 @@ import io.ebeaninternal.server.rawsql.SpiRawSql;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
/**
* Describes Beans including their deployment information.
@@ -118,6 +95,8 @@ public class DeployBeanDescriptor<T> {
* The EntityBean type used to create new EntityBeans.
*/
private final Class<T> beanType;
private Class<?> originalType;
private boolean customized;
private final List<BeanPersistController> persistControllers = new ArrayList<>();
private final List<BeanPersistListener> persistListeners = new ArrayList<>();
private final List<BeanQueryAdapter> queryAdapters = new ArrayList<>();
@@ -254,7 +233,7 @@ public class DeployBeanDescriptor<T> {
}
return partitionMeta;
}
public void setTablespaceMeta(TablespaceMeta tablespaceMeta) {
this.tablespaceMeta = tablespaceMeta;
}
@@ -312,11 +291,9 @@ public class DeployBeanDescriptor<T> {
}
public DeployBeanTable createDeployBeanTable() {
DeployBeanTable beanTable = new DeployBeanTable(getBeanType());
DeployBeanTable beanTable = new DeployBeanTable(beanType);
beanTable.setBaseTable(baseTable);
beanTable.setIdProperty(idProperty());
return beanTable;
}
@@ -392,6 +369,20 @@ public class DeployBeanDescriptor<T> {
this.properties = props;
}
public boolean isCustomized() {
return customized;
}
public void markAsCustomized() {
this.customized = true;
this.entityType = EntityType.VIEW;
this.dependentTables = new String[]{baseTable};
}
public Class<?> getOriginalType() {
return originalType;
}
/**
* Return the class type this BeanDescriptor describes.
*/
@@ -976,15 +967,18 @@ public class DeployBeanDescriptor<T> {
* Check valid mapping annotations on the class hierarchy.
*/
private void checkInheritance(Class<?> beanType) {
Class<?> parent = beanType.getSuperclass();
if (parent == null || Object.class.equals(parent)) {
// all good
return;
}
if (parent.isAnnotationPresent(Entity.class)) {
String msg = "Checking " + getBeanType() + " and found " + parent + " that has @Entity annotation rather than MappedSuperclass?";
throw new IllegalStateException(msg);
// String msg = "Checking " + getBeanType() + " and found " + parent + " that has @Entity annotation rather than MappedSuperclass?";
// throw new IllegalStateException(msg);
// allow an entity to extend an entity ... as "customization"
originalType = parent;
manager.addCustomizedEntity(beanType, parent);
return;
}
if (parent.isAnnotationPresent(MappedSuperclass.class)) {
// continue checking
@@ -77,4 +77,16 @@ public final class DeployBeanInfo<T> {
public boolean isEmbedded() {
return descriptor.isEmbedded();
}
public Class<?> getOriginalType() {
return descriptor.getOriginalType();
}
public void markAsCustomized() {
descriptor.markAsCustomized();
}
public boolean isCustomized() {
return descriptor.isCustomized();
}
}
@@ -3,10 +3,12 @@ package org.tests.model.basic.cache;
import io.ebean.annotation.Cache;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@Cache(naturalKey = "appName")
@Entity
@Table(name="ocached_app")
@UniqueConstraint(name="uq_ocached_app", columnNames = "app_name")
public class OCachedApp extends OCacheBase {
@@ -0,0 +1,28 @@
package org.tests.model.basic.cache;
import io.ebean.annotation.Cache;
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@Cache(naturalKey = "appName")
@Entity
@Table(name = "ocached_app")
@UniqueConstraint(name = "uq_ocached_app", columnNames = "app_name")
public class OCachedAppCustom extends OCachedApp {
String custom;
public OCachedAppCustom(String appName) {
super(appName);
}
public String getCustom() {
return custom;
}
public void setCustom(String custom) {
this.custom = custom;
}
}
@@ -0,0 +1,35 @@
package org.tests.model.basic.cache;
import io.ebean.DB;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
class TestCustomExtends {
@Test
void insert_find() {
OCachedApp orig = new OCachedApp("orig");
DB.save(orig);
OCachedAppCustom custom = new OCachedAppCustom("custom");
custom.setCustom("someCustomValue");
DB.save(custom);
OCachedAppCustom c1 = DB.find(OCachedAppCustom.class, custom.getId());
OCachedApp c2 = DB.find(OCachedApp.class, custom.getId());
OCachedAppCustom o1 = DB.find(OCachedAppCustom.class, orig.getId());
OCachedApp o2 = DB.find(OCachedApp.class, orig.getId());
assertThat(c1).isNotNull();
assertThat(c2).isNotNull();
assertThat(c2.getAppName()).isEqualTo(c1.getAppName());
assertThat(c2).isNotNull().isInstanceOf(OCachedAppCustom.class);
assertThat(o1).isNotNull();
assertThat(o2).isNotNull();
assertThat(o2.getAppName()).isEqualTo(o1.getAppName());
assertThat(o2).isNotNull().isInstanceOf(OCachedAppCustom.class);
}
}