FIX: improved support for @DbForeignKey(noConstraint = true) (#1527)

* FIX: improved support for DbForeignKey(noConstraint = true)

* FIX: Tests for mariadb and sqlserver

* FIX: SqlServer was still incorrect

* FIX: not ommit property for formula

* Update BeanDescriptor.java (typo fix)

fix typo column (no code change!)
This commit is contained in:
Roland Praml
2018-11-13 20:44:45 +13:00
committed by Rob Bygrave
parent 3c6a52da31
commit ce403e72a7
13 changed files with 931 additions and 11 deletions
@@ -829,7 +829,7 @@ public final class EntityBeanIntercept implements Serializable {
if (lazyLoadFailure) {
// failed when batch lazy loaded by another bean in the batch
throw new EntityNotFoundException("Lazy loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted");
throw new EntityNotFoundException("(Lazy) loading failed on type:" + owner.getClass().getName() + " id:" + ownerId + " - Bean has been deleted");
}
if (lazyLoadProperty == -1) {
@@ -168,13 +168,8 @@ public class LoadBeanRequest extends LoadRequest {
// necessary but allow processing to continue until it is accessed by client code
Object id = desc.getId(ebi.getOwner());
if (!loadedIds.contains(id)) {
if (desc.isSoftDelete()) {
// assume this is logically deleted (hence not found)
desc.setSoftDeleteValue(ebi.getOwner());
} else {
logger.info("Lazy loading unsuccessful for type:" + desc.getName() + " id:" + id + " - expecting when bean has been deleted");
ebi.setLazyLoadFailure(id);
}
// assume this is logically deleted (hence not found)
desc.markAsDeleted(ebi.getOwner());
}
}
}
@@ -7,6 +7,7 @@ import io.ebean.SqlUpdate;
import io.ebean.Transaction;
import io.ebean.ValuePair;
import io.ebean.annotation.DocStoreMode;
import io.ebean.annotation.Formula;
import io.ebean.bean.BeanCollection;
import io.ebean.bean.EntityBean;
import io.ebean.bean.EntityBeanIntercept;
@@ -496,7 +497,10 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
DeployBeanPropertyLists listHelper = new DeployBeanPropertyLists(owner, this, deploy);
this.softDeleteProperty = listHelper.getSoftDeleteProperty();
this.softDelete = (softDeleteProperty != null);
// if formula is set, the property is virtual only (there is no column in db) the formula must evaluate to true,
// if there is a join to a deleted bean. Example: '@Formula(select = "${ta}.user_id is null")'
// this is required to support markAsDelete on beans that may have no FK constraint.
this.softDelete = (softDeleteProperty != null && !softDeleteProperty.isFormula());
this.idProperty = listHelper.getId();
this.versionProperty = listHelper.getVersionProperty();
this.unmappedJson = listHelper.getUnmappedJson();
@@ -2920,6 +2924,20 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
return softDeleteProperty.getSoftDeleteDbPredicate(tableAlias);
}
@Override
public void markAsDeleted(EntityBean bean) {
if (softDeleteProperty == null) {
Object id = getId(bean);
logger.info("(Lazy) loading unsuccessful for type:{} id:{} - expecting when bean has been deleted", getName(), id);
bean._ebean_getIntercept().setLazyLoadFailure(id);
} else {
setSoftDeleteValue(bean);
bean._ebean_getIntercept().setLoaded();
setAllLoaded(bean);
}
}
/**
* Return true if this entity type is draftable.
*/
@@ -1,5 +1,6 @@
package io.ebeaninternal.server.query;
import io.ebean.bean.EntityBean;
import io.ebeaninternal.server.type.ScalarType;
public interface STreePropertyAssocOne extends STreePropertyAssoc {
@@ -13,4 +14,14 @@ public interface STreePropertyAssocOne extends STreePropertyAssoc {
* Return the scalar type of the associated id property.
*/
ScalarType<?> getIdScalarType();
/**
* Returns true, if this relation has a foreign key.
*/
boolean hasForeignKey();
/**
* Return the property value as an entity bean from the parent.
*/
public EntityBean getValueAsEntityBean(EntityBean parentBean);
}
@@ -132,5 +132,13 @@ public interface STreeType {
*/
void inheritanceLoad(SqlBeanLoad sqlBeanLoad, STreeProperty property, DbReadContext ctx);
/**
* Mark the bean as deleted by setting the softDelete property to true.
*
* This works also, if there is only a virtual softDelete property computed by a formula.
*
* If there is no softdelete property, it sets the lazyLoadFailure flag in EBI.
*/
void markAsDeleted(EntityBean bean);
}
@@ -498,8 +498,11 @@ public final class SqlTreeBuilder {
for (STreePropertyAssocOne propertyAssocOne : desc.propsOne()) {
//noinspection StatementWithEmptyBody
if (queryProps != null && queryProps.isIncludedBeanJoin(propertyAssocOne.getName())) {
// if it is a joined bean... then don't add the property
if (queryProps != null
&& queryProps.isIncludedBeanJoin(propertyAssocOne.getName())
&& propertyAssocOne.hasForeignKey()
&& !propertyAssocOne.isFormula()) {
// if it is a joined bean with FK constraint... then don't add the property
// as it will have its own entire Node in the SqlTree
} else {
selectProps.add(propertyAssocOne);
@@ -266,6 +266,14 @@ class SqlTreeNodeBean implements SqlTreeNode {
if (id == null) {
// bean must be null...
localBean = null;
// ... but there may exist as reference bean in parent which has to be marked as deleted.
if (parentBean != null && nodeBeanProp instanceof STreePropertyAssocOne) {
contextBean = ((STreePropertyAssocOne)nodeBeanProp).getValueAsEntityBean(parentBean);
if (contextBean != null) {
desc.markAsDeleted(contextBean);
}
}
} else if (!temporalVersions) {
// check the PersistenceContext to see if the bean already exists
contextBean = (EntityBean) localDesc.contextPutIfAbsent(persistenceContext, id, localBean);