diff --git a/src/main/java/io/ebeaninternal/server/cache/CachedBeanId.java b/src/main/java/io/ebeaninternal/server/cache/CachedBeanId.java
new file mode 100644
index 000000000..45eab630c
--- /dev/null
+++ b/src/main/java/io/ebeaninternal/server/cache/CachedBeanId.java
@@ -0,0 +1,54 @@
+package io.ebeaninternal.server.cache;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+
+/**
+ * Bean Id value plus discriminator type.
+ *
+ * Put into L2 cache such that we know the type of a bean with inheritance.
+ */
+public class CachedBeanId implements Externalizable {
+
+ private String discValue;
+ private Object id;
+
+ public CachedBeanId(String discValue, Object id) {
+ this.discValue = discValue;
+ this.id = id;
+ }
+
+ /**
+ * Construct from serialisation.
+ */
+ public CachedBeanId() {
+ }
+
+ @Override
+ public void writeExternal(ObjectOutput out) throws IOException {
+ out.writeUTF(discValue);
+ out.writeObject(id);
+ }
+
+ @Override
+ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
+ discValue = in.readUTF();
+ id = in.readObject();
+ }
+
+ @Override
+ public String toString() {
+ return discValue + ":" + id;
+ }
+
+ public String getDiscValue() {
+ return discValue;
+ }
+
+ public Object getId() {
+ return id;
+ }
+
+}
diff --git a/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java b/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java
index 6db27933a..f7637296d 100644
--- a/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java
+++ b/src/main/java/io/ebeaninternal/server/core/PersistRequestBean.java
@@ -699,7 +699,7 @@ public final class PersistRequestBean extends PersistRequest implements BeanP
* Create and return a new reference bean matching this beans Id value.
*/
public T createReference() {
- return beanDescriptor.createReference(getBeanId(), null);
+ return beanDescriptor.createRef(getBeanId(), null);
}
/**
diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java
index d2adb35f4..78cf988dd 100644
--- a/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java
+++ b/src/main/java/io/ebeaninternal/server/deploy/BeanDescriptor.java
@@ -2053,14 +2053,16 @@ public class BeanDescriptor implements BeanType, STreeType {
/**
* Create a non read only reference bean without checking cacheSharableBeans.
*/
- @SuppressWarnings("unchecked")
public T createReference(Object id, PersistenceContext pc) {
+ if (inheritInfo != null && !inheritInfo.isConcrete()) {
+ return findReferenceBean(id, pc);
+ }
+ return createRef(id, pc);
+ }
+ @SuppressWarnings("unchecked")
+ public T createRef(Object id, PersistenceContext pc) {
try {
- if (inheritInfo != null && !inheritInfo.isConcrete()) {
- return findReferenceBean(id, pc);
- }
-
EntityBean eb = createEntityBean();
id = convertSetId(id, eb);
EntityBeanIntercept ebi = eb._ebean_getIntercept();
@@ -2071,7 +2073,6 @@ public class BeanDescriptor implements BeanType, STreeType {
ebi.setPersistenceContext(pc);
}
return (T) eb;
-
} catch (Exception ex) {
throw new PersistenceException(ex);
}
diff --git a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java
index a9919dce8..d33dd95c2 100644
--- a/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java
+++ b/src/main/java/io/ebeaninternal/server/deploy/BeanPropertyAssocOne.java
@@ -13,6 +13,7 @@ import io.ebeaninternal.api.json.SpiJsonReader;
import io.ebeaninternal.api.json.SpiJsonWriter;
import io.ebeaninternal.server.cache.CacheChangeSet;
import io.ebeaninternal.server.cache.CachedBeanData;
+import io.ebeaninternal.server.cache.CachedBeanId;
import io.ebeaninternal.server.core.DefaultSqlUpdate;
import io.ebeaninternal.server.deploy.id.ImportedId;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
@@ -433,11 +434,19 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STr
}
if (embedded) {
return targetDescriptor.cacheEmbeddedBeanExtract((EntityBean) ap);
+ } else if (targetInheritInfo != null) {
+ return createCacheBeanId(ap);
} else {
return targetDescriptor.getIdProperty().getCacheDataValue((EntityBean) ap);
}
}
+ private Object createCacheBeanId(Object bean) {
+ final BeanDescriptor> desc = targetDescriptor.descOf(bean.getClass());
+ final Object id = desc.getIdProperty().getCacheDataValue((EntityBean) bean);
+ return new CachedBeanId(desc.getDiscValue(), id);
+ }
+
@Override
public String format(Object value) {
return targetDescriptor.getIdBinder().cacheKey(value);
@@ -451,19 +460,31 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STr
if (embedded) {
setValue(bean, targetDescriptor.cacheEmbeddedBeanLoad((CachedBeanData) cacheData, context));
} else {
- if (cacheData instanceof String) {
- cacheData = targetDescriptor.getIdProperty().scalarType.parse((String) cacheData);
+ if (cacheData instanceof CachedBeanId) {
+ setValue(bean, refInheritBean((CachedBeanId) cacheData, context));
+ } else {
+ setValue(bean, refBean(targetDescriptor, cacheData, context));
}
- // cacheData is the id value, maybe already in persistence context
- Object assocBean = targetDescriptor.contextGet(context, cacheData);
- if (assocBean == null) {
- assocBean = targetDescriptor.createReference(cacheData, context);
- }
- setValue(bean, assocBean);
}
}
}
+ private Object refInheritBean(CachedBeanId cacheId, PersistenceContext context) {
+ final InheritInfo rowInheritInfo = targetInheritInfo.readType(cacheId.getDiscValue());
+ return refBean(rowInheritInfo.desc(), cacheId.getId(), context);
+ }
+
+ private Object refBean(BeanDescriptor> desc, Object id, PersistenceContext context) {
+ if (id instanceof String) {
+ id = desc.getIdProperty().scalarType.parse((String) id);
+ }
+ Object bean = desc.contextGet(context, id);
+ if (bean == null) {
+ bean = desc.createRef(id, context);
+ }
+ return bean;
+ }
+
@Override
public ScalarDataReader> getIdReader() {
return targetDescriptor.getIdProperty();
@@ -640,14 +661,12 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc implements STr
@Override
public void addTenant(SpiQuery> query, Object tenantId) {
- T refBean = targetDescriptor.createReference(tenantId, null);
- query.where().eq(name, refBean);
+ query.where().eq(name, targetDescriptor.createRef(tenantId, null));
}
@Override
public void setTenantValue(EntityBean entityBean, Object tenantId) {
- T refBean = targetDescriptor.createReference(tenantId, null);
- setValue(entityBean, refBean);
+ setValue(entityBean, targetDescriptor.createRef(tenantId, null));
}
@Override
diff --git a/src/test/java/org/tests/inheritance/cache/CIAddress.java b/src/test/java/org/tests/inheritance/cache/CIAddress.java
new file mode 100644
index 000000000..fea779587
--- /dev/null
+++ b/src/test/java/org/tests/inheritance/cache/CIAddress.java
@@ -0,0 +1,19 @@
+package org.tests.inheritance.cache;
+
+import javax.persistence.Entity;
+import javax.persistence.ManyToOne;
+
+@Entity
+public class CIAddress extends CIBaseModel {
+
+ @ManyToOne
+ public CIStreetParent street;
+
+ public CIStreetParent getStreet() {
+ return street;
+ }
+
+ public void setStreet(CIStreetParent street) {
+ this.street = street;
+ }
+}
diff --git a/src/test/java/org/tests/inheritance/cache/CIBaseModel.java b/src/test/java/org/tests/inheritance/cache/CIBaseModel.java
new file mode 100644
index 000000000..64e983fa8
--- /dev/null
+++ b/src/test/java/org/tests/inheritance/cache/CIBaseModel.java
@@ -0,0 +1,23 @@
+package org.tests.inheritance.cache;
+
+import io.ebean.Model;
+import io.ebean.annotation.Cache;
+
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+
+@Cache(enableQueryCache=true)
+@MappedSuperclass
+public abstract class CIBaseModel extends Model {
+
+ @Id
+ protected long id;
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(long id) {
+ this.id = id;
+ }
+}
diff --git a/src/test/java/org/tests/inheritance/cache/CICustomer.java b/src/test/java/org/tests/inheritance/cache/CICustomer.java
new file mode 100644
index 000000000..3ac832e3d
--- /dev/null
+++ b/src/test/java/org/tests/inheritance/cache/CICustomer.java
@@ -0,0 +1,19 @@
+package org.tests.inheritance.cache;
+
+import javax.persistence.DiscriminatorValue;
+import javax.persistence.Entity;
+
+@Entity
+@DiscriminatorValue(value="1")
+public class CICustomer extends CICustomerParent {
+
+ public String notes;
+
+ public String getNotes() {
+ return notes;
+ }
+
+ public void setNotes(String notes) {
+ this.notes = notes;
+ }
+}
diff --git a/src/test/java/org/tests/inheritance/cache/CICustomerParent.java b/src/test/java/org/tests/inheritance/cache/CICustomerParent.java
new file mode 100644
index 000000000..fdcbf0a3f
--- /dev/null
+++ b/src/test/java/org/tests/inheritance/cache/CICustomerParent.java
@@ -0,0 +1,27 @@
+package org.tests.inheritance.cache;
+
+import javax.persistence.DiscriminatorColumn;
+import javax.persistence.DiscriminatorType;
+import javax.persistence.DiscriminatorValue;
+import javax.persistence.Entity;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+import javax.persistence.ManyToOne;
+
+@Entity
+@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
+@DiscriminatorColumn(name = "dtype", discriminatorType = DiscriminatorType.INTEGER)
+@DiscriminatorValue(value = "0")
+public class CICustomerParent extends CIBaseModel {
+
+ @ManyToOne
+ protected CIAddress address;
+
+ public CIAddress getAddress() {
+ return address;
+ }
+
+ public void setAddress(CIAddress adress) {
+ this.address = adress;
+ }
+}
diff --git a/src/test/java/org/tests/inheritance/cache/CIStreet.java b/src/test/java/org/tests/inheritance/cache/CIStreet.java
new file mode 100644
index 000000000..0b4293396
--- /dev/null
+++ b/src/test/java/org/tests/inheritance/cache/CIStreet.java
@@ -0,0 +1,21 @@
+package org.tests.inheritance.cache;
+
+import javax.persistence.DiscriminatorValue;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+@Entity
+@Table(name = "street")
+@DiscriminatorValue(value = "1")
+public class CIStreet extends CIStreetParent {
+
+ protected String number;
+
+ public String getNumber() {
+ return number;
+ }
+
+ public void setNumber(String number) {
+ this.number = number;
+ }
+}
diff --git a/src/test/java/org/tests/inheritance/cache/CIStreetParent.java b/src/test/java/org/tests/inheritance/cache/CIStreetParent.java
new file mode 100644
index 000000000..9ea1f90a7
--- /dev/null
+++ b/src/test/java/org/tests/inheritance/cache/CIStreetParent.java
@@ -0,0 +1,25 @@
+package org.tests.inheritance.cache;
+
+import javax.persistence.DiscriminatorColumn;
+import javax.persistence.DiscriminatorType;
+import javax.persistence.DiscriminatorValue;
+import javax.persistence.Entity;
+import javax.persistence.Inheritance;
+import javax.persistence.InheritanceType;
+
+@Entity
+@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
+@DiscriminatorColumn(name = "dtype", discriminatorType = DiscriminatorType.INTEGER)
+@DiscriminatorValue(value = "0")
+public class CIStreetParent extends CIBaseModel {
+
+ protected String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/src/test/java/org/tests/inheritance/cache/TestInheritCacheRefLoad.java b/src/test/java/org/tests/inheritance/cache/TestInheritCacheRefLoad.java
new file mode 100644
index 000000000..0f79a4206
--- /dev/null
+++ b/src/test/java/org/tests/inheritance/cache/TestInheritCacheRefLoad.java
@@ -0,0 +1,63 @@
+package org.tests.inheritance.cache;
+
+import io.ebean.DB;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestInheritCacheRefLoad {
+
+ @Test
+ public void findWithRefToInheritStreetBean_expect_correctStreetType() {
+
+ DB.find(CICustomerParent.class).delete();
+ DB.find(CIAddress.class).delete();
+ DB.find(CIStreet.class).delete();
+
+ //=========================================================================
+ // 1 - Create Data (Customer(dtype 1) -> Address(no dtype) -> Street(dtype 1)
+ //=========================================================================
+ CIStreet street = new CIStreet();
+ street.save();
+ CIAddress address = new CIAddress();
+ address.setStreet(street);
+ address.save();
+ CICustomer customer = new CICustomer();
+ customer.setAddress(address);
+ customer.save();
+
+ //=========================================================================
+ // 2 - Read Data (no Cache Hit)
+ //=========================================================================
+ Class streetClass = reloadCustomer(customer, false); //returns Street -> OK
+ Assert.assertEquals("org.tests.inheritance.cache.CIStreet", streetClass.getName());
+ streetClass = reloadCustomer(customer, false); //returns Street -> OK
+ Assert.assertEquals("org.tests.inheritance.cache.CIStreet", streetClass.getName());
+
+ //=========================================================================
+ // 3 - Read Data (L2-Cache Hit)
+ //=========================================================================
+ streetClass = reloadCustomer(customer, true); //returns Street -> OK
+ Assert.assertEquals("org.tests.inheritance.cache.CIStreet", streetClass.getName());
+ streetClass = reloadCustomer(customer, true); //returns StreetParent -> NOT OK
+ Assert.assertEquals("org.tests.inheritance.cache.CIStreet", streetClass.getName());
+ }
+
+
+ public Class reloadCustomer(CICustomer customer, boolean l2Cache) {
+
+ // Load Customer via Query, L2Cache on/off
+ CICustomer customerReloaded =
+ DB.find(CICustomer.class)
+ .where().eq("id", customer.getId())
+ .setUseCache(l2Cache)
+ .findOne();
+
+ //Access Street by lazy Loading
+ Class streetClassLazyLoaded = customerReloaded.getAddress().getStreet().getClass();
+
+ //Show Cache Hits
+ System.out.println("Class of Street (Cache on: " + l2Cache + "): " + streetClassLazyLoaded + " | Cache Hit: " + DB.getDefault().getServerCacheManager().getQueryCache(streetClassLazyLoaded).getStatistics(false).getHitCount());
+ return streetClassLazyLoaded;
+ }
+
+}
diff --git a/src/test/java/org/tests/inheritance/cache/TestInheritanceRefCache.java b/src/test/java/org/tests/inheritance/cache/TestInheritanceRefCache.java
index 103563e8f..ad104db21 100644
--- a/src/test/java/org/tests/inheritance/cache/TestInheritanceRefCache.java
+++ b/src/test/java/org/tests/inheritance/cache/TestInheritanceRefCache.java
@@ -49,8 +49,7 @@ public class TestInheritanceRefCache extends BaseTestCase {
assertThat(gotRef).isInstanceOf(CInhRef.class);
assertThat(gotRef.getRef()).isInstanceOf(CInhOne.class);
sql = LoggedSqlCollector.stop();
- assertThat(sql).hasSize(1);
- assertSql(sql.get(0)).contains("from cinh_root");
+ assertThat(sql).hasSize(0);
// fetch again - both from cache