io.ebeaninternal.server.deploy.BeanPropertyAssoc Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ebean Show documentation
Show all versions of ebean Show documentation
composite of common runtime dependencies for all platforms
package io.ebeaninternal.server.deploy;
import io.ebean.Query;
import io.ebean.bean.EntityBean;
import io.ebean.text.PathProperties;
import io.ebean.util.SplitName;
import io.ebeaninternal.api.SpiEbeanServer;
import io.ebeaninternal.api.SpiQuery;
import io.ebeaninternal.server.core.DefaultSqlUpdate;
import io.ebeaninternal.server.core.InternString;
import io.ebeaninternal.server.deploy.id.IdBinder;
import io.ebeaninternal.server.deploy.id.ImportedId;
import io.ebeaninternal.server.deploy.id.ImportedIdEmbedded;
import io.ebeaninternal.server.deploy.id.ImportedIdSimple;
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssoc;
import io.ebeaninternal.server.el.ElPropertyChainBuilder;
import io.ebeaninternal.server.el.ElPropertyValue;
import io.ebeaninternal.server.persist.MultiValueWrapper;
import io.ebeaninternal.server.query.STreePropertyAssoc;
import io.ebeaninternal.server.query.STreeType;
import io.ebeaninternal.server.query.SqlJoinType;
import io.ebeaninternal.server.querydefn.DefaultOrmQuery;
import io.ebeanservice.docstore.api.mapping.DocMappingBuilder;
import io.ebeanservice.docstore.api.mapping.DocPropertyMapping;
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.util.ArrayList;
import java.util.List;
/**
* Abstract base for properties mapped to an associated bean, list, set or map.
*/
public abstract class BeanPropertyAssoc extends BeanProperty implements STreePropertyAssoc {
private static final Logger logger = LoggerFactory.getLogger(BeanPropertyAssoc.class);
/**
* The descriptor of the target. This MUST be initialised after construction
* so as to avoid a dependency loop between BeanDescriptors.
*/
BeanDescriptor targetDescriptor;
IdBinder targetIdBinder;
InheritInfo targetInheritInfo;
String targetIdProperty;
/**
* Derived list of exported property and matching foreignKey
*/
protected ExportedProperty[] exportedProperties;
/**
* Persist settings.
*/
final BeanCascadeInfo cascadeInfo;
/**
* Join between the beans.
*/
final TableJoin tableJoin;
final PropertyForeignKey foreignKey;
/**
* The type of the joined bean.
*/
final Class targetType;
/**
* The join table information.
*/
final BeanTable beanTable;
final String mappedBy;
final String docStoreDoc;
final String extraWhere;
final int fetchPreference;
boolean saveRecurseSkippable;
/**
* Construct the property.
*/
public BeanPropertyAssoc(BeanDescriptor> descriptor, DeployBeanPropertyAssoc deploy) {
super(descriptor, deploy);
this.foreignKey = deploy.getForeignKey();
this.extraWhere = InternString.intern(deploy.getExtraWhere());
this.beanTable = deploy.getBeanTable();
this.mappedBy = InternString.intern(deploy.getMappedBy());
this.docStoreDoc = deploy.getDocStoreDoc();
this.tableJoin = new TableJoin(deploy.getTableJoin());
this.targetType = deploy.getTargetType();
this.cascadeInfo = deploy.getCascadeInfo();
this.fetchPreference = deploy.getFetchPreference();
}
/**
* Initialise post construction.
*/
@Override
public void initialise(BeanDescriptorInitContext initContext) {
// this *MUST* execute after the BeanDescriptor is
// put into the map to stop infinite recursion
initialiseTargetDescriptor(initContext);
}
void initialiseTargetDescriptor(BeanDescriptorInitContext initContext) {
targetDescriptor = descriptor.getBeanDescriptor(targetType);
if (!isTransient) {
targetIdBinder = targetDescriptor.getIdBinder();
targetInheritInfo = targetDescriptor.getInheritInfo();
saveRecurseSkippable = targetDescriptor.isSaveRecurseSkippable();
if (!targetIdBinder.isComplexId()) {
targetIdProperty = targetIdBinder.getIdProperty();
}
}
}
@Override
public int getFetchPreference() {
return fetchPreference;
}
/**
* Return the extra configuration for the foreign key.
*/
public PropertyForeignKey getForeignKey() {
return foreignKey;
}
/**
* Create a ElPropertyValue for a *ToOne or *ToMany.
*/
protected ElPropertyValue createElPropertyValue(String propName, String remainder, ElPropertyChainBuilder chain, boolean propertyDeploy) {
// associated or embedded bean
BeanDescriptor> embDesc = getTargetDescriptor();
if (chain == null) {
chain = new ElPropertyChainBuilder(isEmbedded(), propName);
}
chain.add(this);
if (containsMany()) {
chain.setContainsMany();
}
return embDesc.buildElGetValue(remainder, chain, propertyDeploy);
}
/**
* Add table join with table alias based on prefix.
*/
@Override
public SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx) {
return tableJoin.addJoin(joinType, prefix, ctx);
}
/**
* Add table join with explicit table alias.
*/
@Override
public SqlJoinType addJoin(SqlJoinType joinType, String a1, String a2, DbSqlContext ctx) {
return tableJoin.addJoin(joinType, a1, a2, ctx);
}
/**
* Return false.
*/
@Override
public boolean isScalar() {
return false;
}
/**
* Return the mappedBy property.
* This will be null on the owning side.
*/
public String getMappedBy() {
return mappedBy;
}
/**
* Return the Id property of the target entity type.
*
* This will return null for multiple Id properties.
*
*/
public String getTargetIdProperty() {
return targetIdProperty;
}
/**
* Return the BeanDescriptor of the target.
*/
public BeanDescriptor getTargetDescriptor() {
return targetDescriptor;
}
SpiEbeanServer server() {
return descriptor.getEbeanServer();
}
/**
* Create a new query for the target type.
*
* We use target descriptor rather than target property type to support ElementCollection.
*/
public SpiQuery newQuery(SpiEbeanServer server) {
return new DefaultOrmQuery<>(targetDescriptor, server, server.getExpressionFactory());
}
@Override
public IdBinder getIdBinder() {
return descriptor.getIdBinder();
}
@Override
public STreeType target() {
return targetDescriptor;
}
/**
* Return true if the target side has soft delete.
*/
public boolean isTargetSoftDelete() {
return targetDescriptor.isSoftDelete();
}
/**
* Return true if REFRESH should cascade.
*/
public boolean isCascadeRefresh() {
return cascadeInfo.isRefresh();
}
public boolean isSaveRecurseSkippable(Object bean) {
return saveRecurseSkippable && bean instanceof EntityBean && !((EntityBean) bean)._ebean_getIntercept().isNewOrDirty();
}
/**
* Return true if save can be skipped for unmodified bean(s) of this
* property.
*
* That is, if a bean of this property is unmodified we don't need to
* saveRecurse because none of its associated beans have cascade save set to
* true.
*
*/
public boolean isSaveRecurseSkippable() {
return saveRecurseSkippable;
}
/**
* Return true if the unique id properties are all not null for this bean.
*/
public boolean hasId(EntityBean bean) {
BeanDescriptor> targetDesc = getTargetDescriptor();
BeanProperty idProp = targetDesc.getIdProperty();
if (idProp != null) {
Object value = idProp.getValue(bean);
if (value == null) {
return false;
}
}
// all the unique properties are non-null
return true;
}
/**
* Return the type of the target.
*
* This is the class of the associated bean, or beans contained in a list,
* set or map.
*
*/
public Class> getTargetType() {
return targetType;
}
/**
* Return an extra clause to add to the query for loading or joining
* to this bean type.
*/
public String getExtraWhere() {
return extraWhere;
}
/**
* Return the elastic search doc for this embedded property.
*/
public String getDocStoreDoc() {
return docStoreDoc;
}
/**
* Determine if and how the associated bean is included in the doc store document.
*/
@Override
public void docStoreInclude(boolean includeByDefault, DocStructure docStructure) {
String embeddedDoc = getDocStoreDoc();
if (embeddedDoc == null) {
// not annotated so use include by default
// which is *ToOne included and *ToMany excluded
if (includeByDefault) {
docStoreIncludeByDefault(docStructure.doc());
}
} else {
// explicitly annotated to be included
if (embeddedDoc.isEmpty()) {
embeddedDoc = "*";
}
// add in a nested way
PathProperties embDoc = PathProperties.parse(embeddedDoc);
docStructure.addNested(name, embDoc);
}
}
/**
* Include the property in the document store by default.
*/
protected void docStoreIncludeByDefault(PathProperties pathProps) {
pathProps.addToPath(null, name);
}
@Override
public void docStoreMapping(DocMappingBuilder mapping, String prefix) {
if (mapping.includesPath(prefix, name)) {
String fullName = SplitName.add(prefix, name);
DocPropertyType type = isMany() ? DocPropertyType.LIST : DocPropertyType.OBJECT;
DocPropertyMapping nested = new DocPropertyMapping(name, type);
mapping.push(nested);
targetDescriptor.docStoreMapping(mapping, fullName);
mapping.pop();
if (!nested.getChildren().isEmpty()) {
mapping.add(nested);
}
}
}
/**
* Return true if this association is updateable.
*/
public boolean isUpdateable() {
return tableJoin.columns().length <= 0 || tableJoin.columns()[0].isUpdateable();
}
/**
* Return true if this association is insertable.
*/
public boolean isInsertable() {
return tableJoin.columns().length <= 0 || tableJoin.columns()[0].isInsertable();
}
/**
* Return the underlying BeanTable for this property.
*/
public BeanTable getBeanTable() {
return beanTable;
}
/**
* return the join to use for the bean.
*/
public TableJoin getTableJoin() {
return tableJoin;
}
/**
* Get the persist info.
*/
public BeanCascadeInfo getCascadeInfo() {
return cascadeInfo;
}
/**
* Build the list of imported property. Matches BeanProperty from the target
* descriptor back to local database columns in the TableJoin.
*/
protected ImportedId createImportedId(BeanPropertyAssoc> owner, BeanDescriptor> target, TableJoin join) {
BeanProperty idProp = target.getIdProperty();
BeanProperty[] others = target.propertiesBaseScalar();
if (descriptor.isRawSqlBased()) {
String dbColumn = owner.getDbColumn();
return new ImportedIdSimple(owner, dbColumn, null, idProp, 0);
}
TableJoinColumn[] cols = join.columns();
if (idProp == null) {
return null;
}
if (!idProp.isEmbedded()) {
// simple single scalar id
if (cols.length != 1) {
String msg = "No Imported Id column for [" + idProp + "] in table [" + join.getTable() + "]";
logger.error(msg);
return null;
} else {
BeanProperty[] idProps = {idProp};
return createImportedScalar(owner, cols[0], idProps, others);
}
} else {
// embedded id
BeanPropertyAssocOne> embProp = (BeanPropertyAssocOne>) idProp;
BeanProperty[] embBaseProps = embProp.getTargetDescriptor().propertiesBaseScalar();
ImportedIdSimple[] scalars = createImportedList(owner, cols, embBaseProps, others);
return new ImportedIdEmbedded(owner, embProp, scalars);
}
}
private ImportedIdSimple[] createImportedList(BeanPropertyAssoc> owner, TableJoinColumn[] cols, BeanProperty[] props, BeanProperty[] others) {
ArrayList list = new ArrayList<>(cols.length);
for (TableJoinColumn col : cols) {
list.add(createImportedScalar(owner, col, props, others));
}
return ImportedIdSimple.sort(list);
}
private ImportedIdSimple createImportedScalar(BeanPropertyAssoc> owner, TableJoinColumn col, BeanProperty[] props, BeanProperty[] others) {
String matchColumn = col.getForeignDbColumn();
String localColumn = col.getLocalDbColumn();
String localSqlFormula = col.getLocalSqlFormula();
for (int j = 0; j < props.length; j++) {
if (props[j].getDbColumn().equalsIgnoreCase(matchColumn)) {
return new ImportedIdSimple(owner, localColumn, localSqlFormula, props[j], j);
}
}
for (int j = 0; j < others.length; j++) {
if (others[j].getDbColumn().equalsIgnoreCase(matchColumn)) {
return new ImportedIdSimple(owner, localColumn, localSqlFormula, others[j], j + props.length);
}
}
String msg = "Error with the Join on [" + getFullBeanName()
+ "]. Could not find the local match for [" + matchColumn + "] "//in table["+searchTable+"]?"
+ " Perhaps an error in a @JoinColumn";
throw new PersistenceException(msg);
}
private List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy