mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-24 15:07:14 +00:00
Merge pull request #874 from FOCONIS/feature/inheritance
Feature/inheritance
This commit is contained in:
@@ -85,6 +85,7 @@ import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.SQLFeatureNotSupportedException;
|
||||
import java.sql.Types;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -868,7 +869,9 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
|
||||
|
||||
public void initialiseFkeys() {
|
||||
for (BeanPropertyAssocOne<?> aPropertiesOneImported : propertiesOneImported) {
|
||||
aPropertiesOneImported.addFkey();
|
||||
if (!aPropertiesOneImported.isFormula()) {
|
||||
aPropertiesOneImported.addFkey();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -330,7 +330,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
|
||||
if (descriptor.isRawSqlBased()) {
|
||||
String dbColumn = owner.getDbColumn();
|
||||
return new ImportedIdSimple(owner, dbColumn, idProp, 0);
|
||||
return new ImportedIdSimple(owner, dbColumn, null, idProp, 0);
|
||||
}
|
||||
|
||||
TableJoinColumn[] cols = join.columns();
|
||||
@@ -373,16 +373,17 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
|
||||
String matchColumn = col.getForeignDbColumn();
|
||||
String localColumn = col.getLocalDbColumn();
|
||||
String localSqlFormula = col.getLocalSqlFormula();
|
||||
|
||||
for (int j = 0; j < props.length; j++) {
|
||||
if (props[j].getDbColumn().equalsIgnoreCase(matchColumn)) {
|
||||
return new ImportedIdSimple(owner, localColumn, props[j], j);
|
||||
return new ImportedIdSimple(owner, localColumn, localSqlFormula, props[j], j);
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 0; j < others.length; j++) {
|
||||
if (others[j].getDbColumn().equalsIgnoreCase(matchColumn)) {
|
||||
return new ImportedIdSimple(owner, localColumn, others[j], j + props.length);
|
||||
return new ImportedIdSimple(owner, localColumn, localSqlFormula, others[j], j + props.length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -570,6 +570,9 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
if (!isTransient) {
|
||||
localHelp.appendFrom(ctx, joinType);
|
||||
if (sqlFormulaJoin != null) {
|
||||
ctx.appendFormulaJoin(sqlFormulaJoin, joinType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ public class BeanTable {
|
||||
return beanType;
|
||||
}
|
||||
|
||||
public void createJoinColumn(String foreignKeyPrefix, DeployTableJoin join, boolean reverse) {
|
||||
public void createJoinColumn(String foreignKeyPrefix, DeployTableJoin join, boolean reverse, String sqlFormulaSelect) {
|
||||
|
||||
boolean complexKey = false;
|
||||
BeanProperty[] props = idProperties;
|
||||
@@ -104,8 +104,12 @@ public class BeanTable {
|
||||
logger.debug(msg);
|
||||
fk = lc;
|
||||
}
|
||||
if (sqlFormulaSelect != null) {
|
||||
fk = sqlFormulaSelect;
|
||||
}
|
||||
|
||||
DeployTableJoinColumn joinCol = new DeployTableJoinColumn(lc, fk);
|
||||
joinCol.setForeignSqlFormula(sqlFormulaSelect);
|
||||
if (reverse) {
|
||||
joinCol = joinCol.reverse();
|
||||
}
|
||||
|
||||
@@ -13,14 +13,19 @@ public class TableJoinColumn {
|
||||
*/
|
||||
private final String localDbColumn;
|
||||
|
||||
private final String localSqlFormula;
|
||||
|
||||
/**
|
||||
* The foreign database column name.
|
||||
*/
|
||||
private final String foreignDbColumn;
|
||||
|
||||
private final String foreignSqlFormula;
|
||||
|
||||
private final boolean insertable;
|
||||
|
||||
private final boolean updateable;
|
||||
|
||||
|
||||
/**
|
||||
* Hash for including in a query plan
|
||||
@@ -33,6 +38,9 @@ public class TableJoinColumn {
|
||||
public TableJoinColumn(DeployTableJoinColumn deploy) {
|
||||
this.localDbColumn = InternString.intern(deploy.getLocalDbColumn());
|
||||
this.foreignDbColumn = InternString.intern(deploy.getForeignDbColumn());
|
||||
this.localSqlFormula = InternString.intern(deploy.getLocalSqlFormula());
|
||||
this.foreignSqlFormula = InternString.intern(deploy.getForeignSqlFormula());
|
||||
|
||||
this.insertable = deploy.isInsertable();
|
||||
this.updateable = deploy.isUpdateable();
|
||||
this.queryHash = hash();
|
||||
@@ -41,8 +49,11 @@ public class TableJoinColumn {
|
||||
int hash() {
|
||||
int result = localDbColumn != null ? localDbColumn.hashCode() : 0;
|
||||
result = 92821 * result + (foreignDbColumn != null ? foreignDbColumn.hashCode() : 0);
|
||||
result = 92821 * result + (localSqlFormula != null ? localSqlFormula.hashCode() : 0);
|
||||
result = 92821 * result + (foreignSqlFormula != null ? foreignSqlFormula.hashCode() : 0);
|
||||
result = 92821 * result + (insertable ? 1 : 0);
|
||||
result = 92821 * result + (updateable ? 1 : 0);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -64,7 +75,8 @@ public class TableJoinColumn {
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return localDbColumn + " = " + foreignDbColumn;
|
||||
return (localSqlFormula == null ? localDbColumn : localSqlFormula) + " = "
|
||||
+ (foreignSqlFormula == null ? foreignDbColumn : foreignSqlFormula);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,4 +113,13 @@ public class TableJoinColumn {
|
||||
public boolean isUpdateable() {
|
||||
return updateable;
|
||||
}
|
||||
|
||||
public String getLocalSqlFormula() {
|
||||
return localSqlFormula;
|
||||
}
|
||||
|
||||
public String getForeignSqlFormula() {
|
||||
return foreignSqlFormula;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,6 +37,8 @@ public final class ImportedIdSimple implements ImportedId, Comparable<ImportedId
|
||||
protected final BeanPropertyAssoc<?> owner;
|
||||
|
||||
protected final String localDbColumn;
|
||||
|
||||
protected final String localSqlFormula;
|
||||
|
||||
protected final String logicalName;
|
||||
|
||||
@@ -44,9 +46,10 @@ public final class ImportedIdSimple implements ImportedId, Comparable<ImportedId
|
||||
|
||||
protected final int position;
|
||||
|
||||
public ImportedIdSimple(BeanPropertyAssoc<?> owner, String localDbColumn, BeanProperty foreignProperty, int position) {
|
||||
public ImportedIdSimple(BeanPropertyAssoc<?> owner, String localDbColumn, String localSqlFormula, BeanProperty foreignProperty, int position) {
|
||||
this.owner = owner;
|
||||
this.localDbColumn = InternString.intern(localDbColumn);
|
||||
this.localSqlFormula = InternString.intern(localSqlFormula);
|
||||
this.foreignProperty = foreignProperty;
|
||||
this.position = position;
|
||||
this.logicalName = InternString.intern(owner.getName() + "." + foreignProperty.getName());
|
||||
@@ -103,7 +106,11 @@ public final class ImportedIdSimple implements ImportedId, Comparable<ImportedId
|
||||
}
|
||||
|
||||
public void sqlAppend(DbSqlContext ctx) {
|
||||
ctx.appendColumn(localDbColumn);
|
||||
if (localSqlFormula != null) {
|
||||
ctx.appendFormulaSelect(localSqlFormula);
|
||||
} else {
|
||||
ctx.appendColumn(localDbColumn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+9
@@ -96,4 +96,13 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
tableJoin.setLocalColumn(dbColumn);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSqlFormula(String formulaSelect, String formulaJoin) {
|
||||
super.setSqlFormula(formulaSelect, formulaJoin);
|
||||
DeployTableJoinColumn[] columns = tableJoin.columns();
|
||||
if (columns.length == 1) {
|
||||
columns[0].setLocalSqlFormula(formulaSelect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+55
-5
@@ -15,14 +15,26 @@ public class DeployTableJoinColumn {
|
||||
*/
|
||||
String localDbColumn;
|
||||
|
||||
/**
|
||||
* SQL formula used for local column
|
||||
*/
|
||||
String localSqlFormula;
|
||||
|
||||
/**
|
||||
* The foreign database column name.
|
||||
*/
|
||||
String foreignDbColumn;
|
||||
|
||||
/**
|
||||
* SQL formula used for foreign column
|
||||
*/
|
||||
String foreignSqlFormula;
|
||||
|
||||
final boolean insertable;
|
||||
boolean insertable;
|
||||
|
||||
boolean updateable;
|
||||
|
||||
|
||||
final boolean updateable;
|
||||
|
||||
/**
|
||||
* Construct when automatically determining the join.
|
||||
@@ -43,7 +55,34 @@ public class DeployTableJoinColumn {
|
||||
this.insertable = insertable;
|
||||
this.updateable = updateable;
|
||||
}
|
||||
|
||||
|
||||
public void setLocalSqlFormula(String localSqlFormula) {
|
||||
if (localSqlFormula != null) {
|
||||
this.localSqlFormula = localSqlFormula;
|
||||
this.localDbColumn = null;
|
||||
this.insertable = false;
|
||||
this.updateable = false;
|
||||
}
|
||||
}
|
||||
|
||||
public String getLocalSqlFormula() {
|
||||
return localSqlFormula;
|
||||
}
|
||||
|
||||
public void setForeignSqlFormula(String foreignSqlFormula) {
|
||||
if (foreignSqlFormula != null) {
|
||||
this.foreignSqlFormula = foreignSqlFormula;
|
||||
this.foreignDbColumn = null;
|
||||
this.insertable = false;
|
||||
this.updateable = false;
|
||||
}
|
||||
}
|
||||
|
||||
public String getForeignSqlFormula() {
|
||||
return foreignSqlFormula;
|
||||
}
|
||||
|
||||
|
||||
public DeployTableJoinColumn(boolean order, JoinColumn jc, BeanTable beanTable) {
|
||||
this(jc.referencedColumnName(), jc.name(), jc.insertable(), jc.updatable());
|
||||
setReferencedColumn(beanTable);
|
||||
@@ -68,6 +107,10 @@ public class DeployTableJoinColumn {
|
||||
String temp = localDbColumn;
|
||||
localDbColumn = foreignDbColumn;
|
||||
foreignDbColumn = temp;
|
||||
|
||||
temp = localSqlFormula;
|
||||
localSqlFormula = foreignSqlFormula;
|
||||
foreignSqlFormula = temp;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -86,12 +129,18 @@ public class DeployTableJoinColumn {
|
||||
// Note that the insertable and updateable are just copied
|
||||
// which may not always be the correct thing to do
|
||||
// but will leave it like this for now
|
||||
DeployTableJoinColumn ret;
|
||||
if (reverse){
|
||||
return new DeployTableJoinColumn(foreignDbColumn, localDbColumn, insertable, updateable);
|
||||
ret = new DeployTableJoinColumn(foreignDbColumn, localDbColumn, insertable, updateable);
|
||||
ret.setLocalSqlFormula(foreignSqlFormula);
|
||||
ret.setForeignSqlFormula(localSqlFormula);
|
||||
|
||||
} else {
|
||||
return new DeployTableJoinColumn(localDbColumn, foreignDbColumn, insertable, updateable);
|
||||
ret = new DeployTableJoinColumn(localDbColumn, foreignDbColumn, insertable, updateable);
|
||||
ret.setLocalSqlFormula(localSqlFormula);
|
||||
ret.setForeignSqlFormula(foreignSqlFormula);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
@@ -132,4 +181,5 @@ public class DeployTableJoinColumn {
|
||||
public void setLocalDbColumn(String localDbColumn) {
|
||||
this.localDbColumn = localDbColumn;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ public class AnnotationAssocManys extends AnnotationParser {
|
||||
|
||||
// Use the owning bean table to define the join
|
||||
BeanTable owningBeanTable = factory.getBeanTable(descriptor.getBeanType());
|
||||
owningBeanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), false);
|
||||
owningBeanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), false, prop.getSqlFormulaSelect());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -142,7 +142,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
|
||||
fkeyPrefix = nc.getColumnFromProperty(beanType, prop.getName());
|
||||
}
|
||||
|
||||
beanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), true);
|
||||
beanTable.createJoinColumn(fkeyPrefix, prop.getTableJoin(), true, prop.getSqlFormulaSelect());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +103,11 @@ public class AnnotationFields extends AnnotationParser {
|
||||
}
|
||||
readEmbeddedAttributeOverrides((DeployBeanPropertyAssocOne<?>) prop);
|
||||
}
|
||||
|
||||
Formula formula = get(prop, Formula.class);
|
||||
if (formula != null) {
|
||||
prop.setSqlFormula(formula.select(), formula.join());
|
||||
}
|
||||
}
|
||||
|
||||
private void readField(DeployBeanProperty prop) {
|
||||
|
||||
@@ -152,6 +152,7 @@ public class CQueryPredicates {
|
||||
// this is a find by id type query...
|
||||
request.getBeanDescriptor().bindId(dataBind, idValue);
|
||||
dataBind.append(idValue);
|
||||
dataBind.append(", ");
|
||||
}
|
||||
|
||||
if (bindParams != null) {
|
||||
|
||||
@@ -136,9 +136,20 @@ class DefaultDbSqlContext implements DbSqlContext {
|
||||
if (i > 0) {
|
||||
sb.append(" and ");
|
||||
}
|
||||
sb.append(a2).append(".").append(pair.getForeignDbColumn());
|
||||
|
||||
if (pair.getForeignSqlFormula() != null) {
|
||||
sb.append(StringHelper.replaceString(pair.getForeignSqlFormula(), tableAliasPlaceHolder, a2));
|
||||
} else {
|
||||
sb.append(a2).append(".").append(pair.getForeignDbColumn());
|
||||
}
|
||||
|
||||
sb.append(" = ");
|
||||
sb.append(a1).append(".").append(pair.getLocalDbColumn());
|
||||
|
||||
if (pair.getLocalSqlFormula() != null) {
|
||||
sb.append(StringHelper.replaceString(pair.getLocalSqlFormula(), tableAliasPlaceHolder, a1));
|
||||
} else {
|
||||
sb.append(a1).append(".").append(pair.getLocalDbColumn());
|
||||
}
|
||||
}
|
||||
|
||||
// add on any inheritance where clause
|
||||
@@ -244,6 +255,8 @@ class DefaultDbSqlContext implements DbSqlContext {
|
||||
|
||||
sb.append(COMMA);
|
||||
sb.append(converted);
|
||||
|
||||
appendColumnAlias();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
|
||||
package com.avaje.tests.family;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.avaje.ebean.BaseTestCase;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
|
||||
import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import com.avaje.tests.lib.EbeanTestCase;
|
||||
import com.avaje.tests.model.basic.EBasic;
|
||||
import com.avaje.tests.model.family.ChildPerson;
|
||||
import com.avaje.tests.model.family.GrandParentPerson;
|
||||
import com.avaje.tests.model.family.ParentPerson;
|
||||
|
||||
|
||||
|
||||
public class TestInheritance extends BaseTestCase {
|
||||
|
||||
|
||||
@Test
|
||||
public void testDescriptor() {
|
||||
BeanDescriptor<GrandParentPerson> desc = spiEbeanServer().getBeanDescriptor(GrandParentPerson.class);
|
||||
BeanProperty prop1 = desc.getBeanProperty("someBean");
|
||||
BeanProperty prop2 = desc.getBeanProperty("effectiveBean");
|
||||
|
||||
assertEquals(BeanPropertyAssocOne.class, prop1.getClass());
|
||||
assertEquals(BeanPropertyAssocOne.class, prop2.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* grandparent1 Josef Foo(65) from Munich
|
||||
* +- parent1 Maria Bar (40) has someBean1
|
||||
* | +- child1 Fred(13) has someBean2
|
||||
* | '- child2 Julia Baz(8)
|
||||
* +- parent2 Sandra (50) from Berlin
|
||||
* | '- child3 Roland(36)
|
||||
* +- parent3 Michael(60)
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testInheritance() {
|
||||
// create some other beans
|
||||
EBasic someBean1 = new EBasic();
|
||||
someBean1.setName("A Bean");
|
||||
server().save(someBean1);
|
||||
|
||||
EBasic someBean2 = new EBasic();
|
||||
someBean2.setName("An other Bean");
|
||||
server().save(someBean2);
|
||||
|
||||
ChildPerson child1 = new ChildPerson();
|
||||
child1.setAge(13);
|
||||
child1.setName("Fred");
|
||||
|
||||
ChildPerson child2 = new ChildPerson();
|
||||
child2.setAge(8);
|
||||
child2.setFamilyName("Baz");
|
||||
child2.setName("Julia");
|
||||
|
||||
ChildPerson child3 = new ChildPerson();
|
||||
child3.setAge(36);
|
||||
child3.setName("Roland");
|
||||
|
||||
ParentPerson parent1 = new ParentPerson();
|
||||
parent1.setAge(40);
|
||||
parent1.setName("Maria");
|
||||
parent1.setFamilyName("Bar");
|
||||
parent1.setSomeBean(someBean1);
|
||||
child1.setSomeBean(someBean2);
|
||||
parent1.getChildren().add(child1);
|
||||
parent1.getChildren().add(child2);
|
||||
|
||||
ParentPerson parent2 = new ParentPerson();
|
||||
parent2.setAge(50);
|
||||
parent2.setName("Sandra");
|
||||
parent2.getChildren().add(child3);
|
||||
parent2.setAddress("Berlin");
|
||||
|
||||
|
||||
ParentPerson parent3 = new ParentPerson();
|
||||
parent3.setAge(60);
|
||||
parent3.setName("Michael");
|
||||
|
||||
GrandParentPerson grandparent1 = new GrandParentPerson();
|
||||
grandparent1.setAge(65);
|
||||
grandparent1.setName("Josef");
|
||||
grandparent1.setFamilyName("Foo");
|
||||
grandparent1.setAddress("Munich");
|
||||
|
||||
grandparent1.getChildren().add(parent1);
|
||||
grandparent1.getChildren().add(parent2);
|
||||
grandparent1.getChildren().add(parent3);
|
||||
|
||||
|
||||
server().save(grandparent1);
|
||||
// Test setup complete, so retrieve bean from db
|
||||
//grandparent1 = server().find(GrandParentPerson.class).setId(grandparent1.getIdentifier()).where()
|
||||
// .in("effectiveBean.id",2,null) // geht nicht!
|
||||
// .findUnique();
|
||||
parent1 = server().find(ParentPerson.class).setId(1).fetch("someBean").findUnique();
|
||||
assertEquals("A Bean", parent1.getSomeBean().getName());
|
||||
grandparent1 = server().find(GrandParentPerson.class)
|
||||
.fetch("children","*") // FIXME If I do not fetch childrenBean, I will get an exception
|
||||
.fetch("children.effectiveBean","*")
|
||||
.fetch("children.children","*")
|
||||
.fetch("children.children.effectiveBean","*")
|
||||
.setId(grandparent1.getIdentifier()).where()
|
||||
.or()
|
||||
.in("effectiveBean.id",1)
|
||||
.isNull("effectiveBean.id")
|
||||
.endOr()
|
||||
.findUnique();
|
||||
assertNotNull(grandparent1);
|
||||
|
||||
// check if aggregation works
|
||||
assertEquals(3, grandparent1.getChildCount().intValue());
|
||||
assertEquals(150, grandparent1.getTotalAge().intValue());
|
||||
|
||||
// check normal properties
|
||||
assertEquals("Josef", grandparent1.getName());
|
||||
assertEquals("Foo", grandparent1.getFamilyName());
|
||||
assertEquals("Munich", grandparent1.getAddress());
|
||||
assertEquals(1, grandparent1.getEffectiveBean().getId().intValue());
|
||||
|
||||
// now check children of grandparent
|
||||
parent1 = grandparent1.getChildren().get(0);
|
||||
parent2 = grandparent1.getChildren().get(1);
|
||||
parent3 = grandparent1.getChildren().get(2);
|
||||
|
||||
assertNotNull(parent1);
|
||||
assertNotNull(parent2);
|
||||
assertNotNull(parent3);
|
||||
// parent1
|
||||
assertEquals("Maria", parent1.getName());
|
||||
assertEquals("Bar", parent1.getFamilyName()); // overwritten family name
|
||||
assertEquals("Bar", parent1.getEffectiveFamilyName()); // test inheritance
|
||||
assertNull(parent1.getAddress()); // no alternative address set
|
||||
assertEquals("Munich", parent1.getEffectiveAddress()); // -> inherit munich
|
||||
assertEquals(2, parent1.getChildCount().intValue());
|
||||
assertEquals(21, parent1.getTotalAge().intValue());
|
||||
assertEquals("A Bean", parent1.getEffectiveBean().getName());
|
||||
|
||||
// parent2
|
||||
assertEquals("Sandra", parent2.getName());
|
||||
assertNull(parent2.getFamilyName());
|
||||
assertEquals("Foo", parent2.getEffectiveFamilyName());
|
||||
assertEquals("Berlin", parent2.getAddress());
|
||||
assertEquals("Berlin", parent2.getEffectiveAddress());
|
||||
assertEquals(1, parent2.getChildCount().intValue());
|
||||
assertEquals(36, parent2.getTotalAge().intValue());
|
||||
assertNull(parent2.getEffectiveBean());
|
||||
|
||||
// parent3
|
||||
assertEquals("Michael", parent3.getName());
|
||||
assertNull(parent3.getFamilyName());
|
||||
assertEquals("Foo", parent3.getEffectiveFamilyName());
|
||||
assertNull(parent3.getAddress());
|
||||
assertEquals("Munich", parent3.getEffectiveAddress());
|
||||
assertEquals(0, parent3.getChildCount().intValue());
|
||||
assertEquals(0, parent3.getTotalAge().intValue());
|
||||
assertNull(parent3.getEffectiveBean());
|
||||
|
||||
child1 = parent1.getChildren().get(0);
|
||||
child2 = parent1.getChildren().get(1);
|
||||
|
||||
child3 = parent2.getChildren().get(0);
|
||||
|
||||
assertNotNull(child1);
|
||||
assertNotNull(child2);
|
||||
assertNotNull(child3);
|
||||
|
||||
assertEquals("Bar", child1.getEffectiveFamilyName());
|
||||
assertEquals("Munich", child1.getEffectiveAddress());
|
||||
assertEquals("An other Bean", child1.getSomeBean().getName());
|
||||
assertEquals("An other Bean", child1.getEffectiveBean().getName());
|
||||
|
||||
assertTrue(child1.getSomeBean() == child1.getEffectiveBean());
|
||||
|
||||
assertEquals("Baz", child2.getEffectiveFamilyName());
|
||||
assertEquals("Munich", child2.getEffectiveAddress());
|
||||
assertNull(child2.getSomeBean());
|
||||
assertEquals("A Bean", child2.getEffectiveBean().getName());
|
||||
|
||||
assertEquals("Foo", child3.getEffectiveFamilyName());
|
||||
assertEquals("Berlin", child3.getEffectiveAddress());
|
||||
assertNull(child3.getEffectiveBean());
|
||||
|
||||
// Now start from bottom up
|
||||
child2 = server().find(ChildPerson.class).where().eq("name", "Julia").select("parent.name").findUnique();
|
||||
assertEquals("Baz", child2.getEffectiveFamilyName());
|
||||
assertEquals("Munich", child2.getEffectiveAddress());
|
||||
assertNull(child2.getSomeBean());
|
||||
assertEquals("A Bean", child2.getEffectiveBean().getName());
|
||||
|
||||
parent1 = child2.getParent();
|
||||
assertEquals("Maria", parent1.getName());
|
||||
assertEquals("Bar", parent1.getFamilyName()); // overwritten family name
|
||||
assertEquals("Bar", parent1.getEffectiveFamilyName()); // test inheritance
|
||||
assertNull(parent1.getAddress()); // no alternative address set
|
||||
assertEquals("Munich", parent1.getEffectiveAddress()); // -> inherit munich
|
||||
assertEquals(2, parent1.getChildCount().intValue());
|
||||
assertEquals(21, parent1.getTotalAge().intValue());
|
||||
assertEquals("A Bean", parent1.getEffectiveBean().getName());
|
||||
|
||||
grandparent1 = parent1.getParent();
|
||||
// check if aggregation works
|
||||
assertEquals(3, grandparent1.getChildCount().intValue());
|
||||
assertEquals(150, grandparent1.getTotalAge().intValue());
|
||||
|
||||
// check normal properties
|
||||
assertEquals("Josef", grandparent1.getName());
|
||||
assertEquals("Foo", grandparent1.getFamilyName());
|
||||
assertEquals("Munich", grandparent1.getAddress());
|
||||
assertEquals(1, grandparent1.getEffectiveBean().getId().intValue());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
package com.avaje.tests.model.family;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import com.avaje.ebean.annotation.Formula;
|
||||
import com.avaje.tests.model.basic.EBasic;
|
||||
|
||||
@Entity
|
||||
public class ChildPerson extends InheritablePerson {
|
||||
|
||||
private static final String PARENTS_JOIN = "join parent_person j1 on j1.identifier = ${ta}.parent_identifier "
|
||||
+ "join grand_parent_person j2 on j2.identifier = j1.parent_identifier";
|
||||
|
||||
@ManyToOne(cascade = CascadeType.ALL)
|
||||
private ParentPerson parent;
|
||||
|
||||
private String familyName;
|
||||
|
||||
private String address;
|
||||
|
||||
//@Coalesce({ "familyName", "parent.familyName", "parent.parent.familyName" })
|
||||
@Formula(select = "coalesce(${ta}.family_name, j1.family_name, j2.family_name)", join = PARENTS_JOIN )
|
||||
private String effectiveFamilyName;
|
||||
|
||||
//@Coalesce({ "address", "parent.address", "parent.parent.address" })
|
||||
@Formula(select = "coalesce(${ta}.address, j1.address, j2.address)", join = PARENTS_JOIN )
|
||||
private String effectiveAddress;
|
||||
|
||||
@Formula(select = "coalesce(${ta}.some_bean_id, j1.some_bean_id, j2.some_bean_id)")
|
||||
@ManyToOne
|
||||
private EBasic effectiveBean;
|
||||
|
||||
public ParentPerson getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(ParentPerson parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public String getFamilyName() {
|
||||
return familyName;
|
||||
}
|
||||
|
||||
public void setFamilyName(String familyName) {
|
||||
this.familyName = familyName;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public String getEffectiveFamilyName() {
|
||||
return effectiveFamilyName;
|
||||
}
|
||||
|
||||
public String getEffectiveAddress() {
|
||||
return effectiveAddress;
|
||||
}
|
||||
|
||||
public EBasic getEffectiveBean() {
|
||||
return effectiveBean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
package com.avaje.tests.model.family;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OrderBy;
|
||||
|
||||
import com.avaje.ebean.annotation.Formula;
|
||||
import com.avaje.tests.model.basic.EBasic;
|
||||
|
||||
@Entity
|
||||
public class GrandParentPerson extends InheritablePerson {
|
||||
|
||||
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
|
||||
@OrderBy("identifier")
|
||||
private List<ParentPerson> children = new ArrayList<>();
|
||||
|
||||
// This rather complex formulas should be built later by CustomAnnotationParser
|
||||
private static final String PARENT_PERSON_AGGREGATE_JOIN = "left join "
|
||||
+ "(select i1.parent_identifier, count(*) as child_count, sum(i1.age) as child_age from parent_person i1 group by i1.parent_identifier) "
|
||||
+ "as f1 on f1.parent_identifier = ${ta}.identifier";
|
||||
|
||||
//@Count("children")
|
||||
@Formula(select = "coalesce(f1.child_count, 0)", join = PARENT_PERSON_AGGREGATE_JOIN )
|
||||
private Integer childCount;
|
||||
|
||||
//@Sum("children.age")
|
||||
@Formula(select = "coalesce(f1.child_age, 0)", join = PARENT_PERSON_AGGREGATE_JOIN )
|
||||
private Integer totalAge;
|
||||
|
||||
private String familyName;
|
||||
|
||||
private String address;
|
||||
|
||||
|
||||
// Demonstrate formula usage
|
||||
@Formula(select = "coalesce(${ta}.some_bean_id,1)")
|
||||
@ManyToOne
|
||||
private EBasic effectiveBean;
|
||||
|
||||
public List<ParentPerson> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List<ParentPerson> children) {
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
|
||||
public Integer getTotalAge() {
|
||||
return totalAge;
|
||||
}
|
||||
|
||||
public String getFamilyName() {
|
||||
return familyName;
|
||||
}
|
||||
|
||||
public void setFamilyName(String familyName) {
|
||||
this.familyName = familyName;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public Integer getChildCount() {
|
||||
return childCount;
|
||||
}
|
||||
|
||||
public EBasic getEffectiveBean() {
|
||||
return effectiveBean;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
|
||||
package com.avaje.tests.model.family;
|
||||
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
import com.avaje.tests.model.basic.EBasic;
|
||||
|
||||
@MappedSuperclass
|
||||
public abstract class InheritablePerson {
|
||||
|
||||
@Id
|
||||
private Integer identifier;
|
||||
|
||||
private String name;
|
||||
|
||||
private Integer age;
|
||||
|
||||
@ManyToOne
|
||||
private EBasic someBean;
|
||||
|
||||
public Integer getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public void setIdentifier(Integer identifier) {
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(Integer age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public void setSomeBean(EBasic someBean) {
|
||||
this.someBean = someBean;
|
||||
}
|
||||
|
||||
public EBasic getSomeBean() {
|
||||
return someBean;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
|
||||
package com.avaje.tests.model.family;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OrderBy;
|
||||
|
||||
import com.avaje.ebean.annotation.Formula;
|
||||
import com.avaje.tests.model.basic.EBasic;
|
||||
|
||||
|
||||
@Entity
|
||||
public class ParentPerson extends InheritablePerson {
|
||||
|
||||
//This rather complex formulas should be built later by CustomAnnotationParser
|
||||
private static final String CHILD_PERSON_AGGREGATE_JOIN = "left join "
|
||||
+ "(select i2.parent_identifier, count(*) as child_count, sum(i2.age) as child_age from child_person i2 group by i2.parent_identifier) "
|
||||
+ "as f2 on f2.parent_identifier = ${ta}.identifier";
|
||||
|
||||
private static final String GRAND_PARENT_PERSON_JOIN = "join grand_parent_person j1 on j1.identifier = ${ta}.parent_identifier";
|
||||
|
||||
@ManyToOne(cascade = CascadeType.ALL)
|
||||
private GrandParentPerson parent;
|
||||
|
||||
@OneToMany(mappedBy = "parent", cascade = CascadeType.ALL)
|
||||
@OrderBy("identifier")
|
||||
private List<ChildPerson> children = new ArrayList<>();
|
||||
|
||||
//@Count("children")
|
||||
@Formula(select = "coalesce(f2.child_count, 0)", join = CHILD_PERSON_AGGREGATE_JOIN )
|
||||
private Integer childCount;
|
||||
|
||||
//@Sum("children.age")
|
||||
@Formula(select = "coalesce(f2.child_age, 0)", join = CHILD_PERSON_AGGREGATE_JOIN )
|
||||
private Integer totalAge;
|
||||
|
||||
private String familyName;
|
||||
|
||||
private String address;
|
||||
|
||||
//@Coalesce({ "familyName", "parent.familyName" })
|
||||
@Formula(select = "coalesce(${ta}.family_name, j1.family_name)", join = GRAND_PARENT_PERSON_JOIN )
|
||||
private String effectiveFamilyName;
|
||||
|
||||
//@Coalesce({ "address", "parent.address" })
|
||||
@Formula(select = "coalesce(${ta}.address, j1.address)", join = GRAND_PARENT_PERSON_JOIN )
|
||||
private String effectiveAddress;
|
||||
|
||||
@Formula(select = "coalesce(${ta}.some_bean_id, j1.some_bean_id)", join = GRAND_PARENT_PERSON_JOIN )
|
||||
@ManyToOne
|
||||
private EBasic effectiveBean;
|
||||
|
||||
public GrandParentPerson getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public void setParent(GrandParentPerson parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public String getFamilyName() {
|
||||
return familyName;
|
||||
}
|
||||
|
||||
public void setFamilyName(String familyName) {
|
||||
this.familyName = familyName;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public List<ChildPerson> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public Integer getChildCount() {
|
||||
return childCount;
|
||||
}
|
||||
|
||||
public Integer getTotalAge() {
|
||||
return totalAge;
|
||||
}
|
||||
|
||||
public String getEffectiveFamilyName() {
|
||||
return effectiveFamilyName;
|
||||
}
|
||||
|
||||
public String getEffectiveAddress() {
|
||||
return effectiveAddress;
|
||||
}
|
||||
|
||||
public EBasic getEffectiveBean() {
|
||||
return effectiveBean;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user