diff --git a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java index c580f0bb7..a87c95ee6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java +++ b/src/main/java/com/avaje/ebeaninternal/server/core/DefaultServer.java @@ -580,47 +580,31 @@ public final class DefaultServer implements SpiServer, SpiEbeanServer { } BeanDescriptor desc = getBeanDescriptor(type); - // convert the id type if necessary id = desc.convertId(id); - Object ref = null; - PersistenceContext ctx = null; - + PersistenceContext pc = null; SpiTransaction t = transactionScopeManager.get(); if (t != null) { - // first try the persistence context - ctx = t.getPersistenceContext(); - ref = ctx.get(type, id); - } - - if (ref == null) { - InheritInfo inheritInfo = desc.getInheritInfo(); - if (inheritInfo != null) { - // we actually need to do a query because - // we don't know the type without the - // discriminator value - BeanProperty idProp = desc.getIdProperty(); - if (idProp == null) { - throw new PersistenceException("No ID properties for this type? " + desc); - } - - // just select the id properties and - // the discriminator column (auto added) - Query query = createQuery(type); - query.select(idProp.getName()).setId(id); - - ref = query.findUnique(); - - } else { - // use the default reference options - ref = desc.createReference(null, id); - } - - if (ctx != null) { - desc.contextPut(ctx, id, ref); + pc = t.getPersistenceContext(); + Object existing = desc.contextGet(pc, id); + if (existing != null) { + return (T)existing; } } - return (T) ref; + + InheritInfo inheritInfo = desc.getInheritInfo(); + if (inheritInfo == null) { + return (T)desc.contextRef(pc, null, id); + } + + BeanProperty idProp = desc.getIdProperty(); + if (idProp == null) { + throw new PersistenceException("No ID properties for this type? " + desc); + } + + // we actually need to do a query because we don't know the type without the discriminator + // value, just select the id property and discriminator column (auto added) + return find(type).select(idProp.getName()).setId(id).findUnique(); } @Override diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelp.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelp.java index 400c223a8..a3a3de6c3 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelp.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelp.java @@ -1,6 +1,8 @@ package com.avaje.ebeaninternal.server.deploy; import com.avaje.ebean.bean.EntityBean; +import com.avaje.ebean.bean.EntityBeanIntercept; +import com.avaje.ebean.bean.PersistenceContext; import com.avaje.ebeaninternal.server.query.SqlJoinType; import java.sql.SQLException; @@ -13,8 +15,11 @@ abstract class AssocOneHelp { protected final BeanPropertyAssocOne property; + protected final BeanDescriptor target; + AssocOneHelp(BeanPropertyAssocOne property) { this.property = property; + this.target = property.targetDescriptor; } /** * Effectively skip reading (the jdbc resultSet as already in the persistence context etc). @@ -26,7 +31,26 @@ abstract class AssocOneHelp { /** * Read and return the bean. */ - abstract Object read(DbReadContext ctx) throws SQLException; + Object read(DbReadContext ctx) throws SQLException { + + // Support for Inheritance hierarchy on exported OneToOne ? + Object id = property.targetIdBinder.read(ctx); + if (id == null) { + return null; + } + + PersistenceContext pc = ctx.getPersistenceContext(); + Object existing = target.contextGet(pc, id); + if (existing != null) { + return existing; + } + + Object ref = target.contextRef(pc, ctx.isReadOnly(), id); + EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept(); + ctx.register(property.name, ebi); + return ref; + } + /** * Read setting values into the bean. diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefExported.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefExported.java new file mode 100644 index 000000000..c0ff608f2 --- /dev/null +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefExported.java @@ -0,0 +1,33 @@ +package com.avaje.ebeaninternal.server.deploy; + +import com.avaje.ebeaninternal.server.query.SqlJoinType; + +/** + * Helper for BeanPropertyAssocOne for OneToOne exported reference - not so common. + */ +class AssocOneHelpRefExported extends AssocOneHelp { + + public AssocOneHelpRefExported(BeanPropertyAssocOne property) { + super(property); + } + + /** + * Append columns for foreign key columns. + */ + @Override + void appendSelect(DbSqlContext ctx, boolean subQuery) { + + // set appropriate tableAlias for the exported id columns + String relativePrefix = ctx.getRelativePrefix(property.getName()); + ctx.pushTableAlias(relativePrefix); + property.targetIdBinder.appendSelect(ctx, subQuery); + ctx.popTableAlias(); + } + + @Override + void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { + + String relativePrefix = ctx.getRelativePrefix(property.getName()); + property.tableJoin.addJoin(joinType, relativePrefix, ctx); + } +} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefInherit.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefInherit.java index 8aec32391..6b642d0f6 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefInherit.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefInherit.java @@ -2,7 +2,7 @@ package com.avaje.ebeaninternal.server.deploy; import com.avaje.ebean.bean.EntityBean; import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.bean.PersistenceContextUtil; +import com.avaje.ebean.bean.PersistenceContext; import com.avaje.ebeaninternal.server.query.SqlJoinType; import java.sql.SQLException; @@ -36,30 +36,24 @@ class AssocOneHelpRefInherit extends AssocOneHelp { if (rowInheritInfo == null) { return null; } - BeanDescriptor desc = rowInheritInfo.getBeanDescriptor(); - // read the foreign key column(s) Object id = property.targetIdBinder.read(ctx); if (id == null) { return null; } // check transaction context to see if it already exists - Class rowType = desc.rootBeanType; - Object existing = ctx.getPersistenceContext().get(rowType, id); + PersistenceContext pc = ctx.getPersistenceContext(); + BeanDescriptor desc = rowInheritInfo.getBeanDescriptor(); + Object existing = desc.contextGet(pc, id); if (existing != null) { return existing; } // for inheritance hierarchy create the correct type for this row... - Object ref = desc.createReference(ctx.isReadOnly(), id); - - Class rootType = PersistenceContextUtil.root(ref.getClass()); - ctx.getPersistenceContext().put(rootType, id, ref); - + Object ref = desc.contextRef(pc, ctx.isReadOnly(), id); EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept(); ctx.register(property.name, ebi); - return ref; } diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefSimple.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefSimple.java index 34bcd571c..72dd6a533 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefSimple.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpRefSimple.java @@ -1,49 +1,12 @@ package com.avaje.ebeaninternal.server.deploy; -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; - -import java.sql.SQLException; - /** * Helper for BeanPropertyAssocOne imported reference - this is the common case. */ class AssocOneHelpRefSimple extends AssocOneHelp { - private final BeanDescriptor target; - AssocOneHelpRefSimple(BeanPropertyAssocOne property) { super(property); - this.target = property.targetDescriptor; - } - - /** - * Read and set a Reference bean. - */ - @Override - Object read(DbReadContext ctx) throws SQLException { - - // read the foreign key column(s) - Object id = property.targetIdBinder.read(ctx); - if (id == null) { - return null; - } - - Class rowType = property.targetType; - - // check transaction context to see if it already exists - Object existing = ctx.getPersistenceContext().get(rowType, id); - if (existing != null) { - return existing; - } - - Object ref = target.createReference(ctx.isReadOnly(), id); - target.contextPut(ctx.getPersistenceContext(), id, ref); - - EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept(); - ctx.register(property.name, ebi); - - return ref; } /** diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpReferenceExported.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpReferenceExported.java deleted file mode 100644 index b1f3cca4e..000000000 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/AssocOneHelpReferenceExported.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.avaje.ebeaninternal.server.deploy; - -import com.avaje.ebean.bean.EntityBean; -import com.avaje.ebean.bean.EntityBeanIntercept; -import com.avaje.ebean.bean.PersistenceContext; -import com.avaje.ebeaninternal.server.query.SqlJoinType; - -import java.sql.SQLException; - -/** - * Helper for BeanPropertyAssocOne for OneToOne exported reference - not so common. - */ -final class AssocOneHelpReferenceExported extends AssocOneHelp { - - public AssocOneHelpReferenceExported(BeanPropertyAssocOne property) { - super(property); - } - - @Override - Object read(DbReadContext ctx) throws SQLException { - - // Support for Inheritance hierarchy on exported OneToOne ? - Object id = property.targetIdBinder.read(ctx); - if (id == null) { - return null; - } - - PersistenceContext pc = ctx.getPersistenceContext(); - Object existing = pc.get(property.targetType, id); - if (existing != null) { - return existing; - } - - Object ref = property.targetDescriptor.createReference(ctx.isReadOnly(), id); - property.targetDescriptor.contextPut(pc, id, ref); - - EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept(); - ctx.register(property.name, ebi); - return ref; - } - - /** - * Append columns for foreign key columns. - */ - @Override - void appendSelect(DbSqlContext ctx, boolean subQuery) { - - // set appropriate tableAlias for the exported id columns - String relativePrefix = ctx.getRelativePrefix(property.getName()); - ctx.pushTableAlias(relativePrefix); - property.targetIdBinder.appendSelect(ctx, subQuery); - ctx.popTableAlias(); - } - - @Override - void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { - - String relativePrefix = ctx.getRelativePrefix(property.getName()); - property.tableJoin.addJoin(joinType, relativePrefix, ctx); - } -} diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java index 004048df5..dbeec00f1 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanDescriptor.java @@ -1668,6 +1668,13 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType { return fullName; } + /** + * Get the bean from the persistence context. + */ + public Object contextGet(PersistenceContext pc, Object id) { + return pc.get(rootBeanType, id); + } + /** * Put the bean into the persistence context. */ @@ -1682,6 +1689,17 @@ public class BeanDescriptor implements MetaBeanInfo, BeanType { return pc.putIfAbsent(rootBeanType, id, localBean); } + /** + * Create a reference bean and put it in the persistence context (and return it). + */ + public Object contextRef(PersistenceContext pc, Boolean readOnly, Object id) { + Object ref = createReference(readOnly, id); + if (pc != null) { + contextPut(pc, id, ref); + } + return ref; + } + /** * Helper method to return the unique property. If only one property makes up * the unique id then it's value is returned. If there is a concatenated diff --git a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java index 7a826359b..f3115df82 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java +++ b/src/main/java/com/avaje/ebeaninternal/server/deploy/BeanPropertyAssocOne.java @@ -632,7 +632,7 @@ public class BeanPropertyAssocOne extends BeanPropertyAssoc { if (embedded) { return new AssocOneHelpEmbedded(this); } else if (oneToOneExported) { - return new AssocOneHelpReferenceExported(this); + return new AssocOneHelpRefExported(this); } else { if (targetInheritInfo != null) { return new AssocOneHelpRefInherit(this); diff --git a/src/test/java/com/avaje/ebeaninternal/server/core/DefaultServer_getReferenceTest.java b/src/test/java/com/avaje/ebeaninternal/server/core/DefaultServer_getReferenceTest.java new file mode 100644 index 000000000..49ce761c1 --- /dev/null +++ b/src/test/java/com/avaje/ebeaninternal/server/core/DefaultServer_getReferenceTest.java @@ -0,0 +1,73 @@ +package com.avaje.ebeaninternal.server.core; + + +import com.avaje.ebean.BaseTestCase; +import com.avaje.ebean.Ebean; +import com.avaje.tests.model.basic.Car; +import com.avaje.tests.model.basic.Customer; +import com.avaje.tests.model.basic.ResetBasicData; +import com.avaje.tests.model.basic.Vehicle; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class DefaultServer_getReferenceTest extends BaseTestCase { + + @Test + public void getReference_noPC() { + Ebean.getReference(Customer.class, 42); + } + + @Test + public void getReference_when_inPC_expect_getFromPC() { + + ResetBasicData.reset(); + + Ebean.beginTransaction(); + try { + Customer loaded = Ebean.find(Customer.class, 1); + Customer reference = Ebean.getReference(Customer.class, 1); + assertThat(loaded).isSameAs(reference); + + } finally { + Ebean.endTransaction(); + } + } + + + @Test + public void inherit_getReference_when_noPC() { + + Car car = new Car(); + car.setDriver("TestForRef"); + Ebean.save(car); + + Vehicle reference = Ebean.getReference(Vehicle.class, car.getId()); + + assertThat(reference).isInstanceOf(Car.class); + assertThat(reference.getId()).isSameAs(car.getId()); + + Ebean.delete(car); + } + + + @Test + public void inherit_getReference_when_inPC() { + + Car car = new Car(); + car.setDriver("TestForRef"); + Ebean.save(car); + + Ebean.beginTransaction(); + try { + Vehicle loaded = Ebean.find(Vehicle.class, car.getId()); + Vehicle reference = Ebean.getReference(Vehicle.class, car.getId()); + assertThat(reference).isSameAs(loaded); + + } finally { + Ebean.endTransaction(); + } + + Ebean.delete(car); + } +} \ No newline at end of file