#225 - Mapping - Add / review support for @IdClass

This commit is contained in:
Rob Bygrave committed 2018-03-15 18:19:17 +13:00
1 parent 602a35d0b6
commit 4799c56e25
34 files changed
+765 -309

No files matched your search

@@ -133,6 +133,20 @@ public final class EntityBeanIntercept implements Serializable {
this.nodeUsageCollector = usageCollector;
}
/**
* Return the ownerId (IdClass).
*/
public Object getOwnerId() {
return ownerId;
}
/**
* Set the ownerId (IdClass).
*/
public void setOwnerId(Object ownerId) {
this.ownerId = ownerId;
}
/**
* Return the owning bean for an embedded bean.
*/
@@ -1350,7 +1350,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
// already assigned (So custom or UUID)
return;
}
if (desc.propertiesId().isEmpty()) {
if (desc.idProperty() == null) {
// bean doesn't have an Id property
if (desc.isBaseTableType() && desc.getBeanFinder() == null) {
// expecting an id property
@@ -1491,6 +1491,9 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private boolean isPersistentField(DeployBeanProperty prop) {
Field field = prop.getField();
if (field == null) {
return false;
}
int modifiers = field.getModifiers();
return !(Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) && !field.isAnnotationPresent(Transient.class);
}
@@ -61,10 +61,12 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
private static final Logger logger = LoggerFactory.getLogger(BeanProperty.class);
/**
* Flag to mark this at part of the unique id.
* Flag to mark this is the id property.
*/
final boolean id;
final boolean importedPrimaryKey;
/**
* Flag to make this as a dummy property for unidirecitonal relationships.
*/
@@ -274,6 +276,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
this.name = InternString.intern(deploy.getName());
this.propertyIndex = deploy.getPropertyIndex();
this.unidirectionalShadow = deploy.isUndirectionalShadow();
this.importedPrimaryKey = deploy.isImportedPrimaryKey();
this.discriminator = deploy.isDiscriminator();
this.localEncrypted = deploy.isLocalEncrypted();
this.dbEncrypted = deploy.isDbEncrypted();
@@ -395,6 +398,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
this.softDeleteDbSet = source.softDeleteDbSet;
this.softDeleteDbPredicate = source.softDeleteDbPredicate;
this.fetchEager = source.fetchEager;
this.importedPrimaryKey = source.importedPrimaryKey;
this.unidirectionalShadow = source.unidirectionalShadow;
this.discriminator = source.discriminator;
this.localEncrypted = source.isLocalEncrypted();
@@ -473,8 +477,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
this.deployOrder = deployOrder;
}
public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain,
boolean propertyDeploy) {
public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) {
return null;
}
@@ -935,6 +938,13 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
return null;
}
/**
* If true this bean maps to the primary key.
*/
public boolean isImportedPrimaryKey() {
return importedPrimaryKey;
}
@Override
public boolean isAssocMany() {
// Returns false - override in BeanPropertyAssocMany.
@@ -40,8 +40,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
private final boolean orphanRemoval;
private final boolean importedPrimaryKey;
private final boolean primaryKeyExport;
private final PropertyForeignKey foreignKey;
@@ -76,7 +74,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
foreignKey = deploy.getForeignKey();
primaryKeyExport = deploy.isPrimaryKeyExport();
importedPrimaryKey = deploy.isImportedPrimaryKey();
oneToOne = deploy.isOneToOne();
oneToOneExported = deploy.isOneToOneExported();
orphanRemoval = deploy.isOrphanRemoval();
@@ -346,13 +343,6 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
return orphanRemoval;
}
/**
* If true this bean maps to the primary key.
*/
public boolean isImportedPrimaryKey() {
return importedPrimaryKey;
}
@Override
public void diff(String prefix, Map<String, ValuePair> map, EntityBean newBean, EntityBean oldBean) {
@@ -626,6 +616,14 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
}
@Override
public void setValueIntercept(EntityBean bean, Object value) {
super.setValueIntercept(bean, value);
if (embedded && value instanceof EntityBean) {
setEmbeddedOwner(bean, value);
}
}
/**
* For embedded bean set the owner and all properties to be loaded (recursively).
*/
@@ -638,18 +636,10 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
}
}
private void setEmbeddedOwner(EntityBean bean, Object value) {
void setEmbeddedOwner(EntityBean bean, Object value) {
((EntityBean) value)._ebean_getIntercept().setEmbeddedOwner(bean, propertyIndex);
}
@Override
public void setValueIntercept(EntityBean bean, Object value) {
super.setValueIntercept(bean, value);
if (embedded && value instanceof EntityBean) {
setEmbeddedOwner(bean, value);
}
}
@Override
public void loadIgnore(DbReadContext ctx) {
localHelp.loadIgnore(ctx);
@@ -708,10 +698,10 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
if (embedded) {
writeJson.writeFieldName(name);
BeanDescriptor<?> refDesc = descriptor.getBeanDescriptor(value.getClass());
refDesc.jsonWriteForInsert(writeJson, (EntityBean)value);
refDesc.jsonWriteForInsert(writeJson, (EntityBean) value);
} else {
jsonWriteTargetId(writeJson, (EntityBean)value);
jsonWriteTargetId(writeJson, (EntityBean) value);
}
}
}
@@ -0,0 +1,35 @@
package io.ebeaninternal.server.deploy;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
/**
* Bean property for an IdClass embeddedId.
*/
public class BeanPropertyIdClass extends BeanPropertyAssocOne {
public BeanPropertyIdClass(BeanDescriptorMap owner, BeanDescriptor descriptor, DeployBeanPropertyAssocOne deploy) {
super(owner, descriptor, deploy);
}
@Override
public void setValue(EntityBean bean, Object value) {
bean._ebean_getIntercept().setOwnerId(value);
}
@Override
public void setValueIntercept(EntityBean bean, Object value) {
bean._ebean_getIntercept().setOwnerId(value);
}
@Override
public Object getValue(EntityBean bean) {
return bean._ebean_getIntercept().getOwnerId();
}
@Override
public Object getValueIntercept(EntityBean bean) {
return bean._ebean_getIntercept().getOwnerId();
}
}
@@ -30,7 +30,7 @@ public class BeanTable {
*/
private final String baseTable;
private final BeanProperty[] idProperties;
private final BeanProperty idProperty;
/**
* Create the BeanTable.
@@ -39,7 +39,7 @@ public class BeanTable {
this.owner = owner;
this.beanType = mutable.getBeanType();
this.baseTable = InternString.intern(mutable.getBaseTable());
this.idProperties = mutable.createIdProperties(owner);
this.idProperty = mutable.createIdProperty(owner);
}
@Override
@@ -69,8 +69,8 @@ public class BeanTable {
/**
* Return the Id properties.
*/
public BeanProperty[] getIdProperties() {
return idProperties;
public BeanProperty getIdProperty() {
return idProperty;
}
/**
@@ -82,52 +82,51 @@ public class BeanTable {
public void createJoinColumn(String foreignKeyPrefix, DeployTableJoin join, boolean reverse, String sqlFormulaSelect) {
boolean complexKey = false;
BeanProperty[] props = idProperties;
if (idProperties.length == 1) {
if (idProperties[0] instanceof BeanPropertyAssocOne<?>) {
BeanPropertyAssocOne<?> assocOne = (BeanPropertyAssocOne<?>) idProperties[0];
props = assocOne.getProperties();
complexKey = true;
}
if (idProperty == null) {
return;
}
for (BeanProperty prop : props) {
if (idProperty instanceof BeanPropertyAssocOne<?>) {
BeanPropertyAssocOne<?> assocOne = (BeanPropertyAssocOne<?>) idProperty;
BeanProperty[] props = assocOne.getProperties();
for (BeanProperty prop : props) {
addToJoin(foreignKeyPrefix, join, reverse, sqlFormulaSelect, true, prop);
}
} else {
addToJoin(foreignKeyPrefix, join, reverse, sqlFormulaSelect, false, idProperty);
}
}
String lc = prop.getDbColumn();
String fk = lc;
if (foreignKeyPrefix != null) {
fk = owner.getNamingConvention().getForeignKey(foreignKeyPrefix, fk);
}
private void addToJoin(String foreignKeyPrefix, DeployTableJoin join, boolean reverse, String sqlFormulaSelect, boolean complexKey, BeanProperty prop) {
if (complexKey) {
// just to copy the column name rather than prefix with the foreignKeyPrefix.
// I think that with complex keys this is the more common approach.
logger.debug("On table[{}] foreign key column [{}]", baseTable, lc);
fk = lc;
}
if (sqlFormulaSelect != null) {
fk = sqlFormulaSelect;
}
DeployTableJoinColumn joinCol = new DeployTableJoinColumn(lc, fk);
joinCol.setForeignSqlFormula(sqlFormulaSelect);
if (reverse) {
joinCol = joinCol.reverse();
}
join.addJoinColumn(joinCol);
String lc = prop.getDbColumn();
String fk = lc;
if (foreignKeyPrefix != null) {
fk = owner.getNamingConvention().getForeignKey(foreignKeyPrefix, fk);
}
if (complexKey) {
// just to copy the column name rather than prefix with the foreignKeyPrefix.
// I think that with complex keys this is the more common approach.
logger.debug("On table[{}] foreign key column [{}]", baseTable, lc);
fk = lc;
}
if (sqlFormulaSelect != null) {
fk = sqlFormulaSelect;
}
DeployTableJoinColumn joinCol = new DeployTableJoinColumn(lc, fk);
joinCol.setForeignSqlFormula(sqlFormulaSelect);
if (reverse) {
joinCol = joinCol.reverse();
}
join.addJoinColumn(joinCol);
}
/**
* 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;
return idProperty.dbColumn;
}
}
@@ -15,7 +15,6 @@ import io.ebean.event.BeanPostLoad;
import io.ebean.event.BeanQueryAdapter;
import io.ebean.event.changelog.ChangeLogFilter;
import io.ebean.text.PathProperties;
import io.ebean.util.CamelCaseHelper;
import io.ebeaninternal.api.ConcurrencyMode;
import io.ebeaninternal.server.core.CacheOptions;
import io.ebeaninternal.server.deploy.BeanDescriptor.EntityType;
@@ -91,6 +90,10 @@ public class DeployBeanDescriptor<T> {
*/
private IdType idType;
private Class<?> idClass;
private DeployBeanPropertyAssocOne<?> idClassProperty;
/**
* Set to true if the identity is default for the platform.
*/
@@ -205,7 +208,7 @@ public class DeployBeanDescriptor<T> {
private DocStoreMode docStoreUpdate;
private DocStoreMode docStoreDelete;
private List<DeployBeanProperty> idProperties;
private DeployBeanProperty idProperty;
private TableJoin primaryKeyJoin;
private short profileId;
@@ -219,6 +222,20 @@ public class DeployBeanDescriptor<T> {
this.beanType = beanType;
}
/**
* Set the IdClass to use.
*/
public void setIdClass(Class idClass) {
this.idClass = idClass;
}
/**
* Return true if there is a IdClass set.
*/
public boolean isIdClass() {
return idClass != null;
}
/**
* PK is also a FK.
*/
@@ -335,7 +352,7 @@ public class DeployBeanDescriptor<T> {
DeployBeanTable beanTable = new DeployBeanTable(getBeanType());
beanTable.setBaseTable(baseTable);
beanTable.setIdProperties(propertiesId());
beanTable.setIdProperty(idProperty());
return beanTable;
}
@@ -443,6 +460,10 @@ public class DeployBeanDescriptor<T> {
return cacheOptions;
}
public DeployBeanPropertyAssocOne<?> getIdClassProperty() {
return idClassProperty;
}
public DeployBeanPropertyAssocOne<?> getUnidirectional() {
return unidirectional;
}
@@ -684,6 +705,15 @@ public class DeployBeanDescriptor<T> {
}
}
public void postAnnotations() {
if (idClass != null) {
idClassProperty = new DeployBeanPropertyAssocOne<>(this, idClass);
idClassProperty.setName("_idClass");
idClassProperty.setEmbedded();
idClassProperty.setNullable(false);
}
}
/**
* Add a bean property.
*/
@@ -691,32 +721,8 @@ public class DeployBeanDescriptor<T> {
return propMap.put(prop.getName(), prop);
}
/**
* Find the matching property for a given property name or dbColumn.
* <p>
* This is primarily to find imported primary key columns (ManyToOne that also match the PK).
* </p>
*/
DeployBeanProperty findMatch(String propertyName, String dbColumn) {
DeployBeanProperty prop = propMap.get(propertyName);
if (prop != null) {
return prop;
}
if (dbColumn != null) {
String asCamel = CamelCaseHelper.toCamelFromUnderscore(dbColumn);
prop = propMap.get(asCamel);
if (prop != null) {
return prop;
}
// scan looking for dbColumn match
for (DeployBeanProperty property : propMap.values()) {
if (dbColumn.equals(property.getDbColumn())) {
return property;
}
}
}
return null;
public Collection<DeployBeanProperty> properties() {
return propMap.values();
}
/**
@@ -935,16 +941,14 @@ public class DeployBeanDescriptor<T> {
*/
public boolean isPrimaryKeyCompoundOrNonNumeric() {
List<DeployBeanProperty> ids = propertiesId();
if (ids.size() != 1) {
// compound key
return true;
DeployBeanProperty id = idProperty();
if (id == null) {
return false;
}
DeployBeanProperty p = ids.get(0);
if (p instanceof DeployBeanPropertyAssocOne<?>) {
return ((DeployBeanPropertyAssocOne<?>) p).isCompound();
if (id instanceof DeployBeanPropertyAssocOne<?>) {
return ((DeployBeanPropertyAssocOne<?>) id).isCompound();
} else {
return !p.isDbNumberType();
return !id.isDbNumberType();
}
}
@@ -953,37 +957,32 @@ public class DeployBeanDescriptor<T> {
* compound). This is for the purpose of defining a sequence name.
*/
public String getSinglePrimaryKeyColumn() {
List<DeployBeanProperty> ids = propertiesId();
if (ids.size() == 1) {
DeployBeanProperty p = ids.get(0);
if (p instanceof DeployBeanPropertyAssoc<?>) {
DeployBeanProperty id = idProperty();
if (id != null) {
if (id instanceof DeployBeanPropertyAssoc<?>) {
// its a compound primary key
return null;
} else {
return p.getDbColumn();
return id.getDbColumn();
}
}
return null;
}
/**
* Return the BeanProperty that make up the unique id.
* <p>
* The order of these properties can be relied on to be consistent if the bean
* itself doesn't change or the xml deployment order does not change.
* </p>
* Return the BeanProperty that is the Id.
*/
public List<DeployBeanProperty> propertiesId() {
if (idProperties == null) {
idProperties = new ArrayList<>(2);
for (DeployBeanProperty prop : propMap.values()) {
if (prop.isId()) {
idProperties.add(prop);
}
public DeployBeanProperty idProperty() {
if (idProperty != null) {
return idProperty;
}
for (DeployBeanProperty prop : propMap.values()) {
if (prop.isId()) {
idProperty = prop;
return idProperty;
}
}
return idProperties;
return null;
}
public DeployBeanPropertyAssocOne<?> findJoinToTable(String tableName) {
@@ -51,6 +51,7 @@ public class DeployBeanProperty {
private static final int AUDITCOLUMN_ORDER = -1000000;
private static final int VERSIONCOLUMN_ORDER = -1000000;
private static final Set<Class<?>> PRIMITIVE_NUMBER_TYPES = new HashSet<>();
static {
PRIMITIVE_NUMBER_TYPES.add(float.class);
PRIMITIVE_NUMBER_TYPES.add(double.class);
@@ -64,6 +65,8 @@ public class DeployBeanProperty {
*/
private boolean id;
boolean importedPrimaryKey;
/**
* Flag to mark the property as embedded. This could be on
* BeanPropertyAssocOne rather than here. Put it here for checking Id type
@@ -611,6 +614,16 @@ public class DeployBeanProperty {
this.dbUpdateable = false;
}
public void setImportedPrimaryKey() {
this.importedPrimaryKey = true;
}
/**
* Set to true if this is part of the primary key.
*/
public void setImportedPrimaryKeyColumn(DeployBeanProperty primaryKey) {
this.importedPrimaryKey = true;
}
public boolean isAggregation() {
return aggregation != null;
@@ -889,6 +902,13 @@ public class DeployBeanProperty {
return genericType;
}
/**
* Return true if this is part of the primary key.
*/
public boolean isImportedPrimaryKey() {
return importedPrimaryKey;
}
/**
* Return true if this is included in the unique id.
*/
@@ -1046,6 +1066,10 @@ public class DeployBeanProperty {
return tenantId;
}
public boolean isIdClass() {
return desc.isIdClass();
}
public void addDbMigrationInfo(DbMigrationInfo info) {
if (dbMigrationInfos == null) {
dbMigrationInfos = new ArrayList<>();
@@ -17,8 +17,6 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
private boolean primaryKeyExport;
private boolean importedPrimaryKey;
private DeployBeanEmbedded deployEmbedded;
private String columnPrefix;
@@ -92,17 +90,11 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
this.oneToOneExported = true;
}
/**
* Return true if this is part of the primary key.
*/
public boolean isImportedPrimaryKey() {
return importedPrimaryKey;
}
/**
* Set to true if this is part of the primary key.
*/
void setImportedPrimaryKey(DeployBeanProperty primaryKey) {
@Override
public void setImportedPrimaryKeyColumn(DeployBeanProperty primaryKey) {
this.importedPrimaryKey = true;
String dbColumn = primaryKey.getDbColumn();
if (dbColumn != null) {
@@ -5,6 +5,7 @@ import io.ebeaninternal.server.deploy.BeanDescriptorMap;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.deploy.BeanPropertyIdClass;
import io.ebeaninternal.server.deploy.BeanPropertyOrderColumn;
import io.ebeaninternal.server.deploy.BeanPropertySimpleCollection;
import io.ebeaninternal.server.deploy.InheritInfo;
@@ -39,7 +40,7 @@ public class DeployBeanPropertyLists {
private final LinkedHashMap<String, BeanProperty> propertyMap;
private final List<BeanProperty> ids = new ArrayList<>();
private BeanProperty id;
private final List<BeanProperty> local = new ArrayList<>();
@@ -70,7 +71,13 @@ public class DeployBeanPropertyLists {
public DeployBeanPropertyLists(BeanDescriptorMap owner, BeanDescriptor<?> desc, DeployBeanDescriptor<?> deploy) {
this.desc = desc;
setImportedPrimaryKeys(deploy);
DeployBeanPropertyAssocOne<?> deployId = deploy.getIdClassProperty();
if (deployId != null) {
this.id = new BeanPropertyIdClass(owner, desc, deployId);
setImportedPrimaryKeysFor(deploy, deployId);
} else {
setImportedPrimaryKeys(deploy);
}
DeployBeanProperty deployOrderColumn = deploy.getOrderColumn();
this.orderColumn = deployOrderColumn != null ? new BeanPropertyOrderColumn(desc, deployOrderColumn) : null;
@@ -142,23 +149,43 @@ public class DeployBeanPropertyLists {
* </p>
*/
private void setImportedPrimaryKeys(DeployBeanDescriptor<?> deploy) {
DeployBeanProperty id = deploy.idProperty();
if (id instanceof DeployBeanPropertyAssocOne<?>) {
setImportedPrimaryKeysFor(deploy, (DeployBeanPropertyAssocOne<?>) id);
}
}
List<DeployBeanProperty> ids = deploy.propertiesId();
if (ids.size() == 1) {
DeployBeanProperty id = ids.get(0);
if (id instanceof DeployBeanPropertyAssocOne<?>) {
// only interested if the primary key is a compound key
DeployBeanDescriptor<?> targetDeploy = ((DeployBeanPropertyAssocOne<?>) id).getTargetDeploy();
for (DeployBeanPropertyAssocOne<?> assoc : deploy.propertiesAssocOne()) {
DeployBeanProperty pkMatch = targetDeploy.findMatch(assoc.getName(), assoc.getDbColumn());
if (pkMatch != null) {
assoc.setImportedPrimaryKey(pkMatch);
}
}
private void setImportedPrimaryKeysFor(DeployBeanDescriptor<?> deploy, DeployBeanPropertyAssocOne<?> id) {
for (DeployBeanProperty prop : id.getTargetDeploy().properties()) {
DeployBeanProperty match = findImported(deploy, prop);
if (match != null) {
match.setImportedPrimaryKeyColumn(prop);
}
}
}
private DeployBeanProperty findImported(DeployBeanDescriptor<?> deploy, DeployBeanProperty embeddedScalar) {
// the logical name and db column we are looking for a match on
String name = embeddedScalar.getName();
String dbColumn = embeddedScalar.getDbColumn();
DeployBeanProperty match = deploy.getBeanProperty(name);
if (match != null) {
return match;
}
// could look to match more by dbColumn
for (DeployBeanPropertyAssocOne<?> assocOne : deploy.propertiesAssocOne()) {
if (name.equals(assocOne.getName()) || (dbColumn != null && dbColumn.equals(assocOne.getDbColumn()))) {
return assocOne;
}
}
return null;
}
/**
* Return the unidirectional.
*/
@@ -188,12 +215,13 @@ public class DeployBeanPropertyLists {
return;
}
if (prop.isId()) {
ids.add(prop);
if (id != null) {
throw new IllegalStateException("More that one @Id property on " + desc.getFullName() + " ?");
}
id = prop;
return;
} else {
nonTransients.add(prop);
}
nonTransients.add(prop);
if (prop.isMutableScalarType()) {
mutable.add(prop);
}
@@ -255,15 +283,7 @@ public class DeployBeanPropertyLists {
}
public BeanProperty getId() {
if (ids.size() > 1) {
String msg = "Issue with bean " + desc + ". Ebean does not support multiple @Id properties. You need to convert to using an @EmbeddedId."
+ " Please email the ebean google group if you need further clarification.";
throw new IllegalStateException(msg);
}
if (ids.isEmpty()) {
return null;
}
return ids.get(0);
return id;
}
public BeanProperty[] getNonTransients() {
@@ -4,9 +4,6 @@ import io.ebeaninternal.server.deploy.BeanDescriptorMap;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import java.util.List;
/**
* Used for associated beans in place of a BeanDescriptor. This is done to avoid
* recursion issues due to the potentially bi-directional and circular
@@ -25,12 +22,12 @@ public class DeployBeanTable {
*/
private String baseTable;
private List<DeployBeanProperty> idProperties;
private DeployBeanProperty idProperty;
/**
* Create the BeanTable.
*/
public DeployBeanTable(Class<?> beanType) {
DeployBeanTable(Class<?> beanType) {
this.beanType = beanType;
}
@@ -53,12 +50,8 @@ public class DeployBeanTable {
/**
* Return the id properties.
*/
public BeanProperty[] createIdProperties(BeanDescriptorMap owner) {
BeanProperty[] props = new BeanProperty[idProperties.size()];
for (int i = 0; i < idProperties.size(); i++) {
props[i] = createProperty(owner, idProperties.get(i));
}
return props;
public BeanProperty createIdProperty(BeanDescriptorMap owner) {
return idProperty == null ? null : createProperty(owner, idProperty);
}
@SuppressWarnings({"unchecked", "rawtypes"})
@@ -70,14 +63,13 @@ public class DeployBeanTable {
} else {
return new BeanProperty(prop);
}
}
/**
* Set the Id properties.
*/
public void setIdProperties(List<DeployBeanProperty> idProperties) {
this.idProperties = idProperties;
public void setIdProperty(DeployBeanProperty idProperty) {
this.idProperty = idProperty;
}
/**
@@ -92,9 +92,9 @@ public class DeployTableJoinColumn {
private void setReferencedColumn(BeanTable beanTable) {
if (localDbColumn == null) {
BeanProperty[] idProperties = beanTable.getIdProperties();
if (idProperties.length == 1) {
localDbColumn = idProperties[0].getDbColumn();
BeanProperty idProperty = beanTable.getIdProperty();
if (idProperty != null) {
localDbColumn = idProperty.getDbColumn();
}
}
}
@@ -247,8 +247,8 @@ class AnnotationAssocManys extends AnnotationParser {
}
if (!intJoin.hasJoinColumns()) {
// define foreign key columns
BeanProperty[] localIds = localTable.getIdProperties();
for (BeanProperty localId : localIds) {
BeanProperty localId = localTable.getIdProperty();
if (localId != null) {
// add the source to intersection join columns
String fkCol = localTableName + "_" + localId.getDbColumn();
intJoin.addJoinColumn(new DeployTableJoinColumn(localId.getDbColumn(), namingConvention.getColumnFromProperty(null, fkCol)));
@@ -257,8 +257,8 @@ class AnnotationAssocManys extends AnnotationParser {
if (!destJoin.hasJoinColumns()) {
// define inverse foreign key columns
BeanProperty[] otherIds = otherTable.getIdProperties();
for (BeanProperty otherId : otherIds) {
BeanProperty otherId = otherTable.getIdProperty();
if (otherId != null) {
// set the intersection to dest table join columns
final String fkCol = otherTableName + "_" + otherId.getDbColumn();
destJoin.addJoinColumn(new DeployTableJoinColumn(namingConvention.getColumnFromProperty(null, fkCol), otherId.getDbColumn()));
@@ -92,9 +92,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
// May as well check for Id. Makes sense to me.
Id id = get(prop, Id.class);
if (id != null) {
prop.setEmbedded();
prop.setId();
prop.setNullable(false);
readIdAssocOne(prop);
}
DbForeignKey dbForeignKey = get(prop, DbForeignKey.class);
@@ -23,6 +23,7 @@ import javax.persistence.AttributeOverride;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.IdClass;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
@@ -127,6 +128,11 @@ public class AnnotationClass extends AnnotationParser {
}
}
IdClass idClass = AnnotationUtil.findAnnotationRecursive(cls, IdClass.class);
if (idClass != null) {
descriptor.setIdClass(idClass.value());
}
Embeddable embeddable = AnnotationUtil.findAnnotationRecursive(cls, Embeddable.class);
if (embeddable != null) {
descriptor.setEntityType(EntityType.EMBEDDED);
@@ -91,7 +91,7 @@ public class AnnotationFields extends AnnotationParser {
*/
private FetchType defaultLobFetchType = FetchType.LAZY;
public AnnotationFields(GeneratedPropertyFactory generatedPropFactory, DeployBeanInfo<?> info,
AnnotationFields(GeneratedPropertyFactory generatedPropFactory, DeployBeanInfo<?> info,
boolean javaxValidationAnnotations, boolean jacksonAnnotationsPresent, boolean eagerFetchLobs) {
super(info, javaxValidationAnnotations);
@@ -127,8 +127,7 @@ public class AnnotationFields extends AnnotationParser {
Id id = get(prop, Id.class);
if (id != null) {
prop.setId();
prop.setNullable(false);
readIdAssocOne(prop);
}
EmbeddedId embeddedId = get(prop, EmbeddedId.class);
@@ -211,7 +210,7 @@ public class AnnotationFields extends AnnotationParser {
Id id = get(prop, Id.class);
if (id != null) {
readId(prop);
readIdScalar(prop);
}
// determine the JDBC type using Lob/Temporal
@@ -517,18 +516,6 @@ public class AnnotationFields extends AnnotationParser {
return util.createDataEncryptSupport(table, column);
}
private void readId(DeployBeanProperty prop) {
prop.setId();
prop.setNullable(false);
if (prop.getPropertyType().equals(UUID.class)) {
if (descriptor.getIdGeneratorName() == null) {
descriptor.setUuidGenerator();
}
}
}
private void readGenValue(GeneratedValue gen, DeployBeanProperty prop) {
String genName = gen.generator();
@@ -3,6 +3,7 @@ package io.ebeaninternal.server.deploy.parse;
import io.ebeaninternal.server.deploy.BeanCascadeInfo;
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
import javax.persistence.AttributeOverride;
@@ -11,6 +12,7 @@ import javax.persistence.Column;
import javax.validation.groups.Default;
import java.util.HashMap;
import java.util.Set;
import java.util.UUID;
/**
* Base class for reading deployment annotations.
@@ -39,6 +41,36 @@ public abstract class AnnotationParser extends AnnotationBase {
@Override
public abstract void parse();
/**
* Read the Id annotation on an embeddedId.
*/
protected void readIdAssocOne(DeployBeanPropertyAssoc<?> prop) {
prop.setNullable(false);
if (prop.isIdClass()) {
prop.setImportedPrimaryKey();
} else {
prop.setId();
prop.setEmbedded();
}
}
/**
* Read the Id annotation on scalar property.
*/
protected void readIdScalar(DeployBeanProperty prop) {
prop.setNullable(false);
if (prop.isIdClass()) {
prop.setImportedPrimaryKey();
} else {
prop.setId();
if (prop.getPropertyType().equals(UUID.class)) {
if (descriptor.getIdGeneratorName() == null) {
descriptor.setUuidGenerator();
}
}
}
}
/**
* Helper method to set cascade types to the CascadeInfo on BeanProperty.
*/
@@ -83,6 +83,7 @@ public class ReadAnnotations {
new AnnotationSql(info, javaxValidationAnnotations).parse();
new AnnotationClass(info).parseAttributeOverride();
info.getDescriptor().postAnnotations();
} catch (RuntimeException e) {
throw new RuntimeException("Error reading annotations for " + info, e);
@@ -69,7 +69,6 @@ public class InsertHandler extends DmlHandler {
// expecting a concatenated key that can
// be built from supplied AssocOne beans
withId = meta.deriveConcatenatedId(persistRequest);
} else if (meta.supportsGetGeneratedKeys()) {
// Identity with getGeneratedKeys
useGeneratedKeys = true;
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.persist.dmlbind;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.core.PersistRequestBean;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import io.ebeaninternal.server.persist.dml.GenerateDmlRequest;
@@ -15,7 +14,7 @@ import java.util.List;
/**
* Bindable for a EmbeddedId.
*/
public final class BindableIdEmbedded implements BindableId {
final class BindableIdEmbedded implements BindableId {
private final BeanPropertyAssocOne<?> embId;
@@ -23,10 +22,10 @@ public final class BindableIdEmbedded implements BindableId {
private final MatchedImportedProperty[] matches;
public BindableIdEmbedded(BeanPropertyAssocOne<?> embId, BeanDescriptor<?> desc) {
BindableIdEmbedded(BeanPropertyAssocOne<?> embId, MatchedImportedProperty[] matches) {
this.embId = embId;
this.props = embId.getProperties();
matches = MatchedImportedProperty.build(props, desc);
this.matches = matches;
}
@Override
@@ -67,13 +66,10 @@ public final class BindableIdEmbedded implements BindableId {
public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
EntityBean idValue = (EntityBean) embId.getValue(bean);
for (BeanProperty prop : props) {
Object value = prop.getValue(idValue);
request.bind(value, prop);
}
request.setIdValue(idValue);
}
@@ -88,7 +84,7 @@ public final class BindableIdEmbedded implements BindableId {
public boolean deriveConcatenatedId(PersistRequestBean<?> persist) {
if (matches == null) {
String m = "Matches for the concatenated key columns where not found?"
String m = "No matches for " + embId.getFullBeanName() + " the concatenated key columns where not found?"
+ " I expect that the concatenated key was null, and this bean does"
+ " not have ManyToOne assoc beans matching the primary key columns?";
throw new PersistenceException(m);
@@ -100,8 +96,8 @@ public final class BindableIdEmbedded implements BindableId {
EntityBean newId = (EntityBean) embId.createEmbeddedId();
// populate it from the assoc one id values...
for (MatchedImportedProperty matche : matches) {
matche.populate(bean, newId);
for (MatchedImportedProperty match : matches) {
match.populate(bean, newId);
}
// support PropertyChangeSupport
@@ -27,9 +27,11 @@ public class FactoryBaseProperties {
public void create(List<Bindable> list, BeanDescriptor<?> desc, DmlMode mode, boolean withLobs) {
for (BeanProperty prop : desc.propertiesBaseScalar()) {
Bindable item = factoryProperty.create(prop, mode, withLobs);
if (item != null) {
list.add(item);
if (!prop.isImportedPrimaryKey()) {
Bindable item = factoryProperty.create(prop, mode, withLobs);
if (item != null) {
list.add(item);
}
}
}
}
@@ -27,18 +27,14 @@ public class FactoryEmbedded {
BeanPropertyAssocOne<?>[] embedded = desc.propertiesEmbedded();
for (BeanPropertyAssocOne<?> anEmbedded : embedded) {
BeanProperty[] props = anEmbedded.getProperties();
List<Bindable> bindList = new ArrayList<>(props.length);
for (BeanProperty prop : props) {
Bindable item = factoryProperty.create(prop, mode, withLobs);
if (item != null) {
bindList.add(item);
}
}
list.add(new BindableEmbedded(anEmbedded, bindList));
}
}
@@ -20,14 +20,14 @@ public class FactoryId {
BeanProperty id = desc.getIdProperty();
if (id == null) {
return new BindableIdEmpty();
}
if (!id.isEmbedded()) {
return new BindableIdScalar(id);
} else {
BeanPropertyAssocOne<?> embId = (BeanPropertyAssocOne<?>) id;
return new BindableIdEmbedded(embId, desc);
MatchedImportedProperty[] matches = MatchedImportedFactory.build(embId.getProperties(), desc);
return new BindableIdEmbedded(embId, matches);
}
}
}
@@ -0,0 +1,35 @@
package io.ebeaninternal.server.persist.dmlbind;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
/**
* Matches local embedded id properties to 'matching' properties from a
* ManyToOne associated bean that is a 'imported primary key'.
*/
class MatchedImportedEmbedded implements MatchedImportedProperty {
private final BeanProperty localProp;
private final BeanPropertyAssocOne<?> assocOne;
private final BeanProperty foreignProp;
MatchedImportedEmbedded(BeanProperty localProp, BeanPropertyAssocOne<?> assocOne, BeanProperty foreignProp) {
this.localProp = localProp;
this.assocOne = assocOne;
this.foreignProp = foreignProp;
}
@Override
public void populate(EntityBean sourceBean, EntityBean embeddedId) {
Object assocBean = assocOne.getValue(sourceBean);
if (assocBean == null) {
throw new NullPointerException("The assoc bean for " + assocOne + " is null?");
}
Object value = foreignProp.getValue((EntityBean) assocBean);
localProp.setValue(embeddedId, value);
}
}
@@ -0,0 +1,54 @@
package io.ebeaninternal.server.persist.dmlbind;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
class MatchedImportedFactory {
/**
* Create the array of matchedImportedProperty based on the properties and descriptor.
*/
protected static MatchedImportedProperty[] build(BeanProperty[] props, BeanDescriptor<?> desc) {
MatchedImportedProperty[] matches = new MatchedImportedProperty[props.length];
for (int i = 0; i < props.length; i++) {
// find matching assoc one property for dbColumn
matches[i] = findMatch(props[i], desc);
if (matches[i] == null) {
// ok, the assoc ones are not on the bean?
return null;
}
}
return matches;
}
private static MatchedImportedProperty findMatch(BeanProperty prop, BeanDescriptor<?> desc) {
// find matching against the local database column
String dbColumn = prop.getDbColumn();
BeanPropertyAssocOne<?>[] assocOnes = desc.propertiesOne();
for (BeanPropertyAssocOne<?> assocOne1 : assocOnes) {
if (assocOne1.isImportedPrimaryKey()) {
// search using the ImportedId from the assoc one
BeanProperty foreignMatch = assocOne1.getImportedId().findMatchImport(dbColumn);
if (foreignMatch != null) {
return new MatchedImportedEmbedded(prop, assocOne1, foreignMatch);
}
}
}
BeanProperty[] scalar = desc.propertiesBaseScalar();
for (BeanProperty beanProperty : scalar) {
if (dbColumn.equals(beanProperty.getDbColumn())) {
return new MatchedImportedScalar(prop, beanProperty);
}
}
// there was no matching assoc one property.
// example UserRole bean missing assoc one to User?
return null;
}
}
@@ -1,86 +1,11 @@
package io.ebeaninternal.server.persist.dmlbind;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.BeanDescriptor;
import io.ebeaninternal.server.deploy.BeanProperty;
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
/**
* Matches local embedded id properties to 'matching' properties from a
* ManyToOne associated bean that is a 'imported primary key'.
* <p>
* This object is designed to help BindableIdEmbedded and BindableIdMap to
* create a concatenated id from the id values from ManyToOne associated beans.
* This can be done when those ManyToOne associated beans make up the primary
* key. This typically means the BindableIdEmbedded is for a intersection table
* of a Many to Many relationship.
* </p>
*/
class MatchedImportedProperty {
private final BeanPropertyAssocOne<?> assocOne;
private final BeanProperty foreignProp;
private final BeanProperty localProp;
protected MatchedImportedProperty(BeanPropertyAssocOne<?> assocOne, BeanProperty foreignProp,
BeanProperty localProp) {
this.assocOne = assocOne;
this.foreignProp = foreignProp;
this.localProp = localProp;
}
protected void populate(EntityBean sourceBean, EntityBean destBean) {
Object assocBean = assocOne.getValue(sourceBean);
if (assocBean == null) {
String msg = "The assoc bean for " + assocOne + " is null?";
throw new NullPointerException(msg);
}
Object value = foreignProp.getValue((EntityBean) assocBean);
localProp.setValue(destBean, value);
}
public interface MatchedImportedProperty {
/**
* Create the array of matchedImportedProperty based on the properties and descriptor.
* Populate the embeddedId bean from the source entity.
*/
protected static MatchedImportedProperty[] build(BeanProperty[] props, BeanDescriptor<?> desc) {
MatchedImportedProperty[] matches = new MatchedImportedProperty[props.length];
for (int i = 0; i < props.length; i++) {
// find matching assoc one property for dbColumn
matches[i] = MatchedImportedProperty.findMatch(props[i], desc);
if (matches[i] == null) {
// ok, the assoc ones are not on the bean?
return null;
}
}
return matches;
}
private static MatchedImportedProperty findMatch(BeanProperty prop, BeanDescriptor<?> desc) {
// find matching against the local database column
String dbColumn = prop.getDbColumn();
BeanPropertyAssocOne<?>[] assocOnes = desc.propertiesOne();
for (BeanPropertyAssocOne<?> assocOne1 : assocOnes) {
if (assocOne1.isImportedPrimaryKey()) {
// search using the ImportedId from the assoc one
BeanProperty foreignMatch = assocOne1.getImportedId().findMatchImport(dbColumn);
if (foreignMatch != null) {
return new MatchedImportedProperty(assocOne1, foreignMatch, prop);
}
}
}
// there was no matching assoc one property.
// example UserRole bean missing assoc one to User?
return null;
}
void populate(EntityBean sourceBean, EntityBean embeddedId);
}
@@ -0,0 +1,27 @@
package io.ebeaninternal.server.persist.dmlbind;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.deploy.BeanProperty;
/**
* Matches local embedded id properties to 'matching' imported primary key scalar properties.
*/
class MatchedImportedScalar implements MatchedImportedProperty {
private final BeanProperty localProp;
private final BeanProperty foreignProp;
MatchedImportedScalar(BeanProperty localProp, BeanProperty foreignProp) {
this.localProp = localProp;
this.foreignProp = foreignProp;
}
@Override
public void populate(EntityBean sourceBean, EntityBean embeddedId) {
Object value = foreignProp.getValue(sourceBean);
localProp.setValue(embeddedId, value);
}
}