#107 - Mapping - Add support for @PrimaryKeyJoinColumn/@PrimaryKeyJoinColumns

This commit is contained in:
Rob Bygrave
2018-02-14 22:59:57 +13:00
parent 6db9a60f4f
commit 842292ef6b
13 changed files with 337 additions and 25 deletions
@@ -11,6 +11,7 @@ import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.IndexDefinition;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.TableJoinColumn;
import io.ebeaninternal.server.deploy.id.ImportedId;
@@ -148,8 +149,7 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
TableJoinColumn[] columns = p.getTableJoin().columns();
if (columns.length == 0) {
String msg = "No join columns for " + p.getFullBeanName();
throw new RuntimeException(msg);
throw new RuntimeException("No join columns for " + p.getFullBeanName());
}
ImportedId importedId = p.getImportedId();
@@ -231,6 +231,12 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
if (p.getBeanDescriptor().isUseIdGenerator()) {
col.setIdentity(true);
}
TableJoin primaryKeyJoin = p.getBeanDescriptor().getPrimaryKeyJoin();
if (primaryKeyJoin != null) {
TableJoinColumn[] columns = primaryKeyJoin.columns();
col.setReferences(primaryKeyJoin.getTable() + "." + columns[0].getForeignDbColumn());
col.setForeignKeyName(determineForeignKeyConstraintName(col.getName()));
}
} else {
col.setDefaultValue(p.getDbColumnDefault());
col.setDbMigrationInfos(p.getDbMigrationInfos());
@@ -22,7 +22,7 @@ public class BeanCascadeInfo {
}
}
private void setType(CascadeType type) {
public void setType(CascadeType type) {
switch (type) {
case ALL:
save = true;
@@ -179,6 +179,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
private final String baseTableAsOf;
private final String baseTableVersionsBetween;
private final boolean historySupport;
private final TableJoin primaryKeyJoin;
private final BeanProperty softDeleteProperty;
private final boolean softDelete;
@@ -454,6 +455,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
this.draftTable = deploy.getDraftTable();
this.baseTable = InternString.intern(deploy.getBaseTable());
this.baseTableAsOf = deploy.getBaseTableAsOf();
this.primaryKeyJoin = deploy.getPrimaryKeyJoin();
this.baseTableVersionsBetween = deploy.getBaseTableVersionsBetween();
this.dependentTables = deploy.getDependentTables();
this.dbComment = deploy.getDbComment();
@@ -2888,6 +2890,10 @@ public class BeanDescriptor<T> implements BeanType<T> {
}
}
public TableJoin getPrimaryKeyJoin() {
return primaryKeyJoin;
}
@Override
public BeanProperty getIdProperty() {
return idProperty;
@@ -1546,6 +1546,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
return changeLogListener;
}
public void addPrimaryKeyJoin(DeployBeanPropertyAssocOne<?> prop, DeployTableJoin inverse) {
TableJoin inverseJoin = new TableJoin(inverse);
DeployBeanInfo<?> target = deployInfoMap.get(prop.getTargetType());
target.setPrimaryKeyJoin(inverseJoin);
}
/**
* Comparator to sort the BeanDescriptors by name.
*/
@@ -39,15 +39,14 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
private final boolean importedPrimaryKey;
private final boolean primaryKeyExport;
private AssocOneHelp localHelp;
protected final BeanProperty[] embeddedProps;
private final HashMap<String, BeanProperty> embeddedPropsMap;
/**
* The information for Imported foreign Keys.
*/
protected ImportedId importedId;
private String deleteByParentIdSql;
@@ -70,6 +69,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
super(descriptor, deploy);
primaryKeyExport = deploy.isPrimaryKeyExport();
importedPrimaryKey = deploy.isImportedPrimaryKey();
oneToOne = deploy.isOneToOne();
oneToOneExported = deploy.isOneToOneExported();
@@ -550,13 +550,17 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
@Override
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
if (!isTransient) {
localHelp.appendSelect(ctx, subQuery);
if (primaryKeyExport) {
descriptor.getIdProperty().appendSelect(ctx, subQuery);
} else {
localHelp.appendSelect(ctx, subQuery);
}
}
}
@Override
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
if (!isTransient) {
if (!isTransient && !primaryKeyExport) {
localHelp.appendFrom(ctx, joinType);
if (sqlFormulaJoin != null) {
ctx.appendFormulaJoin(sqlFormulaJoin, joinType);
@@ -734,6 +738,11 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
*/
public void setParentBeanToChild(EntityBean parent, EntityBean child) {
if (primaryKeyExport) {
Object parentId = descriptor.getId(parent);
targetDescriptor.convertSetId(parentId, child);
}
if (mappedBy != null) {
BeanProperty beanProperty = targetDescriptor.getBeanProperty(mappedBy);
if (beanProperty != null && beanProperty.getValue(child) == null) {
@@ -121,4 +121,13 @@ public class BeanTable {
}
/**
* Return the primary key DB column.
*/
public String getIdColumn() {
if (idProperties.length != 1) {
throw new IllegalStateException("Expecting only one Id column to join to on "+beanType);
}
return idProperties[0].dbColumn;
}
}
@@ -27,6 +27,7 @@ import io.ebeaninternal.server.deploy.ChainedBeanPostLoad;
import io.ebeaninternal.server.deploy.ChainedBeanQueryAdapter;
import io.ebeaninternal.server.deploy.IndexDefinition;
import io.ebeaninternal.server.deploy.InheritInfo;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.parse.DeployBeanInfo;
import io.ebeaninternal.server.idgen.UuidIdGenerator;
import io.ebeaninternal.server.rawsql.SpiRawSql;
@@ -55,10 +56,7 @@ public class DeployBeanDescriptor<T> {
@Override
public int compare(DeployBeanProperty o1, DeployBeanProperty o2) {
int v2 = o1.getSortOrder();
int v1 = o2.getSortOrder();
return (v1 < v2 ? -1 : (v1 == v2 ? 0 : 1));
return Integer.compare(o2.getSortOrder(), o1.getSortOrder());
}
}
@@ -203,6 +201,7 @@ public class DeployBeanDescriptor<T> {
private DocStoreMode docStoreDelete;
private List<DeployBeanProperty> idProperties;
private TableJoin primaryKeyJoin;
private short profileId;
@@ -215,6 +214,20 @@ public class DeployBeanDescriptor<T> {
this.beanType = beanType;
}
/**
* PK is also a FK.
*/
public void setPrimaryKeyJoin(TableJoin join) {
this.primaryKeyJoin = join;
this.idType = IdType.EXTERNAL;
this.idGeneratorName = null;
this.idGenerator = null;
}
public TableJoin getPrimaryKeyJoin() {
return primaryKeyJoin;
}
/**
* Return the DeployBeanInfo for the given bean class.
*/
@@ -9,6 +9,8 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
private boolean oneToOneExported;
private boolean primaryKeyExport;
private boolean importedPrimaryKey;
private DeployBeanEmbedded deployEmbedded;
@@ -115,4 +117,12 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
public String getColumnPrefix() {
return columnPrefix;
}
public void setPrimaryKeyExport() {
this.primaryKeyExport = true;
}
public boolean isPrimaryKeyExport() {
return primaryKeyExport;
}
}
@@ -6,9 +6,15 @@ import io.ebean.config.NamingConvention;
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
import io.ebeaninternal.server.deploy.BeanTable;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
import io.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
import io.ebeaninternal.server.query.SqlJoinType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.EmbeddedId;
@@ -17,6 +23,7 @@ import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.validation.constraints.NotNull;
/**
@@ -24,12 +31,14 @@ import javax.validation.constraints.NotNull;
*/
public class AnnotationAssocOnes extends AnnotationParser {
private static final Logger log = LoggerFactory.getLogger(AnnotationAssocOnes.class);
private final BeanDescriptorManager factory;
/**
* Create with the deploy Info.
*/
public AnnotationAssocOnes(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
AnnotationAssocOnes(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
super(info, javaxValidationAnnotations);
this.factory = factory;
}
@@ -88,6 +97,11 @@ public class AnnotationAssocOnes extends AnnotationParser {
prop.setExtraWhere(where.clause());
}
PrimaryKeyJoinColumn primaryKeyJoin = get(prop, PrimaryKeyJoinColumn.class);
if (primaryKeyJoin != null) {
readPrimaryKeyJoin(primaryKeyJoin, prop);
}
FetchPreference fetchPreference = get(prop, FetchPreference.class);
if (fetchPreference != null) {
prop.setFetchPreference(fetchPreference.value());
@@ -156,18 +170,21 @@ public class AnnotationAssocOnes extends AnnotationParser {
return "Error with association to [" + type + "] from [" + from + "]. Is " + type + " registered?";
}
private BeanTable beanTable(DeployBeanPropertyAssoc<?> prop) {
BeanTable assoc = factory.getBeanTable(prop.getPropertyType());
if (assoc == null) {
throw new RuntimeException(errorMsgMissingBeanTable(prop.getPropertyType(), prop.getFullBeanName()));
}
return assoc;
}
private void readManyToOne(ManyToOne propAnn, DeployBeanProperty prop) {
DeployBeanPropertyAssocOne<?> beanProp = (DeployBeanPropertyAssocOne<?>) prop;
setCascadeTypes(propAnn.cascade(), beanProp.getCascadeInfo());
BeanTable assoc = factory.getBeanTable(beanProp.getPropertyType());
if (assoc == null) {
String msg = errorMsgMissingBeanTable(beanProp.getPropertyType(), prop.getFullBeanName());
throw new RuntimeException(msg);
}
beanProp.setBeanTable(assoc);
beanProp.setBeanTable(beanTable(beanProp));
beanProp.setDbInsertable(true);
beanProp.setDbUpdateable(true);
beanProp.setNullable(propAnn.optional());
@@ -187,14 +204,37 @@ public class AnnotationAssocOnes extends AnnotationParser {
}
setCascadeTypes(propAnn.cascade(), prop.getCascadeInfo());
prop.setBeanTable(beanTable(prop));
}
BeanTable assoc = factory.getBeanTable(prop.getPropertyType());
if (assoc == null) {
String msg = errorMsgMissingBeanTable(prop.getPropertyType(), prop.getFullBeanName());
throw new RuntimeException(msg);
private void readPrimaryKeyJoin(PrimaryKeyJoinColumn primaryKeyJoin, DeployBeanPropertyAssocOne<?> prop) {
if (!prop.isOneToOne()) {
throw new IllegalStateException("Expecting property " + prop.getFullBeanName() + " with PrimaryKeyJoinColumn to be a OneToOne?");
}
prop.setPrimaryKeyExport();
prop.setOneToOneExported();
if (!prop.getCascadeInfo().isSave()) {
// we pretty much need to cascade save so turning that on automatically ...
prop.getCascadeInfo().setType(CascadeType.ALL);
}
prop.setBeanTable(assoc);
if (!primaryKeyJoin.name().isEmpty()) {
log.warn("Automatically determining join columns and ignoring PrimaryKeyJoinColumn.name {} on {}", primaryKeyJoin.name(), prop.getFullBeanName());
}
if (!primaryKeyJoin.referencedColumnName().isEmpty()) {
log.warn("Automatically determining join columns and Ignoring PrimaryKeyJoinColumn.referencedColumnName {} on {}", primaryKeyJoin.referencedColumnName(), prop.getFullBeanName());
}
BeanTable baseBeanTable = factory.getBeanTable(info.getDescriptor().getBeanType());
String localPrimaryKey = baseBeanTable.getIdColumn();
String foreignColumn = beanTable(prop).getIdColumn();
prop.getTableJoin().addJoinColumn(new DeployTableJoinColumn(localPrimaryKey, foreignColumn, false, false));
DeployTableJoin inverse = prop.getTableJoin().createInverse(baseBeanTable.getBaseTable());
factory.addPrimaryKeyJoin(prop, inverse);
}
private void readEmbedded(DeployBeanPropertyAssocOne<?> prop, Embedded embedded) {
@@ -1,11 +1,12 @@
package io.ebeaninternal.server.deploy.parse;
import io.ebean.RawSql;
import io.ebeaninternal.server.rawsql.SpiRawSql;
import io.ebeaninternal.server.deploy.TableJoin;
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.rawsql.SpiRawSql;
import java.util.HashMap;
@@ -91,4 +92,11 @@ public class DeployBeanInfo<T> {
public void addNamedQuery(String name, String query) {
descriptor.addNamedQuery(name, query);
}
/**
* Set that the PK is also a foreign key.
*/
public void setPrimaryKeyJoin(TableJoin join) {
descriptor.setPrimaryKeyJoin(join);
}
}