#593 - Refactor SPI - Move methods from ElPropertyValue to ExpressionPath

This commit is contained in:
Robin Bygrave
2016-03-08 21:02:39 +13:00
parent 3ba4660bbb
commit 583fc11945
24 changed files with 447 additions and 272 deletions
@@ -6,6 +6,7 @@ import com.avaje.ebean.event.BeanPersistController;
import com.avaje.ebean.event.BeanPersistListener;
import com.avaje.ebean.event.BeanQueryAdapter;
import com.avaje.ebean.text.json.JsonReadOptions;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeanservice.docstore.api.mapping.DocumentMapping;
import com.fasterxml.jackson.core.JsonParser;
@@ -17,6 +18,11 @@ import java.util.Collection;
*/
public interface BeanType<T> {
/**
* Return the short name of the bean type.
*/
String getName();
/**
* Return the full name of the bean type.
*/
@@ -53,12 +59,12 @@ public interface BeanType<T> {
Property getWhenCreatedProperty();
/**
* Return the SpiProperty for a property to read values from a bean.
* Return the Property to read values from a bean.
*/
Property getProperty(String propertyName);
/**
* Return the SpiExpressionPath for a given property path.
* Return the ExpressionPath for a given property path.
* <p>
* This can return a property or nested property path.
* </p>
@@ -154,4 +160,28 @@ public interface BeanType<T> {
*/
T jsonRead(JsonParser parser, JsonReadOptions readOptions, Object objectMapper) throws IOException;
/**
* Add the discriminator value to the query if needed.
*/
void addInheritanceWhere(SpiQuery<?> query);
/**
* Return the root bean type for an inheritance hierarchy.
*/
BeanType<?> root();
/**
* Return true if this bean type has an inheritance hierarchy.
*/
boolean hasInheritance();
/**
* Return the discriminator column.
*/
String getDiscColumn();
/**
* Create a bean given the discriminator value.
*/
T createBeanUsingDisc(Object discValue);
}
@@ -1,5 +1,7 @@
package com.avaje.ebean.plugin;
import com.avaje.ebean.text.StringParser;
/**
* A dot notation expression path.
*/
@@ -10,11 +12,46 @@ public interface ExpressionPath {
*/
boolean containsMany();
/**
* Return the value from a given entity bean.
*/
Object pathGet(Object bean);
/**
* Set a value to the bean for this expression path.
*
* @param bean the bean to set the value on
* @param value the value to set
*/
void set(Object bean, Object value);
void pathSet(Object bean, Object value);
/**
* Convert the value to the expected type.
* <p>
* Typically useful for converting strings to the appropriate number type etc.
* </p>
*/
Object convert(Object value);
/**
* Return the default StringParser for the scalar property.
*/
StringParser getStringParser();
/**
* For DateTime capable scalar types convert the long systemTimeMillis into
* an appropriate java time (Date,Timestamp,Time,Calendar, JODA type etc).
*/
Object parseDateTime(long systemTimeMillis);
/**
* Return true if the last type is "DateTime capable" - can support
* {@link #parseDateTime(long)}.
*/
boolean isDateTimeCapable();
/**
* Return the underlying JDBC type or 0 if this is not a scalar type.
*/
int getJdbcType();
}
@@ -309,7 +309,7 @@ public final class PersistRequestBean<T> extends PersistRequest implements BeanP
*/
private boolean isDocStoreNotify() {
// Either queue or directly update the document store
return docStoreMode != DocStoreMode.IGNORE;
return docStoreMode != DocStoreMode.IGNORE || beanDescriptor.docStoreAdapter().hasEmbeddedInvalidation();
}
public boolean isNotifyPersistListener() {
@@ -71,6 +71,8 @@ import com.avaje.ebeanservice.docstore.api.DocStoreBeanAdapter;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateContext;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdates;
import com.avaje.ebeanservice.docstore.api.mapping.DocMappingBuilder;
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyMapping;
import com.avaje.ebeanservice.docstore.api.mapping.DocPropertyType;
import com.avaje.ebeanservice.docstore.api.mapping.DocumentMapping;
import com.fasterxml.jackson.core.JsonParser;
import org.slf4j.Logger;
@@ -80,6 +82,7 @@ import javax.persistence.PersistenceException;
import java.io.IOException;
import java.lang.reflect.Modifier;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -358,12 +361,12 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
private final String docStoreQueueId;
private DocumentMapping docMapping;
private final BeanDescriptorDraftHelp<T> draftHelp;
private final BeanDescriptorCacheHelp<T> cacheHelp;
private final BeanDescriptorJsonHelp<T> jsonHelp;
private final DocStoreBeanAdapter<T> docStoreAdapter;
private DocStoreBeanAdapter<T> docStoreAdapter;
private DocumentMapping docMapping;
private final String defaultSelectClause;
@@ -680,7 +683,6 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
namedUpdate.initialise(parser);
}
}
docStoreAdapter.registerPaths();
}
/**
@@ -690,7 +692,11 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
for (int i = 0; i < propertiesMany.length; i++) {
propertiesMany[i].initialisePostTarget();
}
if (inheritInfo != null && !inheritInfo.isRoot()) {
docStoreAdapter = (DocStoreBeanAdapter<T>)inheritInfo.getRoot().getBeanDescriptor().docStoreAdapter();
}
docMapping = docStoreAdapter.createDocMapping();
docStoreAdapter.registerPaths();
}
public void initInheritInfo() {
@@ -932,16 +938,44 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
/**
* Build the Document mapping recursively with the given prefix relative to the root of the document.
*/
public void docStoreMapping(DocMappingBuilder mapping, String prefix) {
public void docStoreMapping(final DocMappingBuilder mapping, final String prefix) {
if (prefix != null && idProperty != null) {
// id property not included in the
idProperty.docStoreMapping(mapping, prefix);
}
if (inheritInfo != null) {
String discCol = inheritInfo.getDiscriminatorColumn();
if (Types.VARCHAR == inheritInfo.getDiscriminatorType()) {
mapping.add(new DocPropertyMapping(discCol, DocPropertyType.ENUM));
} else {
mapping.add(new DocPropertyMapping(discCol, DocPropertyType.INTEGER));
}
}
for (BeanProperty prop: propertiesNonTransient) {
prop.docStoreMapping(mapping, prefix);
}
if (inheritInfo != null) {
inheritInfo.visitChildren(new InheritInfoVisitor() {
@Override
public void visit(InheritInfo inheritInfo) {
for (BeanProperty localProperty : inheritInfo.localProperties()) {
localProperty.docStoreMapping(mapping, prefix);
}
}
});
}
}
/**
* Return the root bean type if part of inheritance hierarchy.
*/
public BeanType<?> root() {
if (inheritInfo != null && !inheritInfo.isRoot()) {
return inheritInfo.getRoot().getBeanDescriptor();
}
return this;
}
/**
@@ -1539,7 +1573,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
}
String[] splitBegin = SplitName.splitBegin(path);
BeanProperty beanProperty = propMap.get(splitBegin[0]);
BeanProperty beanProperty = findBeanProperty(splitBegin[0]);
if (beanProperty instanceof BeanPropertyAssoc<?>) {
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) beanProperty;
return assocProp.getTargetDescriptor().getBeanDescriptor(splitBegin[1]);
@@ -1709,7 +1743,7 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
@Override
public Property getProperty(String propName) {
return getBeanProperty(propName);
return findBeanProperty(propName);
}
/**
@@ -1976,6 +2010,30 @@ public class BeanDescriptor<T> implements MetaBeanInfo, BeanType<T> {
return inheritInfo;
}
@Override
public boolean hasInheritance() {
return inheritInfo != null;
}
@Override
public String getDiscColumn() {
return inheritInfo.getDiscriminatorColumn();
}
@Override
@SuppressWarnings("unchecked")
public T createBeanUsingDisc(Object discValue) {
InheritInfo type = inheritInfo.getType(discValue.toString());
return (T)type.getBeanDescriptor().createBean();
}
@Override
public void addInheritanceWhere(SpiQuery<?> query) {
if (inheritInfo != null && !inheritInfo.isRoot()) {
query.where().eq(inheritInfo.getDiscriminatorColumn(), inheritInfo.getDiscriminatorValue());
}
}
/**
* Return true if this is an embedded bean.
*/
@@ -162,23 +162,22 @@ public final class BeanFkeyProperty implements ElPropertyValue {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public Object elConvertType(Object value) {
@Override
public Object convert(Object value) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
@Override
public void set(Object bean, Object value) {
public void pathSet(Object bean, Object value) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public void elSetValue(EntityBean bean, Object value, boolean populate) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
public Object elGetValue(EntityBean bean) {
@Override
public Object pathGet(Object bean) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
@Override
public Object elGetReference(EntityBean bean) {
throw new RuntimeException("ElPropertyDeploy only - not implemented");
}
@@ -767,7 +767,7 @@ public class BeanProperty implements ElPropertyValue, Property {
}
}
public Object elConvertType(Object value) {
public Object convert(Object value) {
if (value == null) {
return null;
}
@@ -775,25 +775,20 @@ public class BeanProperty implements ElPropertyValue, Property {
}
@Override
public void set(Object bean, Object value) {
public void pathSet(Object bean, Object value) {
// convert for Enums etc
Object logicalVal = convertToLogicalType(value);
elSetValue((EntityBean) bean, logicalVal, true);
}
public void elSetValue(EntityBean bean, Object value, boolean populate) {
if (bean != null) {
// Not using setValueIntercept at this stage
setValue(bean, value);
Object logicalVal = convertToLogicalType(value);
setValue((EntityBean)bean, logicalVal);
}
}
public Object elGetValue(EntityBean bean) {
@Override
public Object pathGet(Object bean) {
if (bean == null) {
return null;
}
return getValueIntercept(bean);
return getValueIntercept((EntityBean)bean);
}
public Object elGetReference(EntityBean bean) {
@@ -1330,7 +1325,7 @@ public class BeanProperty implements ElPropertyValue, Property {
* Return true if this is a String Id property and should be treated as a code by the document store.
*/
private boolean isStringId(DocPropertyType type) {
return DocPropertyType.STRING == type && id;
return DocPropertyType.STRING == type && (id || discriminator);
}
}
@@ -77,8 +77,8 @@ public class BeanPropertyCompoundScalar extends BeanProperty {
}
@Override
public Object elGetValue(EntityBean bean) {
return getValue(bean);
public Object pathGet(Object bean) {
return ctProperty.getValue(bean);
}
}
@@ -145,6 +145,13 @@ public class InheritInfo {
return descriptor;
}
/**
* Return the local properties for this node in the hierarchy.
*/
public BeanProperty[] localProperties() {
return descriptor.propertiesLocal();
}
/**
* Get the bean property additionally looking in the sub types.
*/
@@ -2,8 +2,6 @@ package com.avaje.ebeaninternal.server.el;
import java.util.Comparator;
import com.avaje.ebean.bean.EntityBean;
/**
* Comparator based on a ElGetValue.
*/
@@ -23,16 +21,14 @@ public final class ElComparatorProperty<T> implements Comparator<T>, ElComparato
public int compare(T o1, T o2) {
Object val1 = elGetValue.elGetValue((EntityBean) o1);
Object val2 = elGetValue.elGetValue((EntityBean) o2);
Object val1 = elGetValue.pathGet(o1);
Object val2 = elGetValue.pathGet(o2);
return compareValues(val1, val2);
}
public int compareValue(Object value, T o2) {
Object val2 = elGetValue.elGetValue((EntityBean) o2);
Object val2 = elGetValue.pathGet(o2);
return compareValues(value, val2);
}
@@ -28,7 +28,7 @@ public final class ElFilter<T> implements Filter<T> {
private Object convertValue(String propertyName, Object value) {
// convert type of value to match expected type
ElPropertyValue elGetValue = beanDescriptor.getElGetValue(propertyName);
return elGetValue.elConvertType(value);
return elGetValue.convert(value);
}
private ElComparator<T> getElComparator(String propertyName) {
@@ -87,8 +87,8 @@ public final class ElFilter<T> implements Filter<T> {
public Filter<T> between(String propertyName, Object min, Object max) {
ElPropertyValue elGetValue = getElGetValue(propertyName);
min = elGetValue.elConvertType(min);
max = elGetValue.elConvertType(max);
min = elGetValue.convert(min);
max = elGetValue.convert(max);
ElComparator<T> elComparator = getElComparator(propertyName);
@@ -28,7 +28,7 @@ class ElMatchBuilder {
}
public boolean isMatch(T bean) {
String v = (String) elGetValue.elGetValue((EntityBean) bean);
String v = (String) elGetValue.pathGet(bean);
return pattern.matcher(v).matches();
}
}
@@ -55,7 +55,7 @@ class ElMatchBuilder {
}
public boolean isMatch(T bean) {
String v = (String) elGetValue.elGetValue((EntityBean) bean);
String v = (String) elGetValue.pathGet(bean);
return value.equalsIgnoreCase(v);
}
}
@@ -75,7 +75,7 @@ class ElMatchBuilder {
public boolean isMatch(T bean) {
String v = (String) elGetValue.elGetValue((EntityBean) bean);
String v = (String) elGetValue.pathGet(bean);
return charMatch.startsWith(v);
}
}
@@ -95,7 +95,7 @@ class ElMatchBuilder {
public boolean isMatch(T bean) {
String v = (String) elGetValue.elGetValue((EntityBean) bean);
String v = (String) elGetValue.pathGet(bean);
return charMatch.endsWith(v);
}
}
@@ -106,7 +106,7 @@ class ElMatchBuilder {
}
public boolean isMatch(T bean) {
String v = (String) elGetValue.elGetValue((EntityBean) bean);
String v = (String) elGetValue.pathGet(bean);
return value.startsWith(v);
}
}
@@ -117,7 +117,7 @@ class ElMatchBuilder {
}
public boolean isMatch(T bean) {
String v = (String) elGetValue.elGetValue((EntityBean) bean);
String v = (String) elGetValue.pathGet(bean);
return value.endsWith(v);
}
}
@@ -131,7 +131,7 @@ class ElMatchBuilder {
}
public boolean isMatch(T bean) {
return (null == elGetValue.elGetValue((EntityBean) bean));
return (null == elGetValue.pathGet(bean));
}
}
@@ -144,7 +144,7 @@ class ElMatchBuilder {
}
public boolean isMatch(T bean) {
return (null != elGetValue.elGetValue((EntityBean) bean));
return (null != elGetValue.pathGet(bean));
}
}
@@ -175,7 +175,7 @@ class ElMatchBuilder {
public boolean isMatch(T bean) {
Object value = elGetValue.elGetValue((EntityBean) bean);
Object value = elGetValue.pathGet(bean);
return value != null && set.contains(value);
}
}
@@ -220,20 +220,19 @@ public class ElPropertyChain implements ElPropertyValue {
return scalarType;
}
public Object elConvertType(Object value) {
public Object convert(Object value) {
// just convert using the last one in the chain
return lastElPropertyValue.elConvertType(value);
return lastElPropertyValue.convert(value);
}
public Object elGetValue(EntityBean bean) {
@Override
public Object pathGet(Object bean) {
for (int i = 0; i < chain.length; i++) {
bean = (EntityBean) chain[i].elGetValue(bean);
bean = chain[i].pathGet(bean);
if (bean == null) {
return null;
}
}
return bean;
}
@@ -245,39 +244,25 @@ public class ElPropertyChain implements ElPropertyValue {
prevBean = (EntityBean) chain[i].elGetReference(prevBean);
}
// try the last step in the chain
return chain[last].elGetValue(prevBean);
return chain[last].pathGet(prevBean);
}
@Override
public void set(Object bean, Object value) {
elSetValue((EntityBean)bean, value, true);
}
public void pathSet(Object bean, Object value) {
public void elSetValue(EntityBean bean, Object value, boolean populate) {
EntityBean prevBean = bean;
if (populate) {
for (int i = 0; i < last; i++) {
// always return non null prevBean
prevBean = (EntityBean) chain[i].elGetReference(prevBean);
}
} else {
for (int i = 0; i < last; i++) {
// always return non null prevBean
prevBean = (EntityBean) chain[i].elGetValue(prevBean);
if (prevBean == null) {
break;
}
}
EntityBean prevBean = (EntityBean)bean;
for (int i = 0; i < last; i++) {
// always return non null prevBean
prevBean = (EntityBean) chain[i].elGetReference(prevBean);
}
if (prevBean != null) {
if (lastBeanProperty != null) {
// last chain element maps to a real scalar property
lastBeanProperty.setValueIntercept(prevBean, value);
lastBeanProperty.pathSet(prevBean, value);
} else {
// a non-scalar property of a Compound value object
lastElPropertyValue.elSetValue(prevBean, value, populate);
lastElPropertyValue.pathSet(prevBean, value);
}
}
}
@@ -60,53 +60,10 @@ public interface ElPropertyValue extends ElPropertyDeploy, ExpressionPath {
*/
int getDeployOrder();
/**
* Return the default StringParser for the scalar property.
*/
StringParser getStringParser();
/**
* Return true if the last type is "DateTime capable" - can support
* {@link #parseDateTime(long)}.
*/
boolean isDateTimeCapable();
/**
* Return the underlying JDBC type or 0 if this is not a scalar type.
*/
int getJdbcType();
/**
* For DateTime capable scalar types convert the long systemTimeMillis into
* an appropriate java time (Date,Timestamp,Time,Calendar, JODA type etc).
*/
Object parseDateTime(long systemTimeMillis);
/**
* Return the value from a given entity bean.
*/
Object elGetValue(EntityBean bean);
/**
* Return the value ensuring objects prior to the top scalar property are
* automatically populated.
*/
Object elGetReference(EntityBean bean);
/**
* Set a value given a root level bean.
* <p>
* If populate then
* </p>
*/
void elSetValue(EntityBean bean, Object value, boolean populate);
/**
* Convert the value to the expected type.
* <p>
* Typically useful for converting strings to the appropriate number type
* etc.
* </p>
*/
Object elConvertType(Object value);
}
@@ -230,7 +230,6 @@ public final class CQueryPlanStats {
return queryPlan.isAutoTuned();
}
@Override
public String getQueryPlanHash() {
return queryPlan.getPlanKey().toString();
@@ -13,6 +13,7 @@ import java.util.Locale;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.plugin.ExpressionPath;
import com.avaje.ebean.text.StringParser;
import com.avaje.ebean.text.TextException;
import com.avaje.ebean.text.TimeStringParser;
@@ -24,8 +25,7 @@ import com.avaje.ebeaninternal.server.deploy.BeanPropertyAssocOne;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
/**
*
* @author rbygrave
* Implementation of the CsvReader
*/
public class TCsvReader<T> implements CsvReader<T> {
@@ -55,9 +55,6 @@ public class TCsvReader<T> implements CsvReader<T> {
private boolean addPropertiesFromHeader;
// private String addHeaderDateTimeFormat;
// private Locale addHeaderLocale;
public TCsvReader(EbeanServer server, BeanDescriptor<T> descriptor) {
this.server = server;
this.descriptor = descriptor;
@@ -114,11 +111,10 @@ public class TCsvReader<T> implements CsvReader<T> {
public void addDateTime(String propertyName, String dateTimeFormat, Locale locale) {
ElPropertyValue elProp = descriptor.getElGetValue(propertyName);
ExpressionPath elProp = descriptor.getExpressionPath(propertyName);
if (!elProp.isDateTimeCapable()) {
throw new TextException("Property " + propertyName + " is not DateTime capable");
}
if (dateTimeFormat == null) {
dateTimeFormat = getDefaultDateTimeFormat(elProp.getJdbcType());
}
@@ -150,7 +146,7 @@ public class TCsvReader<T> implements CsvReader<T> {
public void addProperty(String propertyName, StringParser parser) {
ElPropertyValue elProp = descriptor.getElGetValue(propertyName);
ExpressionPath elProp = descriptor.getExpressionPath(propertyName);
if (parser == null) {
parser = elProp.getStringParser();
}
@@ -214,7 +210,7 @@ public class TCsvReader<T> implements CsvReader<T> {
callback.end(row);
} catch (Exception e) {
// notify that an error occured so that any
// notify that an error occurred so that any
// transaction can be rolled back if required
callback.endWithError(row, e);
throw e;
@@ -285,26 +281,23 @@ public class TCsvReader<T> implements CsvReader<T> {
*/
public static class CsvColumn {
private final ElPropertyValue elProp;
private final ExpressionPath path;
private final StringParser parser;
private final boolean ignore;
/**
* Constructor for the IGNORE column.
*/
private CsvColumn() {
this.elProp = null;
this.path = null;
this.parser = null;
this.ignore = true;
}
/**
* Construct with a property and parser.
*/
public CsvColumn(ElPropertyValue elProp, StringParser parser) {
this.elProp = elProp;
public CsvColumn(ExpressionPath path, StringParser parser) {
this.path = path;
this.parser = parser;
this.ignore = false;
}
/**
@@ -312,9 +305,9 @@ public class TCsvReader<T> implements CsvReader<T> {
*/
public void convertAndSet(String strValue, EntityBean bean) {
if (!ignore) {
if (parser != null && path != null) {
Object value = parser.parse(strValue);
elProp.elSetValue(bean, value, true);
path.pathSet(bean, value);
}
}
}
@@ -327,19 +320,19 @@ public class TCsvReader<T> implements CsvReader<T> {
private static class DateTimeParser implements StringParser {
private final DateFormat dateFormat;
private final ElPropertyValue elProp;
private final ExpressionPath path;
private final String format;
DateTimeParser(DateFormat dateFormat, String format, ElPropertyValue elProp) {
DateTimeParser(DateFormat dateFormat, String format, ExpressionPath path) {
this.dateFormat = dateFormat;
this.elProp = elProp;
this.path = path;
this.format = format;
}
public Object parse(String value) {
try {
Date dt = dateFormat.parse(value);
return elProp.parseDateTime(dt.getTime());
return path.parseDateTime(dt.getTime());
} catch (ParseException e) {
throw new TextException("Error parsing [" + value + "] using format[" + format + "]", e);
@@ -28,7 +28,8 @@ public class CtCompoundPropertyElAdapter implements ElPropertyValue {
this.deployOrder = deployOrder;
}
public Object elConvertType(Object value) {
@Override
public Object convert(Object value) {
return value;
}
@@ -36,16 +37,13 @@ public class CtCompoundPropertyElAdapter implements ElPropertyValue {
return bean;
}
public Object elGetValue(EntityBean bean) {
@Override
public Object pathGet(Object bean) {
return prop.getValue(bean);
}
@Override
public void set(Object bean, Object value) {
elSetValue((EntityBean) bean, value, true);
}
public void elSetValue(EntityBean bean, Object value, boolean populate) {
public void pathSet(Object bean, Object value) {
prop.setValue(bean, value);
}
@@ -113,4 +113,9 @@ public interface DocStoreBeanAdapter<T> extends BeanDocType<T> {
* </p>
*/
String rawProperty(String property);
/**
* Return true if this bean type as embedded invalidate registered.
*/
boolean hasEmbeddedInvalidation();
}
@@ -4,12 +4,15 @@ import com.avaje.ebean.FetchPath;
import com.avaje.ebean.Query;
import com.avaje.ebean.annotation.DocStore;
import com.avaje.ebean.annotation.DocStoreMode;
import com.avaje.ebean.plugin.BeanType;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.core.PersistRequest;
import com.avaje.ebeaninternal.server.core.PersistRequestBean;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.deploy.BeanProperty;
import com.avaje.ebeaninternal.server.deploy.InheritInfo;
import com.avaje.ebeaninternal.server.deploy.InheritInfoVisitor;
import com.avaje.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
import com.avaje.ebeanservice.docstore.api.DocStoreBeanAdapter;
import com.avaje.ebeanservice.docstore.api.DocStoreUpdateContext;
@@ -41,11 +44,6 @@ public abstract class DocStoreBeanBaseAdapter<T> implements DocStoreBeanAdapter<
*/
protected final boolean mapped;
/**
* Nested path properties defining the doc structure for indexing.
*/
protected final DocStructure docStructure;
/**
* Identifier used in the queue system to identify the index.
*/
@@ -87,18 +85,28 @@ public abstract class DocStoreBeanBaseAdapter<T> implements DocStoreBeanAdapter<
*/
protected final List<DocStoreEmbeddedInvalidation> embeddedInvalidation = new ArrayList<DocStoreEmbeddedInvalidation>();
protected final PathProperties pathProps;
/**
* Map of properties to 'raw' properties.
*/
private Map<String, String> sortableMap;
protected Map<String, String> sortableMap;
/**
* Nested path properties defining the doc structure for indexing.
*/
protected DocStructure docStructure;
protected DocumentMapping documentMapping;
private boolean registerPaths;
public DocStoreBeanBaseAdapter(BeanDescriptor<T> desc, DeployBeanDescriptor<T> deploy) {
this.desc = desc;
this.server = desc.getEbeanServer();
this.mapped = deploy.isDocStoreMapped();
this.docStructure = (!mapped) ? null : derivePathProperties(deploy);
this.pathProps = deploy.getDocStorePathProperties();
this.docStore = deploy.getDocStore();
this.queueId = derive(desc, deploy.getDocStoreQueueId());
this.indexName = derive(desc, deploy.getDocStoreIndexName());
@@ -108,28 +116,30 @@ public abstract class DocStoreBeanBaseAdapter<T> implements DocStoreBeanAdapter<
this.delete = deploy.getDocStoreDeleteEvent();
}
@Override
public boolean hasEmbeddedInvalidation() {
return !embeddedInvalidation.isEmpty();
}
@Override
public DocumentMapping createDocMapping() {
if (documentMapping != null) {
return documentMapping;
}
if (!mapped) return null;
PathProperties paths = docStructure.doc();
this.docStructure = derivePathProperties(pathProps);
DocMappingBuilder mappingBuilder = new DocMappingBuilder(paths, docStore);
DocMappingBuilder mappingBuilder = new DocMappingBuilder(docStructure.doc(), docStore);
desc.docStoreMapping(mappingBuilder, null);
mappingBuilder.applyMapping();
prepareMapping(mappingBuilder);
sortableMap = mappingBuilder.collectSortable();
docStructure.prepareMany(desc);
return mappingBuilder.create(queueId, indexName, indexType);
}
protected void prepareMapping(DocMappingBuilder mappingBuilder) {
// do nothing by default
documentMapping = mappingBuilder.create(queueId, indexName, indexType);
return documentMapping;
}
@Override
@@ -159,7 +169,7 @@ public abstract class DocStoreBeanBaseAdapter<T> implements DocStoreBeanAdapter<
*/
@Override
public void registerPaths() {
if (mapped) {
if (mapped && !registerPaths) {
Collection<PathProperties.Props> pathProps = docStructure.doc().getPathProps();
for (PathProperties.Props pathProp : pathProps) {
String path = pathProp.getPath();
@@ -170,6 +180,7 @@ public abstract class DocStoreBeanBaseAdapter<T> implements DocStoreBeanAdapter<
targetDesc.docStoreAdapter().registerInvalidationPath(desc.getDocStoreQueueId(), fullPath, pathProp.getProperties());
}
}
registerPaths = true;
}
}
@@ -223,13 +234,8 @@ public abstract class DocStoreBeanBaseAdapter<T> implements DocStoreBeanAdapter<
* Return the pathProperties which defines the JSON document to index.
* This can add derived/embedded/nested parts to the document.
*/
protected DocStructure derivePathProperties(DeployBeanDescriptor<T> deploy) {
protected DocStructure derivePathProperties(PathProperties pathProps) {
if (!mapped) {
return null;
}
PathProperties pathProps = deploy.getDocStorePathProperties();
boolean includeByDefault = (pathProps == null);
if (pathProps == null) {
pathProps = new PathProperties();
@@ -238,13 +244,27 @@ public abstract class DocStoreBeanBaseAdapter<T> implements DocStoreBeanAdapter<
return getDocStructure(pathProps, includeByDefault);
}
protected DocStructure getDocStructure(PathProperties pathProps, boolean includeByDefault) {
protected DocStructure getDocStructure(PathProperties pathProps, final boolean includeByDefault) {
final DocStructure docStructure = new DocStructure(pathProps);
DocStructure docStructure = new DocStructure(pathProps);
BeanProperty[] properties = desc.propertiesNonTransient();
for (int i = 0; i < properties.length; i++) {
properties[i].docStoreInclude(includeByDefault, docStructure);
}
InheritInfo inheritInfo = desc.getInheritInfo();
if (inheritInfo != null) {
inheritInfo.visitChildren(new InheritInfoVisitor() {
@Override
public void visit(InheritInfo inheritInfo) {
for (BeanProperty localProperty : inheritInfo.localProperties()) {
localProperty.docStoreInclude(includeByDefault, docStructure);
}
}
});
}
return docStructure;
}
@@ -293,7 +313,7 @@ public abstract class DocStoreBeanBaseAdapter<T> implements DocStoreBeanAdapter<
/**
* Return the supplied value or default to the bean name lower case.
*/
protected String derive(BeanDescriptor<T> desc, String suppliedValue) {
protected String derive(BeanType<?> desc, String suppliedValue) {
return (suppliedValue != null && suppliedValue.length() > 0) ? suppliedValue : desc.getName().toLowerCase();
}
@@ -73,7 +73,7 @@ public class DocStructure {
public <T> void prepareMany(BeanDescriptor<T> desc) {
Set<String> strings = embedded.keySet();
for (String prop : strings) {
BeanPropertyAssoc<?> embProp = (BeanPropertyAssoc<?>)desc.getBeanProperty(prop);
BeanPropertyAssoc<?> embProp = (BeanPropertyAssoc<?>)desc.findBeanProperty(prop);
if (embProp.isMany()) {
prepare(prop, embProp);
}
@@ -3,17 +3,24 @@ package com.avaje.ebean.plugin;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.FetchPath;
import com.avaje.ebean.Query;
import com.avaje.ebean.text.PathProperties;
import com.avaje.ebeaninternal.api.SpiQuery;
import com.avaje.ebeaninternal.server.querydefn.OrmQueryDetail;
import com.avaje.tests.inheritance.Stockforecast;
import com.avaje.tests.model.basic.Car;
import com.avaje.tests.model.basic.Customer;
import com.avaje.tests.model.basic.Order;
import com.avaje.tests.model.basic.OrderDetail;
import com.avaje.tests.model.basic.Person;
import com.avaje.tests.model.basic.Product;
import com.avaje.tests.model.basic.Vehicle;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class BeanTypeTest {
@@ -189,4 +196,48 @@ public class BeanTypeTest {
public void docStoreUpdateEmbedded() throws Exception {
beanType(Order.class).docStore().updateEmbedded(1, "customer", "someJson", null);
}
@Test
public void hasInheritance_when_not() {
assertFalse(beanType(Order.class).hasInheritance());
}
@Test
public void hasInheritance_when_root() {
assertTrue(beanType(Vehicle.class).hasInheritance());
}
@Test
public void hasInheritance_when_leaf() {
assertTrue(beanType(Car.class).hasInheritance());
}
@Test
public void getDiscColumn_when_default() {
assertEquals(beanType(Car.class).getDiscColumn(),"dtype");
}
@Test
public void getDiscColumn_when_set() {
assertEquals(beanType(Stockforecast.class).getDiscColumn(),"type");
}
@Test
public void createBeanUsingDisc_when_set() {
Vehicle vehicle = beanType(Vehicle.class).createBeanUsingDisc("C");
assertTrue(vehicle instanceof Car);
}
@Test
public void addInheritanceWhere_when_leaf() {
Query<Vehicle> query = server.find(Vehicle.class);
beanType(Car.class).addInheritanceWhere((SpiQuery<?>)query);
}
@Test
public void addInheritanceWhere_when_root() {
Query<Vehicle> query = server.find(Vehicle.class);
beanType(Vehicle.class).addInheritanceWhere((SpiQuery<?>)query);
}
}
@@ -63,7 +63,7 @@ public class ExpressionPathTest {
BeanType<Order> beanType = beanType(Order.class);
Order order = new Order();
beanType.getExpressionPath("id").set(order, 42);
beanType.getExpressionPath("id").pathSet(order, 42);
assertThat(order.getId()).isEqualTo(42);
}
@@ -72,7 +72,7 @@ public class ExpressionPathTest {
BeanType<Order> beanType = beanType(Order.class);
Order order = new Order();
beanType.getExpressionPath("customer.name").set(order, "Rob");
beanType.getExpressionPath("customer.name").pathSet(order, "Rob");
assertThat(order.getCustomer().getName()).isEqualTo("Rob");
}
@@ -2,62 +2,67 @@ package com.avaje.tests.ddd.iud;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.plugin.BeanType;
import com.avaje.ebean.plugin.ExpressionPath;
import com.avaje.ebean.plugin.SpiServer;
import com.avaje.ebean.text.json.JsonContext;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.tests.model.ddd.DPerson;
import com.avaje.tests.model.ivo.CMoney;
import com.avaje.tests.model.ivo.Money;
import junit.framework.TestCase;
import org.junit.Assert;
import org.junit.Test;
import java.io.IOException;
import java.util.Currency;
public class TestDPersonEl extends TestCase {
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
public void test() throws IOException {
public class TestDPersonEl {
Currency NZD = Currency.getInstance("NZD");
DPerson p = new DPerson();
p.setFirstName("first");
p.setLastName("last");
p.setSalary(new Money("12200"));
p.setCmoney(new CMoney(new Money("12"), NZD));
SpiEbeanServer server = (SpiEbeanServer)Ebean.getServer(null);
BeanDescriptor<DPerson> descriptor = server.getBeanDescriptor(DPerson.class);
ElPropertyValue elCmoney = descriptor.getElGetValue("cmoney");
// ElPropertyValue elCmoneyAmt = descriptor.getElGetValue("cmoney.amount");
// ElPropertyValue elCmoneyCur = descriptor.getElGetValue("cmoney.currency");
JsonContext jsonContext = server.json();
String json = jsonContext.toJson(p);
DPerson bean = jsonContext.toBean(DPerson.class, json);
Assert.assertEquals("first", bean.getFirstName());
Assert.assertEquals(new Money("12200"), bean.getSalary());
Assert.assertEquals(new Money("12"), bean.getCmoney().getAmount());
Assert.assertEquals(NZD, bean.getCmoney().getCurrency());
EntityBean entityBean = (EntityBean)p;
Object cmoney = elCmoney.elGetValue(entityBean);
// Object amt = elCmoneyAmt.elGetValue(entityBean);
// Object cur = elCmoneyCur.elGetValue(entityBean);
Assert.assertNotNull(cmoney);
// Assert.assertEquals(new Money("12"), amt);
// Assert.assertEquals(NZD, cur);
p.setCmoney(null);
Assert.assertNull(p.getCmoney());
@Test
public void test() throws IOException {
Currency NZD = Currency.getInstance("NZD");
DPerson p = new DPerson();
p.setFirstName("first");
p.setLastName("last");
p.setSalary(new Money("12200"));
p.setCmoney(new CMoney(new Money("12"), NZD));
SpiServer server = Ebean.getDefaultServer().getPluginApi();
BeanType<DPerson> descriptor = server.getBeanType(DPerson.class);
JsonContext jsonContext = server.json();
String json = jsonContext.toJson(p);
DPerson bean = jsonContext.toBean(DPerson.class, json);
assertEquals("first", bean.getFirstName());
assertEquals(new Money("12200"), bean.getSalary());
assertEquals(new Money("12"), bean.getCmoney().getAmount());
assertEquals(NZD, bean.getCmoney().getCurrency());
EntityBean entityBean = (EntityBean) p;
ExpressionPath elCmoney = descriptor.getExpressionPath("cmoney");
ExpressionPath elCmoneyAmt = descriptor.getExpressionPath("cmoney.amount");
ExpressionPath elCmoneyCur = descriptor.getExpressionPath("cmoney.currency");
Object cmoney = elCmoney.pathGet(entityBean);
Object amt = elCmoneyAmt.pathGet(entityBean);
Object cur = elCmoneyCur.pathGet(entityBean);
assertNotNull(cmoney);
assertEquals(new Money("12"), amt);
assertEquals(NZD, cur);
p.setCmoney(null);
assertNull(p.getCmoney());
}
}
}
@@ -1,41 +0,0 @@
package com.avaje.tests.el;
import junit.framework.TestCase;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.el.ElPropertyValue;
import com.avaje.tests.model.basic.Address;
import com.avaje.tests.model.basic.Customer;
public class TestElGetReference extends TestCase {
public void test() {
Address a = new Address();
a.setId((short)12);
a.setLine1("line1");
a.setCity("Auckland");
Customer c0 = new Customer();
c0.setBillingAddress(a);
Customer c1 = new Customer();
SpiEbeanServer server = (SpiEbeanServer)Ebean.getServer(null);
BeanDescriptor<Customer> descriptor = server.getBeanDescriptor(Customer.class);
ElPropertyValue elProp = descriptor.getElGetValue("billingAddress.id");
ElPropertyValue addrLine1Prop = descriptor.getElGetValue("billingAddress.line1");
ElPropertyValue addrCityProp = descriptor.getElGetValue("billingAddress.city");
elProp.elGetReference((EntityBean)c0);
elProp.elGetReference((EntityBean)c1);
addrLine1Prop.elSetValue((EntityBean)c1, "12 someplace", true);
addrCityProp.elSetValue((EntityBean)c1, "Auckland", true);
}
}
@@ -0,0 +1,81 @@
package com.avaje.tests.el;
import com.avaje.ebean.Ebean;
import com.avaje.ebean.bean.EntityBean;
import com.avaje.ebean.plugin.BeanType;
import com.avaje.ebean.plugin.ExpressionPath;
import com.avaje.ebean.plugin.SpiServer;
import com.avaje.tests.model.basic.Address;
import com.avaje.tests.model.basic.Country;
import com.avaje.tests.model.basic.Customer;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class TestPathExpression {
final BeanType<Customer> beanType;
final ExpressionPath billingId;
final ExpressionPath line1;
final ExpressionPath city;
public TestPathExpression() {
SpiServer server = Ebean.getDefaultServer().getPluginApi();
beanType = server.getBeanType(Customer.class);
billingId = beanType.getExpressionPath("billingAddress.id");
line1 = beanType.getExpressionPath("billingAddress.line1");
city = beanType.getExpressionPath("billingAddress.city");
}
@Test
public void pathSet() {
Customer c1 = new Customer();
line1.pathSet(c1, "12 someplace");
city.pathSet(c1, "Auckland");
billingId.pathSet(c1, 4);
beanType.getExpressionPath("id").pathSet(c1, 42L);
beanType.getExpressionPath("name").pathSet(c1, "jimmy");
beanType.getExpressionPath("status").pathSet(c1, "ACTIVE");
beanType.getExpressionPath("billingAddress.country.code").pathSet(c1, "NZ");
beanType.getExpressionPath("billingAddress.country.name").pathSet(c1, "New Zealand");
assertEquals(c1.getId(), Integer.valueOf(42));
assertEquals(c1.getName(), "jimmy");
assertEquals(c1.getStatus(), Customer.Status.ACTIVE);
assertEquals(c1.getBillingAddress().getLine1(), "12 someplace");
assertEquals(c1.getBillingAddress().getCity(), "Auckland");
assertEquals(c1.getBillingAddress().getId(), Short.valueOf("4"));
assertEquals(c1.getBillingAddress().getCountry().getCode(), "NZ");
assertEquals(c1.getBillingAddress().getCountry().getName(), "New Zealand");
}
@Test
public void pathGet() {
Address billingAddress = new Address();
billingAddress.setId((short)12);
billingAddress.setLine1("line1");
billingAddress.setCity("Auckland");
Country nz = new Country();
nz.setCode("NZ");
nz.setName("New Zealand");
billingAddress.setCountry(nz);
Customer c0 = new Customer();
c0.setBillingAddress(billingAddress);
EntityBean e0 = (EntityBean)c0;
assertEquals(line1.pathGet(e0), "line1");
assertEquals(city.pathGet(e0), "Auckland");
assertEquals(billingId.pathGet(e0), Short.valueOf("12"));
assertEquals(beanType.getExpressionPath("billingAddress.country.code").pathGet(e0), "NZ");
assertEquals(beanType.getExpressionPath("billingAddress.country.name").pathGet(e0), "New Zealand");
}
}