package io.ebeaninternal.server.deploy;

import com.fasterxml.jackson.core.JsonToken;
import io.ebean.ValuePair;
import io.ebean.bean.EntityBean;
import io.ebean.bean.PersistenceContext;
import io.ebean.config.EncryptKey;
import io.ebean.config.dbplatform.DbEncryptFunction;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.plugin.Property;
import io.ebean.text.StringParser;
import io.ebean.util.SplitName;
import io.ebean.util.StringHelper;
import io.ebeaninternal.api.SpiExpressionRequest;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.api.json.SpiJsonReader;
import io.ebeaninternal.api.json.SpiJsonWriter;
import io.ebeaninternal.server.core.InternString;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedProperty;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedWhenCreated;
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedWhenModified;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import io.ebeaninternal.server.el.ElPropertyChainBuilder;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.properties.BeanPropertyGetter;
import io.ebeaninternal.server.properties.BeanPropertySetter;
import io.ebeaninternal.server.query.STreeProperty;
import io.ebeaninternal.server.query.SqlBeanLoad;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.type.DataBind;
import io.ebeaninternal.server.type.DataReader;
import io.ebeaninternal.server.type.LocalEncryptedType;
import io.ebeaninternal.server.type.ScalarType;
import io.ebeaninternal.server.type.ScalarTypeBoolean;
import io.ebeaninternal.server.type.ScalarTypeEnum;
import io.ebeaninternal.server.type.ScalarTypeLogicalType;
import io.ebeaninternal.util.ValueUtil;
import io.ebeanservice.docstore.api.mapping.DocMappingBuilder;
import io.ebeanservice.docstore.api.mapping.DocPropertyMapping;
import io.ebeanservice.docstore.api.mapping.DocPropertyOptions;
import io.ebeanservice.docstore.api.mapping.DocPropertyType;
import io.ebeanservice.docstore.api.support.DocStructure;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.PersistenceException;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.lang.reflect.Field;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import java.util.Map;
import java.util.Set;

Description of a property of a bean. Includes its deployment information such as database column mapping information.
/** * Description of a property of a bean. Includes its deployment information such * as database column mapping information. */
public class BeanProperty implements ElPropertyValue, Property, STreeProperty { private static final Logger logger = LoggerFactory.getLogger(BeanProperty.class);
Flag to mark this is the id property.
/** * Flag to mark this is the id property. */
final boolean id; final boolean importedPrimaryKey;
Flag to make this as a dummy property for unidirecitonal relationships.
/** * Flag to make this as a dummy property for unidirecitonal relationships. */
final boolean unidirectionalShadow;
Flag set if this maps to the inheritance discriminator column
/** * Flag set if this maps to the inheritance discriminator column */
final boolean discriminator;
Flag to mark the property as embedded. This could be on BeanPropertyAssocOne rather than here. Put it here for checking Id type (embedded or not).
/** * Flag to mark the property as embedded. This could be on * BeanPropertyAssocOne rather than here. Put it here for checking Id type * (embedded or not). */
final boolean embedded;
Flag indicating if this the version property.
/** * Flag indicating if this the version property. */
final boolean version; final boolean naturalKey;
Set if this property is nullable.
/** * Set if this property is nullable. */
final boolean nullable; final boolean unique;
Is this property include in database resultSet.
/** * Is this property include in database resultSet. */
final boolean dbRead;
Include in DB insert.
/** * Include in DB insert. */
final boolean dbInsertable;
Include in DB update.
/** * Include in DB update. */
final boolean dbUpdatable;
True if the property is based on a SECONDARY table.
/** * True if the property is based on a SECONDARY table. */
final boolean secondaryTable; final TableJoin secondaryTableJoin; final String secondaryTableJoinPrefix;
The property is inherited from a super class.
/** * The property is inherited from a super class. */
final boolean inherited; final Class<?> owningType; final boolean local;
True if the property is a Clob, Blob LongVarchar or LongVarbinary.
/** * True if the property is a Clob, Blob LongVarchar or LongVarbinary. */
final boolean lob; final boolean fetchEager; final boolean isTransient;
The logical bean property name.
/** * The logical bean property name. */
final String name; final int propertyIndex;
The reflected field.
/** * The reflected field. */
final Field field;
The bean type.
/** * The bean type. */
final Class<?> propertyType; final String dbBind;
The database column. This can include quoted identifiers.
/** * The database column. This can include quoted identifiers. */
final String dbColumn; final String elPrefix; final String elPlaceHolder; final String elPlaceHolderEncrypted;
Select part of a SQL Formula used to populate this property.
/** * Select part of a SQL Formula used to populate this property. */
final String sqlFormulaSelect;
Join part of a SQL Formula.
/** * Join part of a SQL Formula. */
final String sqlFormulaJoin; final String aggregation; final boolean formula;
Set to true if stored encrypted.
/** * Set to true if stored encrypted. */
final boolean dbEncrypted; final boolean localEncrypted; final int dbEncryptedType;
The jdbc data type this maps to.
/** * The jdbc data type this maps to. */
final int dbType; final boolean excludedFromHistory;
Generator for insert or update timestamp etc.
/** * Generator for insert or update timestamp etc. */
final GeneratedProperty generatedProperty; final BeanPropertyGetter getter; final BeanPropertySetter setter; final BeanDescriptor<?> descriptor;
Used for non-jdbc native types (java.util.Date Enums etc). Converts from logical to jdbc types.
/** * Used for non-jdbc native types (java.util.Date Enums etc). Converts from * logical to jdbc types. */
@SuppressWarnings("rawtypes") final ScalarType scalarType; final DocPropertyOptions docOptions;
The length or precision for DB column.
/** * The length or precision for DB column. */
final int dbLength;
The scale for DB column (decimal).
/** * The scale for DB column (decimal). */
final int dbScale;
Deployment defined DB column definition.
/** * Deployment defined DB column definition. */
final String dbColumnDefn;
DB Column default value for DDL definition (FALSE, NOW etc).
/** * DB Column default value for DDL definition (FALSE, NOW etc). */
final String dbColumnDefault; final List<DbMigrationInfo> dbMigrationInfos;
Database DDL column comment.
/** * Database DDL column comment. */
final String dbComment; final DbEncryptFunction dbEncryptFunction; int deployOrder; final boolean jsonSerialize; final boolean jsonDeserialize; final boolean unmappedJson; final boolean tenantId; final boolean draft; final boolean draftOnly; final boolean draftDirty; final boolean draftReset; final boolean softDelete; final String softDeleteDbSet; final String softDeleteDbPredicate; public BeanProperty(DeployBeanProperty deploy) { this(null, deploy); } public BeanProperty(BeanDescriptor<?> descriptor, DeployBeanProperty deploy) { this.descriptor = descriptor; this.name = InternString.intern(deploy.getName()); this.propertyIndex = deploy.getPropertyIndex(); this.unidirectionalShadow = deploy.isUndirectionalShadow(); this.importedPrimaryKey = deploy.isImportedPrimaryKey(); this.discriminator = deploy.isDiscriminator(); this.localEncrypted = deploy.isLocalEncrypted(); this.dbEncrypted = deploy.isDbEncrypted(); this.dbEncryptedType = deploy.getDbEncryptedType(); this.dbEncryptFunction = deploy.getDbEncryptFunction(); this.dbBind = deploy.getDbBind(); this.dbRead = deploy.isDbRead(); this.dbInsertable = deploy.isDbInsertable(); this.dbUpdatable = deploy.isDbUpdateable(); this.excludedFromHistory = deploy.isExcludedFromHistory(); this.unmappedJson = deploy.isUnmappedJson(); this.tenantId = deploy.isTenantId(); this.draft = deploy.isDraft(); this.draftDirty = deploy.isDraftDirty(); this.draftOnly = deploy.isDraftOnly(); this.draftReset = deploy.isDraftReset(); this.secondaryTable = deploy.isSecondaryTable(); if (secondaryTable) { this.secondaryTableJoin = new TableJoin(deploy.getSecondaryTableJoin()); this.secondaryTableJoinPrefix = deploy.getSecondaryTableJoinPrefix(); } else { this.secondaryTableJoin = null; this.secondaryTableJoinPrefix = null; } this.fetchEager = deploy.isFetchEager(); this.isTransient = deploy.isTransient(); this.nullable = deploy.isNullable(); this.unique = deploy.isUnique(); this.naturalKey = deploy.isNaturalKey(); this.dbLength = deploy.getDbLength(); this.dbScale = deploy.getDbScale(); this.dbColumnDefn = InternString.intern(deploy.getDbColumnDefn()); this.dbColumnDefault = deploy.getDbColumnDefaultSqlLiteral(); this.dbMigrationInfos = deploy.getDbMigrationInfos(); this.inherited = false;// deploy.isInherited(); this.owningType = deploy.getOwningType(); this.local = deploy.isLocal(); this.version = deploy.isVersionColumn(); this.embedded = deploy.isEmbedded(); this.id = deploy.isId(); this.generatedProperty = deploy.getGeneratedProperty(); this.getter = deploy.getGetter(); this.setter = deploy.getSetter(); this.dbColumn = tableAliasIntern(descriptor, deploy.getDbColumn(), false, null); this.dbComment = deploy.getDbComment(); this.aggregation = deploy.parseAggregation(); this.sqlFormulaJoin = InternString.intern(deploy.getSqlFormulaJoin()); this.sqlFormulaSelect = InternString.intern(deploy.getSqlFormulaSelect()); this.formula = sqlFormulaSelect != null; this.dbType = deploy.getDbType(); this.scalarType = deploy.getScalarType(); this.lob = isLobType(dbType); this.propertyType = deploy.getPropertyType(); this.field = deploy.getField(); this.docOptions = deploy.getDocPropertyOptions(); this.elPlaceHolder = tableAliasIntern(descriptor, deploy.getElPlaceHolder(), false, null); this.elPlaceHolderEncrypted = tableAliasIntern(descriptor, deploy.getElPlaceHolder(), dbEncrypted, dbColumn); this.elPrefix = deploy.getElPrefix(); this.softDelete = deploy.isSoftDelete(); if (softDelete) { ScalarTypeBoolean.BooleanBase boolType = (ScalarTypeBoolean.BooleanBase) scalarType; this.softDeleteDbSet = dbColumn + "=" + boolType.getDbTrueLiteral(); this.softDeleteDbPredicate = "." + dbColumn + " = " + boolType.getDbFalseLiteral(); } else { this.softDeleteDbSet = null; this.softDeleteDbPredicate = null; } this.jsonSerialize = deploy.isJsonSerialize(); this.jsonDeserialize = deploy.isJsonDeserialize(); } private String tableAliasIntern(BeanDescriptor<?> descriptor, String s, boolean dbEncrypted, String dbColumn) { if (descriptor != null) { s = StringHelper.replaceString(s, "${ta}.", "${}"); s = StringHelper.replaceString(s, "${ta}", "${}"); if (dbEncrypted) { s = dbEncryptFunction.getDecryptSql(s); String namedParam = ":encryptkey_" + descriptor.getBaseTable() + "___" + dbColumn; s = StringHelper.replaceString(s, "?", namedParam); } } return InternString.intern(s); }
Create a Matching BeanProperty with some attributes overridden.

Primarily for supporting Embedded beans with overridden dbColumn mappings.

/** * Create a Matching BeanProperty with some attributes overridden. * <p> * Primarily for supporting Embedded beans with overridden dbColumn * mappings. * </p> */
public BeanProperty(BeanProperty source, BeanPropertyOverride override) { this.descriptor = source.descriptor; this.name = InternString.intern(source.getName()); this.propertyIndex = source.propertyIndex; this.dbColumn = InternString.intern(override.getDbColumn()); // override with sqlFormula not currently supported this.sqlFormulaJoin = null; this.sqlFormulaSelect = null; this.formula = false; this.aggregation = null; this.excludedFromHistory = source.excludedFromHistory; this.tenantId = source.tenantId; this.draft = source.draft; this.draftDirty = source.draftDirty; this.draftOnly = source.draftOnly; this.draftReset = source.draftReset; this.softDelete = source.softDelete; this.softDeleteDbSet = source.softDeleteDbSet; this.softDeleteDbPredicate = source.softDeleteDbPredicate; this.fetchEager = source.fetchEager; this.importedPrimaryKey = source.importedPrimaryKey; this.unidirectionalShadow = source.unidirectionalShadow; this.discriminator = source.discriminator; this.localEncrypted = source.isLocalEncrypted(); this.isTransient = source.isTransient(); this.secondaryTable = source.isSecondaryTable(); this.secondaryTableJoin = source.secondaryTableJoin; this.secondaryTableJoinPrefix = source.secondaryTableJoinPrefix; this.dbComment = source.dbComment; this.dbBind = source.getDbBind(); this.dbEncrypted = source.isDbEncrypted(); this.dbEncryptedType = source.getDbEncryptedType(); this.dbEncryptFunction = source.dbEncryptFunction; this.dbRead = source.isDbRead(); this.dbInsertable = source.isDbInsertable(); this.dbUpdatable = source.isDbUpdatable(); this.nullable = source.isNullable(); this.unique = source.isUnique(); this.naturalKey = source.isNaturalKey(); this.dbLength = source.getDbLength(); this.dbScale = source.getDbScale(); this.dbColumnDefn = InternString.intern(source.getDbColumnDefn()); this.dbColumnDefault = source.dbColumnDefault; this.dbMigrationInfos = source.dbMigrationInfos; this.inherited = source.isInherited(); this.owningType = source.owningType; this.local = owningType.equals(descriptor.getBeanType()); this.version = source.isVersion(); this.embedded = source.isEmbedded(); this.id = source.isId(); this.generatedProperty = source.getGeneratedProperty(); this.getter = source.getter; this.setter = source.setter; this.dbType = source.getDbType(true); this.scalarType = source.scalarType; this.lob = isLobType(dbType); this.propertyType = source.getPropertyType(); this.field = source.getField(); this.docOptions = source.docOptions; this.unmappedJson = source.unmappedJson; this.elPrefix = override.replace(source.elPrefix, source.dbColumn); this.elPlaceHolder = override.replace(source.elPlaceHolder, source.dbColumn); this.elPlaceHolderEncrypted = override.replace(source.elPlaceHolderEncrypted, source.dbColumn); this.jsonSerialize = source.jsonSerialize; this.jsonDeserialize = source.jsonDeserialize; }
Initialise the property before returning to client code. Used to initialise variables that can't be done in construction due to recursive issues.
/** * Initialise the property before returning to client code. Used to * initialise variables that can't be done in construction due to recursive * issues. */
public void initialise(BeanDescriptorInitContext initContext) { // do nothing for normal BeanProperty if (!isTransient && scalarType == null) { throw new RuntimeException("No ScalarType assigned to " + descriptor.getFullName() + "." + getName()); } }
Return the order this property appears in the bean.
/** * Return the order this property appears in the bean. */
public int getDeployOrder() { return deployOrder; }
Set the order this property appears in the bean.
/** * Set the order this property appears in the bean. */
public void setDeployOrder(int deployOrder) { this.deployOrder = deployOrder; } public ElPropertyValue buildElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) { return null; }
Return the BeanDescriptor that owns this property.
/** * Return the BeanDescriptor that owns this property. */
public BeanDescriptor<?> getBeanDescriptor() { return descriptor; }
Return true is this is a simple scalar property.
/** * Return true is this is a simple scalar property. */
public boolean isScalar() { return true; }
Return true if this property should have a DB Column created in DDL.
/** * Return true if this property should have a DB Column created in DDL. */
public boolean isDDLColumn() { return !formula && !secondaryTable && (aggregation == null); }
Return true if this property is based on a formula.
/** * Return true if this property is based on a formula. */
@Override public boolean isFormula() { return formula; }
Return true if this property maps to the inheritance discriminator column.
/** * Return true if this property maps to the inheritance discriminator column. */
public boolean isDiscriminator() { return discriminator; }
Return true if the underlying type is mutable.
/** * Return true if the underlying type is mutable. */
public boolean isMutableScalarType() { return scalarType != null && scalarType.isMutable(); }
Return the encrypt key for the column matching this property.
/** * Return the encrypt key for the column matching this property. */
public EncryptKey getEncryptKey() { return descriptor.getEncryptKey(this); } @Override public String getEncryptKeyAsString() { return getEncryptKey().getStringValue(); } public String getDecryptProperty(String propertyName) { return dbEncryptFunction.getDecryptSql(propertyName); } public String getDecryptSql() { return dbEncryptFunction.getDecryptSql(this.getDbColumn()); } public String getDecryptSql(String tableAlias) { return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn()); } @Override public int getFetchPreference() { // return some decently high value - override on ToMany property return 1000; }
Add any extra joins required to support this property. Generally a no operation except for a OneToOne exported.
/** * Add any extra joins required to support this property. Generally a no * operation except for a OneToOne exported. */
@Override public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) { if (formula && sqlFormulaJoin != null) { ctx.appendFormulaJoin(sqlFormulaJoin, joinType); } else if (secondaryTableJoin != null) { String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix); secondaryTableJoin.addJoin(joinType, relativePrefix, ctx); } }
Returns null unless this property is using a secondary table. In that case this returns the logical property prefix.
/** * Returns null unless this property is using a secondary table. In that * case this returns the logical property prefix. */
public String getSecondaryTableJoinPrefix() { return secondaryTableJoinPrefix; } @Override public boolean isAggregation() { return aggregation != null; } @Override public void appendSelect(DbSqlContext ctx, boolean subQuery) { if (aggregation != null) { ctx.appendFormulaSelect(aggregation); } else if (formula) { ctx.appendFormulaSelect(sqlFormulaSelect); } else if (!isTransient && !ignoreDraftOnlyProperty(ctx.isDraftQuery())) { if (secondaryTableJoin != null) { ctx.pushTableAlias(ctx.getRelativePrefix(secondaryTableJoinPrefix)); } if (dbEncrypted) { String decryptSql = getDecryptSql(ctx.peekTableAlias()); ctx.appendRawColumn(decryptSql); ctx.addEncryptedProp(this); } else { ctx.appendColumn(dbColumn); } if (secondaryTableJoin != null) { ctx.popTableAlias(); } } } @Override public boolean isMany() { return false; } public boolean isAssignableFrom(Class<?> type) { return owningType.isAssignableFrom(type); } @Override public void loadIgnore(DbReadContext ctx) { scalarType.loadIgnore(ctx.getDataReader()); } @Override public void load(SqlBeanLoad sqlBeanLoad) { sqlBeanLoad.load(this); } @Override public void buildRawSqlSelectChain(String prefix, List<String> selectChain) { if (prefix == null) { selectChain.add(name); } else { selectChain.add(prefix + "." + name); } } @Override public Object read(DataReader reader) throws SQLException { return scalarType.read(reader); } public Object readSet(DataReader reader, EntityBean bean) throws SQLException { try { Object value = scalarType.read(reader); if (bean != null) { setValue(bean, value); } return value; } catch (Exception e) { throw new PersistenceException("Error readSet on " + descriptor + "." + name, e); } } public Object read(DbReadContext ctx) throws SQLException { return scalarType.read(ctx.getDataReader()); } public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException { try { Object value = scalarType.read(ctx.getDataReader()); if (bean != null) { setValue(bean, value); } return value; } catch (Exception e) { throw new PersistenceException("Error readSet on " + descriptor + "." + name, e); } } @SuppressWarnings("unchecked") public void bind(DataBind b, Object value) throws SQLException { scalarType.bind(b, value); } @SuppressWarnings(value = "unchecked") public void writeData(DataOutput dataOutput, Object value) throws IOException { scalarType.writeData(dataOutput, value); } public Object readData(DataInput dataInput) throws IOException { return scalarType.readData(dataInput); } @Override public BeanProperty getBeanProperty() { return this; } @Override public Property getProperty() { return this; }
Return true if this object is part of an inheritance hierarchy.
/** * Return true if this object is part of an inheritance hierarchy. */
public boolean isInherited() { return inherited; }
Return true is this type is not from a super type.
/** * Return true is this type is not from a super type. */
public boolean isLocal() { return local; }
Copy/set the property value from the draft bean to the live bean.
/** * Copy/set the property value from the draft bean to the live bean. */
public void publish(EntityBean draftBean, EntityBean liveBean) { if (!version && !draftOnly) { // set property value from draft to live Object value = getValueIntercept(draftBean); setValueIntercept(liveBean, value); } }
Return the DB literal expression to set the deleted state to true.
/** * Return the DB literal expression to set the deleted state to true. */
public String getSoftDeleteDbSet() { return softDeleteDbSet; }
Return the DB literal predicate used to filter out soft deleted rows from a query.
/** * Return the DB literal predicate used to filter out soft deleted rows from a query. */
public String getSoftDeleteDbPredicate(String tableAlias) { return tableAlias + softDeleteDbPredicate; }
Set the soft delete property value on the bean without invoking lazy loading.
/** * Set the soft delete property value on the bean without invoking lazy loading. */
public void setSoftDeleteValue(EntityBean bean) { // assumes boolean deleted true being set which is ok limitation for now setValue(bean, true); bean._ebean_getIntercept().setChangedProperty(propertyIndex); }
Set the changed value without invoking interception (lazy loading etc). Typically used to set generated values on update.
/** * Set the changed value without invoking interception (lazy loading etc). * Typically used to set generated values on update. */
public void setValueChanged(EntityBean bean, Object value) { setValue(bean, value); bean._ebean_getIntercept().setChangedProperty(propertyIndex); }
Add the tenantId predicate to the query.
/** * Add the tenantId predicate to the query. */
public void addTenant(SpiQuery<?> query, Object tenantId) { query.where().eq(name, tenantId); }
Set the tenantId onto the bean.
/** * Set the tenantId onto the bean. */
public void setTenantValue(EntityBean entityBean, Object tenantId) { setValue(entityBean, tenantId); }
Set the value of the property without interception or PropertyChangeSupport.
/** * Set the value of the property without interception or * PropertyChangeSupport. */
public void setValue(EntityBean bean, Object value) { try { setter.set(bean, value); } catch (Exception ex) { throw new RuntimeException(setterErrorMsg(bean, value, "set "), ex); } }
Set the value of the property.
/** * Set the value of the property. */
public void setValueIntercept(EntityBean bean, Object value) { try { setter.setIntercept(bean, value); } catch (Exception ex) { throw new RuntimeException(setterErrorMsg(bean, value, "setIntercept "), ex); } }
Return an error message when calling a setter.
/** * Return an error message when calling a setter. */
private String setterErrorMsg(EntityBean bean, Object value, String prefix) { String beanType = bean == null ? "null" : bean.getClass().getName(); return prefix + name + " on [" + descriptor + "] arg[" + value + "] type[" + beanType + "] threw error"; }
Return true if this property should be included in the cache bean data.
/** * Return true if this property should be included in the cache bean data. */
public boolean isCacheDataInclude() { return true; }
Return the value for this property which we hold in the L2 cache entry.

This uses format() where possible to store the value as a string and this is done to make any resulting Java object serialisation content smaller as strings get special treatment.

/** * Return the value for this property which we hold in the L2 cache entry. * <p> * This uses format() where possible to store the value as a string and this * is done to make any resulting Java object serialisation content smaller as * strings get special treatment. * </p> */
public Object getCacheDataValue(EntityBean bean) { Object value = getValue(bean); if (value == null || scalarType.isBinaryType()) { return value; } else { // convert to string as an optimisation for java object serialisation return scalarType.format(value); } }
Return the value in String format (for bean cache key).
/** * Return the value in String format (for bean cache key). */
public String format(Object value) { return scalarType.format(value); }
Read the value for this property from L2 cache entry and set it to the bean.

This uses parse() as per the comment in getCacheDataValue().

/** * Read the value for this property from L2 cache entry and set it to the bean. * <p> * This uses parse() as per the comment in getCacheDataValue(). * </p> */
public void setCacheDataValue(EntityBean bean, Object cacheData, PersistenceContext context) { if (cacheData instanceof String) { // parse back from string to support optimisation of java object serialisation cacheData = scalarType.parse((String) cacheData); } setValue(bean, cacheData); } @Override public Object getVal(Object bean) { return getValue((EntityBean) bean); }
Return the value of the property method.
/** * Return the value of the property method. */
public Object getValue(EntityBean bean) { try { return getter.get(bean); } catch (Exception ex) { String beanType = bean == null ? "null" : bean.getClass().getName(); String msg = "get " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; throw new RuntimeException(msg, ex); } } public Object getValueIntercept(EntityBean bean) { try { return getter.getIntercept(bean); } catch (Exception ex) { String beanType = bean == null ? "null" : bean.getClass().getName(); String msg = "getIntercept " + name + " on [" + descriptor + "] type[" + beanType + "] threw error."; throw new RuntimeException(msg, ex); } } @Override public Object convert(Object value) { if (value == null) { return null; } return convertToLogicalType(value); } @Override public void pathSet(Object bean, Object value) { if (bean != null) { Object logicalVal = convertToLogicalType(value); setValueIntercept((EntityBean) bean, logicalVal); } } @Override public Object pathGet(Object bean) { if (bean == null) { return null; } return getValueIntercept((EntityBean) bean); } @Override public Object pathGetNested(Object bean) { throw new RuntimeException("Not expected to call this"); }
Return the name of the property.
/** * Return the name of the property. */
@Override public String getName() { return name; }
Return the position of this property in the enhanced bean.
/** * Return the position of this property in the enhanced bean. */
public int getPropertyIndex() { return propertyIndex; } @Override public String getElName() { return name; } @Override public boolean containsFormulaWithJoin() { return formula && sqlFormulaJoin != null; } @Override public boolean containsManySince(String sinceProperty) { return containsMany(); } @Override public boolean containsMany() { return aggregation != null; } @Override public String getAssocIsEmpty(SpiExpressionRequest request, String path) { // overridden in BanePropertyAssocMany throw new RuntimeException("Not Supported or Expected"); } @Override public Object[] getAssocIdValues(EntityBean bean) { // Returns null as not an AssocOne. return null; } @Override public String getAssocIdExpression(String prefix, String operator) { // Returns null as not an AssocOne. return null; } @Override public String getAssocIdInExpr(String prefix) { // Returns null as not an AssocOne. return null; } @Override public String getAssocIdInValueExpr(boolean not, int size) { // Returns null as not an AssocOne. return null; }
If true this bean maps to the primary key.
/** * If true this bean maps to the primary key. */
public boolean isImportedPrimaryKey() { return importedPrimaryKey; } @Override public boolean isAssocMany() { // Returns false - override in BeanPropertyAssocMany. return false; } @Override public boolean isAssocId() { // Returns false - override in BeanPropertyAssocOne. return false; } @Override public boolean isAssocProperty() { // Returns false - override in BeanPropertyAssocOne. return false; } @Override public String getElPlaceholder(boolean encrypted) { return encrypted ? elPlaceHolderEncrypted : elPlaceHolder; } @Override public String getElPrefix() { return elPrefix; }
Return the full name of this property.
/** * Return the full name of this property. */
@Override public String getFullBeanName() { return descriptor.getFullName() + "." + name; }
Return true if the mutable value is considered dirty. This is only used for 'mutable' scalar types like hstore etc.
/** * Return true if the mutable value is considered dirty. * This is only used for 'mutable' scalar types like hstore etc. */
public boolean isDirtyValue(Object value) { return scalarType.isDirty(value); }
Return the scalarType.
/** * Return the scalarType. */
@Override @SuppressWarnings(value = "unchecked") public ScalarType<Object> getScalarType() { return scalarType; } @Override public StringParser getStringParser() { return scalarType; } @Override public boolean isDateTimeCapable() { return scalarType != null && scalarType.isDateTimeCapable(); } @Override public int getJdbcType() { return scalarType == null ? 0 : scalarType.getJdbcType(); } @Override public Object parseDateTime(long systemTimeMillis) { return scalarType.convertFromMillis(systemTimeMillis); }
Return the DB max length (varchar) or precision (decimal).
/** * Return the DB max length (varchar) or precision (decimal). */
public int getDbLength() { return dbLength; }
Return the DB scale for numeric columns.
/** * Return the DB scale for numeric columns. */
public int getDbScale() { return dbScale; }
Return a specific column DDL definition if specified (otherwise null).
/** * Return a specific column DDL definition if specified (otherwise null). */
public String getDbColumnDefn() { return dbColumnDefn; }
Return the DB constraint expression (can be null).

For an Enum returns IN expression for the set of Enum values.

/** * Return the DB constraint expression (can be null). * <p> * For an Enum returns IN expression for the set of Enum values. * </p> */
public Set<String> getDbCheckConstraintValues() { if (scalarType instanceof ScalarTypeEnum) { return ((ScalarTypeEnum<?>) scalarType).getDbCheckConstraintValues(); } return null; }
Return the DB column type definition.
/** * Return the DB column type definition. */
public String renderDbType(DbPlatformType dbType, boolean strict) { if (dbColumnDefn != null) { return dbColumnDefn; } return dbType.renderType(dbLength, dbScale, strict); }
Return the DB column default to use for DDL.
/** * Return the DB column default to use for DDL. */
public String getDbColumnDefault() { return dbColumnDefn != null ? null : dbColumnDefault; }
Return the DDL-Migration Infos
/** * Return the DDL-Migration Infos */
public List<DbMigrationInfo> getDbMigrationInfos() { return dbMigrationInfos; }
Return the bean Field associated with this property.
/** * Return the bean Field associated with this property. */
public Field getField() { return field; }
Return the GeneratedValue. Used to generate update timestamp etc.
/** * Return the GeneratedValue. Used to generate update timestamp etc. */
public GeneratedProperty getGeneratedProperty() { return generatedProperty; }
Return true if this is the natural key property.
/** * Return true if this is the natural key property. */
public boolean isNaturalKey() { return naturalKey; }
Return true if this property is mandatory.
/** * Return true if this property is mandatory. */
public boolean isNullable() { return nullable; }
Return true if DDL Not NULL constraint should be defined for this column based on it being a version column or having a generated property.
/** * Return true if DDL Not NULL constraint should be defined for this column * based on it being a version column or having a generated property. */
public boolean isDDLNotNull() { return isVersion() || (generatedProperty != null && generatedProperty.isDDLNotNullable()); }
Return true if this is a generated property mapping to @WhenCreated or @CreatedTimestamp.
/** * Return true if this is a generated property mapping to @WhenCreated or @CreatedTimestamp. */
public boolean isGeneratedWhenCreated() { return generatedProperty instanceof GeneratedWhenCreated; }
Return true if this is a generated property mapping to @WhenModified or @UpdatedTimestamp.
/** * Return true if this is a generated property mapping to @WhenModified or @UpdatedTimestamp. */
public boolean isGeneratedWhenModified() { return generatedProperty instanceof GeneratedWhenModified; }
Return true if this is a generated or Id property.
/** * Return true if this is a generated or Id property. */
public boolean isGenerated() { return id || generatedProperty != null; }
Return true if the DB column should be unique.
/** * Return true if the DB column should be unique. */
public boolean isUnique() { return unique; }
Return true if the property is transient.
/** * Return true if the property is transient. */
public boolean isTransient() { return isTransient; }
Return true if this property is loadable from a resultSet.
/** * Return true if this property is loadable from a resultSet. */
public boolean isLoadProperty(boolean draftQuery) { return !ignoreDraftOnlyProperty(draftQuery) && (!isTransient || formula); }
Return true if this is a draftOnly property on a non-asDraft query and as such this property should not be included in a sql query.
/** * Return true if this is a draftOnly property on a non-asDraft query and as such this * property should not be included in a sql query. */
protected boolean ignoreDraftOnlyProperty(boolean draftQuery) { return draftOnly && !draftQuery; }
Return true if this is a version column used for concurrency checking.
/** * Return true if this is a version column used for concurrency checking. */
public boolean isVersion() { return version; }
The database column name this is mapped to.
/** * The database column name this is mapped to. */
@Override public String getDbColumn() { return dbColumn; }
Return the comment for the associated DB column.
/** * Return the comment for the associated DB column. */
public String getDbComment() { return dbComment; }
Return the database jdbc data type this is mapped to.
Params:
  • platformTypes – Set as false when we want logical platform agnostic types.
/** * Return the database jdbc data type this is mapped to. * * @param platformTypes Set as false when we want logical platform agnostic types. */
public int getDbType(boolean platformTypes) { if (platformTypes || !(scalarType instanceof ScalarTypeLogicalType)) { return dbType; } return ((ScalarTypeLogicalType) scalarType).getLogicalType(); }
Perform DB to Logical type conversion (if necessary).
/** * Perform DB to Logical type conversion (if necessary). */
public Object convertToLogicalType(Object value) { if (scalarType != null) { return scalarType.toBeanType(value); } return value; }
Return true if by default this property is set to fetch eager. Lob's usually default to fetch lazy.
/** * Return true if by default this property is set to fetch eager. * Lob's usually default to fetch lazy. */
public boolean isFetchEager() { return fetchEager; }
Return true if this is mapped to a Clob Blob LongVarchar or LongVarbinary.
/** * Return true if this is mapped to a Clob Blob LongVarchar or * LongVarbinary. */
public boolean isLob() { return lob; } public static boolean isLobType(int type) { switch (type) { case Types.CLOB: return true; case Types.BLOB: return true; case Types.LONGVARBINARY: return true; case Types.LONGVARCHAR: return true; default: return false; } }
Return the DB bind parameter. Typically is "?" but different for encrypted bind.
/** * Return the DB bind parameter. Typically is "?" but different for * encrypted bind. */
public String getDbBind() { return dbBind; } @Override public Object localEncrypt(Object value) { return ((LocalEncryptedType)scalarType).localEncrypt(value); }
Returns true if DB encrypted.
/** * Returns true if DB encrypted. */
@Override public boolean isLocalEncrypted() { return localEncrypted; }
Return true if this property is stored encrypted.
/** * Return true if this property is stored encrypted. */
@Override public boolean isDbEncrypted() { return dbEncrypted; } public int getDbEncryptedType() { return dbEncryptedType; }
Return true if this property is excluded from history.
/** * Return true if this property is excluded from history. */
public boolean isExcludedFromHistory() { return excludedFromHistory; }
Return true if this is a ManyToMany with history support (on the intersection table).
/** * Return true if this is a ManyToMany with history support (on the intersection table). */
public boolean isManyToManyWithHistory() { return false; }
Return true if this property hold unmapped JSON.
/** * Return true if this property hold unmapped JSON. */
public boolean isUnmappedJson() { return unmappedJson; }
Return true if this is the tenantId property (for multi-tenant partitioning).
/** * Return true if this is the tenantId property (for multi-tenant partitioning). */
public boolean isTenantId() { return tenantId; }
Return true if this property only exists on the draft table.
/** * Return true if this property only exists on the draft table. */
public boolean isDraftOnly() { return draftOnly; }
Return true if this property is a boolean flag on a draftable bean indicating if the instance is a draft or live bean.
/** * Return true if this property is a boolean flag on a draftable bean * indicating if the instance is a draft or live bean. */
public boolean isDraft() { return draft; }
Return true if this property is a boolean flag only on the draft table indicating that when the draft is different from the published row.
/** * Return true if this property is a boolean flag only on the draft table * indicating that when the draft is different from the published row. */
public boolean isDraftDirty() { return draftDirty; }
Return true if this property is reset/cleared on publish (on the draft bean).
/** * Return true if this property is reset/cleared on publish (on the draft bean). */
public boolean isDraftReset() { return draftReset; }
Return true if this property is the soft delete property.
/** * Return true if this property is the soft delete property. */
public boolean isSoftDelete() { return softDelete; }
Return true if this property should be included in an Insert.
/** * Return true if this property should be included in an Insert. */
public boolean isDbInsertable() { return dbInsertable; }
Return true if this property should be included in an Update.
/** * Return true if this property should be included in an Update. */
public boolean isDbUpdatable() { return dbUpdatable; }
Return true if this property is included in database queries.
/** * Return true if this property is included in database queries. */
public boolean isDbRead() { return dbRead; }
Return true if this property is based on a secondary table (not the base table).
/** * Return true if this property is based on a secondary table (not the base * table). */
public boolean isSecondaryTable() { return secondaryTable; }
Return the property type.
/** * Return the property type. */
@Override public Class<?> getPropertyType() { return propertyType; }
Return true if this is included in the unique id.
/** * Return true if this is included in the unique id. */
@Override public boolean isId() { return id; }
Return true if this is an Embedded property. In this case it shares the table and primary key of its owner object.
/** * Return true if this is an Embedded property. In this case it shares the * table and primary key of its owner object. */
@Override public boolean isEmbedded() { return embedded; } @Override public String toString() { return name; }
Append this property to the document store based on includeByDefault setting.
/** * Append this property to the document store based on includeByDefault setting. */
public void docStoreInclude(boolean includeByDefault, DocStructure docStructure) { if (includeByDefault) { docStructure.addProperty(name); } } public boolean isJsonSerialize() { return jsonSerialize; }
JSON write the property for 'insert only depth'.
/** * JSON write the property for 'insert only depth'. */
public void jsonWriteForInsert(SpiJsonWriter writeJson, EntityBean bean) throws IOException { if (!jsonSerialize) { return; } Object value = getValue(bean); if (value != null) { jsonWriteScalar(writeJson, value); } }
JSON write the property value.
/** * JSON write the property value. */
public void jsonWriteValue(SpiJsonWriter writeJson, Object value) throws IOException { if (!jsonSerialize) { return; } jsonWriteVal(writeJson, value); }
JSON write the bean property.
/** * JSON write the bean property. */
public void jsonWrite(SpiJsonWriter writeJson, EntityBean bean) throws IOException { if (!jsonSerialize) { return; } jsonWriteVal(writeJson, getValueIntercept(bean)); } private void jsonWriteVal(SpiJsonWriter writeJson, Object value) throws IOException { if (value == null) { writeJson.writeNullField(name); } else { jsonWriteScalar(writeJson, value); } } @SuppressWarnings("unchecked") private void jsonWriteScalar(SpiJsonWriter writeJson, Object value) throws IOException { if (scalarType != null) { writeJson.writeFieldName(name); scalarType.jsonWrite(writeJson.gen(), value); } else { writeJson.writeValueUsingObjectMapper(name, value); } } public void jsonRead(SpiJsonReader ctx, EntityBean bean) throws IOException { JsonToken event = ctx.nextToken(); if (JsonToken.VALUE_NULL == event) { if (jsonDeserialize) { setValue(bean, null); } } else { // expect to read non-null json value Object objValue; if (scalarType != null) { objValue = scalarType.jsonRead(ctx.getParser()); } else { try { objValue = ctx.readValueUsingObjectMapper(propertyType); } catch (IOException e) { // change in behavior for #318 objValue = null; String msg = "Error trying to use Jackson ObjectMapper to read transient property " + getFullBeanName() + " - consider marking this property with @JsonIgnore"; logger.error(msg, e); } } if (jsonDeserialize) { setValue(bean, objValue); } } }
Populate diff map comparing the property values between the beans.
/** * Populate diff map comparing the property values between the beans. */
public void diff(String prefix, Map<String, ValuePair> map, EntityBean newBean, EntityBean oldBean) { Object newVal = (newBean == null) ? null : getValue(newBean); Object oldVal = (oldBean == null) ? null : getValue(oldBean); diffVal(prefix, map, newVal, oldVal); }
Populate diff map comparing the property values.
/** * Populate diff map comparing the property values. */
public void diffVal(String prefix, Map<String, ValuePair> map, Object newVal, Object oldVal) { if (!ValueUtil.areEqual(newVal, oldVal)) { String propName = (prefix == null) ? name : prefix + "." + name; map.put(propName, new ValuePair(newVal, oldVal)); } }
Add to the document mapping if this property is included for this index.
/** * Add to the document mapping if this property is included for this index. */
public void docStoreMapping(DocMappingBuilder mapping, String prefix) { if (mapping.includesProperty(prefix, name)) { DocPropertyType type = scalarType.getDocType(); DocPropertyOptions options = docOptions.copy(); if (isKeywordType(type, options)) { type = DocPropertyType.KEYWORD; } mapping.add(new DocPropertyMapping(name, type, options)); } } private boolean isKeywordType(DocPropertyType type, DocPropertyOptions docOptions) { return type == DocPropertyType.TEXT && (docOptions.isCode() || id || discriminator); } public void merge(EntityBean bean, EntityBean existing) { // do nothing unless Many property } public void registerColumn(BeanDescriptor<?> desc, String prefix) { String path = SplitName.add(prefix, name); if (formula && dbColumn != null) { // trim off table alias placeholder if found String[] split = dbColumn.split("}"); if (split.length == 2) { desc.registerColumn(split[1], path); } else { desc.registerColumn(dbColumn, path); } } else if (dbColumn != null) { desc.registerColumn(dbColumn, path); } } }