#595 - Refactor - persistence context root type handling - contextGet() and contextRef()

This commit is contained in:
Robin Bygrave
2016-03-11 20:26:03 +13:00
parent cf783fbbb7
commit 33f69902dc
9 changed files with 174 additions and 146 deletions
@@ -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<T> 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
@@ -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.
@@ -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);
}
}
@@ -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;
}
@@ -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;
}
/**
@@ -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);
}
}
@@ -1668,6 +1668,13 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
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<T> implements MetaBeanInfo, BeanType<T> {
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
@@ -632,7 +632,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
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);
@@ -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);
}
}