mirror of
https://github.com/ebean-orm/ebean.git
synced 2026-10-23 23:28:55 +00:00
#115 - Mapping - Add support for @ElementCollection enhancement
Add embedded bean support
This commit is contained in:
@@ -72,6 +72,11 @@ public interface BeanCollection<E> extends Serializable {
|
||||
*/
|
||||
boolean isSkipSave();
|
||||
|
||||
/**
|
||||
* Return true if the collection holds modifications.
|
||||
*/
|
||||
boolean holdsModifications();
|
||||
|
||||
/**
|
||||
* Return the bean that owns this collection.
|
||||
*/
|
||||
|
||||
@@ -207,7 +207,8 @@ abstract class AbstractBeanCollection<E> implements BeanCollection<E> {
|
||||
/**
|
||||
* Return true if there are underlying additions or removals.
|
||||
*/
|
||||
boolean holdsModifications() {
|
||||
@Override
|
||||
public boolean holdsModifications() {
|
||||
return modifyHolder != null && modifyHolder.hasModifications();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,16 @@ import java.util.Map;
|
||||
*/
|
||||
public class BeanCollectionUtil {
|
||||
|
||||
/**
|
||||
* Return true if this is a bean collection and not considered dirty.
|
||||
*/
|
||||
public static boolean isModified(Object collection) {
|
||||
if ((collection instanceof BeanCollection<?>)) {
|
||||
return ((BeanCollection<?>) collection).holdsModifications();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the details of the collection or map taking care to avoid
|
||||
* unnecessary fetching of the data.
|
||||
|
||||
@@ -235,7 +235,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
/**
|
||||
* The type of bean this describes.
|
||||
*/
|
||||
private final Class<T> beanType;
|
||||
final Class<T> beanType;
|
||||
|
||||
protected final Class<?> rootBeanType;
|
||||
|
||||
@@ -428,8 +428,8 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
this.name = InternString.intern(deploy.getName());
|
||||
this.baseTableAlias = "t0";
|
||||
this.fullName = InternString.intern(deploy.getFullName());
|
||||
this.locationById = ProfileLocation.createAt(fullName+".byId");
|
||||
this.locationAll = ProfileLocation.createAt(fullName+".all");
|
||||
this.locationById = ProfileLocation.createAt(fullName + ".byId");
|
||||
this.locationAll = ProfileLocation.createAt(fullName + ".all");
|
||||
this.profileBeanId = deploy.getProfileId();
|
||||
this.beanType = deploy.getBeanType();
|
||||
this.rootBeanType = PersistenceContextUtil.root(beanType);
|
||||
@@ -776,7 +776,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
for (BeanProperty prop : propertiesNonTransient) {
|
||||
if (prop.isUnique()) {
|
||||
propertiesUnique.add(new BeanProperty[] { prop });
|
||||
propertiesUnique.add(new BeanProperty[]{prop});
|
||||
}
|
||||
}
|
||||
// convert unique columns to properties
|
||||
@@ -860,6 +860,16 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind all the property values to the SqlUpdate.
|
||||
*/
|
||||
public void bindElementValue(SqlUpdate insert, Object value) {
|
||||
EntityBean bean = (EntityBean) value;
|
||||
for (BeanProperty property : propertiesBaseScalar) {
|
||||
insert.setNextParameter(property.getValue(bean));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ReadAuditLogger for logging read audit events.
|
||||
*/
|
||||
@@ -1420,7 +1430,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void cacheBeanPutAll(Collection<?> beans) {
|
||||
if (!beans.isEmpty()) {
|
||||
cacheHelp.beanPutAll((Collection<EntityBean>)beans);
|
||||
cacheHelp.beanPutAll((Collection<EntityBean>) beans);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1879,12 +1889,19 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new entitybean without invoking {@link BeanPostConstructListener#postCreate(Object)}
|
||||
* Creates a new entity bean without invoking {@link BeanPostConstructListener#postCreate(Object)}
|
||||
*/
|
||||
public EntityBean createEntityBean() {
|
||||
return createEntityBean(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an entity bean for JSON marshalling (which differs for the element collection case).
|
||||
*/
|
||||
public EntityBean createEntityBeanForJson() {
|
||||
return createEntityBean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a reference bean based on the id.
|
||||
*/
|
||||
@@ -2471,7 +2488,7 @@ public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
/**
|
||||
* Return a property that is part of the SQL tree.
|
||||
*
|
||||
* <p>
|
||||
* The property can be a dynamic formula or a well known bean property.
|
||||
*/
|
||||
@Override
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.PersistenceIOException;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Bean descriptor used with ElementCollection (where we don't have a mapped type/class).
|
||||
* <p>
|
||||
* This is somewhat a BeanDescriptor created 'on the fly' for a specific element collection property
|
||||
* with a unidirectional property and mapping etc specific to the property (and not the type if embedded).
|
||||
*/
|
||||
class BeanDescriptorElement<T> extends BeanDescriptor<T> {
|
||||
abstract class BeanDescriptorElement<T> extends BeanDescriptor<T> {
|
||||
|
||||
private final ScalarType<Object> scalarType;
|
||||
private final ElementHelp elementHelp;
|
||||
final ElementHelp elementHelp;
|
||||
|
||||
BeanDescriptorElement(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy, ElementHelp elementHelp) {
|
||||
super(owner, deploy);
|
||||
this.elementHelp = elementHelp;
|
||||
}
|
||||
|
||||
BeanProperty[] props = propertiesNonTransient();
|
||||
if (props.length == 1) {
|
||||
this.scalarType = props[0].getScalarType();
|
||||
} else {
|
||||
this.scalarType = null;
|
||||
/**
|
||||
* Find and return the first base scalar type (and we expect only 1).
|
||||
*/
|
||||
ScalarType<Object> firstBaseScalarType() {
|
||||
BeanProperty[] props = propertiesBaseScalar();
|
||||
if (props.length != 1) {
|
||||
throw new IllegalStateException("Expecting 1 property for element scalar but got " + Arrays.toString(props));
|
||||
}
|
||||
return props[0].getScalarType();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -36,35 +37,13 @@ class BeanDescriptorElement<T> extends BeanDescriptor<T> {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Our entity beans used are somewhat fake ones (ElementEntityBean) such that we hold the unidirectional property
|
||||
* value (foreign key) and the actual element collection value (scalar or embedded plus map key).
|
||||
*/
|
||||
@Override
|
||||
protected EntityBean createPrototypeEntityBean(Class<T> beanType) {
|
||||
return new ElementEntityBean(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWriteElement(SpiJsonWriter ctx, Object element) {
|
||||
try {
|
||||
scalarType.jsonWrite(ctx.gen(), element);
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object jsonReadCollection(ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
JsonParser parser = readJson.getParser();
|
||||
ElementCollector add = elementHelp.createCollector();
|
||||
do {
|
||||
JsonToken token = parser.nextToken();
|
||||
if (JsonToken.VALUE_NULL == token || JsonToken.END_ARRAY == token) {
|
||||
break;
|
||||
}
|
||||
Object element = scalarType.jsonRead(parser);
|
||||
add.addElement(element);
|
||||
} while (true);
|
||||
|
||||
return add.collection();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.PersistenceIOException;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Bean descriptor used with element collection of list/set of embeddable.
|
||||
*/
|
||||
class BeanDescriptorElementEmbedded<T> extends BeanDescriptorElement<T> {
|
||||
|
||||
private final BeanPropertyAssocOne embeddedProperty;
|
||||
|
||||
private final EntityBean prototype;
|
||||
|
||||
private BeanDescriptor targetDescriptor;
|
||||
|
||||
BeanDescriptorElementEmbedded(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy, ElementHelp elementHelp) {
|
||||
super(owner, deploy, elementHelp);
|
||||
try {
|
||||
this.prototype = (EntityBean) beanType.newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Unable to create entity bean prototype for "+beanType);
|
||||
}
|
||||
BeanPropertyAssocOne<?>[] embedded = propertiesEmbedded();
|
||||
if (embedded.length == 1) {
|
||||
embeddedProperty = embedded[0];
|
||||
} else {
|
||||
embeddedProperty = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialiseOther(BeanDescriptorInitContext initContext) {
|
||||
super.initialiseOther(initContext);
|
||||
this.targetDescriptor = embeddedProperty.getTargetDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBean createEntityBeanForJson() {
|
||||
return (EntityBean)prototype._ebean_newInstance();
|
||||
}
|
||||
|
||||
public void bindElementValue(SqlUpdate insert, Object value) {
|
||||
targetDescriptor.bindElementValue(insert, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWriteElement(SpiJsonWriter ctx, Object element) {
|
||||
writeJsonElement(ctx, element);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T jsonRead(ReadJson jsonRead, String path) throws IOException {
|
||||
return readJsonElement(jsonRead, path);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
T readJsonElement(ReadJson jsonRead, String path) throws IOException {
|
||||
return (T)targetDescriptor.jsonRead(jsonRead, path);
|
||||
}
|
||||
|
||||
void writeJsonElement(SpiJsonWriter ctx, Object element) {
|
||||
try {
|
||||
if (element == null) {
|
||||
ctx.writeNull();
|
||||
} else {
|
||||
targetDescriptor.jsonWrite(ctx, (EntityBean)element);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceIOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Descriptor for element collection using Map with the value holding an embedded bean.
|
||||
* <p>
|
||||
* The expected limitation is that the key is a scalar type.
|
||||
*/
|
||||
class BeanDescriptorElementEmbeddedMap<T> extends BeanDescriptorElementEmbedded<T> {
|
||||
|
||||
private final ScalarType scalarTypeKey;
|
||||
|
||||
private final boolean stringKey;
|
||||
|
||||
BeanDescriptorElementEmbeddedMap(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy, ElementHelp elementHelp) {
|
||||
super(owner, deploy, elementHelp);
|
||||
this.scalarTypeKey = firstBaseScalarType();
|
||||
this.stringKey = String.class.equals(scalarTypeKey.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void jsonWriteMapEntry(SpiJsonWriter ctx, Map.Entry<?, ?> entry) throws IOException {
|
||||
ctx.writeStartObject();
|
||||
if (stringKey) {
|
||||
Object key = entry.getKey();
|
||||
String keyName = (key == null) ? "null" : key.toString();
|
||||
ctx.writeFieldName(keyName);
|
||||
writeJsonElement(ctx, entry.getValue());
|
||||
} else {
|
||||
ctx.writeFieldName("key");
|
||||
scalarTypeKey.jsonWrite(ctx.gen(), entry.getKey());
|
||||
ctx.writeFieldName("value");
|
||||
writeJsonElement(ctx, entry.getValue());
|
||||
}
|
||||
ctx.writeEndObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object jsonReadCollection(ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
JsonParser parser = readJson.getParser();
|
||||
ElementCollector add = elementHelp.createCollector();
|
||||
do {
|
||||
JsonToken token = parser.nextToken();
|
||||
if (token != JsonToken.START_OBJECT) {
|
||||
break;
|
||||
}
|
||||
if (stringKey) {
|
||||
String key = parser.nextFieldName();
|
||||
parser.nextToken();
|
||||
Object val = readJsonElement(readJson, null);
|
||||
add.addKeyValue(key, val);
|
||||
|
||||
} else {
|
||||
parser.nextFieldName();
|
||||
Object key = scalarTypeKey.jsonRead(parser);
|
||||
|
||||
parser.nextFieldName();
|
||||
Object val = readJsonElement(readJson, null);
|
||||
add.addKeyValue(key, val);
|
||||
}
|
||||
|
||||
token = parser.nextToken();
|
||||
if (token != JsonToken.END_OBJECT) {
|
||||
break;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
return add.collection();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Bean descriptor used with ElementCollection (where we don't have a mapped type/class).
|
||||
*/
|
||||
class BeanDescriptorElementMap<T> extends BeanDescriptor<T> {
|
||||
|
||||
private final ScalarType[] scalarTypes;
|
||||
private final ElementHelp elementHelp;
|
||||
private final boolean stringKey;
|
||||
|
||||
BeanDescriptorElementMap(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy, ElementHelp elementHelp) {
|
||||
super(owner, deploy);
|
||||
this.elementHelp = elementHelp;
|
||||
BeanProperty[] props = propertiesNonTransient();
|
||||
this.scalarTypes = new ScalarType[props.length];
|
||||
for (int i = 0; i < props.length; i++) {
|
||||
scalarTypes[i] = props[i].getScalarType();
|
||||
}
|
||||
this.stringKey = String.class.equals(scalarTypes[0].getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isElementType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EntityBean createPrototypeEntityBean(Class<T> beanType) {
|
||||
return new ElementEntityBean(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void jsonWriteMapEntry(SpiJsonWriter ctx, Map.Entry<?, ?> entry) throws IOException {
|
||||
ctx.writeStartObject();
|
||||
if (stringKey) {
|
||||
Object key = entry.getKey();
|
||||
String keyName = (key == null) ? "null" : key.toString();
|
||||
ctx.writeFieldName(keyName);
|
||||
scalarTypes[1].jsonWrite(ctx.gen(), entry.getValue());
|
||||
} else {
|
||||
ctx.writeFieldName("key");
|
||||
scalarTypes[0].jsonWrite(ctx.gen(), entry.getKey());
|
||||
ctx.writeFieldName("value");
|
||||
scalarTypes[1].jsonWrite(ctx.gen(), entry.getValue());
|
||||
}
|
||||
ctx.writeEndObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object jsonReadCollection(ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
JsonParser parser = readJson.getParser();
|
||||
ElementCollector add = elementHelp.createCollector();
|
||||
do {
|
||||
JsonToken token = parser.nextToken();
|
||||
if (token != JsonToken.START_OBJECT) {
|
||||
break;
|
||||
}
|
||||
if (stringKey) {
|
||||
String key = parser.nextFieldName();
|
||||
parser.nextToken();
|
||||
Object val = scalarTypes[0].jsonRead(parser);
|
||||
add.addKeyValue(key, val);
|
||||
|
||||
} else {
|
||||
parser.nextFieldName();
|
||||
Object key= scalarTypes[0].jsonRead(parser);
|
||||
|
||||
parser.nextFieldName();
|
||||
Object val = scalarTypes[0].jsonRead(parser);
|
||||
add.addKeyValue(key, val);
|
||||
}
|
||||
|
||||
token = parser.nextToken();
|
||||
if (token != JsonToken.END_OBJECT) {
|
||||
break;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
return add.collection();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.PersistenceIOException;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Bean descriptor used with element collection mapped to a list or set of scalar values.
|
||||
*/
|
||||
class BeanDescriptorElementScalar<T> extends BeanDescriptorElement<T> {
|
||||
|
||||
private final ScalarType<Object> scalarType;
|
||||
|
||||
BeanDescriptorElementScalar(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy, ElementHelp elementHelp) {
|
||||
super(owner, deploy, elementHelp);
|
||||
this.scalarType = firstBaseScalarType();
|
||||
}
|
||||
|
||||
public void bindElementValue(SqlUpdate insert, Object value) {
|
||||
insert.setNextParameter(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWriteElement(SpiJsonWriter ctx, Object element) {
|
||||
try {
|
||||
scalarType.jsonWrite(ctx.gen(), element);
|
||||
} catch (IOException e) {
|
||||
throw new PersistenceIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object jsonReadCollection(ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
JsonParser parser = readJson.getParser();
|
||||
ElementCollector add = elementHelp.createCollector();
|
||||
do {
|
||||
JsonToken token = parser.nextToken();
|
||||
if (JsonToken.VALUE_NULL == token || JsonToken.END_ARRAY == token) {
|
||||
break;
|
||||
}
|
||||
add.addElement(scalarType.jsonRead(parser));
|
||||
} while (true);
|
||||
|
||||
return add.collection();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.core.JsonToken;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Bean descriptor used with element collection mapped to Map where key and value are scalar types.
|
||||
*/
|
||||
class BeanDescriptorElementScalarMap<T> extends BeanDescriptorElement<T> {
|
||||
|
||||
private final ScalarType scalarTypeKey;
|
||||
private final ScalarType scalarTypeVal;
|
||||
private final boolean stringKey;
|
||||
|
||||
BeanDescriptorElementScalarMap(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy, ElementHelp elementHelp) {
|
||||
super(owner, deploy, elementHelp);
|
||||
BeanProperty[] props = propertiesNonTransient();
|
||||
if (props.length != 2) {
|
||||
throw new IllegalStateException("Expecting 2 properties for key and value but got " + Arrays.toString(props));
|
||||
}
|
||||
this.scalarTypeKey = props[0].getScalarType();
|
||||
this.scalarTypeVal = props[1].getScalarType();
|
||||
this.stringKey = String.class.equals(scalarTypeKey.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void jsonWriteMapEntry(SpiJsonWriter ctx, Map.Entry<?, ?> entry) throws IOException {
|
||||
ctx.writeStartObject();
|
||||
if (stringKey) {
|
||||
Object key = entry.getKey();
|
||||
String keyName = (key == null) ? "null" : key.toString();
|
||||
ctx.writeFieldName(keyName);
|
||||
scalarTypeVal.jsonWrite(ctx.gen(), entry.getValue());
|
||||
} else {
|
||||
ctx.writeFieldName("key");
|
||||
scalarTypeKey.jsonWrite(ctx.gen(), entry.getKey());
|
||||
ctx.writeFieldName("value");
|
||||
scalarTypeVal.jsonWrite(ctx.gen(), entry.getValue());
|
||||
}
|
||||
ctx.writeEndObject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object jsonReadCollection(ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
JsonParser parser = readJson.getParser();
|
||||
ElementCollector add = elementHelp.createCollector();
|
||||
do {
|
||||
JsonToken token = parser.nextToken();
|
||||
if (token != JsonToken.START_OBJECT) {
|
||||
break;
|
||||
}
|
||||
if (stringKey) {
|
||||
String key = parser.nextFieldName();
|
||||
parser.nextToken();
|
||||
Object val = scalarTypeVal.jsonRead(parser);
|
||||
add.addKeyValue(key, val);
|
||||
|
||||
} else {
|
||||
parser.nextFieldName();
|
||||
Object key = scalarTypeKey.jsonRead(parser);
|
||||
|
||||
parser.nextFieldName();
|
||||
Object val = scalarTypeVal.jsonRead(parser);
|
||||
add.addKeyValue(key, val);
|
||||
}
|
||||
|
||||
token = parser.nextToken();
|
||||
if (token != JsonToken.END_OBJECT) {
|
||||
break;
|
||||
}
|
||||
|
||||
} while (true);
|
||||
|
||||
return add.collection();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -118,7 +118,7 @@ public class BeanDescriptorJsonHelp<T> {
|
||||
|
||||
protected T jsonReadObject(ReadJson readJson, String path) throws IOException {
|
||||
|
||||
EntityBean bean = desc.createEntityBean();
|
||||
EntityBean bean = desc.createEntityBeanForJson();
|
||||
return jsonReadProperties(readJson, bean, path);
|
||||
}
|
||||
|
||||
|
||||
@@ -637,7 +637,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
/**
|
||||
* Return a BeanTable for an ElementCollection.
|
||||
*/
|
||||
public BeanTable getCollectionBeanTable(String fullTableName, Class<?> targetType) {
|
||||
public BeanTable createCollectionBeanTable(String fullTableName, Class<?> targetType) {
|
||||
return new BeanTable(this, fullTableName, targetType);
|
||||
}
|
||||
|
||||
@@ -1637,13 +1637,21 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
/**
|
||||
* Create a BeanDescriptor for an ElementCollection target.
|
||||
*/
|
||||
public <A> BeanDescriptor<A> createElementDescriptor(DeployBeanDescriptor<A> elementDescriptor, ManyType manyType) {
|
||||
public <A> BeanDescriptor<A> createElementDescriptor(DeployBeanDescriptor<A> elementDescriptor, ManyType manyType, boolean scalar) {
|
||||
|
||||
ElementHelp elementHelp = elementHelper(manyType);
|
||||
if (manyType.isMap()) {
|
||||
return new BeanDescriptorElementMap<>(this, elementDescriptor, elementHelp);
|
||||
if (scalar) {
|
||||
return new BeanDescriptorElementScalarMap<>(this, elementDescriptor, elementHelp);
|
||||
} else {
|
||||
return new BeanDescriptorElementEmbeddedMap<>(this, elementDescriptor, elementHelp);
|
||||
}
|
||||
}
|
||||
if (scalar) {
|
||||
return new BeanDescriptorElementScalar<>(this, elementDescriptor, elementHelp);
|
||||
} else {
|
||||
return new BeanDescriptorElementEmbedded<>(this, elementDescriptor, elementHelp);
|
||||
}
|
||||
return new BeanDescriptorElement<>(this, elementDescriptor, elementHelp);
|
||||
}
|
||||
|
||||
private ElementHelp elementHelper(ManyType manyType) {
|
||||
|
||||
@@ -70,6 +70,11 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
|
||||
private final boolean elementCollection;
|
||||
|
||||
/**
|
||||
* Descriptor for the 'target' when the property maps to an element collection.
|
||||
*/
|
||||
BeanDescriptor<T> elementDescriptor;
|
||||
|
||||
/**
|
||||
* Order by used when fetch joining the associated many.
|
||||
*/
|
||||
@@ -116,6 +121,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
this.hasOrderColumn = deploy.hasOrderColumn();
|
||||
this.manyToMany = deploy.isManyToMany();
|
||||
this.elementCollection = deploy.isElementCollection();
|
||||
this.elementDescriptor = deploy.getElementDescriptor();
|
||||
this.manyType = deploy.getManyType();
|
||||
this.mapKey = deploy.getMapKey();
|
||||
this.fetchOrderBy = deploy.getFetchOrderBy();
|
||||
@@ -136,6 +142,19 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
public void initialise(BeanDescriptorInitContext initContext) {
|
||||
super.initialise(initContext);
|
||||
initialiseAssocMany();
|
||||
if (elementCollection) {
|
||||
// initialise all non-id properties (we don't have an Id property)
|
||||
elementDescriptor.initialiseOther(initContext);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
void initialiseTargetDescriptor(BeanDescriptorInitContext initContext) {
|
||||
if (elementCollection) {
|
||||
targetDescriptor = elementDescriptor;
|
||||
} else {
|
||||
targetDescriptor = descriptor.getBeanDescriptor(targetType);
|
||||
}
|
||||
}
|
||||
|
||||
private void initialiseAssocMany() {
|
||||
@@ -170,7 +189,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
}
|
||||
|
||||
String targetTable() {
|
||||
return targetDescriptor.getBaseTable();
|
||||
return beanTable.getBaseTable();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -883,18 +902,22 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
}
|
||||
|
||||
public void jsonWriteMapEntry(SpiJsonWriter ctx, Map.Entry<?, ?> entry) throws IOException {
|
||||
// Writing as json array rather than object ...
|
||||
targetDescriptor.jsonWrite(ctx, (EntityBean) entry.getValue());
|
||||
elementDescriptor.jsonWriteMapEntry(ctx, entry);
|
||||
}
|
||||
|
||||
public void jsonWriteElementValue(SpiJsonWriter ctx, Object element) {
|
||||
throw new IllegalStateException("Unexpected - expect Element override");
|
||||
elementDescriptor.jsonWriteElement(ctx, element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the collection (JSON Array) containing entity beans.
|
||||
*/
|
||||
public Object jsonReadCollection(ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
if (elementDescriptor != null && manyType.isMap()) {
|
||||
return elementDescriptor.jsonReadCollection(readJson, parentBean);
|
||||
}
|
||||
|
||||
BeanCollection<?> collection = createEmpty(parentBean);
|
||||
BeanCollectionAdd add = getBeanCollectionAdd(collection, null);
|
||||
do {
|
||||
@@ -913,4 +936,11 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements ST
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind all the property values to the SqlUpdate.
|
||||
*/
|
||||
public void bindElementValue(SqlUpdate insert, Object value) {
|
||||
targetDescriptor.bindElementValue(insert, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,47 +1,21 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public class BeanPropertySimpleCollection<T> extends BeanPropertyAssocMany<T> {
|
||||
|
||||
private BeanDescriptor<T> elementDescriptor;
|
||||
|
||||
public BeanPropertySimpleCollection(BeanDescriptor<?> descriptor, DeployBeanPropertySimpleCollection<T> deploy) {
|
||||
super(descriptor, deploy);
|
||||
this.elementDescriptor = deploy.getElementDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise(BeanDescriptorInitContext initContext) {
|
||||
super.initialise(initContext);
|
||||
if (isElementCollection()) {
|
||||
// initialise all non-id properties (we don't have an Id property)
|
||||
elementDescriptor.initialiseOther(initContext);
|
||||
}
|
||||
}
|
||||
|
||||
void initialiseTargetDescriptor(BeanDescriptorInitContext initContext) {
|
||||
if (isElementCollection()) {
|
||||
targetDescriptor = elementDescriptor;
|
||||
} else {
|
||||
targetDescriptor = descriptor.getBeanDescriptor(targetType);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWriteMapEntry(SpiJsonWriter ctx, Map.Entry<?, ?> entry) throws IOException {
|
||||
elementDescriptor.jsonWriteMapEntry(ctx, entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void jsonWriteElementValue(SpiJsonWriter ctx, Object element) {
|
||||
elementDescriptor.jsonWriteElement(ctx, element);
|
||||
public void bindElementValue(SqlUpdate insert, Object value) {
|
||||
insert.setNextParameter(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,6 +4,9 @@ import io.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.ManyType;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.type.TypeReflectHelper;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
/**
|
||||
* Property mapped to a List Set or Map.
|
||||
@@ -179,6 +182,14 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
|
||||
return fetchOrderBy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of the map key (valid only when this property is a Map).
|
||||
*/
|
||||
public Class<?> getMapKeyType() {
|
||||
Type genericType = getField().getGenericType();
|
||||
return TypeReflectHelper.getMapKeyType(genericType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default mapKey when returning a Map.
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,7 @@ import io.ebeaninternal.server.deploy.BeanTable;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployOrderColumn;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
|
||||
@@ -204,7 +205,7 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
}
|
||||
}
|
||||
|
||||
BeanTable beanTable = factory.getCollectionBeanTable(fullTableName, prop.getTargetType());
|
||||
BeanTable beanTable = factory.createCollectionBeanTable(fullTableName, prop.getTargetType());
|
||||
prop.setBeanTable(beanTable);
|
||||
|
||||
Class<?> elementType = prop.getTargetType();
|
||||
@@ -212,8 +213,6 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
DeployBeanDescriptor<?> elementDescriptor = factory.createDeployDescriptor(elementType);
|
||||
elementDescriptor.setBaseTable(new TableName(fullTableName), readConfig.getAsOfViewSuffix(), readConfig.getVersionsBetweenSuffix());
|
||||
|
||||
ScalarType<?> scalarType = util.getTypeManager().getScalarType(elementType);
|
||||
|
||||
int sortOrder = 0;
|
||||
if (!prop.getManyType().isMap()) {
|
||||
elementDescriptor.setProperties(new String[]{"value"});
|
||||
@@ -225,27 +224,46 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
dbKeyColumn = mapKeyColumn.name();
|
||||
}
|
||||
|
||||
DeployBeanProperty keyProp = new DeployBeanProperty(elementDescriptor, elementType, scalarType, null);
|
||||
ScalarType<?> keyScalarType = util.getTypeManager().getScalarType(prop.getMapKeyType());
|
||||
|
||||
DeployBeanProperty keyProp = new DeployBeanProperty(elementDescriptor, elementType, keyScalarType, null);
|
||||
setElementProperty(keyProp, "key", dbKeyColumn, sortOrder++);
|
||||
elementDescriptor.addBeanProperty(keyProp);
|
||||
if (mapKeyColumn != null) {
|
||||
keyProp.setDbLength(mapKeyColumn.length());
|
||||
keyProp.setDbScale(mapKeyColumn.scale());
|
||||
keyProp.setUnique(mapKeyColumn.unique());
|
||||
}
|
||||
}
|
||||
|
||||
DeployBeanProperty valueProp = new DeployBeanProperty(elementDescriptor, elementType, scalarType, null);
|
||||
setElementProperty(valueProp, "value", prop.getDbColumn(), sortOrder++);
|
||||
if (column != null) {
|
||||
valueProp.setDbLength(column.length());
|
||||
valueProp.setDbScale(column.scale());
|
||||
ScalarType<?> valueScalarType = util.getTypeManager().getScalarType(elementType);
|
||||
|
||||
boolean scalar = true;
|
||||
if (valueScalarType == null) {
|
||||
// embedded value type
|
||||
scalar = false;
|
||||
DeployBeanPropertyAssocOne valueProp = new DeployBeanPropertyAssocOne<>(elementDescriptor, elementType);
|
||||
valueProp.setName("value");
|
||||
valueProp.setEmbedded();
|
||||
valueProp.setElementProperty();
|
||||
valueProp.setSortOrder(sortOrder++);
|
||||
elementDescriptor.addBeanProperty(valueProp);
|
||||
|
||||
} else {
|
||||
// scalar value type
|
||||
DeployBeanProperty valueProp = new DeployBeanProperty(elementDescriptor, elementType, valueScalarType, null);
|
||||
setElementProperty(valueProp, "value", prop.getDbColumn(), sortOrder++);
|
||||
if (column != null) {
|
||||
valueProp.setDbLength(column.length());
|
||||
valueProp.setDbScale(column.scale());
|
||||
}
|
||||
elementDescriptor.addBeanProperty(valueProp);
|
||||
}
|
||||
|
||||
elementDescriptor.addBeanProperty(valueProp);
|
||||
elementDescriptor.setName(prop.getFullBeanName());
|
||||
|
||||
factory.createUnidirectional(elementDescriptor, prop.getOwningType(), beanTable, prop.getTableJoin());
|
||||
prop.setElementDescriptor(factory.createElementDescriptor(elementDescriptor, prop.getManyType()));
|
||||
prop.setElementDescriptor(factory.createElementDescriptor(elementDescriptor, prop.getManyType(), scalar));
|
||||
}
|
||||
|
||||
private void setElementProperty(DeployBeanProperty elementProp, String name, String dbColumn, int sortOrder) {
|
||||
@@ -307,7 +325,7 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
* Return the full table name
|
||||
*/
|
||||
private String getFullTableName(CollectionTable collectionTable) {
|
||||
if (collectionTable == null) {
|
||||
if (collectionTable == null || collectionTable.name().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
@@ -906,10 +906,10 @@ public final class DefaultPersister implements Persister {
|
||||
return new SaveManyBeans(insertedParent, many, parentBean, request, this);
|
||||
|
||||
} else if (many.getManyType().isMap()) {
|
||||
return new SaveManySimpleMap(insertedParent, many, parentBean, request);
|
||||
return new SaveManyElementCollectionMap(insertedParent, many, parentBean, request);
|
||||
|
||||
} else {
|
||||
return new SaveManySimpleCollection(insertedParent, many, parentBean, request);
|
||||
return new SaveManyElementCollection(insertedParent, many, parentBean, request);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiTransaction;
|
||||
@@ -34,4 +35,17 @@ abstract class SaveManyBase {
|
||||
*/
|
||||
abstract void save();
|
||||
|
||||
void resetModifyState() {
|
||||
if (value instanceof BeanCollection<?>) {
|
||||
modifyListenReset((BeanCollection<?>) value);
|
||||
}
|
||||
}
|
||||
|
||||
void modifyListenReset(BeanCollection<?> c) {
|
||||
if (insertedParent) {
|
||||
// after insert set the modify listening mode for private owned etc
|
||||
c.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
c.modifyReset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,20 +89,6 @@ class SaveManyBeans extends SaveManyBase {
|
||||
return BeanCollection.ModifyListenMode.REMOVALS == many.getModifyListenMode();
|
||||
}
|
||||
|
||||
private void modifyListenReset(BeanCollection<?> c) {
|
||||
if (insertedParent) {
|
||||
// after insert set the modify listening mode for private owned etc
|
||||
c.setModifyListening(many.getModifyListenMode());
|
||||
}
|
||||
c.modifyReset();
|
||||
}
|
||||
|
||||
private void resetModifyState() {
|
||||
if (value instanceof BeanCollection<?>) {
|
||||
modifyListenReset((BeanCollection<?>) value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the details from a OneToMany collection.
|
||||
*/
|
||||
|
||||
+11
-8
@@ -12,9 +12,9 @@ import java.util.Collection;
|
||||
/**
|
||||
* Save details for a simple scalar element collection.
|
||||
*/
|
||||
class SaveManySimpleCollection extends SaveManyBase {
|
||||
class SaveManyElementCollection extends SaveManyBase {
|
||||
|
||||
SaveManySimpleCollection(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
SaveManyElementCollection(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
super(insertedParent, many, parentBean, request);
|
||||
}
|
||||
|
||||
@@ -22,16 +22,18 @@ class SaveManySimpleCollection extends SaveManyBase {
|
||||
void save() {
|
||||
|
||||
Collection<?> collection = BeanCollectionUtil.getActualEntries(value);
|
||||
if (collection == null) {
|
||||
if (collection == null || !BeanCollectionUtil.isModified(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object parentId = request.getBeanId();
|
||||
|
||||
SqlUpdate sqlDelete = many.deleteByParentId(parentId, null);
|
||||
|
||||
SpiEbeanServer server = request.getServer();
|
||||
server.execute(sqlDelete, transaction);
|
||||
|
||||
if (!insertedParent) {
|
||||
SqlUpdate sqlDelete = many.deleteByParentId(parentId, null);
|
||||
server.execute(sqlDelete, transaction);
|
||||
}
|
||||
|
||||
transaction.depth(+1);
|
||||
|
||||
@@ -40,11 +42,12 @@ class SaveManySimpleCollection extends SaveManyBase {
|
||||
|
||||
for (Object value : collection) {
|
||||
|
||||
sqlInsert.setParameter(1, parentId);
|
||||
sqlInsert.setParameter(2, value);
|
||||
sqlInsert.setNextParameter(parentId);
|
||||
many.bindElementValue(sqlInsert, value);
|
||||
server.execute(sqlInsert, transaction);
|
||||
}
|
||||
|
||||
transaction.depth(-1);
|
||||
resetModifyState();
|
||||
}
|
||||
}
|
||||
+13
-10
@@ -13,9 +13,9 @@ import java.util.Set;
|
||||
/**
|
||||
* Save details for a simple scalar map element collection.
|
||||
*/
|
||||
class SaveManySimpleMap extends SaveManyBase {
|
||||
class SaveManyElementCollectionMap extends SaveManyBase {
|
||||
|
||||
SaveManySimpleMap(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
SaveManyElementCollectionMap(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
super(insertedParent, many, parentBean, request);
|
||||
}
|
||||
|
||||
@@ -23,17 +23,19 @@ class SaveManySimpleMap extends SaveManyBase {
|
||||
@Override
|
||||
void save() {
|
||||
|
||||
Set<Map.Entry<?, ?>> entries = (Set<Map.Entry<?, ?>>)BeanCollectionUtil.getActualEntries(value);
|
||||
if (entries == null) {
|
||||
Set<Map.Entry<?, ?>> entries = (Set<Map.Entry<?, ?>>) BeanCollectionUtil.getActualEntries(value);
|
||||
if (entries == null || !BeanCollectionUtil.isModified(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object parentId = request.getBeanId();
|
||||
|
||||
SqlUpdate sqlDelete = many.deleteByParentId(parentId, null);
|
||||
|
||||
SpiEbeanServer server = request.getServer();
|
||||
server.execute(sqlDelete, transaction);
|
||||
|
||||
if (!insertedParent) {
|
||||
SqlUpdate sqlDelete = many.deleteByParentId(parentId, null);
|
||||
server.execute(sqlDelete, transaction);
|
||||
}
|
||||
|
||||
transaction.depth(+1);
|
||||
|
||||
@@ -41,12 +43,13 @@ class SaveManySimpleMap extends SaveManyBase {
|
||||
SqlUpdate sqlInsert = server.createSqlUpdate(insert);
|
||||
|
||||
for (Map.Entry<?, ?> entry : entries) {
|
||||
sqlInsert.setParameter(1, parentId);
|
||||
sqlInsert.setParameter(2, entry.getKey());
|
||||
sqlInsert.setParameter(3, entry.getValue());
|
||||
sqlInsert.setNextParameter(parentId);
|
||||
sqlInsert.setNextParameter(entry.getKey());
|
||||
many.bindElementValue(sqlInsert, entry.getValue());
|
||||
server.execute(sqlInsert, transaction);
|
||||
}
|
||||
|
||||
transaction.depth(-1);
|
||||
resetModifyState();
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,13 @@ public class TypeReflectHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of the map key.
|
||||
*/
|
||||
public static Class<?> getMapKeyType(Type genericType) {
|
||||
return getClass(getValueType(genericType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the value type of a collection type (list, set, map values).
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package org.tests.model.elementcollection;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.validation.constraints.Size;
|
||||
|
||||
@Embeddable
|
||||
public class EcPhone {
|
||||
|
||||
@Size(max = 2)
|
||||
String countryCode;
|
||||
|
||||
@Size(max = 6)
|
||||
String area;
|
||||
|
||||
@Size(max = 20)
|
||||
String number;
|
||||
|
||||
public EcPhone(String countryCode, String area, String number) {
|
||||
this.countryCode = countryCode;
|
||||
this.area = area;
|
||||
this.number = number;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return countryCode + "-" + area + "-" + number;
|
||||
}
|
||||
|
||||
public String getCountryCode() {
|
||||
return countryCode;
|
||||
}
|
||||
|
||||
public void setCountryCode(String countryCode) {
|
||||
this.countryCode = countryCode;
|
||||
}
|
||||
|
||||
public String getArea() {
|
||||
return area;
|
||||
}
|
||||
|
||||
public void setArea(String area) {
|
||||
this.area = area;
|
||||
}
|
||||
|
||||
public String getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public void setNumber(String number) {
|
||||
this.number = number;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package org.tests.model.elementcollection;
|
||||
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.Version;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
public class EcblPerson {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
String name;
|
||||
|
||||
@ElementCollection
|
||||
@CollectionTable(joinColumns = @JoinColumn(name = "person_id"))
|
||||
List<EcPhone> phoneNumbers = new ArrayList<>();
|
||||
|
||||
@Version
|
||||
long version;
|
||||
|
||||
public EcblPerson(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "person id:" + id + " name:" + name + " phs:" + phoneNumbers;
|
||||
}
|
||||
|
||||
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<EcPhone> getPhoneNumbers() {
|
||||
return phoneNumbers;
|
||||
}
|
||||
|
||||
public void setPhoneNumbers(List<EcPhone> phoneNumbers) {
|
||||
this.phoneNumbers = phoneNumbers;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package org.tests.model.elementcollection;
|
||||
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.Version;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@Entity
|
||||
public class EcbmPerson {
|
||||
|
||||
@Id
|
||||
long id;
|
||||
|
||||
String name;
|
||||
|
||||
@ElementCollection
|
||||
@CollectionTable(joinColumns = @JoinColumn(name = "person_id"))
|
||||
Map<String,EcPhone> phoneNumbers = new LinkedHashMap<>();
|
||||
|
||||
@Version
|
||||
long version;
|
||||
|
||||
public EcbmPerson(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "person id:" + id + " name:" + name + " phs:" + phoneNumbers;
|
||||
}
|
||||
|
||||
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 Map<String,EcPhone> getPhoneNumbers() {
|
||||
return phoneNumbers;
|
||||
}
|
||||
|
||||
public void setPhoneNumbers(Map<String,EcPhone> phoneNumbers) {
|
||||
this.phoneNumbers = phoneNumbers;
|
||||
}
|
||||
|
||||
public long getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(long version) {
|
||||
this.version = version;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package org.tests.model.elementcollection;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
@@ -13,17 +14,25 @@ public class TestElementCollectionBasic extends BaseTestCase {
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
EcPerson person = new EcPerson("Fiona021");
|
||||
person.getPhoneNumbers().add("021 1234");
|
||||
person.getPhoneNumbers().add("021 4321");
|
||||
Ebean.save(person);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("insert into ec_person");
|
||||
assertThat(sql.get(1)).contains("insert into ec_person_phone");
|
||||
|
||||
EcPerson person1 = new EcPerson("Fiona09");
|
||||
person1.getPhoneNumbers().add("09 1234");
|
||||
person1.getPhoneNumbers().add("09 4321");
|
||||
Ebean.save(person1);
|
||||
|
||||
LoggedSqlCollector.current();
|
||||
|
||||
List<EcPerson> found =
|
||||
Ebean.find(EcPerson.class).where()
|
||||
.startsWith("name", "Fiona0")
|
||||
@@ -37,6 +46,10 @@ public class TestElementCollectionBasic extends BaseTestCase {
|
||||
assertThat(phoneNumbers0).containsExactly("021 1234", "021 4321");
|
||||
assertThat(phoneNumbers1).containsExactly("09 1234", "09 4321");
|
||||
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(trimSql(sql.get(0))).contains("select t0.id, t0.name, t0.version from ec_person t0 where");
|
||||
assertThat(trimSql(sql.get(1))).contains("select t0.owner_id, t0.phone from ec_person_phone t0 where");
|
||||
|
||||
List<EcPerson> found2 =
|
||||
Ebean.find(EcPerson.class)
|
||||
@@ -47,11 +60,83 @@ public class TestElementCollectionBasic extends BaseTestCase {
|
||||
.findList();
|
||||
|
||||
assertThat(found2).hasSize(2);
|
||||
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(trimSql(sql.get(0))).contains("select t0.id, t0.name, t0.version, t1.phone from ec_person t0 left join ec_person_phone t1");
|
||||
|
||||
EcPerson foundFirst = found2.get(0);
|
||||
|
||||
jsonToFrom(foundFirst);
|
||||
updateBasic(foundFirst);
|
||||
|
||||
LoggedSqlCollector.stop();
|
||||
}
|
||||
|
||||
private void updateBasic(EcPerson bean) {
|
||||
|
||||
bean.setName("Fiona021-mod-0");
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("update ec_person");
|
||||
|
||||
updateBoth(bean);
|
||||
}
|
||||
|
||||
private void updateBoth(EcPerson bean) {
|
||||
|
||||
bean.setName("Fiona021-mod-both");
|
||||
bean.getPhoneNumbers().add("01-22123");
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(3);
|
||||
assertThat(sql.get(0)).contains("update ec_person set name=?, version=? where id=? and version=?");
|
||||
assertThat(sql.get(1)).contains("delete from ec_person_phone where owner_id=?");
|
||||
assertThat(sql.get(2)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)");
|
||||
|
||||
updateNothing(bean);
|
||||
}
|
||||
|
||||
private void updateNothing(EcPerson bean) {
|
||||
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(0);
|
||||
|
||||
updateOnlyCollection(bean);
|
||||
}
|
||||
|
||||
private void updateOnlyCollection(EcPerson bean) {
|
||||
|
||||
bean.getPhoneNumbers().add("01-4321");
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from ec_person_phone where owner_id=?");
|
||||
assertThat(sql.get(1)).contains("insert into ec_person_phone (owner_id,phone) values (?,?)");
|
||||
|
||||
delete(bean);
|
||||
}
|
||||
|
||||
private void delete(EcPerson bean) {
|
||||
|
||||
Ebean.delete(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from ec_person_phone where owner_id = ?");
|
||||
assertThat(sql.get(1)).contains("delete from ec_person where id=? and version=?");
|
||||
}
|
||||
|
||||
private void jsonToFrom(EcPerson foundFirst) {
|
||||
|
||||
String asJson = Ebean.json().toJson(foundFirst);
|
||||
|
||||
EcPerson fromJson = Ebean.json().toBean(EcPerson.class, asJson);
|
||||
|
||||
assertThat(fromJson.getPhoneNumbers()).containsAll(foundFirst.getPhoneNumbers());
|
||||
}
|
||||
}
|
||||
|
||||
+93
-10
@@ -2,6 +2,7 @@ package org.tests.model.elementcollection;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
@@ -14,26 +15,34 @@ public class TestElementCollectionBasicMap extends BaseTestCase {
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
EcmPerson person = new EcmPerson("Fiona021");
|
||||
person.getPhoneNumbers().put("home","021 1234");
|
||||
person.getPhoneNumbers().put("work","021 4321");
|
||||
person.getPhoneNumbers().put("home", "021 1234");
|
||||
person.getPhoneNumbers().put("work", "021 4321");
|
||||
Ebean.save(person);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("insert into ecm_person");
|
||||
assertThat(sql.get(1)).contains("insert into ecm_person_phone");
|
||||
|
||||
EcmPerson person1 = new EcmPerson("Fiona09");
|
||||
person1.getPhoneNumbers().put("home","09 1234");
|
||||
person1.getPhoneNumbers().put("work","09 4321");
|
||||
person1.getPhoneNumbers().put("mob","09 9876");
|
||||
person1.getPhoneNumbers().put("home", "09 1234");
|
||||
person1.getPhoneNumbers().put("work", "09 4321");
|
||||
person1.getPhoneNumbers().put("mob", "09 9876");
|
||||
Ebean.save(person1);
|
||||
|
||||
LoggedSqlCollector.current();
|
||||
|
||||
List<EcmPerson> found =
|
||||
Ebean.find(EcmPerson.class).where()
|
||||
.startsWith("name", "Fiona0")
|
||||
.order().asc("id")
|
||||
.findList();
|
||||
|
||||
Map<String,String> phoneNumbers0 = found.get(0).getPhoneNumbers();
|
||||
Map<String,String> phoneNumbers1 = found.get(1).getPhoneNumbers();
|
||||
Map<String, String> phoneNumbers0 = found.get(0).getPhoneNumbers();
|
||||
Map<String, String> phoneNumbers1 = found.get(1).getPhoneNumbers();
|
||||
phoneNumbers0.size();
|
||||
|
||||
assertThat(phoneNumbers0).containsValues("021 1234", "021 4321");
|
||||
@@ -41,6 +50,10 @@ public class TestElementCollectionBasicMap extends BaseTestCase {
|
||||
assertThat(phoneNumbers1).containsValues("09 1234", "09 4321", "09 9876");
|
||||
assertThat(phoneNumbers1.get("mob")).isEqualTo("09 9876");
|
||||
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(trimSql(sql.get(0))).contains("select t0.id, t0.name, t0.version from ecm_person t0 where");
|
||||
assertThat(trimSql(sql.get(1))).contains("select t0.ecm_person_id, t0.type, t0.number from ecm_person_phone_numbers t0 where");
|
||||
|
||||
List<EcmPerson> found2 =
|
||||
Ebean.find(EcmPerson.class)
|
||||
@@ -51,13 +64,83 @@ public class TestElementCollectionBasicMap extends BaseTestCase {
|
||||
.findList();
|
||||
|
||||
assertThat(found2).hasSize(2);
|
||||
EcmPerson foundFirst = found2.get(0);
|
||||
String asJson = Ebean.json().toJson(foundFirst);
|
||||
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(trimSql(sql.get(0))).contains("select t0.id, t0.name, t0.version, t1.type, t1.number from ecm_person t0 left join ecm_person_phone_numbers t1");
|
||||
|
||||
EcmPerson foundFirst = found2.get(0);
|
||||
jsonToFrom(foundFirst);
|
||||
updateBasic(foundFirst);
|
||||
|
||||
LoggedSqlCollector.stop();
|
||||
}
|
||||
|
||||
private void updateBasic(EcmPerson bean) {
|
||||
|
||||
bean.setName("Fiona021-mod-0");
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("update ecm_person");
|
||||
|
||||
updateBoth(bean);
|
||||
}
|
||||
|
||||
private void updateBoth(EcmPerson bean) {
|
||||
|
||||
bean.setName("Fiona021-mod-both");
|
||||
bean.getPhoneNumbers().put("one", "01-22123");
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(3);
|
||||
assertThat(sql.get(0)).contains("update ecm_person set name=?, version=? where id=? and version=?");
|
||||
assertThat(sql.get(1)).contains("delete from ecm_person_phone_numbers where ecm_person_id=?");
|
||||
assertThat(sql.get(2)).contains("insert into ecm_person_phone_numbers (ecm_person_id,type,number) values (?,?,?)");
|
||||
|
||||
updateNothing(bean);
|
||||
}
|
||||
|
||||
private void updateNothing(EcmPerson bean) {
|
||||
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(0);
|
||||
|
||||
updateOnlyCollection(bean);
|
||||
}
|
||||
|
||||
private void updateOnlyCollection(EcmPerson bean) {
|
||||
|
||||
bean.getPhoneNumbers().put("two", "01-4321");
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from ecm_person_phone_numbers where ecm_person_id=?");
|
||||
assertThat(sql.get(1)).contains("insert into ecm_person_phone_numbers (ecm_person_id,type,number) values (?,?,?)");
|
||||
|
||||
delete(bean);
|
||||
}
|
||||
|
||||
private void delete(EcmPerson bean) {
|
||||
|
||||
Ebean.delete(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from ecm_person_phone_numbers where ecm_person_id = ?");
|
||||
assertThat(sql.get(1)).contains("delete from ecm_person where id=? and version=?");
|
||||
}
|
||||
|
||||
private void jsonToFrom(EcmPerson foundFirst) {
|
||||
String asJson = Ebean.json().toJson(foundFirst);
|
||||
EcmPerson fromJson = Ebean.json().toBean(EcmPerson.class, asJson);
|
||||
|
||||
assertThat(fromJson.getPhoneNumbers()).containsValues("021 1234", "021 4321");
|
||||
assertThat(fromJson.getPhoneNumbers().get("home")).isEqualTo("021 1234");
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.tests.model.elementcollection;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
@@ -14,11 +15,17 @@ public class TestElementCollectionBasicSet extends BaseTestCase {
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
EcsPerson person = new EcsPerson("Fiona021");
|
||||
person.getPhoneNumbers().add("021 1234");
|
||||
person.getPhoneNumbers().add("021 4321");
|
||||
Ebean.save(person);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("insert into ecs_person");
|
||||
assertThat(sql.get(1)).contains("insert into ecs_person_phone");
|
||||
|
||||
EcsPerson person1 = new EcsPerson("Fiona09");
|
||||
person1.getPhoneNumbers().add("09 1234");
|
||||
@@ -26,6 +33,8 @@ public class TestElementCollectionBasicSet extends BaseTestCase {
|
||||
person1.getPhoneNumbers().add("09 9876");
|
||||
Ebean.save(person1);
|
||||
|
||||
LoggedSqlCollector.current();
|
||||
|
||||
List<EcsPerson> found =
|
||||
Ebean.find(EcsPerson.class).where()
|
||||
.startsWith("name", "Fiona0")
|
||||
@@ -39,6 +48,10 @@ public class TestElementCollectionBasicSet extends BaseTestCase {
|
||||
assertThat(phoneNumbers0).containsExactly("021 1234", "021 4321");
|
||||
assertThat(phoneNumbers1).containsExactly("09 1234", "09 4321", "09 9876");
|
||||
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(trimSql(sql.get(0))).contains("select t0.id, t0.name, t0.version from ecs_person t0 where");
|
||||
assertThat(trimSql(sql.get(1))).contains("select t0.ecs_person_id, t0.phone from ecs_person_phone t0 where");
|
||||
|
||||
List<EcsPerson> found2 =
|
||||
Ebean.find(EcsPerson.class)
|
||||
@@ -49,11 +62,82 @@ public class TestElementCollectionBasicSet extends BaseTestCase {
|
||||
.findList();
|
||||
|
||||
assertThat(found2).hasSize(2);
|
||||
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(trimSql(sql.get(0))).contains("select t0.id, t0.name, t0.version, t1.phone from ecs_person t0 left join ecs_person_phone t1");
|
||||
|
||||
EcsPerson foundFirst = found2.get(0);
|
||||
jsonToFrom(foundFirst);
|
||||
|
||||
updateBasic(foundFirst);
|
||||
|
||||
LoggedSqlCollector.stop();
|
||||
}
|
||||
|
||||
private void updateBasic(EcsPerson bean) {
|
||||
|
||||
bean.setName("Fiona021-mod-0");
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("update ecs_person");
|
||||
|
||||
updateBoth(bean);
|
||||
}
|
||||
|
||||
private void updateBoth(EcsPerson bean) {
|
||||
|
||||
bean.setName("Fiona021-mod-both");
|
||||
bean.getPhoneNumbers().add("01-22123");
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(3);
|
||||
assertThat(sql.get(0)).contains("update ecs_person set name=?, version=? where id=? and version=?");
|
||||
assertThat(sql.get(1)).contains("delete from ecs_person_phone where ecs_person_id=?");
|
||||
assertThat(sql.get(2)).contains("insert into ecs_person_phone (ecs_person_id,phone) values (?,?)");
|
||||
|
||||
updateNothing(bean);
|
||||
}
|
||||
|
||||
private void updateNothing(EcsPerson bean) {
|
||||
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(0);
|
||||
|
||||
updateOnlyCollection(bean);
|
||||
}
|
||||
|
||||
private void updateOnlyCollection(EcsPerson bean) {
|
||||
|
||||
bean.getPhoneNumbers().add("01-4321");
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from ecs_person_phone where ecs_person_id=?");
|
||||
assertThat(sql.get(1)).contains("insert into ecs_person_phone (ecs_person_id,phone) values (?,?)");
|
||||
|
||||
delete(bean);
|
||||
}
|
||||
|
||||
private void delete(EcsPerson bean) {
|
||||
|
||||
Ebean.delete(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from ecs_person_phone where ecs_person_id = ?");
|
||||
assertThat(sql.get(1)).contains("delete from ecs_person where id=? and version=?");
|
||||
}
|
||||
|
||||
private void jsonToFrom(EcsPerson foundFirst) {
|
||||
String asJson = Ebean.json().toJson(foundFirst);
|
||||
|
||||
EcsPerson fromJson = Ebean.json().toBean(EcsPerson.class, asJson);
|
||||
|
||||
assertThat(fromJson.getPhoneNumbers()).containsAll(foundFirst.getPhoneNumbers());
|
||||
}
|
||||
}
|
||||
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
package org.tests.model.elementcollection;
|
||||
|
||||
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 TestElementCollectionEmbeddedList extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
EcblPerson person = new EcblPerson("Fiona64021");
|
||||
person.getPhoneNumbers().add(new EcPhone("64", "021","1234"));
|
||||
person.getPhoneNumbers().add(new EcPhone("64","021","4321"));
|
||||
Ebean.save(person);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("insert into ecbl_person");
|
||||
assertThat(sql.get(1)).contains("insert into ecbl_person_phone_numbers");
|
||||
|
||||
EcblPerson person1 = new EcblPerson("Fiona6409");
|
||||
person1.getPhoneNumbers().add(new EcPhone("61","09","1234"));
|
||||
person1.getPhoneNumbers().add(new EcPhone("64","09","4321"));
|
||||
Ebean.save(person1);
|
||||
|
||||
LoggedSqlCollector.current();
|
||||
|
||||
List<EcblPerson> found =
|
||||
Ebean.find(EcblPerson.class).where()
|
||||
.startsWith("name", "Fiona640")
|
||||
.order().asc("id")
|
||||
.findList();
|
||||
|
||||
List<EcPhone> phoneNumbers0 = found.get(0).getPhoneNumbers();
|
||||
List<EcPhone> phoneNumbers1 = found.get(1).getPhoneNumbers();
|
||||
phoneNumbers0.size();
|
||||
|
||||
assertThat(phoneNumbers0.toString()).contains("64-021-1234", "64-021-4321");
|
||||
assertThat(phoneNumbers1.toString()).contains("61-09-1234", "64-09-4321");
|
||||
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(trimSql(sql.get(0))).contains("select t0.id, t0.name, t0.version from ecbl_person");
|
||||
assertThat(trimSql(sql.get(1))).contains("select t0.person_id, t0.country_code, t0.area, t0.number from ecbl_person_phone_numbers");
|
||||
|
||||
List<EcblPerson> found2 =
|
||||
Ebean.find(EcblPerson.class)
|
||||
.fetch("phoneNumbers")
|
||||
.where()
|
||||
.startsWith("name", "Fiona640")
|
||||
.order().asc("id")
|
||||
.findList();
|
||||
|
||||
assertThat(found2).hasSize(2);
|
||||
|
||||
sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(1);
|
||||
String trimmedSql = trimSql(sql.get(0));
|
||||
assertThat(trimmedSql).contains("select t0.id, t0.name, t0.version, t1.country_code, t1.area, t1.number from ecbl_person t0 left join ecbl_person_phone_numbers t1");
|
||||
|
||||
|
||||
EcblPerson foundFirst = found2.get(0);
|
||||
jsonToFrom(foundFirst);
|
||||
|
||||
updateBasic(foundFirst);
|
||||
|
||||
LoggedSqlCollector.stop();
|
||||
}
|
||||
|
||||
private void updateBasic(EcblPerson bean) {
|
||||
|
||||
bean.setName("Fiona64-mod-0");
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("update ecbl_person");
|
||||
|
||||
updateBoth(bean);
|
||||
}
|
||||
|
||||
private void updateBoth(EcblPerson bean) {
|
||||
|
||||
bean.setName("Fiona64-mod-both");
|
||||
bean.getPhoneNumbers().add(new EcPhone("01", "234", "123"));
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(3);
|
||||
assertThat(sql.get(0)).contains("update ecbl_person set name=?, version=? where id=? and version=?");
|
||||
assertThat(sql.get(1)).contains("delete from ecbl_person_phone_numbers where person_id=?");
|
||||
assertThat(sql.get(2)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)");
|
||||
|
||||
updateNothing(bean);
|
||||
}
|
||||
|
||||
private void updateNothing(EcblPerson bean) {
|
||||
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(0);
|
||||
|
||||
updateOnlyCollection(bean);
|
||||
}
|
||||
|
||||
private void updateOnlyCollection(EcblPerson bean) {
|
||||
|
||||
bean.getPhoneNumbers().add(new EcPhone("01", "12", "4321"));
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from ecbl_person_phone_numbers where person_id=?");
|
||||
assertThat(sql.get(1)).contains("insert into ecbl_person_phone_numbers (person_id,country_code,area,number) values (?,?,?,?)");
|
||||
|
||||
delete(bean);
|
||||
}
|
||||
|
||||
private void delete(EcblPerson bean) {
|
||||
|
||||
Ebean.delete(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from ecbl_person_phone_numbers where person_id = ?");
|
||||
assertThat(sql.get(1)).contains("delete from ecbl_person where id=? and version=?");
|
||||
}
|
||||
|
||||
private void jsonToFrom(EcblPerson foundFirst) {
|
||||
|
||||
String asJson = Ebean.json().toJson(foundFirst);
|
||||
|
||||
EcblPerson fromJson = Ebean.json().toBean(EcblPerson.class, asJson);
|
||||
|
||||
String phoneString = fromJson.getPhoneNumbers().toString();
|
||||
assertThat(phoneString).contains("64-021-1234");
|
||||
assertThat(phoneString).contains("64-021-4321");
|
||||
}
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
package org.tests.model.elementcollection;
|
||||
|
||||
import io.ebean.BaseTestCase;
|
||||
import io.ebean.Ebean;
|
||||
import org.ebeantest.LoggedSqlCollector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestElementCollectionEmbeddedMap extends BaseTestCase {
|
||||
|
||||
@Test
|
||||
public void test() {
|
||||
|
||||
LoggedSqlCollector.start();
|
||||
|
||||
EcbmPerson person = new EcbmPerson("Fiona64021");
|
||||
person.getPhoneNumbers().put("home", new EcPhone("64", "021","1234"));
|
||||
person.getPhoneNumbers().put("work", new EcPhone("64","021","4321"));
|
||||
Ebean.save(person);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("insert into ecbm_person");
|
||||
assertThat(sql.get(1)).contains("insert into ecbm_person_phone_numbers");
|
||||
|
||||
EcbmPerson person1 = new EcbmPerson("Fiona6409");
|
||||
person1.getPhoneNumbers().put("home",new EcPhone("64","09","1234"));
|
||||
person1.getPhoneNumbers().put("mob",new EcPhone("61","09","4321"));
|
||||
|
||||
Ebean.save(person1);
|
||||
|
||||
LoggedSqlCollector.current();
|
||||
|
||||
List<EcbmPerson> found =
|
||||
Ebean.find(EcbmPerson.class).where()
|
||||
.startsWith("name", "Fiona640")
|
||||
.order().asc("id")
|
||||
.findList();
|
||||
|
||||
Map<String,EcPhone> phoneNumbers0 = found.get(0).getPhoneNumbers();
|
||||
Map<String,EcPhone> phoneNumbers1 = found.get(1).getPhoneNumbers();
|
||||
phoneNumbers0.size();
|
||||
|
||||
assertThat(phoneNumbers0.toString()).contains("64-021-1234", "64-021-4321");
|
||||
assertThat(phoneNumbers1.toString()).contains("64-09-1234", "61-09-4321");
|
||||
|
||||
List<EcbmPerson> found2 =
|
||||
Ebean.find(EcbmPerson.class)
|
||||
.fetch("phoneNumbers")
|
||||
.where()
|
||||
.startsWith("name", "Fiona640")
|
||||
.order().asc("id")
|
||||
.findList();
|
||||
|
||||
assertThat(found2).hasSize(2);
|
||||
EcbmPerson foundFirst = found2.get(0);
|
||||
|
||||
LoggedSqlCollector.current();
|
||||
|
||||
jsonToFrom(foundFirst);
|
||||
|
||||
updateBasic(foundFirst);
|
||||
|
||||
LoggedSqlCollector.stop();
|
||||
}
|
||||
|
||||
private void updateBasic(EcbmPerson bean) {
|
||||
|
||||
bean.setName("Fiona64-mod-0");
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(1);
|
||||
assertThat(sql.get(0)).contains("update ecbm_person");
|
||||
|
||||
updateBoth(bean);
|
||||
}
|
||||
|
||||
private void updateBoth(EcbmPerson bean) {
|
||||
|
||||
bean.setName("Fiona64-mod-both");
|
||||
bean.getPhoneNumbers().put("more", new EcPhone("01", "234", "123"));
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(3);
|
||||
assertThat(sql.get(0)).contains("update ecbm_person set name=?, version=? where id=? and version=?");
|
||||
assertThat(sql.get(1)).contains("delete from ecbm_person_phone_numbers where person_id=?");
|
||||
assertThat(sql.get(2)).contains("insert into ecbm_person_phone_numbers (person_id,key,country_code,area,number)");
|
||||
|
||||
updateNothing(bean);
|
||||
}
|
||||
|
||||
private void updateNothing(EcbmPerson bean) {
|
||||
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(0);
|
||||
|
||||
updateOnlyCollection(bean);
|
||||
}
|
||||
|
||||
private void updateOnlyCollection(EcbmPerson bean) {
|
||||
|
||||
bean.getPhoneNumbers().put("other", new EcPhone("01", "12", "4321"));
|
||||
Ebean.save(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from ecbm_person_phone_numbers where person_id=?");
|
||||
assertThat(sql.get(1)).contains("insert into ecbm_person_phone_numbers (person_id,key,country_code,area,number) values (?,?,?,?,?)");
|
||||
|
||||
delete(bean);
|
||||
}
|
||||
|
||||
private void delete(EcbmPerson bean) {
|
||||
|
||||
Ebean.delete(bean);
|
||||
|
||||
List<String> sql = LoggedSqlCollector.current();
|
||||
assertThat(sql).hasSize(2);
|
||||
assertThat(sql.get(0)).contains("delete from ecbm_person_phone_numbers where person_id = ?");
|
||||
assertThat(sql.get(1)).contains("delete from ecbm_person where id=? and version=?");
|
||||
}
|
||||
|
||||
private void jsonToFrom(EcbmPerson foundFirst) {
|
||||
String asJson = Ebean.json().toJson(foundFirst);
|
||||
|
||||
EcbmPerson fromJson = Ebean.json().toBean(EcbmPerson.class, asJson);
|
||||
|
||||
String phoneString = fromJson.getPhoneNumbers().toString();
|
||||
assertThat(phoneString).contains("64-021-1234");
|
||||
assertThat(phoneString).contains("64-021-4321");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user