mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
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:
committed by
Rob Bygrave
parent
3c6a52da31
commit
ce403e72a7
@@ -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);
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package org.tests.model.nofk;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
import io.ebean.annotation.Formula;
|
||||
import io.ebean.annotation.Index;
|
||||
|
||||
@Entity
|
||||
public class EFile2NoFk {
|
||||
|
||||
@Id
|
||||
@Size(max = 64) // Note: mysql supports only 767 bytes for index
|
||||
String fileName;
|
||||
|
||||
@Index
|
||||
int ownerId;
|
||||
|
||||
// owner without softdelete property - will throw bean has been deleted
|
||||
@ManyToOne
|
||||
@Formula(select = "${ta}.Owner_Id")
|
||||
EUserNoFk owner;
|
||||
|
||||
// owner with softdelete property - will set the property to true
|
||||
@ManyToOne
|
||||
@Formula(select = "${ta}.Owner_id")
|
||||
EUserNoFkSoftDel ownerSoftDel;
|
||||
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public int getOwnerId() {
|
||||
return ownerId;
|
||||
}
|
||||
|
||||
public void setOwnerId(int ownerId) {
|
||||
this.ownerId = ownerId;
|
||||
}
|
||||
|
||||
public EUserNoFk getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public EUserNoFkSoftDel getOwnerSoftDel() {
|
||||
return ownerSoftDel;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package org.tests.model.nofk;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
import io.ebean.annotation.DbForeignKey;
|
||||
|
||||
@Entity
|
||||
public class EFileNoFk {
|
||||
|
||||
@Id
|
||||
@Size(max = 64) // Note: mysql supports only 767 bytes for index
|
||||
String fileName;
|
||||
|
||||
// owner without softdelete property - will throw bean has been deleted
|
||||
@ManyToOne
|
||||
@DbForeignKey(noConstraint = true)
|
||||
EUserNoFk owner;
|
||||
|
||||
// owner with softdelete property - will set the property to true
|
||||
@ManyToOne
|
||||
@DbForeignKey(noConstraint = true)
|
||||
EUserNoFkSoftDel ownerSoftDel;
|
||||
|
||||
// hold also a many-2-many relation for tests.
|
||||
@ManyToMany
|
||||
@DbForeignKey(noConstraint = true)
|
||||
List<EUserNoFk> editors;
|
||||
|
||||
@ManyToMany
|
||||
@DbForeignKey(noConstraint = true)
|
||||
List<EUserNoFkSoftDel> editorsSoftDel;
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public EUserNoFk getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(EUserNoFk owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public EUserNoFkSoftDel getOwnerSoftDel() {
|
||||
return ownerSoftDel;
|
||||
}
|
||||
|
||||
public void setOwnerSoftDel(EUserNoFkSoftDel ownerSoftDel) {
|
||||
this.ownerSoftDel = ownerSoftDel;
|
||||
}
|
||||
|
||||
public List<EUserNoFk> getEditors() {
|
||||
return editors;
|
||||
}
|
||||
|
||||
public void setEditors(List<EUserNoFk> editors) {
|
||||
this.editors = editors;
|
||||
}
|
||||
|
||||
public List<EUserNoFkSoftDel> getEditorsSoftDel() {
|
||||
return editorsSoftDel;
|
||||
}
|
||||
|
||||
public void setEditorsSoftDel(List<EUserNoFkSoftDel> editorsSoftDel) {
|
||||
this.editorsSoftDel = editorsSoftDel;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package org.tests.model.nofk;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
@Entity
|
||||
public class EUserNoFk {
|
||||
|
||||
@Id
|
||||
int userId;
|
||||
|
||||
String userName;
|
||||
|
||||
@OneToMany(mappedBy = "owner")
|
||||
List<EFileNoFk> files;
|
||||
|
||||
public int getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(int userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public List<EFileNoFk> getFiles() {
|
||||
return files;
|
||||
}
|
||||
|
||||
public void setFiles(List<EFileNoFk> files) {
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package org.tests.model.nofk;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
import io.ebean.annotation.Formula;
|
||||
import io.ebean.annotation.Platform;
|
||||
import io.ebean.annotation.SoftDelete;
|
||||
|
||||
@Entity
|
||||
public class EUserNoFkSoftDel {
|
||||
|
||||
@Id
|
||||
int userId;
|
||||
|
||||
String userName;
|
||||
|
||||
@SoftDelete
|
||||
@Formula(select = "${ta}.user_id is null")
|
||||
@Formula(select = "CASE WHEN ${ta}.user_id is null THEN 1 ELSE 0 END", platforms = Platform.SQLSERVER17)
|
||||
// evaluates to true in a left join if bean has been deleted.
|
||||
boolean deleted;
|
||||
|
||||
@OneToMany(mappedBy = "ownerSoftDel")
|
||||
List<EFileNoFk> files;
|
||||
|
||||
public int getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(int userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getUserName() {
|
||||
return userName;
|
||||
}
|
||||
|
||||
public void setUserName(String userName) {
|
||||
this.userName = userName;
|
||||
}
|
||||
|
||||
public List<EFileNoFk> getFiles() {
|
||||
return files;
|
||||
}
|
||||
|
||||
public void setFiles(List<EFileNoFk> files) {
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
public boolean isDeleted() {
|
||||
return deleted;
|
||||
}
|
||||
|
||||
public void setDeleted(boolean deleted) {
|
||||
this.deleted = deleted;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
package org.tests.model.nofk;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityNotFoundException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
// test that simulates relations with @formula
|
||||
public class Test2NoFk extends BaseTestCase {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
// Reset t
|
||||
Ebean.find(EFile2NoFk.class).delete();
|
||||
Ebean.find(EUserNoFk.class).delete();
|
||||
Ebean.find(EUserNoFkSoftDel.class).delete();
|
||||
|
||||
// There are two user accounts persisted in our database
|
||||
EUserNoFk root = new EUserNoFk();
|
||||
root.setUserId(1);
|
||||
root.setUserName("root");
|
||||
Ebean.save(root);
|
||||
|
||||
EUserNoFk nobody = new EUserNoFk();
|
||||
nobody.setUserId(2);
|
||||
nobody.setUserName("nobody");
|
||||
Ebean.save(nobody);
|
||||
|
||||
|
||||
// Now build the same with the softDel flag.
|
||||
EUserNoFkSoftDel rootSoftDel = new EUserNoFkSoftDel();
|
||||
rootSoftDel.setUserId(1);
|
||||
rootSoftDel.setUserName("root");
|
||||
Ebean.save(rootSoftDel);
|
||||
|
||||
EUserNoFkSoftDel nobodySoftDel = new EUserNoFkSoftDel();
|
||||
nobodySoftDel.setUserId(2);
|
||||
nobodySoftDel.setUserName("nobody");
|
||||
Ebean.save(nobodySoftDel);
|
||||
|
||||
|
||||
EFile2NoFk bash = new EFile2NoFk();
|
||||
bash.setFileName("bash");
|
||||
bash.setOwnerId(1);
|
||||
|
||||
EUserNoFk user501 = new EUserNoFk();
|
||||
EUserNoFkSoftDel user501SoftDel = new EUserNoFkSoftDel();
|
||||
user501.setUserId(501);
|
||||
user501SoftDel.setUserId(501);
|
||||
|
||||
Ebean.save(bash);
|
||||
|
||||
// create relation to non existent user
|
||||
EFile2NoFk cmd = new EFile2NoFk();
|
||||
|
||||
cmd.setFileName("java");
|
||||
cmd.setOwnerId(500);
|
||||
|
||||
Ebean.save(cmd);
|
||||
|
||||
assertThat(Ebean.find(EFile2NoFk.class).findCount()).isEqualTo(2);
|
||||
assertThat(Ebean.find(EUserNoFk.class).findCount()).isEqualTo(2);
|
||||
assertThat(Ebean.find(EUserNoFkSoftDel.class).findCount()).isEqualTo(2);
|
||||
|
||||
// We should have this data in the database:
|
||||
//
|
||||
// Owner: userId | userName File: fileName | ownerId
|
||||
// =======+========= =========+========
|
||||
// 1 | root bash | 1 (= root)
|
||||
// 2 | nobody java | 500 (= non existent user account)
|
||||
//
|
||||
// Editors: userId | fileName
|
||||
// =======+=========
|
||||
// 1 | bash
|
||||
// 2 | bash
|
||||
// 501 | bash
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazyLoadFile() {
|
||||
List<EFile2NoFk> files = Ebean.find(EFile2NoFk.class).findList();
|
||||
assertThat(files).hasSize(2);
|
||||
|
||||
EFile2NoFk file1 = files.get(0);
|
||||
EFile2NoFk file2 = files.get(1);
|
||||
|
||||
assertThat(file1.getFileName()).isEqualTo("bash");
|
||||
assertThat(file2.getFileName()).isEqualTo("java");
|
||||
|
||||
// File1 is owned by "root"
|
||||
EntityBeanIntercept ownerEbi = ((EntityBean)file1.getOwner())._ebean_getIntercept();
|
||||
assertThat(file1.getOwner().getUserId()).isEqualTo(1);
|
||||
assertTrue(ownerEbi.isReference());
|
||||
assertTrue(ownerEbi.isPartial());
|
||||
|
||||
assertThat(file1.getOwner().getUserName()).isEqualTo("root"); // trigger lazy-load
|
||||
assertFalse(ownerEbi.isReference());
|
||||
assertFalse(ownerEbi.isPartial()); // and expect, that bean is fully loaded
|
||||
|
||||
|
||||
|
||||
// File2 is owned by user #500, but user account does not exist
|
||||
ownerEbi = ((EntityBean)file2.getOwner())._ebean_getIntercept();
|
||||
|
||||
assertThat(file2.getOwner().getUserId()).isEqualTo(500);
|
||||
assertThatThrownBy(()->file2.getOwner().getUserName())
|
||||
.isInstanceOf(EntityNotFoundException.class)
|
||||
.hasMessageContaining("id:500 - Bean has been deleted");
|
||||
|
||||
assertTrue(ownerEbi.isReference());
|
||||
assertTrue(ownerEbi.isPartial());
|
||||
assertTrue(ownerEbi.isLazyLoadFailure());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEagerLoadFile() {
|
||||
List<EFile2NoFk> files = Ebean.find(EFile2NoFk.class).fetch("owner").findList();
|
||||
assertThat(files).hasSize(2);
|
||||
|
||||
EFile2NoFk file1 = files.get(0);
|
||||
EFile2NoFk file2 = files.get(1);
|
||||
|
||||
assertThat(file1.getFileName()).isEqualTo("bash");
|
||||
assertThat(file2.getFileName()).isEqualTo("java");
|
||||
|
||||
// File1 is owned by "root"
|
||||
EntityBeanIntercept ownerEbi = ((EntityBean)file1.getOwner())._ebean_getIntercept();
|
||||
assertFalse(ownerEbi.isReference());
|
||||
assertFalse(ownerEbi.isPartial());
|
||||
|
||||
assertThat(file1.getOwner().getUserId()).isEqualTo(1);
|
||||
assertThat(file1.getOwner().getUserName()).isEqualTo("root");
|
||||
|
||||
// File2 is owned by user #500, but user account does not exist
|
||||
ownerEbi = ((EntityBean)file2.getOwner())._ebean_getIntercept();
|
||||
|
||||
assertThat(file2.getOwner().getUserId()).isEqualTo(500);
|
||||
assertThatThrownBy(()->file2.getOwner().getUserName())
|
||||
.isInstanceOf(EntityNotFoundException.class)
|
||||
.hasMessageContaining("id:500 - Bean has been deleted");
|
||||
|
||||
assertTrue(ownerEbi.isReference());
|
||||
assertTrue(ownerEbi.isPartial());
|
||||
assertTrue(ownerEbi.isLazyLoadFailure());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazyLoadFileSoftDel() {
|
||||
List<EFile2NoFk> files = Ebean.find(EFile2NoFk.class).findList();
|
||||
assertThat(files).hasSize(2);
|
||||
|
||||
EFile2NoFk file1 = files.get(0);
|
||||
EFile2NoFk file2 = files.get(1);
|
||||
|
||||
assertThat(file1.getFileName()).isEqualTo("bash");
|
||||
assertThat(file2.getFileName()).isEqualTo("java");
|
||||
|
||||
// File1 is owned by "root"
|
||||
EntityBeanIntercept ownerEbi = ((EntityBean)file1.getOwnerSoftDel())._ebean_getIntercept();
|
||||
assertThat(file1.getOwnerSoftDel().getUserId()).isEqualTo(1);
|
||||
assertTrue(ownerEbi.isReference());
|
||||
assertTrue(ownerEbi.isPartial());
|
||||
|
||||
|
||||
assertThat(file1.getOwnerSoftDel().isDeleted()).isEqualTo(false); // trigger lazy-load
|
||||
assertFalse(ownerEbi.isReference());
|
||||
assertFalse(ownerEbi.isPartial()); // and expect, that bean is fully loaded
|
||||
|
||||
assertThat(file1.getOwnerSoftDel().getUserName()).isEqualTo("root");
|
||||
|
||||
|
||||
// File2 is owned by user #500, but user account does not exist
|
||||
ownerEbi = ((EntityBean)file2.getOwnerSoftDel())._ebean_getIntercept();
|
||||
|
||||
assertThat(file2.getOwnerSoftDel().getUserId()).isEqualTo(500);
|
||||
assertThat(file2.getOwnerSoftDel().isDeleted()).isEqualTo(true);
|
||||
assertThat(file2.getOwnerSoftDel().getUserName()).isNull();
|
||||
|
||||
assertFalse(ownerEbi.isReference());
|
||||
assertFalse(ownerEbi.isPartial());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEagerLoadFileSoftDel() {
|
||||
List<EFile2NoFk> files = Ebean.find(EFile2NoFk.class).fetch("ownerSoftDel").findList();
|
||||
assertThat(files).hasSize(2);
|
||||
|
||||
EFile2NoFk file1 = files.get(0);
|
||||
EFile2NoFk file2 = files.get(1);
|
||||
|
||||
assertThat(file1.getFileName()).isEqualTo("bash");
|
||||
assertThat(file2.getFileName()).isEqualTo("java");
|
||||
|
||||
// File1 is owned by "root"
|
||||
EntityBeanIntercept ownerEbi = ((EntityBean)file1.getOwnerSoftDel())._ebean_getIntercept();
|
||||
assertFalse(ownerEbi.isReference());
|
||||
assertFalse(ownerEbi.isPartial());
|
||||
|
||||
assertThat(file1.getOwnerSoftDel().getUserId()).isEqualTo(1);
|
||||
assertThat(file1.getOwnerSoftDel().isDeleted()).isEqualTo(false);
|
||||
assertThat(file1.getOwnerSoftDel().getUserName()).isEqualTo("root");
|
||||
|
||||
// File2 is owned by user #500, but user account does not exist
|
||||
ownerEbi = ((EntityBean)file2.getOwnerSoftDel())._ebean_getIntercept();
|
||||
// assertFalse(ownerEbi.isReference());
|
||||
// assertFalse(ownerEbi.isPartial());
|
||||
|
||||
assertThat(file2.getOwnerSoftDel().getUserId()).isEqualTo(500);
|
||||
assertThat(file2.getOwnerSoftDel().isDeleted()).isEqualTo(true);
|
||||
assertThat(file2.getOwnerSoftDel().getUserName()).isNull();
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
package org.tests.model.nofk;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.SqlRow;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.plugin.BeanType;
|
||||
import io.ebean.plugin.Property;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.EntityNotFoundException;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class TestNoFk extends BaseTestCase {
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
// Reset t
|
||||
Ebean.find(EFileNoFk.class).delete();
|
||||
Ebean.find(EUserNoFk.class).delete();
|
||||
Ebean.find(EUserNoFkSoftDel.class).delete();
|
||||
|
||||
Ebean.createSqlUpdate("delete from efile_no_fk_euser_no_fk").execute();
|
||||
Ebean.createSqlUpdate("delete from efile_no_fk_euser_no_fk_soft_del").execute();
|
||||
|
||||
// There are two user accounts persisted in our database
|
||||
EUserNoFk root = new EUserNoFk();
|
||||
root.setUserId(1);
|
||||
root.setUserName("root");
|
||||
Ebean.save(root);
|
||||
|
||||
EUserNoFk nobody = new EUserNoFk();
|
||||
nobody.setUserId(2);
|
||||
nobody.setUserName("nobody");
|
||||
Ebean.save(nobody);
|
||||
|
||||
|
||||
// Now build the same with the softDel flag.
|
||||
EUserNoFkSoftDel rootSoftDel = new EUserNoFkSoftDel();
|
||||
rootSoftDel.setUserId(1);
|
||||
rootSoftDel.setUserName("root");
|
||||
Ebean.save(rootSoftDel);
|
||||
|
||||
EUserNoFkSoftDel nobodySoftDel = new EUserNoFkSoftDel();
|
||||
nobodySoftDel.setUserId(2);
|
||||
nobodySoftDel.setUserName("nobody");
|
||||
Ebean.save(nobodySoftDel);
|
||||
|
||||
|
||||
EFileNoFk bash = new EFileNoFk();
|
||||
bash.setFileName("bash");
|
||||
bash.setOwner(root);
|
||||
bash.setOwnerSoftDel(rootSoftDel);
|
||||
|
||||
EUserNoFk user501 = new EUserNoFk();
|
||||
EUserNoFkSoftDel user501SoftDel = new EUserNoFkSoftDel();
|
||||
user501.setUserId(501);
|
||||
user501SoftDel.setUserId(501);
|
||||
bash.getEditors().add(root);
|
||||
bash.getEditors().add(nobody);
|
||||
bash.getEditors().add(user501);
|
||||
bash.getEditorsSoftDel().add(rootSoftDel);
|
||||
bash.getEditorsSoftDel().add(nobodySoftDel);
|
||||
bash.getEditorsSoftDel().add(user501SoftDel);
|
||||
|
||||
Ebean.save(bash);
|
||||
|
||||
// create relation to non existent user
|
||||
EFileNoFk cmd = new EFileNoFk();
|
||||
|
||||
cmd.setFileName("java");
|
||||
EUserNoFk user500 = new EUserNoFk();
|
||||
user500.setUserId(500);
|
||||
user500.setUserName("not persisted");
|
||||
cmd.setOwner(user500);
|
||||
|
||||
EUserNoFkSoftDel user500SoftDel = new EUserNoFkSoftDel();
|
||||
user500SoftDel.setUserId(500);
|
||||
user500SoftDel.setUserName("not persisted");
|
||||
cmd.setOwnerSoftDel(user500SoftDel);
|
||||
|
||||
Ebean.save(cmd);
|
||||
|
||||
|
||||
assertThat(Ebean.find(EFileNoFk.class).findCount()).isEqualTo(2);
|
||||
assertThat(Ebean.find(EUserNoFk.class).findCount()).isEqualTo(2);
|
||||
assertThat(Ebean.find(EUserNoFkSoftDel.class).findCount()).isEqualTo(2);
|
||||
|
||||
SqlRow row = Ebean.createSqlQuery("select count(*) as cnt from efile_no_fk_euser_no_fk").findOne();
|
||||
assertThat(row.getInteger("cnt")).isEqualTo(3);
|
||||
// We should have this data in the database:
|
||||
//
|
||||
// Owner: userId | userName File: fileName | ownerId
|
||||
// =======+========= =========+========
|
||||
// 1 | root bash | 1 (= root)
|
||||
// 2 | nobody java | 500 (= non existent user account)
|
||||
//
|
||||
// Editors: userId | fileName
|
||||
// =======+=========
|
||||
// 1 | bash
|
||||
// 2 | bash
|
||||
// 501 | bash
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazyLoadFile() {
|
||||
List<EFileNoFk> files = Ebean.find(EFileNoFk.class).findList();
|
||||
assertThat(files).hasSize(2);
|
||||
|
||||
EFileNoFk file1 = files.get(0);
|
||||
EFileNoFk file2 = files.get(1);
|
||||
|
||||
assertThat(file1.getFileName()).isEqualTo("bash");
|
||||
assertThat(file2.getFileName()).isEqualTo("java");
|
||||
|
||||
// File1 is owned by "root"
|
||||
EntityBeanIntercept ownerEbi = ((EntityBean)file1.getOwner())._ebean_getIntercept();
|
||||
assertThat(file1.getOwner().getUserId()).isEqualTo(1);
|
||||
assertTrue(ownerEbi.isReference());
|
||||
assertTrue(ownerEbi.isPartial());
|
||||
|
||||
assertThat(file1.getOwner().getUserName()).isEqualTo("root"); // trigger lazy-load
|
||||
assertFalse(ownerEbi.isReference());
|
||||
assertFalse(ownerEbi.isPartial()); // and expect, that bean is fully loaded
|
||||
|
||||
|
||||
|
||||
// File2 is owned by user #500, but user account does not exist
|
||||
ownerEbi = ((EntityBean)file2.getOwner())._ebean_getIntercept();
|
||||
|
||||
assertThat(file2.getOwner().getUserId()).isEqualTo(500);
|
||||
assertThatThrownBy(()->file2.getOwner().getUserName())
|
||||
.isInstanceOf(EntityNotFoundException.class)
|
||||
.hasMessageContaining("id:500 - Bean has been deleted");
|
||||
|
||||
assertTrue(ownerEbi.isReference());
|
||||
assertTrue(ownerEbi.isPartial());
|
||||
assertTrue(ownerEbi.isLazyLoadFailure());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEagerLoadFile() {
|
||||
List<EFileNoFk> files = Ebean.find(EFileNoFk.class).fetch("owner").findList();
|
||||
assertThat(files).hasSize(2);
|
||||
|
||||
EFileNoFk file1 = files.get(0);
|
||||
EFileNoFk file2 = files.get(1);
|
||||
|
||||
assertThat(file1.getFileName()).isEqualTo("bash");
|
||||
assertThat(file2.getFileName()).isEqualTo("java");
|
||||
|
||||
// File1 is owned by "root"
|
||||
EntityBeanIntercept ownerEbi = ((EntityBean)file1.getOwner())._ebean_getIntercept();
|
||||
assertFalse(ownerEbi.isReference());
|
||||
assertFalse(ownerEbi.isPartial());
|
||||
|
||||
assertThat(file1.getOwner().getUserId()).isEqualTo(1);
|
||||
assertThat(file1.getOwner().getUserName()).isEqualTo("root");
|
||||
|
||||
// File2 is owned by user #500, but user account does not exist
|
||||
ownerEbi = ((EntityBean)file2.getOwner())._ebean_getIntercept();
|
||||
|
||||
assertThat(file2.getOwner().getUserId()).isEqualTo(500);
|
||||
assertThatThrownBy(()->file2.getOwner().getUserName())
|
||||
.isInstanceOf(EntityNotFoundException.class)
|
||||
.hasMessageContaining("id:500 - Bean has been deleted");
|
||||
|
||||
assertTrue(ownerEbi.isReference());
|
||||
assertTrue(ownerEbi.isPartial());
|
||||
assertTrue(ownerEbi.isLazyLoadFailure());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazyLoadOwner() {
|
||||
List<EUserNoFk> owners = Ebean.find(EUserNoFk.class).findList();
|
||||
assertThat(owners).hasSize(2);
|
||||
|
||||
EUserNoFk owner1 = owners.get(0);
|
||||
EUserNoFk owner2 = owners.get(1);
|
||||
|
||||
assertThat(owner1.getUserId()).isEqualTo(1);
|
||||
assertThat(owner1.getUserName()).isEqualTo("root");
|
||||
assertThat(owner2.getUserId()).isEqualTo(2);
|
||||
assertThat(owner2.getUserName()).isEqualTo("nobody");
|
||||
|
||||
assertThat(owner1.getFiles()).hasSize(1);
|
||||
assertThat(owner2.getFiles()).hasSize(0);
|
||||
|
||||
assertThat(owner1.getFiles().get(0).getFileName()).isEqualTo("bash");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEagerLoadOwner() {
|
||||
List<EUserNoFk> owners = Ebean.find(EUserNoFk.class)
|
||||
.fetch("files")
|
||||
.findList();
|
||||
assertThat(owners).hasSize(2);
|
||||
|
||||
EUserNoFk owner1 = owners.get(0);
|
||||
EUserNoFk owner2 = owners.get(1);
|
||||
|
||||
assertThat(owner1.getUserId()).isEqualTo(1);
|
||||
assertThat(owner1.getUserName()).isEqualTo("root");
|
||||
assertThat(owner2.getUserId()).isEqualTo(2);
|
||||
assertThat(owner2.getUserName()).isEqualTo("nobody");
|
||||
|
||||
assertThat(owner1.getFiles()).hasSize(1);
|
||||
assertThat(owner2.getFiles()).hasSize(0);
|
||||
|
||||
assertThat(owner1.getFiles().get(0).getFileName()).isEqualTo("bash");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazyLoadFileSoftDel() {
|
||||
List<EFileNoFk> files = Ebean.find(EFileNoFk.class).findList();
|
||||
assertThat(files).hasSize(2);
|
||||
|
||||
EFileNoFk file1 = files.get(0);
|
||||
EFileNoFk file2 = files.get(1);
|
||||
|
||||
assertThat(file1.getFileName()).isEqualTo("bash");
|
||||
assertThat(file2.getFileName()).isEqualTo("java");
|
||||
|
||||
// File1 is owned by "root"
|
||||
EntityBeanIntercept ownerEbi = ((EntityBean)file1.getOwnerSoftDel())._ebean_getIntercept();
|
||||
assertThat(file1.getOwnerSoftDel().getUserId()).isEqualTo(1);
|
||||
assertTrue(ownerEbi.isReference());
|
||||
assertTrue(ownerEbi.isPartial());
|
||||
|
||||
|
||||
assertThat(file1.getOwnerSoftDel().isDeleted()).isEqualTo(false); // trigger lazy-load
|
||||
assertFalse(ownerEbi.isReference());
|
||||
assertFalse(ownerEbi.isPartial()); // and expect, that bean is fully loaded
|
||||
|
||||
assertThat(file1.getOwnerSoftDel().getUserName()).isEqualTo("root");
|
||||
|
||||
|
||||
// File2 is owned by user #500, but user account does not exist
|
||||
ownerEbi = ((EntityBean)file2.getOwnerSoftDel())._ebean_getIntercept();
|
||||
|
||||
assertThat(file2.getOwnerSoftDel().getUserId()).isEqualTo(500);
|
||||
assertThat(file2.getOwnerSoftDel().isDeleted()).isEqualTo(true);
|
||||
assertThat(file2.getOwnerSoftDel().getUserName()).isNull();
|
||||
|
||||
assertFalse(ownerEbi.isReference());
|
||||
assertFalse(ownerEbi.isPartial());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEagerLoadFileSoftDel() {
|
||||
List<EFileNoFk> files = Ebean.find(EFileNoFk.class).fetch("ownerSoftDel").findList();
|
||||
assertThat(files).hasSize(2);
|
||||
|
||||
EFileNoFk file1 = files.get(0);
|
||||
EFileNoFk file2 = files.get(1);
|
||||
|
||||
assertThat(file1.getFileName()).isEqualTo("bash");
|
||||
assertThat(file2.getFileName()).isEqualTo("java");
|
||||
|
||||
// File1 is owned by "root"
|
||||
EntityBeanIntercept ownerEbi = ((EntityBean)file1.getOwnerSoftDel())._ebean_getIntercept();
|
||||
assertFalse(ownerEbi.isReference());
|
||||
assertFalse(ownerEbi.isPartial());
|
||||
|
||||
assertThat(file1.getOwnerSoftDel().getUserId()).isEqualTo(1);
|
||||
assertThat(file1.getOwnerSoftDel().isDeleted()).isEqualTo(false);
|
||||
assertThat(file1.getOwnerSoftDel().getUserName()).isEqualTo("root");
|
||||
|
||||
// File2 is owned by user #500, but user account does not exist
|
||||
ownerEbi = ((EntityBean)file2.getOwnerSoftDel())._ebean_getIntercept();
|
||||
// assertFalse(ownerEbi.isReference());
|
||||
// assertFalse(ownerEbi.isPartial());
|
||||
|
||||
assertThat(file2.getOwnerSoftDel().getUserId()).isEqualTo(500);
|
||||
assertThat(file2.getOwnerSoftDel().isDeleted()).isEqualTo(true);
|
||||
assertThat(file2.getOwnerSoftDel().getUserName()).isNull();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLazyLoadOwnerSoftDel() {
|
||||
List<EUserNoFkSoftDel> owners = Ebean.find(EUserNoFkSoftDel.class).findList();
|
||||
assertThat(owners).hasSize(2);
|
||||
|
||||
EUserNoFkSoftDel owner1 = owners.get(0);
|
||||
EUserNoFkSoftDel owner2 = owners.get(1);
|
||||
|
||||
assertThat(owner1.getUserId()).isEqualTo(1);
|
||||
assertThat(owner1.getUserName()).isEqualTo("root");
|
||||
assertThat(owner2.getUserId()).isEqualTo(2);
|
||||
assertThat(owner2.getUserName()).isEqualTo("nobody");
|
||||
|
||||
assertThat(owner1.getFiles()).hasSize(1);
|
||||
assertThat(owner2.getFiles()).hasSize(0);
|
||||
|
||||
assertThat(owner1.getFiles().get(0).getFileName()).isEqualTo("bash");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEagerLoadOwnerSoftDel() {
|
||||
List<EUserNoFkSoftDel> owners = Ebean.find(EUserNoFkSoftDel.class)
|
||||
.fetch("files")
|
||||
.findList();
|
||||
assertThat(owners).hasSize(2);
|
||||
|
||||
EUserNoFkSoftDel owner1 = owners.get(0);
|
||||
EUserNoFkSoftDel owner2 = owners.get(1);
|
||||
|
||||
assertThat(owner1.getUserId()).isEqualTo(1);
|
||||
assertThat(owner1.getUserName()).isEqualTo("root");
|
||||
assertThat(owner2.getUserId()).isEqualTo(2);
|
||||
assertThat(owner2.getUserName()).isEqualTo("nobody");
|
||||
|
||||
assertThat(owner1.getFiles()).hasSize(1);
|
||||
assertThat(owner2.getFiles()).hasSize(0);
|
||||
|
||||
assertThat(owner1.getFiles().get(0).getFileName()).isEqualTo("bash");
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@Ignore("this would be a bonus task :)")
|
||||
public void testLazyLoadUser500() {
|
||||
// user 500 does not exist in DB
|
||||
EUserNoFk owner = Ebean.find(EUserNoFk.class, 500);
|
||||
assertThat(owner).isNull();
|
||||
|
||||
// but there are files that are owned by #500
|
||||
owner = Ebean.getReference(EUserNoFk.class, 500);
|
||||
assertThat(owner.getUserId()).isEqualTo(500);
|
||||
// this does not work yet, because the executed select contains a join:
|
||||
// select t0.user_id, t1.file_name, t1.owner_user_id
|
||||
// from eowner_no_fk t0
|
||||
// left join efile_no_fk t1 on t1.owner_user_id = t0.user_id
|
||||
// where t0.user_id = ? order by t0.user_id; --bind(500, )
|
||||
//
|
||||
// expected select (without join, as t0.user_id is already set and not neccessary to query):
|
||||
// select t1.file_name, t1.owner_user_id
|
||||
// from efile_no_fk t1
|
||||
// where t1.owner_user_id = ?; --bind(500, )
|
||||
//
|
||||
assertThat(owner.getFiles()).hasSize(1);
|
||||
assertThat(owner.getFiles().get(0).getFileName()).isEqualTo("java");
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Bonus Task 2")
|
||||
public void testM2mLazyLoadFile() {
|
||||
List<EFileNoFk> files = Ebean.find(EFileNoFk.class).findList();
|
||||
assertThat(files).hasSize(2);
|
||||
|
||||
EFileNoFk file1 = files.get(0);
|
||||
EFileNoFk file2 = files.get(1);
|
||||
|
||||
assertThat(file1.getFileName()).isEqualTo("bash");
|
||||
assertThat(file2.getFileName()).isEqualTo("java");
|
||||
|
||||
|
||||
// Currently, the test will fail here, because the generated SQL is:
|
||||
//
|
||||
// select int_.efile_no_fk_file_name, t0.user_id, t0.user_name from euser_no_fk t0
|
||||
// left join efile_no_fk_euser_no_fk int_ on int_.euser_no_fk_user_id = t0.user_id
|
||||
// where (int_.efile_no_fk_file_name) in (?, ?, ?, ?, ? ) ; --bind(Array[5]={bash,java,bash,bash,bash})
|
||||
//
|
||||
// So as there is no entry in 'euser_no_fk', we also cannot read the m2m table
|
||||
assertThat(file1.getEditors()).hasSize(3);
|
||||
assertThat(file1.getEditorsSoftDel()).hasSize(3);
|
||||
|
||||
assertThat(file2.getEditors()).isEmpty();
|
||||
assertThat(file2.getEditorsSoftDel()).isEmpty();
|
||||
|
||||
|
||||
assertThat(file2.getEditors().get(2).getUserId()).isEqualTo(501);
|
||||
assertThatThrownBy(()->file2.getEditors().get(2).getUserName())
|
||||
.isInstanceOf(EntityNotFoundException.class)
|
||||
.hasMessageContaining("id:501 - Bean has been deleted");
|
||||
|
||||
assertThat(file2.getEditorsSoftDel().get(2).getUserId()).isEqualTo(501);
|
||||
assertThat(file2.getEditorsSoftDel().get(2).isDeleted()).isTrue();
|
||||
assertThat(file2.getEditorsSoftDel().get(2).getUserName()).isNull();
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user