mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
Compare commits
7
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49b568e7d4 | ||
|
|
628af31ddd | ||
|
|
31cac60393 | ||
|
|
6a067c0cf7 | ||
|
|
842292ef6b | ||
|
|
6db9a60f4f | ||
|
|
d9125db957 |
@@ -9,7 +9,7 @@
|
||||
|
||||
<groupId>io.ebean</groupId>
|
||||
<artifactId>ebean</artifactId>
|
||||
<version>11.10.4</version>
|
||||
<version>11.10.5</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>ebean</name>
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
<scm>
|
||||
<developerConnection>scm:git:git@github.com:ebean-orm/ebean.git</developerConnection>
|
||||
<tag>ebean-11.10.4</tag>
|
||||
<tag>ebean-11.10.5</tag>
|
||||
</scm>
|
||||
|
||||
<profiles>
|
||||
|
||||
@@ -227,6 +227,11 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
void modifyReset();
|
||||
|
||||
/**
|
||||
* Has been modified by an addition or removal.
|
||||
*/
|
||||
boolean wasTouched();
|
||||
|
||||
/**
|
||||
* Return a shallow copy of this collection that is modifiable.
|
||||
*/
|
||||
|
||||
@@ -90,6 +90,7 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
private int lazyLoadProperty = -1;
|
||||
|
||||
private Object ownerId;
|
||||
private int sortOrder;
|
||||
|
||||
/**
|
||||
* Create a intercept with a given entity.
|
||||
@@ -693,6 +694,9 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
* Add and return a dirty property hash recursing into embedded beans.
|
||||
*/
|
||||
private void addDirtyPropertyKey(StringBuilder sb) {
|
||||
if (sortOrder > 0) {
|
||||
sb.append("s,");
|
||||
}
|
||||
int len = getPropertyLength();
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (changedProps != null && changedProps[i]) {
|
||||
@@ -1050,4 +1054,18 @@ public final class EntityBeanIntercept implements Serializable {
|
||||
public void setOldValue(int propertyIndex, Object oldValue) {
|
||||
setChangedPropertyValue(propertyIndex, true, oldValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the sort order value for an order column.
|
||||
*/
|
||||
public int getSortOrder() {
|
||||
return sortOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sort order value for an order column.
|
||||
*/
|
||||
public void setSortOrder(int sortOrder) {
|
||||
this.sortOrder = sortOrder;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,12 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
boolean holdsModifications() {
|
||||
return modifyHolder != null && modifyHolder.hasModifications();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean wasTouched() {
|
||||
return modifyHolder != null && modifyHolder.wasTouched();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies all relevant properties for a clone. See {@link #getShallowCopy()}
|
||||
* @param other
|
||||
|
||||
@@ -28,7 +28,10 @@ class ModifyHolder<E> implements Serializable {
|
||||
*/
|
||||
private Set<E> modifyAdditions = new LinkedHashSet<>();
|
||||
|
||||
private boolean touched;
|
||||
|
||||
void reset() {
|
||||
touched = false;
|
||||
modifyDeletions = new LinkedHashSet<>();
|
||||
modifyAdditions = new LinkedHashSet<>();
|
||||
}
|
||||
@@ -50,6 +53,7 @@ class ModifyHolder<E> implements Serializable {
|
||||
|
||||
void modifyAddition(E bean) {
|
||||
if (bean != null) {
|
||||
touched = true;
|
||||
// If it is to delete then just remove the deletion
|
||||
if (!undoDeletion(bean)) {
|
||||
// Insert
|
||||
@@ -65,6 +69,7 @@ class ModifyHolder<E> implements Serializable {
|
||||
@SuppressWarnings("unchecked")
|
||||
void modifyRemoval(Object bean) {
|
||||
if (bean != null) {
|
||||
touched = true;
|
||||
// If it is to be added then just remove the addition
|
||||
if (!undoAddition(bean)) {
|
||||
modifyDeletions.add((E) bean);
|
||||
@@ -80,6 +85,14 @@ class ModifyHolder<E> implements Serializable {
|
||||
return modifyDeletions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the collection was touched in some way. This is still true even if
|
||||
* a bean was removed and then added (which is not held as a modification).
|
||||
*/
|
||||
boolean wasTouched() {
|
||||
return touched;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there additions or removals.
|
||||
*/
|
||||
|
||||
+8
-2
@@ -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;
|
||||
@@ -307,6 +308,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
|
||||
|
||||
private final BeanPropertyAssocOne<?> unidirectional;
|
||||
private final BeanProperty orderColumn;
|
||||
|
||||
/**
|
||||
* list of properties that are Lists/Sets/Maps (Derived).
|
||||
@@ -454,6 +456,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();
|
||||
@@ -478,6 +481,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
this.propertiesLocal = listHelper.getLocal();
|
||||
this.propertiesMutable = listHelper.getMutable();
|
||||
this.unidirectional = listHelper.getUnidirectional();
|
||||
this.orderColumn = listHelper.getOrderColumn();
|
||||
this.propertiesOne = listHelper.getOnes();
|
||||
this.propertiesOneExportedSave = listHelper.getOneExportedSave();
|
||||
this.propertiesOneExportedDelete = listHelper.getOneExportedDelete();
|
||||
@@ -1923,6 +1927,13 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
return owner.getBeanDescriptor(otherType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the order column property.
|
||||
*/
|
||||
public BeanProperty getOrderColumn() {
|
||||
return orderColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "shadow" property to support unidirectional relationships.
|
||||
* <p>
|
||||
@@ -2888,6 +2899,10 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
public TableJoin getPrimaryKeyJoin() {
|
||||
return primaryKeyJoin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanProperty getIdProperty() {
|
||||
return idProperty;
|
||||
|
||||
@@ -38,6 +38,7 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanTable;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployOrderColumn;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
import io.ebeaninternal.server.deploy.parse.DeployBeanInfo;
|
||||
import io.ebeaninternal.server.deploy.parse.DeployCreateProperties;
|
||||
@@ -50,6 +51,7 @@ import io.ebeaninternal.server.properties.BeanPropertiesReader;
|
||||
import io.ebeaninternal.server.properties.BeanPropertyAccess;
|
||||
import io.ebeaninternal.server.properties.EnhanceBeanPropertyAccess;
|
||||
import io.ebeaninternal.server.query.CQueryPlan;
|
||||
import io.ebeaninternal.server.type.ScalarTypeInteger;
|
||||
import io.ebeaninternal.xmlmapping.XmlMappingReader;
|
||||
import io.ebeaninternal.xmlmapping.model.XmAliasMapping;
|
||||
import io.ebeaninternal.xmlmapping.model.XmColumnMapping;
|
||||
@@ -797,8 +799,12 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
// We only perform 'circular' checks etc after we have
|
||||
// all the DeployBeanDescriptors created and in the map.
|
||||
|
||||
List<DeployBeanPropertyAssocOne<?>> primaryKeyJoinCheck = new ArrayList<>();
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
checkMappedBy(info);
|
||||
checkMappedBy(info, primaryKeyJoinCheck);
|
||||
}
|
||||
for (DeployBeanPropertyAssocOne<?> prop : primaryKeyJoinCheck) {
|
||||
checkUniDirectionalPrimaryKeyJoin(prop);
|
||||
}
|
||||
|
||||
for (DeployBeanInfo<?> info : deployInfoMap.values()) {
|
||||
@@ -868,12 +874,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* relationships.
|
||||
* </p>
|
||||
*/
|
||||
private void checkMappedBy(DeployBeanInfo<?> info) {
|
||||
private void checkMappedBy(DeployBeanInfo<?> info, List<DeployBeanPropertyAssocOne<?>> primaryKeyJoinCheck) {
|
||||
|
||||
for (DeployBeanPropertyAssocOne<?> oneProp : info.getDescriptor().propertiesAssocOne()) {
|
||||
if (!oneProp.isTransient()) {
|
||||
if (oneProp.getMappedBy() != null) {
|
||||
checkMappedByOneToOne(oneProp);
|
||||
} else if (oneProp.isPrimaryKeyJoin()) {
|
||||
primaryKeyJoinCheck.add(oneProp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -973,6 +981,23 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
|
||||
private void makeOrderColumn(DeployBeanPropertyAssocMany<?> oneToMany) {
|
||||
|
||||
DeployBeanDescriptor<?> targetDesc = getTargetDescriptor(oneToMany);
|
||||
|
||||
DeployOrderColumn orderColumn = oneToMany.getOrderColumn();
|
||||
DeployBeanProperty orderProperty = new DeployBeanProperty(targetDesc, Integer.class, ScalarTypeInteger.INSTANCE, null);
|
||||
|
||||
orderProperty.setName(DeployOrderColumn.LOGICAL_NAME);
|
||||
orderProperty.setDbColumn(orderColumn.getName());
|
||||
orderProperty.setNullable(orderColumn.isNullable());
|
||||
orderProperty.setDbInsertable(orderColumn.isInsertable());
|
||||
orderProperty.setDbUpdateable(orderColumn.isUpdatable());
|
||||
orderProperty.setDbRead(true);
|
||||
|
||||
targetDesc.setOrderColumn(orderProperty);
|
||||
}
|
||||
|
||||
/**
|
||||
* A OneToMany with no matching mappedBy property in the target so must be
|
||||
* unidirectional.
|
||||
@@ -1071,6 +1096,21 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
DeployTableJoin otherTableJoin = mappedAssocOne.getTableJoin();
|
||||
otherTableJoin.copyWithoutType(tableJoin, true, tableJoin.getTable());
|
||||
}
|
||||
|
||||
if (mappedAssocOne.isPrimaryKeyJoin()) {
|
||||
// bi-directional PrimaryKeyJoin ...
|
||||
mappedAssocOne.setPrimaryKeyJoin(false);
|
||||
prop.setPrimaryKeyExport();
|
||||
addPrimaryKeyJoin(prop);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkUniDirectionalPrimaryKeyJoin(DeployBeanPropertyAssocOne<?> prop) {
|
||||
if (prop.isPrimaryKeyJoin()) {
|
||||
// uni-directional PrimaryKeyJoin ...
|
||||
prop.setPrimaryKeyExport();
|
||||
addPrimaryKeyJoin(prop);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1091,6 +1131,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
prop.getCascadeInfo().setSaveDelete(true, true);
|
||||
}
|
||||
|
||||
if (prop.hasOrderColumn()) {
|
||||
makeOrderColumn(prop);
|
||||
}
|
||||
|
||||
if (prop.getMappedBy() == null) {
|
||||
// if we are doc store only we are done
|
||||
// this allowes the use of @OneToMany in @DocStore - Entities
|
||||
@@ -1546,6 +1590,17 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return changeLogListener;
|
||||
}
|
||||
|
||||
public void addPrimaryKeyJoin(DeployBeanPropertyAssocOne<?> prop) {
|
||||
|
||||
String baseTable = prop.getDesc().getBaseTable();
|
||||
DeployTableJoin inverse = prop.getTableJoin().createInverse(baseTable);
|
||||
|
||||
TableJoin inverseJoin = new TableJoin(inverse);
|
||||
|
||||
DeployBeanInfo<?> target = deployInfoMap.get(prop.getTargetType());
|
||||
target.setPrimaryKeyJoin(inverseJoin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparator to sort the BeanDescriptors by name.
|
||||
*/
|
||||
|
||||
@@ -58,6 +58,11 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
*/
|
||||
private final boolean unidirectional;
|
||||
|
||||
/**
|
||||
* Flag to indicate that the target has a order column to auto populate.
|
||||
*/
|
||||
private final boolean hasOrderColumn;
|
||||
|
||||
/**
|
||||
* Flag to indicate manyToMany relationship.
|
||||
*/
|
||||
@@ -109,6 +114,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
public BeanPropertyAssocMany(BeanDescriptor<?> descriptor, DeployBeanPropertyAssocMany<T> deploy) {
|
||||
super(descriptor, deploy);
|
||||
this.unidirectional = deploy.isUnidirectional();
|
||||
this.hasOrderColumn = deploy.hasOrderColumn();
|
||||
this.manyToMany = deploy.isManyToMany();
|
||||
this.manyType = deploy.getManyType();
|
||||
this.mapKey = deploy.getMapKey();
|
||||
@@ -534,6 +540,10 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasOrderColumn() {
|
||||
return hasOrderColumn;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAssocMany() {
|
||||
return true;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
|
||||
/**
|
||||
* Bean property for synthetic sort order value / order column.
|
||||
*
|
||||
* The value of which is held on the entity bean intercept.
|
||||
*/
|
||||
public class BeanPropertyOrderColumn extends BeanProperty {
|
||||
|
||||
public BeanPropertyOrderColumn(BeanDescriptor<?> descriptor, DeployBeanProperty deploy) {
|
||||
super(descriptor, deploy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValue(EntityBean bean) {
|
||||
return bean._ebean_getIntercept().getSortOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getValueIntercept(EntityBean bean) {
|
||||
return bean._ebean_getIntercept().getSortOrder();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(EntityBean bean, Object value) {
|
||||
bean._ebean_getIntercept().setSortOrder((int)value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValueIntercept(EntityBean bean, Object value) {
|
||||
bean._ebean_getIntercept().setSortOrder((int)value);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +81,8 @@ public class DeployBeanDescriptor<T> {
|
||||
|
||||
private DeployBeanPropertyAssocOne<?> unidirectional;
|
||||
|
||||
private DeployBeanProperty orderColumn;
|
||||
|
||||
/**
|
||||
* Type of Identity generation strategy used.
|
||||
*/
|
||||
@@ -203,6 +203,7 @@ public class DeployBeanDescriptor<T> {
|
||||
private DocStoreMode docStoreDelete;
|
||||
|
||||
private List<DeployBeanProperty> idProperties;
|
||||
private TableJoin primaryKeyJoin;
|
||||
|
||||
private short profileId;
|
||||
|
||||
@@ -215,6 +216,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.
|
||||
*/
|
||||
@@ -433,6 +448,14 @@ public class DeployBeanDescriptor<T> {
|
||||
this.unidirectional = unidirectional;
|
||||
}
|
||||
|
||||
public void setOrderColumn(DeployBeanProperty orderColumn) {
|
||||
this.orderColumn = orderColumn;
|
||||
}
|
||||
|
||||
public DeployBeanProperty getOrderColumn() {
|
||||
return orderColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the concurrency mode used for beans of this type.
|
||||
*/
|
||||
|
||||
@@ -17,8 +17,8 @@ import io.ebean.config.dbplatform.DbEncryptFunction;
|
||||
import io.ebean.util.AnnotationUtil;
|
||||
import io.ebeaninternal.server.core.InternString;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.DeployDocPropertyOptions;
|
||||
import io.ebeaninternal.server.deploy.DbMigrationInfo;
|
||||
import io.ebeaninternal.server.deploy.DeployDocPropertyOptions;
|
||||
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.properties.BeanPropertyGetter;
|
||||
@@ -284,6 +284,10 @@ public class DeployBeanProperty {
|
||||
return desc.getFullName() + "." + name;
|
||||
}
|
||||
|
||||
public DeployBeanDescriptor<?> getDesc() {
|
||||
return desc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DB column length for character columns.
|
||||
* <p>
|
||||
|
||||
@@ -16,7 +16,7 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
/**
|
||||
* Persist settings.
|
||||
*/
|
||||
private final BeanCascadeInfo cascadeInfo = new BeanCascadeInfo();
|
||||
protected final BeanCascadeInfo cascadeInfo = new BeanCascadeInfo();
|
||||
|
||||
/**
|
||||
* The join table information.
|
||||
|
||||
@@ -12,35 +12,37 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
|
||||
/**
|
||||
* The type of the many, set, list or map.
|
||||
*/
|
||||
final ManyType manyType;
|
||||
private final ManyType manyType;
|
||||
|
||||
ModifyListenMode modifyListenMode = ModifyListenMode.NONE;
|
||||
|
||||
/**
|
||||
* Flag to indicate manyToMany relationship.
|
||||
*/
|
||||
boolean manyToMany;
|
||||
private boolean manyToMany;
|
||||
|
||||
/**
|
||||
* Flag to indicate this is a unidirectional relationship.
|
||||
*/
|
||||
boolean unidirectional;
|
||||
private boolean unidirectional;
|
||||
|
||||
/**
|
||||
* Join for manyToMany intersection table.
|
||||
*/
|
||||
DeployTableJoin intersectionJoin;
|
||||
private DeployTableJoin intersectionJoin;
|
||||
|
||||
/**
|
||||
* For ManyToMany this is the Inverse join used to build reference queries.
|
||||
*/
|
||||
DeployTableJoin inverseJoin;
|
||||
private DeployTableJoin inverseJoin;
|
||||
|
||||
String fetchOrderBy;
|
||||
private String fetchOrderBy;
|
||||
|
||||
String mapKey;
|
||||
private String mapKey;
|
||||
|
||||
String intersectionDraftTable;
|
||||
private String intersectionDraftTable;
|
||||
|
||||
private DeployOrderColumn orderColumn;
|
||||
|
||||
/**
|
||||
* Create this property.
|
||||
@@ -206,4 +208,16 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
|
||||
public void setIntersectionDraftTable() {
|
||||
this.intersectionDraftTable = intersectionJoin.getTable() + "_draft";
|
||||
}
|
||||
|
||||
public void setOrderColumn(DeployOrderColumn orderColumn) {
|
||||
this.orderColumn = orderColumn;
|
||||
}
|
||||
|
||||
public DeployOrderColumn getOrderColumn() {
|
||||
return orderColumn;
|
||||
}
|
||||
|
||||
public boolean hasOrderColumn() {
|
||||
return orderColumn != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
|
||||
/**
|
||||
* Property mapped to a joined bean.
|
||||
*/
|
||||
@@ -9,6 +11,10 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
|
||||
private boolean oneToOneExported;
|
||||
|
||||
private boolean primaryKeyJoin;
|
||||
|
||||
private boolean primaryKeyExport;
|
||||
|
||||
private boolean importedPrimaryKey;
|
||||
|
||||
private DeployBeanEmbedded deployEmbedded;
|
||||
@@ -115,4 +121,31 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
public String getColumnPrefix() {
|
||||
return columnPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark as PrimaryKeyJoin (we don't know which side is the export side initially).
|
||||
*/
|
||||
public void setPrimaryKeyJoin(boolean primaryKeyJoin) {
|
||||
this.primaryKeyJoin = primaryKeyJoin;
|
||||
}
|
||||
|
||||
public boolean isPrimaryKeyJoin() {
|
||||
return primaryKeyJoin;
|
||||
}
|
||||
|
||||
public boolean isPrimaryKeyExport() {
|
||||
return primaryKeyExport;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set as export side of OneToOne with PrimaryKeyJoin.
|
||||
*/
|
||||
public void setPrimaryKeyExport() {
|
||||
this.primaryKeyExport = true;
|
||||
this.oneToOneExported = true;
|
||||
if (!cascadeInfo.isSave()) {
|
||||
// we pretty much need to cascade save so turning that on automatically ...
|
||||
cascadeInfo.setType(CascadeType.ALL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,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.BeanPropertyOrderColumn;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertySimpleCollection;
|
||||
import io.ebeaninternal.server.deploy.InheritInfo;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
@@ -64,6 +65,7 @@ public class DeployBeanPropertyLists {
|
||||
private final TableJoin[] tableJoins;
|
||||
|
||||
private final BeanPropertyAssocOne<?> unidirectional;
|
||||
private final BeanProperty orderColumn;
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
public DeployBeanPropertyLists(BeanDescriptorMap owner, BeanDescriptor<?> desc, DeployBeanDescriptor<?> deploy) {
|
||||
@@ -71,12 +73,11 @@ public class DeployBeanPropertyLists {
|
||||
|
||||
setImportedPrimaryKeys(deploy);
|
||||
|
||||
DeployBeanProperty deployOrderColumn = deploy.getOrderColumn();
|
||||
this.orderColumn = deployOrderColumn != null ? new BeanPropertyOrderColumn(desc, deployOrderColumn) : null;
|
||||
|
||||
DeployBeanPropertyAssocOne<?> deployUnidirectional = deploy.getUnidirectional();
|
||||
if (deployUnidirectional == null) {
|
||||
unidirectional = null;
|
||||
} else {
|
||||
unidirectional = new BeanPropertyAssocOne(owner, desc, deployUnidirectional);
|
||||
}
|
||||
this.unidirectional = deployUnidirectional == null ? null : new BeanPropertyAssocOne(owner, desc, deployUnidirectional);
|
||||
|
||||
this.propertyMap = new LinkedHashMap<>();
|
||||
|
||||
@@ -89,7 +90,7 @@ public class DeployBeanPropertyLists {
|
||||
// Create a BeanProperty for the discriminator column to support
|
||||
// using RawSql queries with inheritance
|
||||
discriminatorColumn = inheritInfo.getDiscriminatorColumn();
|
||||
DeployBeanProperty discDeployProp = new DeployBeanProperty(deploy, String.class, new ScalarTypeString(), null);
|
||||
DeployBeanProperty discDeployProp = new DeployBeanProperty(deploy, String.class, ScalarTypeString.INSTANCE, null);
|
||||
discDeployProp.setDiscriminator();
|
||||
discDeployProp.setName(discriminatorColumn);
|
||||
discDeployProp.setDbColumn(discriminatorColumn);
|
||||
@@ -115,6 +116,12 @@ public class DeployBeanPropertyLists {
|
||||
allocateToList(prop);
|
||||
}
|
||||
|
||||
if (orderColumn != null) {
|
||||
orderColumn.setDeployOrder(order++);
|
||||
allocateToList(orderColumn);
|
||||
propertyMap.put(orderColumn.getName(), orderColumn);
|
||||
}
|
||||
|
||||
if (discProperty != null) {
|
||||
// put the discriminator property into the property map only
|
||||
// (after the real properties have been organised into their lists)
|
||||
@@ -160,6 +167,13 @@ public class DeployBeanPropertyLists {
|
||||
return unidirectional;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the order column property.
|
||||
*/
|
||||
public BeanProperty getOrderColumn() {
|
||||
return orderColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate the property to a list.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package io.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import javax.persistence.OrderColumn;
|
||||
|
||||
public class DeployOrderColumn {
|
||||
|
||||
/**
|
||||
* Logical property name used for order by and available for expression language.
|
||||
*/
|
||||
public static final String LOGICAL_NAME = "orderColumn";
|
||||
|
||||
private final String name;
|
||||
private final boolean insertable;
|
||||
private final boolean updatable;
|
||||
private final boolean nullable;
|
||||
|
||||
public DeployOrderColumn(OrderColumn orderColumn) {
|
||||
this.name = orderColumn.name();
|
||||
this.insertable = orderColumn.insertable();
|
||||
this.updatable = orderColumn.updatable();
|
||||
this.nullable = orderColumn.nullable();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public boolean isInsertable() {
|
||||
return insertable;
|
||||
}
|
||||
|
||||
public boolean isUpdatable() {
|
||||
return updatable;
|
||||
}
|
||||
|
||||
public boolean isNullable() {
|
||||
return nullable;
|
||||
}
|
||||
}
|
||||
@@ -13,16 +13,19 @@ import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanTable;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployOrderColumn;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
import javax.persistence.MapKey;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OrderBy;
|
||||
import javax.persistence.OrderColumn;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@@ -66,6 +69,14 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
prop.setModifyListenMode(ModifyListenMode.REMOVALS);
|
||||
prop.getCascadeInfo().setDelete(privateOwned.cascadeRemove());
|
||||
}
|
||||
OrderColumn orderColumn = get(prop, OrderColumn.class);
|
||||
if (orderColumn != null) {
|
||||
// need to cascade as we set the order on cascade
|
||||
prop.setOrderColumn(new DeployOrderColumn(orderColumn));
|
||||
prop.setFetchOrderBy(DeployOrderColumn.LOGICAL_NAME);
|
||||
prop.getCascadeInfo().setType(CascadeType.ALL);
|
||||
prop.setModifyListenMode(ModifyListenMode.ALL);
|
||||
}
|
||||
}
|
||||
ManyToMany manyToMany = get(prop, ManyToMany.class);
|
||||
if (manyToMany != null) {
|
||||
|
||||
@@ -6,8 +6,12 @@ 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.DeployTableJoinColumn;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embedded;
|
||||
@@ -17,6 +21,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 +29,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 +95,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 +168,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 +202,29 @@ 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.setPrimaryKeyJoin(true);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
prop.setBeanTable(assoc);
|
||||
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));
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ import io.ebean.Update;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
@@ -21,7 +20,6 @@ import io.ebeaninternal.server.core.PersistRequestCallableSql;
|
||||
import io.ebeaninternal.server.core.PersistRequestOrmUpdate;
|
||||
import io.ebeaninternal.server.core.PersistRequestUpdateSql;
|
||||
import io.ebeaninternal.server.core.Persister;
|
||||
import io.ebeaninternal.server.deploy.BeanCollectionUtil;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.deploy.BeanManager;
|
||||
@@ -29,7 +27,6 @@ import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.IntersectionRow;
|
||||
import io.ebeaninternal.server.deploy.ManyType;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -413,7 +410,7 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
}
|
||||
|
||||
private void saveRecurse(EntityBean bean, Transaction t, Object parentBean, boolean insertMode, boolean publish) {
|
||||
void saveRecurse(EntityBean bean, Transaction t, Object parentBean, boolean insertMode, boolean publish) {
|
||||
|
||||
// determine insert or update taking into account stateless updates
|
||||
PersistRequestBean<?> request = createRequestRecurse(bean, t, parentBean, insertMode, publish);
|
||||
@@ -826,86 +823,6 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to wrap the details when saving a OneToMany or ManyToMany
|
||||
* relationship.
|
||||
*/
|
||||
private static class SaveManyPropRequest {
|
||||
private final boolean insertedParent;
|
||||
private final BeanPropertyAssocMany<?> many;
|
||||
private final EntityBean parentBean;
|
||||
private final SpiTransaction transaction;
|
||||
private final boolean cascade;
|
||||
private final boolean deleteMissingChildren;
|
||||
private final boolean publish;
|
||||
|
||||
private SaveManyPropRequest(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
this.insertedParent = insertedParent;
|
||||
this.many = many;
|
||||
this.cascade = many.getCascadeInfo().isSave();
|
||||
this.parentBean = parentBean;
|
||||
this.transaction = request.getTransaction();
|
||||
this.deleteMissingChildren = request.isDeleteMissingChildren();
|
||||
this.publish = request.isPublish();
|
||||
}
|
||||
|
||||
public boolean isSaveIntersection() {
|
||||
return transaction.isSaveAssocManyIntersection(many.getIntersectionTableJoin().getTable(), many.getBeanDescriptor().getName());
|
||||
}
|
||||
|
||||
private Object getValue() {
|
||||
return many.getValue(parentBean);
|
||||
}
|
||||
|
||||
private boolean isModifyListenMode() {
|
||||
return ModifyListenMode.REMOVALS == many.getModifyListenMode();
|
||||
}
|
||||
|
||||
private boolean isDeleteMissingChildren() {
|
||||
return deleteMissingChildren;
|
||||
}
|
||||
|
||||
private boolean isInsertedParent() {
|
||||
return insertedParent;
|
||||
}
|
||||
|
||||
private BeanPropertyAssocMany<?> getMany() {
|
||||
return many;
|
||||
}
|
||||
|
||||
private EntityBean getParentBean() {
|
||||
return parentBean;
|
||||
}
|
||||
|
||||
private SpiTransaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
private boolean isCascade() {
|
||||
return cascade;
|
||||
}
|
||||
|
||||
public void modifyListenReset(BeanCollection<?> c) {
|
||||
if (insertedParent) {
|
||||
// after insert set the modify listening mode
|
||||
// for private owned etc
|
||||
c.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
c.modifyReset();
|
||||
}
|
||||
|
||||
public boolean isPublish() {
|
||||
return publish;
|
||||
}
|
||||
|
||||
private void resetModifyState() {
|
||||
Object details = getValue();
|
||||
if (details instanceof BeanCollection<?>) {
|
||||
modifyListenReset((BeanCollection<?>) details);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void saveMany(SaveManyPropRequest saveMany, boolean insertMode) {
|
||||
|
||||
if (saveMany.getMany().isManyToMany()) {
|
||||
@@ -967,107 +884,7 @@ public final class DefaultPersister implements Persister {
|
||||
*/
|
||||
private void saveAssocManyDetails(SaveManyPropRequest saveMany, boolean deleteMissingChildren, boolean insertMode) {
|
||||
|
||||
BeanPropertyAssocMany<?> prop = saveMany.getMany();
|
||||
|
||||
Object details = saveMany.getValue();
|
||||
|
||||
// check that the list is not null and if it is a BeanCollection
|
||||
// check that is has been populated (don't trigger lazy loading)
|
||||
// For a Map this is a collection of Map.Entry objects and not beans
|
||||
Collection<?> collection = BeanCollectionUtil.getActualEntries(details);
|
||||
|
||||
if (collection == null) {
|
||||
// nothing to do here
|
||||
return;
|
||||
}
|
||||
|
||||
BeanDescriptor<?> targetDescriptor = prop.getTargetDescriptor();
|
||||
if (saveMany.isInsertedParent()) {
|
||||
// performance optimisation for large collections
|
||||
targetDescriptor.preAllocateIds(collection.size());
|
||||
}
|
||||
|
||||
SpiTransaction t = saveMany.getTransaction();
|
||||
boolean isMap = ManyType.MAP == prop.getManyType();
|
||||
EntityBean parentBean = saveMany.getParentBean();
|
||||
|
||||
if (deleteMissingChildren) {
|
||||
// collect the Id's (to exclude from deleteManyDetails)
|
||||
List<Object> detailIds = collectIds(collection, targetDescriptor, isMap);
|
||||
// deleting missing children - children not in our collected detailIds
|
||||
deleteManyDetails(t, prop.getBeanDescriptor(), parentBean, prop, detailIds, false);
|
||||
}
|
||||
|
||||
// increase depth for batching order
|
||||
t.depth(+1);
|
||||
|
||||
// if a map, then we get the key value and
|
||||
// set it to the appropriate property on the
|
||||
// detail bean before we save it
|
||||
Object mapKeyValue = null;
|
||||
|
||||
boolean saveSkippable = prop.isSaveRecurseSkippable();
|
||||
boolean skipSavingThisBean;
|
||||
|
||||
for (Object detailBean : collection) {
|
||||
if (isMap) {
|
||||
// its a map so need the key and value
|
||||
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) detailBean;
|
||||
mapKeyValue = entry.getKey();
|
||||
detailBean = entry.getValue();
|
||||
}
|
||||
|
||||
if (detailBean instanceof EntityBean) {
|
||||
EntityBean detail = (EntityBean) detailBean;
|
||||
EntityBeanIntercept ebi = detail._ebean_getIntercept();
|
||||
if (prop.isManyToMany()) {
|
||||
skipSavingThisBean = targetDescriptor.isReference(ebi);
|
||||
} else {
|
||||
if (targetDescriptor.isReference(ebi)) {
|
||||
// we can skip this one
|
||||
skipSavingThisBean = true;
|
||||
|
||||
} else if (ebi.isNewOrDirty()) {
|
||||
skipSavingThisBean = false;
|
||||
// set the parent bean to detailBean
|
||||
prop.setJoinValuesToChild(parentBean, detail, mapKeyValue);
|
||||
|
||||
} else {
|
||||
// unmodified so skip depending on prop.isSaveRecurseSkippable();
|
||||
skipSavingThisBean = saveSkippable;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipSavingThisBean) {
|
||||
saveRecurse(detail, t, parentBean, insertMode, saveMany.isPublish());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t.depth(-1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the Id values of the details to remove 'missing children' for stateless updates.
|
||||
*/
|
||||
private List<Object> collectIds(Collection<?> collection, BeanDescriptor<?> targetDescriptor, boolean isMap) {
|
||||
|
||||
List<Object> detailIds = new ArrayList<>();
|
||||
// stateless update with deleteMissingChildren so first
|
||||
// collect the Id values to remove the 'missing children'
|
||||
for (Object detailBean : collection) {
|
||||
if (isMap) {
|
||||
detailBean = ((Map.Entry<?, ?>) detailBean).getValue();
|
||||
}
|
||||
if (detailBean instanceof EntityBean) {
|
||||
Object id = targetDescriptor.getId((EntityBean) detailBean);
|
||||
if (!DmlUtil.isNullOrZero(id)) {
|
||||
// remember the Id (other details not in the collection) will be removed
|
||||
detailIds.add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return detailIds;
|
||||
saveMany.saveDetails(this, deleteMissingChildren, insertMode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1266,7 +1083,7 @@ public final class DefaultPersister implements Persister {
|
||||
* collection (and should not be deleted).
|
||||
* </p>
|
||||
*/
|
||||
private void deleteManyDetails(SpiTransaction t, BeanDescriptor<?> desc, EntityBean parentBean,
|
||||
void deleteManyDetails(SpiTransaction t, BeanDescriptor<?> desc, EntityBean parentBean,
|
||||
BeanPropertyAssocMany<?> many, List<Object> excludeDetailIds, boolean softDelete) {
|
||||
|
||||
if (many.getCascadeInfo().isDelete()) {
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.deploy.BeanCollectionUtil;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.ManyType;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Helper to wrap the details when saving a OneToMany or ManyToMany relationship.
|
||||
*/
|
||||
class SaveManyPropRequest {
|
||||
|
||||
private final boolean insertedParent;
|
||||
private final BeanPropertyAssocMany<?> many;
|
||||
private final EntityBean parentBean;
|
||||
private final SpiTransaction transaction;
|
||||
private final boolean cascade;
|
||||
private final boolean deleteMissingChildren;
|
||||
private final boolean publish;
|
||||
|
||||
private final Object value;
|
||||
private final BeanDescriptor<?> targetDescriptor;
|
||||
private final boolean isMap;
|
||||
private final boolean saveRecurseSkippable;
|
||||
|
||||
private Collection<?> collection;
|
||||
private DefaultPersister persister;
|
||||
private boolean deleteMissing;
|
||||
private boolean insertMode;
|
||||
private int sortOrder;
|
||||
|
||||
SaveManyPropRequest(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
this.insertedParent = insertedParent;
|
||||
this.many = many;
|
||||
this.cascade = many.getCascadeInfo().isSave();
|
||||
this.parentBean = parentBean;
|
||||
this.transaction = request.getTransaction();
|
||||
this.deleteMissingChildren = request.isDeleteMissingChildren();
|
||||
this.publish = request.isPublish();
|
||||
this.value = many.getValue(parentBean);
|
||||
this.targetDescriptor = many.getTargetDescriptor();
|
||||
this.isMap = ManyType.MAP == many.getManyType();
|
||||
this.saveRecurseSkippable = many.isSaveRecurseSkippable();
|
||||
}
|
||||
|
||||
public boolean isSaveIntersection() {
|
||||
return transaction.isSaveAssocManyIntersection(many.getIntersectionTableJoin().getTable(), many.getBeanDescriptor().getName());
|
||||
}
|
||||
|
||||
Object getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
boolean isModifyListenMode() {
|
||||
return BeanCollection.ModifyListenMode.REMOVALS == many.getModifyListenMode();
|
||||
}
|
||||
|
||||
boolean isDeleteMissingChildren() {
|
||||
return deleteMissingChildren;
|
||||
}
|
||||
|
||||
boolean isInsertedParent() {
|
||||
return insertedParent;
|
||||
}
|
||||
|
||||
BeanPropertyAssocMany<?> getMany() {
|
||||
return many;
|
||||
}
|
||||
|
||||
EntityBean getParentBean() {
|
||||
return parentBean;
|
||||
}
|
||||
|
||||
SpiTransaction getTransaction() {
|
||||
return transaction;
|
||||
}
|
||||
|
||||
boolean isCascade() {
|
||||
return cascade;
|
||||
}
|
||||
|
||||
boolean isPublish() {
|
||||
return publish;
|
||||
}
|
||||
|
||||
void modifyListenReset(BeanCollection<?> c) {
|
||||
if (insertedParent) {
|
||||
// after insert set the modify listening mode for private owned etc
|
||||
c.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
c.modifyReset();
|
||||
}
|
||||
|
||||
void resetModifyState() {
|
||||
if (value instanceof BeanCollection<?>) {
|
||||
modifyListenReset((BeanCollection<?>) value);
|
||||
}
|
||||
}
|
||||
|
||||
void saveDetails(DefaultPersister persister, boolean deleteMissing, boolean insertMode) {
|
||||
|
||||
this.persister = persister;
|
||||
this.deleteMissing = deleteMissing;
|
||||
this.insertMode = insertMode;
|
||||
|
||||
// check that the list is not null and if it is a BeanCollection
|
||||
// check that is has been populated (don't trigger lazy loading)
|
||||
// For a Map this is a collection of Map.Entry objects and not beans
|
||||
collection = BeanCollectionUtil.getActualEntries(value);
|
||||
if (collection != null) {
|
||||
processDetails();
|
||||
}
|
||||
}
|
||||
|
||||
private void processDetails() {
|
||||
|
||||
BeanProperty orderColumn = null;
|
||||
boolean hasOrderColumn = many.hasOrderColumn();
|
||||
if (hasOrderColumn) {
|
||||
if (!insertedParent && canSkipForOrderColumn()) {
|
||||
return;
|
||||
}
|
||||
orderColumn = targetDescriptor.getOrderColumn();
|
||||
}
|
||||
|
||||
if (insertedParent) {
|
||||
// performance optimisation for large collections
|
||||
targetDescriptor.preAllocateIds(collection.size());
|
||||
}
|
||||
|
||||
if (deleteMissing) {
|
||||
// collect the Id's (to exclude from deleteManyDetails)
|
||||
List<Object> detailIds = collectIds(collection, targetDescriptor, isMap);
|
||||
// deleting missing children - children not in our collected detailIds
|
||||
persister.deleteManyDetails(transaction, many.getBeanDescriptor(), parentBean, many, detailIds, false);
|
||||
}
|
||||
|
||||
transaction.depth(+1);
|
||||
saveAllBeans(orderColumn);
|
||||
if (hasOrderColumn) {
|
||||
resetModifyState();
|
||||
}
|
||||
transaction.depth(-1);
|
||||
}
|
||||
|
||||
|
||||
private void saveAllBeans(BeanProperty orderColumn) {
|
||||
|
||||
// if a map, then we get the key value and
|
||||
// set it to the appropriate property on the
|
||||
// detail bean before we save it
|
||||
Object mapKeyValue = null;
|
||||
boolean skipSavingThisBean;
|
||||
|
||||
for (Object detailBean : collection) {
|
||||
sortOrder++;
|
||||
if (isMap) {
|
||||
// its a map so need the key and value
|
||||
Map.Entry<?, ?> entry = (Map.Entry<?, ?>) detailBean;
|
||||
mapKeyValue = entry.getKey();
|
||||
detailBean = entry.getValue();
|
||||
}
|
||||
|
||||
if (detailBean instanceof EntityBean) {
|
||||
EntityBean detail = (EntityBean) detailBean;
|
||||
EntityBeanIntercept ebi = detail._ebean_getIntercept();
|
||||
if (many.isManyToMany()) {
|
||||
skipSavingThisBean = targetDescriptor.isReference(ebi);
|
||||
} else {
|
||||
if (orderColumn != null) {
|
||||
orderColumn.setValue(detail, sortOrder);
|
||||
ebi.setDirty(true);
|
||||
}
|
||||
if (targetDescriptor.isReference(ebi)) {
|
||||
// we can skip this one
|
||||
skipSavingThisBean = true;
|
||||
|
||||
} else if (ebi.isNewOrDirty()) {
|
||||
skipSavingThisBean = false;
|
||||
// set the parent bean to detailBean
|
||||
many.setJoinValuesToChild(parentBean, detail, mapKeyValue);
|
||||
|
||||
} else {
|
||||
// unmodified so skip depending on prop.isSaveRecurseSkippable();
|
||||
skipSavingThisBean = saveRecurseSkippable;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skipSavingThisBean) {
|
||||
persister.saveRecurse(detail, transaction, parentBean, insertMode, publish);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if we can skip based on .. no modifications to the collection and no beans are dirty.
|
||||
*/
|
||||
private boolean canSkipForOrderColumn() {
|
||||
return value instanceof BeanCollection
|
||||
&& !((BeanCollection<?>) value).wasTouched()
|
||||
&& noDirtyBeans();
|
||||
}
|
||||
|
||||
private boolean noDirtyBeans() {
|
||||
for (Object bean : collection) {
|
||||
if (bean instanceof EntityBean && ((EntityBean) bean)._ebean_getIntercept().isDirty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collect the Id values of the details to remove 'missing children' for stateless updates.
|
||||
*/
|
||||
private List<Object> collectIds(Collection<?> collection, BeanDescriptor<?> targetDescriptor, boolean isMap) {
|
||||
|
||||
List<Object> detailIds = new ArrayList<>();
|
||||
// stateless update with deleteMissingChildren so first
|
||||
// collect the Id values to remove the 'missing children'
|
||||
for (Object detailBean : collection) {
|
||||
if (isMap) {
|
||||
detailBean = ((Map.Entry<?, ?>) detailBean).getValue();
|
||||
}
|
||||
if (detailBean instanceof EntityBean) {
|
||||
Object id = targetDescriptor.getId((EntityBean) detailBean);
|
||||
if (!DmlUtil.isNullOrZero(id)) {
|
||||
// remember the Id (other details not in the collection) will be removed
|
||||
detailIds.add(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
return detailIds;
|
||||
}
|
||||
}
|
||||
@@ -3,10 +3,12 @@ package io.ebeaninternal.server.persist.dml;
|
||||
import io.ebean.config.dbplatform.DatabasePlatform;
|
||||
import io.ebean.config.dbplatform.DbEncrypt;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.persist.dmlbind.Bindable;
|
||||
import io.ebeaninternal.server.persist.dmlbind.BindableId;
|
||||
import io.ebeaninternal.server.persist.dmlbind.BindableList;
|
||||
import io.ebeaninternal.server.persist.dmlbind.BindableOrderColumn;
|
||||
import io.ebeaninternal.server.persist.dmlbind.BindableUnidirectional;
|
||||
import io.ebeaninternal.server.persist.dmlbind.FactoryAssocOnes;
|
||||
import io.ebeaninternal.server.persist.dmlbind.FactoryBaseProperties;
|
||||
@@ -62,6 +64,11 @@ public class MetaFactory {
|
||||
embeddedFact.create(setList, desc, DmlMode.UPDATE, includeLobs);
|
||||
assocOneFact.create(setList, desc, DmlMode.UPDATE);
|
||||
|
||||
BeanProperty orderColumn = desc.getOrderColumn();
|
||||
if (orderColumn != null) {
|
||||
setList.add(new BindableOrderColumn(orderColumn));
|
||||
}
|
||||
|
||||
BindableId id = idFact.createId(desc);
|
||||
Bindable version = versionFact.create(desc);
|
||||
Bindable tenantId = versionFact.createTenantId(desc);
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package io.ebeaninternal.server.persist.dmlbind;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Bindable for the synthetic order column.
|
||||
*/
|
||||
public class BindableOrderColumn extends BindableProperty {
|
||||
|
||||
public BindableOrderColumn(BeanProperty prop) {
|
||||
super(prop);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addToUpdate(PersistRequestBean<?> request, List<Bindable> list) {
|
||||
int sortOrder = request.getEntityBeanIntercept().getSortOrder();
|
||||
if (sortOrder > 0) {
|
||||
list.add(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normal binding of a property value from the bean.
|
||||
*/
|
||||
@Override
|
||||
public void dmlBind(BindableRequest request, EntityBean bean) throws SQLException {
|
||||
|
||||
int sortOrder = bean._ebean_getIntercept().getSortOrder();
|
||||
request.bind(sortOrder, prop);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -109,7 +109,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
|
||||
private final ScalarType<?> shortType = new ScalarTypeShort();
|
||||
|
||||
private final ScalarType<?> integerType = new ScalarTypeInteger();
|
||||
private final ScalarType<?> integerType = ScalarTypeInteger.INSTANCE;
|
||||
|
||||
private final ScalarType<?> longType = new ScalarTypeLong();
|
||||
|
||||
@@ -130,7 +130,7 @@ public final class DefaultTypeManager implements TypeManager {
|
||||
private final ScalarType<?> currencyType = new ScalarTypeCurrency();
|
||||
private final ScalarType<?> timeZoneType = new ScalarTypeTimeZone();
|
||||
|
||||
private final ScalarType<?> stringType = new ScalarTypeString();
|
||||
private final ScalarType<?> stringType = ScalarTypeString.INSTANCE;
|
||||
|
||||
private final ScalarType<?> classType = new ScalarTypeClass();
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.ebean.text.TextException;
|
||||
import io.ebeaninternal.server.core.BasicTypeConverter;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
@@ -17,7 +17,9 @@ import java.sql.Types;
|
||||
*/
|
||||
public class ScalarTypeInteger extends ScalarTypeBase<Integer> {
|
||||
|
||||
public ScalarTypeInteger() {
|
||||
public static ScalarTypeInteger INSTANCE = new ScalarTypeInteger();
|
||||
|
||||
private ScalarTypeInteger() {
|
||||
super(Integer.class, true, Types.INTEGER);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package io.ebeaninternal.server.type;
|
||||
|
||||
import io.ebeaninternal.server.core.BasicTypeConverter;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import io.ebeaninternal.server.core.BasicTypeConverter;
|
||||
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
|
||||
|
||||
import java.io.DataInput;
|
||||
import java.io.DataOutput;
|
||||
@@ -16,7 +16,9 @@ import java.sql.Types;
|
||||
*/
|
||||
public class ScalarTypeString extends ScalarTypeBase<String> {
|
||||
|
||||
public ScalarTypeString() {
|
||||
public static final ScalarTypeString INSTANCE = new ScalarTypeString();
|
||||
|
||||
private ScalarTypeString() {
|
||||
super(String.class, true, Types.VARCHAR);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package org.tests.cascade;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Version;
|
||||
|
||||
@Entity
|
||||
public class OmOrderedDetail {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
String name;
|
||||
|
||||
@ManyToOne
|
||||
OmOrderedMaster master;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
public OmOrderedDetail(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public OmOrderedMaster getMaster() {
|
||||
return master;
|
||||
}
|
||||
|
||||
public void setMaster(OmOrderedMaster master) {
|
||||
this.master = master;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package org.tests.cascade;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.OrderColumn;
|
||||
import javax.persistence.Version;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
public class OmOrderedMaster {
|
||||
|
||||
@Id
|
||||
Long id;
|
||||
|
||||
String name;
|
||||
|
||||
/**
|
||||
* Cascade ALL set automatically as we set order values when cascading.
|
||||
*/
|
||||
@OneToMany(mappedBy = "master") //, cascade = CascadeType.ALL)
|
||||
@OrderColumn(name="sort_order")
|
||||
List<OmOrderedDetail> details;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
public OmOrderedMaster(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<OmOrderedDetail> getDetails() {
|
||||
return details;
|
||||
}
|
||||
|
||||
public void setDetails(List<OmOrderedDetail> details) {
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package org.tests.cascade;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestOrderedList extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
OmOrderedMaster master = new OmOrderedMaster("m1");
|
||||
|
||||
List<OmOrderedDetail> details = master.getDetails();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
details.add(new OmOrderedDetail("d"+i));
|
||||
}
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
Ebean.save(master);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql.size()).isGreaterThan(1);
|
||||
assertThat(sql.get(0)).contains("insert into om_ordered_master");
|
||||
assertThat(sql.get(1)).contains("insert into om_ordered_detail (name, version, sort_order, master_id) values (?,?,?,?)");
|
||||
|
||||
// update without any changes
|
||||
Ebean.save(master);
|
||||
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).isEmpty();
|
||||
|
||||
|
||||
// update just changing master
|
||||
master.setName("m1-mod");
|
||||
Ebean.save(master);
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("update om_ordered_master set name=?, version=?");
|
||||
|
||||
|
||||
fetchAndReorder(master.getId());
|
||||
|
||||
}
|
||||
|
||||
private void fetchAndReorder(Long id) {
|
||||
|
||||
OmOrderedMaster fresh = Ebean.find(OmOrderedMaster.class).setId(id).fetch("details").findOne();
|
||||
List<OmOrderedDetail> details1 = fresh.getDetails();
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql.get(0)).contains("order by t0.id, t1.sort_order");
|
||||
|
||||
// fetched, not dirty
|
||||
Ebean.save(fresh);
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).isEmpty();
|
||||
|
||||
|
||||
// reorder
|
||||
OmOrderedDetail third = details1.remove(2);
|
||||
OmOrderedDetail first = details1.remove(0);
|
||||
details1.add(first);
|
||||
details1.add(third);
|
||||
|
||||
fresh.setName("m1-reorder");
|
||||
|
||||
Ebean.save(fresh);
|
||||
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(6);
|
||||
assertThat(sql.get(0)).contains("update om_ordered_master set name=?, version=?");
|
||||
assertThat(sql.get(1)).contains("update om_ordered_detail set version=?, sort_order=? where id=? and version=?");
|
||||
|
||||
details1.get(1).setName("was 1");
|
||||
fresh.setName("m1-mod3");
|
||||
Ebean.save(fresh);
|
||||
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(6);
|
||||
assertThat(sql.get(0)).contains("update om_ordered_master set name=?, version=?");
|
||||
assertThat(sql.get(1)).contains("update om_ordered_detail set version=?, sort_order=? where id=? and version=?");
|
||||
assertThat(sql.get(2)).contains("update om_ordered_detail set name=?, version=?, sort_order=? where id=? and version=?");
|
||||
|
||||
|
||||
Ebean.delete(fresh);
|
||||
|
||||
sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from om_ordered_detail where master_id = ?");
|
||||
assertThat(sql.get(1)).contains("delete from om_ordered_master where id=? and version=?");
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,8 @@ package org.tests.model.onetoone;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.PrimaryKeyJoinColumn;
|
||||
|
||||
@Entity
|
||||
public class OtoBChild {
|
||||
@@ -16,7 +16,7 @@ public class OtoBChild {
|
||||
String child;
|
||||
|
||||
@OneToOne
|
||||
@PrimaryKeyJoinColumn(name = "master_id", referencedColumnName = "id")
|
||||
@JoinColumn(name = "master_id", referencedColumnName = "id")
|
||||
OtoBMaster master;
|
||||
|
||||
public Long getId() {
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.tests.model.onetoone;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.PrimaryKeyJoinColumn;
|
||||
import javax.persistence.Version;
|
||||
|
||||
@Entity
|
||||
public class OtoPrime {
|
||||
|
||||
@Id
|
||||
Long pid;
|
||||
|
||||
String name;
|
||||
|
||||
/**
|
||||
* Automatically set Cascade PERSIST and mapped by.
|
||||
* OneToOne not optional so inner join to extra.
|
||||
*/
|
||||
@OneToOne(optional = false, cascade = CascadeType.ALL)
|
||||
@PrimaryKeyJoinColumn
|
||||
OtoPrimeExtra extra;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
public OtoPrime(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "id:"+ pid +" name:"+name+" extra:"+extra;
|
||||
}
|
||||
|
||||
public Long getPid() {
|
||||
return pid;
|
||||
}
|
||||
|
||||
public void setPid(Long pid) {
|
||||
this.pid = pid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public OtoPrimeExtra getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public void setExtra(OtoPrimeExtra extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package org.tests.model.onetoone;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
|
||||
@Entity
|
||||
public class OtoPrimeExtra {
|
||||
|
||||
@Id
|
||||
Long eid;
|
||||
|
||||
String extra;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
public OtoPrimeExtra(String extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "exId:"+ eid +" "+extra;
|
||||
}
|
||||
|
||||
public Long getEid() {
|
||||
return eid;
|
||||
}
|
||||
|
||||
public void setEid(Long eid) {
|
||||
this.eid = eid;
|
||||
}
|
||||
|
||||
public String getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public void setExtra(String extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package org.tests.model.onetoone;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.Version;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
public class OtoUBPrime {
|
||||
|
||||
@Id
|
||||
UUID pid;
|
||||
|
||||
String name;
|
||||
|
||||
/**
|
||||
* Master side of bi-directional PrimaryJoinColumn.
|
||||
*/
|
||||
@OneToOne(mappedBy = "prime")
|
||||
OtoUBPrimeExtra extra;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
public OtoUBPrime(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "id:"+ pid +" name:"+name+" extra:"+extra;
|
||||
}
|
||||
|
||||
public UUID getPid() {
|
||||
return pid;
|
||||
}
|
||||
|
||||
public void setPid(UUID pid) {
|
||||
this.pid = pid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public OtoUBPrimeExtra getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public void setExtra(OtoUBPrimeExtra extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.tests.model.onetoone;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.PrimaryKeyJoinColumn;
|
||||
import javax.persistence.Version;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
public class OtoUBPrimeExtra {
|
||||
|
||||
@Id
|
||||
UUID eid;
|
||||
|
||||
String extra;
|
||||
|
||||
/**
|
||||
* Child side of bi-directional PrimaryJoinColumn.
|
||||
*/
|
||||
@OneToOne
|
||||
@PrimaryKeyJoinColumn
|
||||
OtoUBPrime prime;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
public OtoUBPrimeExtra(String extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "exId:" + eid + " " + extra;
|
||||
}
|
||||
|
||||
public UUID getEid() {
|
||||
return eid;
|
||||
}
|
||||
|
||||
public void setEid(UUID eid) {
|
||||
this.eid = eid;
|
||||
}
|
||||
|
||||
public String getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public void setExtra(String extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
public OtoUBPrime getPrime() {
|
||||
return prime;
|
||||
}
|
||||
|
||||
public void setPrime(OtoUBPrime prime) {
|
||||
this.prime = prime;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.tests.model.onetoone;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToOne;
|
||||
import javax.persistence.PrimaryKeyJoinColumn;
|
||||
import javax.persistence.Version;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
public class OtoUPrime {
|
||||
|
||||
@Id
|
||||
UUID pid;
|
||||
|
||||
String name;
|
||||
|
||||
/**
|
||||
* Effectively Ebean automatically sets Cascade PERSIST and mapped by for PrimaryKeyJoinColumn.
|
||||
* This OneToOne is optional so left join to extra.
|
||||
*/
|
||||
@OneToOne
|
||||
@PrimaryKeyJoinColumn
|
||||
OtoUPrimeExtra extra;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
public OtoUPrime(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "id:"+ pid +" name:"+name+" extra:"+extra;
|
||||
}
|
||||
|
||||
public UUID getPid() {
|
||||
return pid;
|
||||
}
|
||||
|
||||
public void setPid(UUID pid) {
|
||||
this.pid = pid;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public OtoUPrimeExtra getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public void setExtra(OtoUPrimeExtra extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.tests.model.onetoone;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Version;
|
||||
import java.util.UUID;
|
||||
|
||||
@Entity
|
||||
public class OtoUPrimeExtra {
|
||||
|
||||
@Id
|
||||
UUID eid;
|
||||
|
||||
String extra;
|
||||
|
||||
@Version
|
||||
Long version;
|
||||
|
||||
public OtoUPrimeExtra(String extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "exId:"+ eid +" "+extra;
|
||||
}
|
||||
|
||||
public UUID getEid() {
|
||||
return eid;
|
||||
}
|
||||
|
||||
public void setEid(UUID eid) {
|
||||
this.eid = eid;
|
||||
}
|
||||
|
||||
public String getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
public void setExtra(String extra) {
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
public Long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(Long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package org.tests.model.onetoone;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.Query;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestOneToOnePrimaryKeyJoin extends BaseTestCase {
|
||||
|
||||
|
||||
private OtoPrime insert(String desc) {
|
||||
OtoPrime prime = new OtoPrime("p" + desc);
|
||||
OtoPrimeExtra extra = new OtoPrimeExtra("e" + desc);
|
||||
prime.setExtra(extra);
|
||||
Ebean.save(prime);
|
||||
return prime;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertUpdateDelete() {
|
||||
|
||||
String desc = "" + System.currentTimeMillis();
|
||||
OtoPrime p1 = insert(desc);
|
||||
|
||||
assertThat(p1.getExtra().getEid()).isEqualTo(p1.getPid()).as("Same id values");
|
||||
|
||||
|
||||
Query<OtoPrime> query = Ebean.find(OtoPrime.class).setId(p1.getPid());
|
||||
|
||||
OtoPrime found = query.findOne();
|
||||
|
||||
assertThat(found).isNotNull();
|
||||
assertThat(sqlOf(query)).contains("select t0.pid, t0.name, t0.version, t0.pid from oto_prime t0 where t0.pid = ?")
|
||||
.as("we don't join to oto_prime_extra");
|
||||
|
||||
assertThat(found.getName()).isEqualTo("p" + desc);
|
||||
|
||||
Query<OtoPrime> queryWithFetch = Ebean.find(OtoPrime.class)
|
||||
.setId(p1.getPid())
|
||||
.fetch("extra");
|
||||
|
||||
OtoPrime oneWith = queryWithFetch.findOne();
|
||||
|
||||
assertThat(oneWith).isNotNull();
|
||||
assertThat(sqlOf(queryWithFetch)).contains("select t0.pid, t0.name, t0.version, t1.eid, t1.extra, t1.version from oto_prime t0 join oto_prime_extra t1 on t1.eid = t0.pid where t0.pid = ?")
|
||||
.as("we join to oto_prime_extra");
|
||||
|
||||
|
||||
assertThat(oneWith.getExtra().getExtra()).isEqualTo("e" + desc);
|
||||
|
||||
thenUpdate(oneWith);
|
||||
thenDelete(found);
|
||||
}
|
||||
|
||||
private void thenUpdate(OtoPrime oneWith) {
|
||||
|
||||
OtoPrimeExtra extra = oneWith.getExtra();
|
||||
extra.setExtra("modified");
|
||||
|
||||
Ebean.save(oneWith);
|
||||
|
||||
extra.setExtra("mod2");
|
||||
oneWith.setName("mod2");
|
||||
|
||||
Ebean.save(oneWith);
|
||||
}
|
||||
|
||||
private void thenDelete(OtoPrime found) {
|
||||
|
||||
|
||||
OtoPrime bean = Ebean.find(OtoPrime.class, found.getPid());
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
Ebean.delete(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from oto_prime_extra where");
|
||||
assertThat(sql.get(1)).contains("delete from oto_prime where");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package org.tests.model.onetoone;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.Query;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestOneToOnePrimaryKeyJoinBidi extends BaseTestCase {
|
||||
|
||||
|
||||
private OtoUBPrime insert(String desc) {
|
||||
OtoUBPrime prime = new OtoUBPrime("u" + desc);
|
||||
OtoUBPrimeExtra extra = new OtoUBPrimeExtra("v" + desc);
|
||||
prime.setExtra(extra);
|
||||
Ebean.save(prime);
|
||||
return prime;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertUpdateDelete() {
|
||||
|
||||
String desc = "" + System.currentTimeMillis();
|
||||
OtoUBPrime p1 = insert(desc);
|
||||
|
||||
assertThat(p1.getExtra().getEid()).isEqualTo(p1.getPid()).as("Same id values");
|
||||
|
||||
|
||||
Query<OtoUBPrime> query = Ebean.find(OtoUBPrime.class).setId(p1.getPid());
|
||||
|
||||
OtoUBPrime found = query.findOne();
|
||||
|
||||
assertThat(found).isNotNull();
|
||||
assertThat(sqlOf(query)).contains("select t0.pid, t0.name, t0.version, t0.pid from oto_ubprime t0 where t0.pid = ?")
|
||||
.as("we don't join to oto_ubprime_extra");
|
||||
|
||||
assertThat(found.getName()).isEqualTo("u" + desc);
|
||||
|
||||
Query<OtoUBPrime> queryWithFetch = Ebean.find(OtoUBPrime.class)
|
||||
.setId(p1.getPid())
|
||||
.fetch("extra");
|
||||
|
||||
OtoUBPrime oneWith = queryWithFetch.findOne();
|
||||
|
||||
assertThat(oneWith).isNotNull();
|
||||
assertThat(sqlOf(queryWithFetch)).contains("select t0.pid, t0.name, t0.version, t1.eid, t1.extra, t1.version, t1.eid from oto_ubprime t0 left join oto_ubprime_extra t1 on t1.eid = t0.pid where t0.pid = ?")
|
||||
.as("we join to oto_prime_extra");
|
||||
|
||||
|
||||
assertThat(oneWith.getExtra().getExtra()).isEqualTo("v" + desc);
|
||||
|
||||
thenUpdate(oneWith);
|
||||
thenDelete(found);
|
||||
}
|
||||
|
||||
private void thenUpdate(OtoUBPrime oneWith) {
|
||||
|
||||
OtoUBPrimeExtra extra = oneWith.getExtra();
|
||||
extra.setExtra("modified");
|
||||
|
||||
Ebean.save(oneWith);
|
||||
|
||||
extra.setExtra("mod2");
|
||||
oneWith.setName("mod2");
|
||||
|
||||
Ebean.save(oneWith);
|
||||
}
|
||||
|
||||
private void thenDelete(OtoUBPrime found) {
|
||||
|
||||
|
||||
OtoUBPrime bean = Ebean.find(OtoUBPrime.class, found.getPid());
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
Ebean.delete(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from oto_ubprime_extra where");
|
||||
assertThat(sql.get(1)).contains("delete from oto_ubprime where");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package org.tests.model.onetoone;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import io.ebean.Query;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestOneToOnePrimaryKeyJoinOptional extends BaseTestCase {
|
||||
|
||||
|
||||
private OtoUPrime insert(String desc) {
|
||||
OtoUPrime prime = new OtoUPrime("u" + desc);
|
||||
OtoUPrimeExtra extra = new OtoUPrimeExtra("v" + desc);
|
||||
prime.setExtra(extra);
|
||||
Ebean.save(prime);
|
||||
return prime;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void insertUpdateDelete() {
|
||||
|
||||
String desc = "" + System.currentTimeMillis();
|
||||
OtoUPrime p1 = insert(desc);
|
||||
|
||||
assertThat(p1.getExtra().getEid()).isEqualTo(p1.getPid()).as("Same id values");
|
||||
|
||||
|
||||
Query<OtoUPrime> query = Ebean.find(OtoUPrime.class).setId(p1.getPid());
|
||||
|
||||
OtoUPrime found = query.findOne();
|
||||
|
||||
assertThat(found).isNotNull();
|
||||
assertThat(sqlOf(query)).contains("select t0.pid, t0.name, t0.version, t0.pid from oto_uprime t0 where t0.pid = ?")
|
||||
.as("we don't join to oto_uprime_extra");
|
||||
|
||||
assertThat(found.getName()).isEqualTo("u" + desc);
|
||||
|
||||
Query<OtoUPrime> queryWithFetch = Ebean.find(OtoUPrime.class)
|
||||
.setId(p1.getPid())
|
||||
.fetch("extra");
|
||||
|
||||
OtoUPrime oneWith = queryWithFetch.findOne();
|
||||
|
||||
assertThat(oneWith).isNotNull();
|
||||
assertThat(sqlOf(queryWithFetch)).contains("select t0.pid, t0.name, t0.version, t1.eid, t1.extra, t1.version from oto_uprime t0 left join oto_uprime_extra t1 on t1.eid = t0.pid where t0.pid = ?")
|
||||
.as("we join to oto_prime_extra");
|
||||
|
||||
|
||||
assertThat(oneWith.getExtra().getExtra()).isEqualTo("v" + desc);
|
||||
|
||||
thenUpdate(oneWith);
|
||||
thenDelete(found);
|
||||
}
|
||||
|
||||
private void thenUpdate(OtoUPrime oneWith) {
|
||||
|
||||
OtoUPrimeExtra extra = oneWith.getExtra();
|
||||
extra.setExtra("modified");
|
||||
|
||||
Ebean.save(oneWith);
|
||||
|
||||
extra.setExtra("mod2");
|
||||
oneWith.setName("mod2");
|
||||
|
||||
Ebean.save(oneWith);
|
||||
}
|
||||
|
||||
private void thenDelete(OtoUPrime found) {
|
||||
|
||||
|
||||
OtoUPrime bean = Ebean.find(OtoUPrime.class, found.getPid());
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
Ebean.delete(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.stop();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from oto_uprime_extra where");
|
||||
assertThat(sql.get(1)).contains("delete from oto_uprime where");
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user