mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#115 - Mapping - Add support for @ElementCollection enhancement
Initial support - simple List.
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
package io.ebeaninternal.api;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.util.StringHelper;
|
||||
@@ -124,11 +123,11 @@ public class LoadManyRequest extends LoadRequest {
|
||||
return loadContext.getBeanProperty();
|
||||
}
|
||||
|
||||
public SpiQuery<?> createQuery(EbeanServer server, int batchSize) {
|
||||
public SpiQuery<?> createQuery(SpiEbeanServer server, int batchSize) {
|
||||
|
||||
BeanPropertyAssocMany<?> many = getMany();
|
||||
|
||||
SpiQuery<?> query = (SpiQuery<?>) server.createQuery(many.getTargetType());
|
||||
SpiQuery<?> query = many.newQuery(server);
|
||||
String orderBy = many.getLazyFetchOrderBy();
|
||||
if (orderBy != null) {
|
||||
query.orderBy(orderBy);
|
||||
|
||||
@@ -4,13 +4,18 @@ import io.ebean.config.DbConstraintNaming;
|
||||
import io.ebean.config.dbplatform.DbPlatformType;
|
||||
import io.ebean.config.dbplatform.DbPlatformTypeMapping;
|
||||
import io.ebeaninternal.dbmigration.model.MColumn;
|
||||
import io.ebeaninternal.dbmigration.model.MCompoundForeignKey;
|
||||
import io.ebeaninternal.dbmigration.model.MTable;
|
||||
import io.ebeaninternal.dbmigration.model.ModelContainer;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.deploy.TableJoinColumn;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* The context used during DDL generation.
|
||||
@@ -187,4 +192,49 @@ public class ModelBuildContext {
|
||||
addTable(draftTable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a builder to add foreign keys.
|
||||
*/
|
||||
public FkeyBuilder fkeyBuilder(MTable destTable) {
|
||||
return new FkeyBuilder(this, destTable);
|
||||
}
|
||||
|
||||
public static class FkeyBuilder {
|
||||
|
||||
private final AtomicInteger count = new AtomicInteger();
|
||||
|
||||
private final ModelBuildContext ctx;
|
||||
|
||||
private final MTable destTable;
|
||||
|
||||
private final String tableName;
|
||||
|
||||
FkeyBuilder(ModelBuildContext ctx, MTable destTable) {
|
||||
this.ctx = ctx;
|
||||
this.destTable = destTable;
|
||||
this.tableName = destTable.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a foreign key based on the table join.
|
||||
*/
|
||||
public FkeyBuilder addForeignKey(BeanDescriptor<?> desc, TableJoin tableJoin, boolean direction) {
|
||||
|
||||
String baseTable = ctx.normaliseTable(desc.getBaseTable());
|
||||
String fkName = ctx.foreignKeyConstraintName(tableName, baseTable, count.incrementAndGet());
|
||||
String fkIndex = ctx.foreignKeyIndexName(tableName, baseTable, count.get());
|
||||
|
||||
MCompoundForeignKey foreignKey = new MCompoundForeignKey(fkName, desc.getBaseTable(), fkIndex);
|
||||
|
||||
for (TableJoinColumn column : tableJoin.columns()) {
|
||||
String localCol = direction ? column.getForeignDbColumn() : column.getLocalDbColumn();
|
||||
String refCol = !direction ? column.getForeignDbColumn() : column.getLocalDbColumn();
|
||||
foreignKey.addColumnPair(localCol, refCol);
|
||||
}
|
||||
|
||||
destTable.addForeignKey(foreignKey);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package io.ebeaninternal.dbmigration.model.build;
|
||||
|
||||
import io.ebeaninternal.dbmigration.model.MTable;
|
||||
import io.ebeaninternal.dbmigration.model.visitor.VisitAllUsing;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.BeanTable;
|
||||
|
||||
/**
|
||||
* Add the element collection table to the model.
|
||||
*/
|
||||
public class ModelBuildElementTable {
|
||||
|
||||
/**
|
||||
* Build and add the MTable model for the ElementCollection property.
|
||||
*/
|
||||
public static void build(ModelBuildContext ctx, BeanPropertyAssocMany<?> manyProp) {
|
||||
|
||||
BeanTable beanTable = manyProp.getBeanTable();
|
||||
|
||||
BeanDescriptor<?> targetDescriptor = manyProp.getTargetDescriptor();
|
||||
|
||||
MTable table = new MTable(beanTable.getBaseTable());
|
||||
|
||||
VisitAllUsing.visitOne(targetDescriptor, new ModelBuildPropertyVisitor(ctx, table, targetDescriptor));
|
||||
|
||||
ctx.fkeyBuilder(table)
|
||||
.addForeignKey(manyProp.getBeanDescriptor(), manyProp.getTableJoin(), true);
|
||||
|
||||
ctx.addTable(table);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+5
-28
@@ -1,7 +1,6 @@
|
||||
package io.ebeaninternal.dbmigration.model.build;
|
||||
|
||||
import io.ebeaninternal.dbmigration.model.MColumn;
|
||||
import io.ebeaninternal.dbmigration.model.MCompoundForeignKey;
|
||||
import io.ebeaninternal.dbmigration.model.MTable;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
@@ -12,7 +11,7 @@ import io.ebeaninternal.server.deploy.TableJoinColumn;
|
||||
/**
|
||||
* Add the intersection table to the model.
|
||||
*/
|
||||
public class ModelBuildIntersectionTable {
|
||||
class ModelBuildIntersectionTable {
|
||||
|
||||
private final ModelBuildContext ctx;
|
||||
|
||||
@@ -22,9 +21,7 @@ public class ModelBuildIntersectionTable {
|
||||
|
||||
private MTable intersectionTable;
|
||||
|
||||
private int countForeignKey;
|
||||
|
||||
public ModelBuildIntersectionTable(ModelBuildContext ctx, BeanPropertyAssocMany<?> manyProp) {
|
||||
ModelBuildIntersectionTable(ModelBuildContext ctx, BeanPropertyAssocMany<?> manyProp) {
|
||||
this.ctx = ctx;
|
||||
this.manyProp = manyProp;
|
||||
this.intersectionTableJoin = manyProp.getIntersectionTableJoin();
|
||||
@@ -51,33 +48,13 @@ public class ModelBuildIntersectionTable {
|
||||
|
||||
private void buildFkConstraints() {
|
||||
|
||||
BeanDescriptor<?> localDesc = manyProp.getBeanDescriptor();
|
||||
buildFkConstraints(localDesc, intersectionTableJoin.columns(), true);
|
||||
|
||||
BeanDescriptor<?> targetDesc = manyProp.getTargetDescriptor();
|
||||
buildFkConstraints(targetDesc, tableJoin.columns(), false);
|
||||
ctx.fkeyBuilder(intersectionTable)
|
||||
.addForeignKey(manyProp.getBeanDescriptor(), intersectionTableJoin, true)
|
||||
.addForeignKey(manyProp.getTargetDescriptor(), tableJoin, false);
|
||||
|
||||
intersectionTable.checkDuplicateForeignKeys();
|
||||
}
|
||||
|
||||
|
||||
private void buildFkConstraints(BeanDescriptor<?> desc, TableJoinColumn[] columns, boolean direction) {
|
||||
|
||||
String tableName = intersectionTableJoin.getTable();
|
||||
String baseTable = ctx.normaliseTable(desc.getBaseTable());
|
||||
String fkName = ctx.foreignKeyConstraintName(tableName, baseTable, ++countForeignKey);
|
||||
String fkIndex = ctx.foreignKeyIndexName(tableName, baseTable, countForeignKey);
|
||||
|
||||
MCompoundForeignKey foreignKey = new MCompoundForeignKey(fkName, desc.getBaseTable(), fkIndex);
|
||||
intersectionTable.addForeignKey(foreignKey);
|
||||
|
||||
for (TableJoinColumn column : columns) {
|
||||
String localCol = direction ? column.getForeignDbColumn() : column.getLocalDbColumn();
|
||||
String refCol = !direction ? column.getForeignDbColumn() : column.getLocalDbColumn();
|
||||
foreignKey.addColumnPair(localCol, refCol);
|
||||
}
|
||||
}
|
||||
|
||||
private MTable createTable() {
|
||||
|
||||
BeanDescriptor<?> localDesc = manyProp.getBeanDescriptor();
|
||||
|
||||
+15
-18
@@ -123,27 +123,24 @@ public class ModelBuildPropertyVisitor extends BaseTablePropertyVisitor {
|
||||
|
||||
@Override
|
||||
public void visitMany(BeanPropertyAssocMany<?> p) {
|
||||
if (p.hasJoinTable()) {
|
||||
if (p.getMappedBy() == null) {
|
||||
// only create on other 'owning' side
|
||||
if (p.hasJoinTable() && p.getMappedBy() == null) {
|
||||
// only create on other 'owning' side
|
||||
|
||||
//TableJoin intersectionTableJoin = p.getIntersectionTableJoin();
|
||||
// check if the intersection table has already been created
|
||||
|
||||
// build the create table and fkey constraints
|
||||
// putting the DDL into ctx for later output as we are
|
||||
// in the middle of rendering the create table DDL
|
||||
MTable intersectionTable = new ModelBuildIntersectionTable(ctx, p).build();
|
||||
if (p.isO2mJoinTable()) {
|
||||
intersectionTable.clearForeignKeyIndexes();
|
||||
Collection<MColumn> cols = intersectionTable.allColumns();
|
||||
if (cols.size() == 2) {
|
||||
// always the second column that we put the unique constraint on
|
||||
MColumn col = new ArrayList<>(cols).get(1);
|
||||
col.setUnique(determineUniqueConstraintName(col.getName()));
|
||||
}
|
||||
// build the create table and fkey constraints
|
||||
// putting the DDL into ctx for later output as we are
|
||||
// in the middle of rendering the create table DDL
|
||||
MTable intersectionTable = new ModelBuildIntersectionTable(ctx, p).build();
|
||||
if (p.isO2mJoinTable()) {
|
||||
intersectionTable.clearForeignKeyIndexes();
|
||||
Collection<MColumn> cols = intersectionTable.allColumns();
|
||||
if (cols.size() == 2) {
|
||||
// always the second column that we put the unique constraint on
|
||||
MColumn col = new ArrayList<>(cols).get(1);
|
||||
col.setUnique(determineUniqueConstraintName(col.getName()));
|
||||
}
|
||||
}
|
||||
} else if (p.isElementCollection()) {
|
||||
ModelBuildElementTable.build(ctx, p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,11 @@ package io.ebeaninternal.server.autotune.service;
|
||||
|
||||
import io.ebean.bean.NodeUsageCollector;
|
||||
import io.ebean.text.PathProperties;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebean.util.SplitName;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -56,7 +56,7 @@ public class ProfileOriginNodeUsage {
|
||||
}
|
||||
|
||||
for (String propName : aggregateUsed) {
|
||||
BeanProperty beanProp = desc.getBeanPropertyFromPath(propName);
|
||||
BeanProperty beanProp = desc.findPropertyFromPath(propName);
|
||||
if (beanProp == null) {
|
||||
logger.warn("AutoTune: Can't find property[" + propName + "] for " + desc.getName());
|
||||
|
||||
|
||||
@@ -79,6 +79,8 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
|
||||
private List<T> cacheBeans;
|
||||
|
||||
private BeanPropertyAssocMany<?> manyProperty;
|
||||
|
||||
/**
|
||||
* Create the InternalQueryRequest.
|
||||
*/
|
||||
@@ -496,11 +498,18 @@ public final class OrmQueryRequest<T> extends BeanRequest implements SpiOrmQuery
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the many property that is fetched in the query or null if there is
|
||||
* not one.
|
||||
* Determine and return the ToMany property that is included in the query.
|
||||
*/
|
||||
public BeanPropertyAssocMany<?> determineMany() {
|
||||
manyProperty = beanDescriptor.getManyProperty(query);
|
||||
return manyProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the many property that is fetched in the query or null if there is not one.
|
||||
*/
|
||||
public BeanPropertyAssocMany<?> getManyProperty() {
|
||||
return beanDescriptor.getManyProperty(query);
|
||||
return manyProperty;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,11 +1,47 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
|
||||
abstract class BaseCollectionHelp<T> implements BeanCollectionHelp<T> {
|
||||
|
||||
final BeanPropertyAssocMany<T> many;
|
||||
final BeanDescriptor<T> targetDescriptor;
|
||||
final String propertyName;
|
||||
|
||||
BeanCollectionLoader loader;
|
||||
|
||||
BaseCollectionHelp(BeanPropertyAssocMany<T> many) {
|
||||
this.many = many;
|
||||
this.targetDescriptor = many.getTargetDescriptor();
|
||||
this.propertyName = many.getName();
|
||||
}
|
||||
|
||||
BaseCollectionHelp() {
|
||||
this.many = null;
|
||||
this.targetDescriptor = null;
|
||||
this.propertyName = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoader(BeanCollectionLoader loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
if (withCheck) {
|
||||
collection.internalAddWithCheck(bean);
|
||||
} else {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection underlying(Object value) {
|
||||
if (value instanceof BeanCollection) {
|
||||
@@ -14,4 +50,18 @@ abstract class BaseCollectionHelp<T> implements BeanCollectionHelp<T> {
|
||||
return (Collection)value;
|
||||
}
|
||||
}
|
||||
|
||||
void jsonWriteCollection(SpiJsonWriter ctx, String name, Collection<?> list) throws IOException {
|
||||
if (!list.isEmpty() || ctx.isIncludeEmpty()) {
|
||||
ctx.beginAssocMany(name);
|
||||
for (Object bean : list) {
|
||||
jsonWriteElement(ctx, bean);
|
||||
}
|
||||
ctx.endAssocMany();
|
||||
}
|
||||
}
|
||||
|
||||
void jsonWriteElement(SpiJsonWriter ctx, Object bean) throws IOException {
|
||||
targetDescriptor.jsonWrite(ctx, (EntityBean) bean);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.query.CQueryCollectionAdd;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -15,7 +16,7 @@ import java.util.Collection;
|
||||
/**
|
||||
* Helper functions for performing tasks on Lists Sets or Maps.
|
||||
*/
|
||||
public interface BeanCollectionHelp<T> {
|
||||
public interface BeanCollectionHelp<T> extends CQueryCollectionAdd<T> {
|
||||
|
||||
/**
|
||||
* Set the EbeanServer that owns the configuration.
|
||||
|
||||
@@ -20,10 +20,12 @@ public class BeanCollectionHelpFactory {
|
||||
*/
|
||||
public static <T> BeanCollectionHelp<T> create(BeanPropertyAssocMany<T> manyProperty) {
|
||||
|
||||
boolean elementCollection = manyProperty.isElementCollection();
|
||||
|
||||
ManyType manyType = manyProperty.getManyType();
|
||||
switch (manyType) {
|
||||
case LIST:
|
||||
return new BeanListHelp<>(manyProperty);
|
||||
return elementCollection ? new BeanListHelpElement<>(manyProperty) : new BeanListHelp<>(manyProperty);
|
||||
case SET:
|
||||
return new BeanSetHelp<>(manyProperty);
|
||||
case MAP:
|
||||
@@ -35,9 +37,7 @@ public class BeanCollectionHelpFactory {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> BeanCollectionHelp<T> create(OrmQueryRequest<T> request) {
|
||||
|
||||
SpiQuery.Type manyType = request.getQuery().getType();
|
||||
public static <T> BeanCollectionHelp<T> create(SpiQuery.Type manyType, OrmQueryRequest<T> request) {
|
||||
|
||||
if (manyType == SpiQuery.Type.LIST) {
|
||||
return LIST_HELP;
|
||||
|
||||
@@ -62,7 +62,13 @@ import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.persist.DmlUtil;
|
||||
import io.ebeaninternal.server.query.CQueryPlan;
|
||||
import io.ebeaninternal.server.query.SqlTreeProperty;
|
||||
import io.ebeaninternal.server.query.ExtraJoin;
|
||||
import io.ebeaninternal.server.query.STreeProperty;
|
||||
import io.ebeaninternal.server.query.STreePropertyAssoc;
|
||||
import io.ebeaninternal.server.query.STreePropertyAssocMany;
|
||||
import io.ebeaninternal.server.query.STreePropertyAssocOne;
|
||||
import io.ebeaninternal.server.query.STreeType;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
@@ -100,7 +106,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
/**
|
||||
* Describes Beans including their deployment information.
|
||||
*/
|
||||
public class BeanDescriptor<T> implements BeanType<T> {
|
||||
public class BeanDescriptor<T> implements BeanType<T>, STreeType {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanDescriptor.class);
|
||||
|
||||
@@ -114,7 +120,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
|
||||
private final ConcurrentHashMap<String, ElComparator<T>> comparatorCache = new ConcurrentHashMap<>();
|
||||
|
||||
private final ConcurrentHashMap<String, SqlTreeProperty> dynamicProperty = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, STreeProperty> dynamicProperty = new ConcurrentHashMap<>();
|
||||
|
||||
private final Map<String, SpiRawSql> namedRawSql;
|
||||
|
||||
@@ -239,7 +245,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
private final BeanDescriptorMap owner;
|
||||
|
||||
|
||||
private final String[] properties;
|
||||
final String[] properties;
|
||||
|
||||
/**
|
||||
* Intercept pre post on insert,update, and delete .
|
||||
@@ -601,7 +607,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
/**
|
||||
* Create an entity bean that is used as a prototype/factory to create new instances.
|
||||
*/
|
||||
private EntityBean createPrototypeEntityBean(Class<T> beanType) {
|
||||
protected EntityBean createPrototypeEntityBean(Class<T> beanType) {
|
||||
if (Modifier.isAbstract(beanType.getModifiers())) {
|
||||
return null;
|
||||
}
|
||||
@@ -662,21 +668,19 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
* These properties need to be initialised prior to the association properties
|
||||
* as they are used to get the imported and exported properties.
|
||||
* </p>
|
||||
*
|
||||
* @param withHistoryTables map populated if @History is supported on this entity bean
|
||||
*/
|
||||
public void initialiseId(Map<String, String> withHistoryTables, Map<String, String> draftTables) {
|
||||
public void initialiseId(BeanDescriptorInitContext initContext) {
|
||||
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("BeanDescriptor initialise " + fullName);
|
||||
}
|
||||
|
||||
if (draftable) {
|
||||
draftTables.put(baseTable, draftTable);
|
||||
initContext.addDraft(baseTable, draftTable);
|
||||
}
|
||||
if (historySupport) {
|
||||
// add mapping (used to swap out baseTable for asOf queries)
|
||||
withHistoryTables.put(baseTable, baseTableAsOf);
|
||||
initContext.addHistory(baseTable, baseTableAsOf);
|
||||
}
|
||||
|
||||
if (inheritInfo != null) {
|
||||
@@ -686,28 +690,24 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
if (isEmbedded()) {
|
||||
// initialise all the properties
|
||||
for (BeanProperty prop : propertiesAll()) {
|
||||
prop.initialise();
|
||||
prop.initialise(initContext);
|
||||
}
|
||||
} else {
|
||||
// initialise just the Id properties
|
||||
if (idProperty != null) {
|
||||
idProperty.initialise();
|
||||
idProperty.initialise(initContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the exported and imported parts for associated properties.
|
||||
*
|
||||
* @param asOfTableMap the map of base tables to associated 'with history' tables
|
||||
* @param asOfViewSuffix the suffix added to the table name to derive the 'with history' view name
|
||||
* @param draftTableMap the map of base tables to associated 'draft' tables.
|
||||
*/
|
||||
public void initialiseOther(Map<String, String> asOfTableMap, String asOfViewSuffix, Map<String, String> draftTableMap) {
|
||||
public void initialiseOther(BeanDescriptorInitContext initContext) {
|
||||
|
||||
for (BeanPropertyAssocMany<?> aPropertiesManyToMany1 : propertiesManyToMany) {
|
||||
for (BeanPropertyAssocMany<?> many : propertiesManyToMany) {
|
||||
// register associated draft table for M2M intersection
|
||||
aPropertiesManyToMany1.registerDraftIntersectionTable(draftTableMap);
|
||||
many.registerDraftIntersectionTable(initContext);
|
||||
}
|
||||
|
||||
if (historySupport) {
|
||||
@@ -717,8 +717,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
// register associated history table for M2M intersection
|
||||
if (!aPropertiesManyToMany.isExcludedFromHistory()) {
|
||||
TableJoin intersectionTableJoin = aPropertiesManyToMany.getIntersectionTableJoin();
|
||||
String intersectionTableName = intersectionTableJoin.getTable();
|
||||
asOfTableMap.put(intersectionTableName, intersectionTableName + asOfViewSuffix);
|
||||
initContext.addHistoryIntersection(intersectionTableJoin.getTable());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -727,14 +726,14 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
// initialise all the non-id properties
|
||||
for (BeanProperty prop : propertiesAll()) {
|
||||
if (!prop.isId()) {
|
||||
prop.initialise();
|
||||
prop.initialise(initContext);
|
||||
}
|
||||
prop.registerColumn(this, null);
|
||||
}
|
||||
}
|
||||
|
||||
if (unidirectional != null) {
|
||||
unidirectional.initialise();
|
||||
unidirectional.initialise(initContext);
|
||||
}
|
||||
|
||||
idBinder.initialise();
|
||||
@@ -799,7 +798,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
if (propName == null) {
|
||||
return;
|
||||
}
|
||||
props[i] = findBeanProperty(propName);
|
||||
props[i] = findProperty(propName);
|
||||
}
|
||||
if (props.length == 1) {
|
||||
for (BeanProperty[] inserted : propertiesUnique) {
|
||||
@@ -1639,6 +1638,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
/**
|
||||
* Execute the postLoad if a BeanPostLoad exists for this bean.
|
||||
*/
|
||||
@Override
|
||||
public void postLoad(Object bean) {
|
||||
if (beanPostLoad != null) {
|
||||
beanPostLoad.postLoad(bean);
|
||||
@@ -1787,6 +1787,11 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
return idBinder instanceof IdBinderSimple;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasId() {
|
||||
return idProperty != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this type has a simple Id and the platform supports mutli-value binding.
|
||||
*/
|
||||
@@ -1951,7 +1956,8 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
* Return the bean property traversing the object graph and taking into
|
||||
* account inheritance.
|
||||
*/
|
||||
public BeanProperty getBeanPropertyFromPath(String path) {
|
||||
@Override
|
||||
public BeanProperty findPropertyFromPath(String path) {
|
||||
BeanDescriptor<?> other = this;
|
||||
while (true) {
|
||||
|
||||
@@ -1983,7 +1989,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
}
|
||||
String[] splitBegin = SplitName.splitBegin(path);
|
||||
|
||||
BeanProperty beanProperty = result.findBeanProperty(splitBegin[0]);
|
||||
BeanProperty beanProperty = result.findProperty(splitBegin[0]);
|
||||
if (beanProperty instanceof BeanPropertyAssoc<?>) {
|
||||
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) beanProperty;
|
||||
path = splitBegin[1];
|
||||
@@ -2224,7 +2230,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
|
||||
@Override
|
||||
public Property getProperty(String propName) {
|
||||
return findBeanProperty(propName);
|
||||
return findProperty(propName);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2458,7 +2464,7 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
/**
|
||||
* Return a 'dynamic property' used to read a formula.
|
||||
*/
|
||||
private SqlTreeProperty findSqlTreeFormula(String formulaExpression) {
|
||||
private STreeProperty findSqlTreeFormula(String formulaExpression) {
|
||||
|
||||
return dynamicProperty.computeIfAbsent(formulaExpression, (formula) -> new FormulaPropertyPath(this, formula).build());
|
||||
}
|
||||
@@ -2468,7 +2474,8 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
*
|
||||
* The property can be a dynamic formula or a well known bean property.
|
||||
*/
|
||||
public SqlTreeProperty findSqlTreeProperty(String propName) {
|
||||
@Override
|
||||
public STreeProperty findPropertyWithDynamic(String propName) {
|
||||
if (propName.indexOf('(') > -1) {
|
||||
return findSqlTreeFormula(propName);
|
||||
}
|
||||
@@ -2482,7 +2489,8 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
* inheritance tree (not up).
|
||||
* </p>
|
||||
*/
|
||||
public BeanProperty findBeanProperty(String propName) {
|
||||
@Override
|
||||
public BeanProperty findProperty(String propName) {
|
||||
int basePos = propName.indexOf('.');
|
||||
if (basePos > -1) {
|
||||
// embedded property
|
||||
@@ -2540,6 +2548,10 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
return autoTunable;
|
||||
}
|
||||
|
||||
public boolean isElementType() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Inheritance mapping information. This will be null if this type
|
||||
* of bean is not involved in any ORM inheritance mapping.
|
||||
@@ -2809,6 +2821,41 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
return draftableElement;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmbeddedPath(String propertyPath) {
|
||||
ElPropertyDeploy elProp = getElPropertyDeploy(propertyPath);
|
||||
if (elProp == null) {
|
||||
throw new PersistenceException("Invalid path " + propertyPath + " from " + getFullName());
|
||||
}
|
||||
return elProp.getBeanProperty().isEmbedded();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExtraJoin extraJoin(String propertyPath) {
|
||||
|
||||
ElPropertyValue elGetValue = getElGetValue(propertyPath);
|
||||
if (elGetValue != null) {
|
||||
BeanProperty beanProperty = elGetValue.getBeanProperty();
|
||||
if (beanProperty instanceof BeanPropertyAssoc<?>) {
|
||||
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) beanProperty;
|
||||
if (!assocProp.isEmbedded()) {
|
||||
return new ExtraJoin(assocProp, elGetValue.containsMany());
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inheritanceLoad(SqlBeanLoad sqlBeanLoad, STreeProperty property, DbReadContext ctx) {
|
||||
BeanProperty p = getBeanProperty(property.getName());
|
||||
if (p != null) {
|
||||
p.load(sqlBeanLoad);
|
||||
} else {
|
||||
property.loadIgnore(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
public void setUnmappedJson(EntityBean bean, Map<String, Object> unmappedProperties) {
|
||||
if (unmappedJson != null) {
|
||||
unmappedJson.setValueIntercept(bean, unmappedProperties);
|
||||
@@ -2933,16 +2980,6 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
return selectLastInsertedId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the TableJoins.
|
||||
* <p>
|
||||
* For properties mapped to secondary tables rather than the base table.
|
||||
* </p>
|
||||
*/
|
||||
public TableJoin[] tableJoins() {
|
||||
return derivedTableJoins;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<? extends Property> allProperties() {
|
||||
return propertiesAll();
|
||||
@@ -3131,6 +3168,26 @@ public class BeanDescriptor<T> implements BeanType<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public STreeProperty[] propsBaseScalar() {
|
||||
return propertiesBaseScalar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public STreePropertyAssoc[] propsEmbedded() {
|
||||
return propertiesEmbedded;
|
||||
}
|
||||
|
||||
@Override
|
||||
public STreePropertyAssocOne[] propsOne() {
|
||||
return propertiesOne;
|
||||
}
|
||||
|
||||
@Override
|
||||
public STreePropertyAssocMany[] propsMany() {
|
||||
return propertiesMany;
|
||||
}
|
||||
|
||||
/**
|
||||
* All the BeanPropertyAssocOne that are not embedded. These are effectively
|
||||
* joined beans. For ManyToOne and OneToOne associations.
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
|
||||
/**
|
||||
* Bean descriptor used with ElementCollection (where we don't have a mapped type/class).
|
||||
*/
|
||||
public class BeanDescriptorElement<T> extends BeanDescriptor<T> {
|
||||
|
||||
public BeanDescriptorElement(BeanDescriptorMap owner, DeployBeanDescriptor<T> deploy) {
|
||||
super(owner, deploy);
|
||||
}
|
||||
|
||||
public boolean isElementType() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EntityBean createPrototypeEntityBean(Class<T> beanType) {
|
||||
return new ElementEntityBean(properties);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
class BeanDescriptorInitContext {
|
||||
|
||||
private final Map<String, String> withHistoryTables;
|
||||
private final Map<String, String> draftTables;
|
||||
private final String asOfViewSuffix;
|
||||
|
||||
BeanDescriptorInitContext(Map<String, String> withHistoryTables, Map<String, String> draftTables, String asOfViewSuffix) {
|
||||
this.withHistoryTables = withHistoryTables;
|
||||
this.draftTables = draftTables;
|
||||
this.asOfViewSuffix = asOfViewSuffix;
|
||||
}
|
||||
|
||||
void addDraft(String baseTable, String draftTable) {
|
||||
draftTables.put(baseTable, draftTable);
|
||||
}
|
||||
|
||||
void addHistory(String baseTable, String baseTableAsOf) {
|
||||
withHistoryTables.put(baseTable, baseTableAsOf);
|
||||
}
|
||||
|
||||
void addHistoryIntersection(String intersectionTableName) {
|
||||
withHistoryTables.put(intersectionTableName, intersectionTableName + asOfViewSuffix);
|
||||
}
|
||||
|
||||
void addDraftIntersection(String intersectionPublishTable, String intersectionDraftTable) {
|
||||
draftTables.put(intersectionPublishTable, intersectionDraftTable);
|
||||
}
|
||||
}
|
||||
@@ -184,8 +184,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
private final BeanLifecycleAdapterFactory beanLifecycleAdapterFactory;
|
||||
|
||||
private final boolean eagerFetchLobs;
|
||||
|
||||
private final String asOfViewSuffix;
|
||||
|
||||
/**
|
||||
@@ -215,12 +213,11 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
this.databasePlatform = serverConfig.getDatabasePlatform();
|
||||
this.multiValueBind = config.getMultiValueBind();
|
||||
this.idBinderFactory = new IdBinderFactory(databasePlatform.isIdInExpandedForm(), multiValueBind);
|
||||
this.eagerFetchLobs = serverConfig.isEagerFetchLobs();
|
||||
this.queryPlanTTLSeconds = serverConfig.getQueryPlanTTLSeconds();
|
||||
|
||||
this.asOfViewSuffix = getAsOfViewSuffix(databasePlatform, serverConfig);
|
||||
String versionsBetweenSuffix = getVersionsBetweenSuffix(databasePlatform, serverConfig);
|
||||
this.readAnnotations = new ReadAnnotations(config.getGeneratedPropertyFactory(), asOfViewSuffix, versionsBetweenSuffix, serverConfig.isDisableL2Cache());
|
||||
this.readAnnotations = new ReadAnnotations(config.getGeneratedPropertyFactory(), asOfViewSuffix, versionsBetweenSuffix, serverConfig);
|
||||
this.bootupClasses = config.getBootupClasses();
|
||||
this.createProperties = config.getDeployCreateProperties();
|
||||
this.namingConvention = serverConfig.getNamingConvention();
|
||||
@@ -575,12 +572,14 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
// we can initialise them which sorts out circular
|
||||
// dependencies for OneToMany and ManyToOne etc
|
||||
|
||||
BeanDescriptorInitContext initContext = new BeanDescriptorInitContext(asOfTableMap, draftTableMap, asOfViewSuffix);
|
||||
|
||||
// PASS 1:
|
||||
// initialise the ID properties of all the beans
|
||||
// first (as they are needed to initialise the
|
||||
// associated properties in the second pass).
|
||||
for (BeanDescriptor<?> d : descMap.values()) {
|
||||
d.initialiseId(asOfTableMap, draftTableMap);
|
||||
d.initialiseId(initContext);
|
||||
}
|
||||
|
||||
// PASS 2:
|
||||
@@ -595,7 +594,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
// also look for intersection tables with
|
||||
// associated history support and register them
|
||||
// into the asOfTableMap
|
||||
d.initialiseOther(asOfTableMap, asOfViewSuffix, draftTableMap);
|
||||
d.initialiseOther(initContext);
|
||||
}
|
||||
|
||||
// PASS 4:
|
||||
@@ -656,6 +655,13 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
return beanTableMap.get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a BeanTable for an ElementCollection.
|
||||
*/
|
||||
public BeanTable getCollectionBeanTable(String fullTableName, Class<?> targetType) {
|
||||
return new BeanTable(this, fullTableName, targetType);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> BeanManager<T> getBeanManager(Class<T> entityType) {
|
||||
|
||||
@@ -1025,7 +1031,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
* </p>
|
||||
*/
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void makeUnidirectional(DeployBeanInfo<?> info, DeployBeanPropertyAssocMany<?> oneToMany) {
|
||||
private void makeUnidirectional(DeployBeanPropertyAssocMany<?> oneToMany) {
|
||||
|
||||
DeployBeanDescriptor<?> targetDesc = getTargetDescriptor(oneToMany);
|
||||
|
||||
@@ -1045,34 +1051,36 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
// mark this property as unidirectional
|
||||
oneToMany.setUnidirectional();
|
||||
|
||||
// create the 'shadow' unidirectional property
|
||||
// which is put on the target descriptor
|
||||
DeployBeanPropertyAssocOne<?> unidirectional = new DeployBeanPropertyAssocOne(targetDesc, owningType);
|
||||
unidirectional.setUndirectionalShadow();
|
||||
unidirectional.setNullable(false);
|
||||
unidirectional.setDbRead(true);
|
||||
unidirectional.setDbInsertable(true);
|
||||
unidirectional.setDbUpdateable(false);
|
||||
|
||||
targetDesc.setUnidirectional(unidirectional);
|
||||
|
||||
// specify table and table alias...
|
||||
BeanTable beanTable = getBeanTable(owningType);
|
||||
unidirectional.setBeanTable(beanTable);
|
||||
unidirectional.setName(beanTable.getBaseTable());
|
||||
|
||||
info.setBeanJoinType(unidirectional, true);
|
||||
|
||||
// define the TableJoin
|
||||
DeployTableJoin oneToManyJoin = oneToMany.getTableJoin();
|
||||
if (!oneToManyJoin.hasJoinColumns()) {
|
||||
throw new RuntimeException("No join columns");
|
||||
}
|
||||
createUnidirectional(targetDesc, owningType, beanTable, oneToManyJoin);
|
||||
}
|
||||
|
||||
// inverse of the oneToManyJoin
|
||||
DeployTableJoin unidirectionalJoin = unidirectional.getTableJoin();
|
||||
unidirectionalJoin.setColumns(oneToManyJoin.columns(), true);
|
||||
/**
|
||||
* Create and add a Unidirectional property (for ElementCollection) which maps to the foreign key.
|
||||
*/
|
||||
public <A> void createUnidirectional(DeployBeanDescriptor<?> targetDesc, Class<A> targetType, BeanTable beanTable, DeployTableJoin oneToManyJoin) {
|
||||
|
||||
// create the 'shadow' unidirectional property
|
||||
// which is put on the target descriptor
|
||||
DeployBeanPropertyAssocOne<A> unidirectional = new DeployBeanPropertyAssocOne<>(targetDesc, targetType);
|
||||
unidirectional.setUndirectionalShadow();
|
||||
unidirectional.setNullable(false);
|
||||
unidirectional.setDbRead(true);
|
||||
unidirectional.setDbInsertable(true);
|
||||
unidirectional.setDbUpdateable(false);
|
||||
unidirectional.setBeanTable(beanTable);
|
||||
unidirectional.setName(beanTable.getBaseTable());
|
||||
unidirectional.setJoinType(true);
|
||||
unidirectional.setJoinColumns(oneToManyJoin.columns(), true);
|
||||
|
||||
targetDesc.setUnidirectional(unidirectional);
|
||||
}
|
||||
|
||||
private void checkMappedByOneToOne(DeployBeanPropertyAssocOne<?> prop) {
|
||||
@@ -1137,6 +1145,10 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
*/
|
||||
private void checkMappedByOneToMany(DeployBeanInfo<?> info, DeployBeanPropertyAssocMany<?> prop) {
|
||||
|
||||
if (prop.isElementCollection()) {
|
||||
// skip mapping check
|
||||
return;
|
||||
}
|
||||
DeployBeanDescriptor<?> targetDesc = getTargetDescriptor(prop);
|
||||
|
||||
if (targetDesc.isDraftableElement()) {
|
||||
@@ -1159,7 +1171,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
if (!findMappedBy(prop)) {
|
||||
if (!prop.isO2mJoinTable()) {
|
||||
makeUnidirectional(info, prop);
|
||||
makeUnidirectional(prop);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -1311,7 +1323,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
|
||||
DeployBeanInfo<T> info = new DeployBeanInfo<>(deployUtil, desc);
|
||||
|
||||
readAnnotations.readInitial(info, eagerFetchLobs);
|
||||
readAnnotations.readInitial(info);
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -1636,6 +1648,20 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
|
||||
target.setPrimaryKeyJoin(inverseJoin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a DeployBeanDescriptor for an ElementCollection target.
|
||||
*/
|
||||
public <A> DeployBeanDescriptor<A> createDeployDescriptor(Class<A> targetType) {
|
||||
return new DeployBeanDescriptor<>(this, targetType, serverConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a BeanDescriptor for an ElementCollection target.
|
||||
*/
|
||||
public <A> BeanDescriptor<A> createElementDescriptor(DeployBeanDescriptor<A> elementDescriptor) {
|
||||
return new BeanDescriptorElement<>(this, elementDescriptor);
|
||||
}
|
||||
|
||||
public void visitMetrics(MetricVisitor visitor) {
|
||||
for (BeanDescriptor<?> desc : immutableDescriptorList) {
|
||||
desc.visitMetrics(visitor);
|
||||
|
||||
@@ -5,7 +5,6 @@ import io.ebean.Query;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.common.BeanList;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
@@ -17,48 +16,20 @@ import java.util.List;
|
||||
/**
|
||||
* Helper object for dealing with Lists.
|
||||
*/
|
||||
public final class BeanListHelp<T> extends BaseCollectionHelp<T> {
|
||||
public class BeanListHelp<T> extends BaseCollectionHelp<T> {
|
||||
|
||||
private final BeanPropertyAssocMany<T> many;
|
||||
private final BeanDescriptor<T> targetDescriptor;
|
||||
private final String propertyName;
|
||||
|
||||
private BeanCollectionLoader loader;
|
||||
|
||||
public BeanListHelp(BeanPropertyAssocMany<T> many) {
|
||||
this.many = many;
|
||||
this.targetDescriptor = many.getTargetDescriptor();
|
||||
this.propertyName = many.getName();
|
||||
BeanListHelp(BeanPropertyAssocMany<T> many) {
|
||||
super(many);
|
||||
}
|
||||
|
||||
public BeanListHelp() {
|
||||
this.many = null;
|
||||
this.targetDescriptor = null;
|
||||
this.propertyName = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoader(BeanCollectionLoader loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal add bypassing any modify listening.
|
||||
*/
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
if (withCheck) {
|
||||
collection.internalAddWithCheck(bean);
|
||||
} else {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
BeanListHelp() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) {
|
||||
|
||||
if (bc instanceof BeanList<?>) {
|
||||
|
||||
BeanList<?> bl = (BeanList<?>) bc;
|
||||
if (bl.getActualList() == null) {
|
||||
bl.setActualList(new ArrayList<>());
|
||||
@@ -144,13 +115,7 @@ public final class BeanListHelp<T> extends BaseCollectionHelp<T> {
|
||||
list = (List<?>) collection;
|
||||
}
|
||||
|
||||
if (!list.isEmpty() || ctx.isIncludeEmpty()) {
|
||||
ctx.beginAssocMany(name);
|
||||
for (Object aList : list) {
|
||||
targetDescriptor.jsonWrite(ctx, (EntityBean) aList);
|
||||
}
|
||||
ctx.endAssocMany();
|
||||
}
|
||||
jsonWriteCollection(ctx, name, list);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
|
||||
/**
|
||||
* Helper for element collection List.
|
||||
*/
|
||||
public class BeanListHelpElement<T> extends BeanListHelp<T> {
|
||||
|
||||
BeanListHelpElement(BeanPropertyAssocMany<T> many) {
|
||||
super(many);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
Object elementValue = bean._ebean_getField(0);
|
||||
if (withCheck) {
|
||||
collection.internalAddWithCheck(elementValue);
|
||||
} else {
|
||||
collection.internalAdd(elementValue);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) {
|
||||
return super.getBeanCollectionAdd(bc, mapKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
void jsonWriteElement(SpiJsonWriter ctx, Object element) {
|
||||
many.jsonWriteValue(ctx, element);
|
||||
//targetDescriptor.jsonWrite(ctx, (EntityBean) element);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import io.ebean.Query;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.common.BeanMap;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
@@ -24,12 +23,11 @@ public final class BeanMapHelp<T> extends BaseCollectionHelp<T> {
|
||||
private final BeanDescriptor<T> targetDescriptor;
|
||||
private final String propertyName;
|
||||
private final BeanProperty beanProperty;
|
||||
private BeanCollectionLoader loader;
|
||||
|
||||
/**
|
||||
* When created for a given query that will return a map.
|
||||
*/
|
||||
public BeanMapHelp(BeanDescriptor<T> targetDescriptor, String mapKey) {
|
||||
BeanMapHelp(BeanDescriptor<T> targetDescriptor, String mapKey) {
|
||||
this.targetDescriptor = targetDescriptor;
|
||||
this.beanProperty = targetDescriptor.getBeanProperty(mapKey);
|
||||
this.many = null;
|
||||
@@ -39,18 +37,13 @@ public final class BeanMapHelp<T> extends BaseCollectionHelp<T> {
|
||||
/**
|
||||
* When help is attached to a specific many property.
|
||||
*/
|
||||
public BeanMapHelp(BeanPropertyAssocMany<T> many) {
|
||||
BeanMapHelp(BeanPropertyAssocMany<T> many) {
|
||||
this.many = many;
|
||||
this.targetDescriptor = many.getTargetDescriptor();
|
||||
this.propertyName = many.getName();
|
||||
this.beanProperty = targetDescriptor.getBeanProperty(many.getMapKey());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoader(BeanCollectionLoader loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public BeanCollectionAdd getBeanCollectionAdd(Object bc, String mapKey) {
|
||||
|
||||
@@ -22,9 +22,9 @@ 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.query.SqlTreeProperty;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
@@ -56,7 +56,7 @@ import java.util.Set;
|
||||
* Description of a property of a bean. Includes its deployment information such
|
||||
* as database column mapping information.
|
||||
*/
|
||||
public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty {
|
||||
public class BeanProperty implements ElPropertyValue, Property, STreeProperty {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanProperty.class);
|
||||
|
||||
@@ -455,11 +455,10 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
* initialise variables that can't be done in construction due to recursive
|
||||
* issues.
|
||||
*/
|
||||
public void initialise() {
|
||||
public void initialise(BeanDescriptorInitContext initContext) {
|
||||
// do nothing for normal BeanProperty
|
||||
if (!isTransient && scalarType == null) {
|
||||
String msg = "No ScalarType assigned to " + descriptor.getFullName() + "." + getName();
|
||||
throw new RuntimeException(msg);
|
||||
throw new RuntimeException("No ScalarType assigned to " + descriptor.getFullName() + "." + getName());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,6 +504,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
/**
|
||||
* Return true if this property is based on a formula.
|
||||
*/
|
||||
@Override
|
||||
public boolean isFormula() {
|
||||
return formula;
|
||||
}
|
||||
@@ -530,6 +530,11 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
return descriptor.getEncryptKey(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEncryptKeyAsString() {
|
||||
return getEncryptKey().getStringValue();
|
||||
}
|
||||
|
||||
public String getDecryptProperty(String propertyName) {
|
||||
return dbEncryptFunction.getDecryptSql(propertyName);
|
||||
}
|
||||
@@ -552,6 +557,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
* 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);
|
||||
@@ -576,6 +582,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
return aggregation != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendSelect(DbSqlContext ctx, boolean subQuery) {
|
||||
|
||||
if (aggregation != null) {
|
||||
@@ -586,8 +593,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
} else if (!isTransient && !ignoreDraftOnlyProperty(ctx.isDraftQuery())) {
|
||||
|
||||
if (secondaryTableJoin != null) {
|
||||
String relativePrefix = ctx.getRelativePrefix(secondaryTableJoinPrefix);
|
||||
ctx.pushTableAlias(relativePrefix);
|
||||
ctx.pushTableAlias(ctx.getRelativePrefix(secondaryTableJoinPrefix));
|
||||
}
|
||||
|
||||
if (dbEncrypted) {
|
||||
@@ -614,14 +620,17 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
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);
|
||||
@@ -808,13 +817,6 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
setValue(bean, cacheData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property value from a compound value type.
|
||||
*/
|
||||
public Object getValueObject(Object bean) {
|
||||
throw new RuntimeException("Expected to be called only on BeanPropertyCompoundScalar");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getVal(Object bean) {
|
||||
return getValue((EntityBean) bean);
|
||||
@@ -1368,6 +1370,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
/**
|
||||
* Return true if this is included in the unique id.
|
||||
*/
|
||||
@Override
|
||||
public boolean isId() {
|
||||
return id;
|
||||
}
|
||||
@@ -1376,6 +1379,7 @@ public class BeanProperty implements ElPropertyValue, Property, SqlTreeProperty
|
||||
* 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;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
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;
|
||||
@@ -15,7 +16,10 @@ 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;
|
||||
@@ -30,7 +34,7 @@ import java.util.List;
|
||||
/**
|
||||
* Abstract base for properties mapped to an associated bean, list, set or map.
|
||||
*/
|
||||
public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
public abstract class BeanPropertyAssoc<T> extends BeanProperty implements STreePropertyAssoc {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanPropertyAssoc.class);
|
||||
|
||||
@@ -101,15 +105,18 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
* Initialise post construction.
|
||||
*/
|
||||
@Override
|
||||
public void initialise() {
|
||||
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();
|
||||
}
|
||||
@@ -142,6 +149,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@@ -149,6 +157,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
@@ -186,6 +195,29 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
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<T> 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.
|
||||
*/
|
||||
@@ -325,6 +357,13 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
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.
|
||||
*/
|
||||
@@ -440,10 +479,6 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
}
|
||||
}
|
||||
|
||||
EbeanServer server() {
|
||||
return getBeanDescriptor().getEbeanServer();
|
||||
}
|
||||
|
||||
void bindParentIds(DefaultSqlUpdate delete, List<Object> parentIds) {
|
||||
|
||||
if (isExportedSimple()) {
|
||||
@@ -492,4 +527,26 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
private boolean isExportedSimple() {
|
||||
return exportedProperties.length == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and return the exported property matching to this property.
|
||||
*/
|
||||
ExportedProperty findMatch(boolean embedded, BeanProperty prop, String matchColumn, TableJoin tableJoin) {
|
||||
|
||||
String searchTable = tableJoin.getTable();
|
||||
|
||||
for (TableJoinColumn column : tableJoin.columns()) {
|
||||
String matchTo = column.getLocalDbColumn();
|
||||
|
||||
if (matchColumn.equalsIgnoreCase(matchTo)) {
|
||||
String foreignCol = column.getForeignDbColumn();
|
||||
return new ExportedProperty(embedded, foreignCol, prop);
|
||||
}
|
||||
}
|
||||
|
||||
String msg = "Error with the Join on [" + getFullBeanName()
|
||||
+ "]. Could not find the matching foreign key for [" + matchColumn + "] in table[" + searchTable + "]?"
|
||||
+ " Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?";
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.EbeanServer;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
@@ -12,11 +10,11 @@ import io.ebean.bean.EntityBean;
|
||||
import io.ebean.text.PathProperties;
|
||||
import io.ebeaninternal.api.SpiExpressionRequest;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.deploy.id.ImportedId;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.query.STreePropertyAssocMany;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
@@ -35,7 +33,7 @@ import java.util.Map;
|
||||
/**
|
||||
* Property mapped to a List Set or Map.
|
||||
*/
|
||||
public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> implements STreePropertyAssocMany {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(BeanPropertyAssocMany.class);
|
||||
|
||||
@@ -51,7 +49,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
/**
|
||||
* For ManyToMany this is the Inverse join used to build reference queries.
|
||||
*/
|
||||
private final TableJoin inverseJoin;
|
||||
final TableJoin inverseJoin;
|
||||
|
||||
/**
|
||||
* Flag to indicate that this is a unidirectional relationship.
|
||||
@@ -70,6 +68,8 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
*/
|
||||
private final boolean manyToMany;
|
||||
|
||||
private final boolean elementCollection;
|
||||
|
||||
/**
|
||||
* Order by used when fetch joining the associated many.
|
||||
*/
|
||||
@@ -91,8 +91,6 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
private BeanProperty mapKeyProperty;
|
||||
|
||||
private String exportedPropertyBindProto = "?";
|
||||
|
||||
/**
|
||||
* Property on the 'child' bean that links back to the 'master'.
|
||||
*/
|
||||
@@ -106,9 +104,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
private ImportedId importedId;
|
||||
|
||||
private String deleteByParentIdSql;
|
||||
|
||||
private String deleteByParentIdInSql;
|
||||
private BeanPropertyAssocManySqlHelp<T> sqlHelp;
|
||||
|
||||
/**
|
||||
* Create this property.
|
||||
@@ -119,6 +115,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
this.o2mJoinTable = deploy.isO2mJoinTable();
|
||||
this.hasOrderColumn = deploy.hasOrderColumn();
|
||||
this.manyToMany = deploy.isManyToMany();
|
||||
this.elementCollection = deploy.isElementCollection();
|
||||
this.manyType = deploy.getManyType();
|
||||
this.mapKey = deploy.getMapKey();
|
||||
this.fetchOrderBy = deploy.getFetchOrderBy();
|
||||
@@ -136,13 +133,15 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise() {
|
||||
super.initialise();
|
||||
public void initialise(BeanDescriptorInitContext initContext) {
|
||||
super.initialise(initContext);
|
||||
initialiseAssocMany();
|
||||
}
|
||||
|
||||
private void initialiseAssocMany() {
|
||||
if (!isTransient) {
|
||||
this.help = BeanCollectionHelpFactory.create(this);
|
||||
|
||||
if (hasJoinTable()) {
|
||||
if (hasJoinTable() || elementCollection) {
|
||||
importedId = createImportedId(this, targetDescriptor, tableJoin);
|
||||
|
||||
} else {
|
||||
@@ -159,37 +158,21 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
exportedProperties = createExported();
|
||||
this.sqlHelp = new BeanPropertyAssocManySqlHelp<>(this, exportedProperties);
|
||||
|
||||
if (exportedProperties.length > 0) {
|
||||
embeddedExportedProperties = exportedProperties[0].isEmbedded();
|
||||
exportedPropertyBindProto = deriveExportedPropertyBindProto();
|
||||
|
||||
if (fetchOrderBy != null) {
|
||||
// derive lazyFetchOrderBy
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
// these fk columns are either on the intersection (int_) or base table (t0)
|
||||
String fkTableAlias = hasJoinTable() ? "int_" : "t0";
|
||||
sb.append(fkTableAlias).append(".").append(exportedProperties[i].getForeignDbColumn());
|
||||
}
|
||||
sb.append(", ").append(fetchOrderBy);
|
||||
lazyFetchOrderBy = sb.toString().trim();
|
||||
lazyFetchOrderBy = sqlHelp.lazyFetchOrderBy(fetchOrderBy);
|
||||
}
|
||||
}
|
||||
|
||||
String delStmt;
|
||||
if (hasJoinTable()) {
|
||||
delStmt = "delete from " + inverseJoin.getTable() + " where ";
|
||||
} else {
|
||||
delStmt = "delete from " + targetDescriptor.getBaseTable() + " where ";
|
||||
}
|
||||
deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false, "");
|
||||
deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true, "");
|
||||
}
|
||||
}
|
||||
|
||||
String targetTable() {
|
||||
return targetDescriptor.getBaseTable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise after the target bean descriptors have been all set.
|
||||
*/
|
||||
@@ -295,44 +278,23 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
public SqlUpdate deleteByParentId(Object parentId, List<Object> parentIdist) {
|
||||
if (parentId != null) {
|
||||
return deleteByParentId(parentId);
|
||||
return sqlHelp.deleteByParentId(parentId);
|
||||
} else {
|
||||
return deleteByParentIdList(parentIdist);
|
||||
return sqlHelp.deleteByParentIdList(parentIdist);
|
||||
}
|
||||
}
|
||||
|
||||
private SqlUpdate deleteByParentId(Object parentId) {
|
||||
DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByParentIdSql);
|
||||
bindParentId(sqlDelete, parentId);
|
||||
return sqlDelete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the Id's of detail beans given a parent Id or list of parent Id's.
|
||||
*/
|
||||
public List<Object> findIdsByParentId(Object parentId, List<Object> parentIdList, Transaction t, List<Object> excludeDetailIds) {
|
||||
if (parentId != null) {
|
||||
return findIdsByParentId(parentId, t, excludeDetailIds);
|
||||
return sqlHelp.findIdsByParentId(parentId, t, excludeDetailIds);
|
||||
} else {
|
||||
return findIdsByParentIdList(parentIdList, t, excludeDetailIds);
|
||||
return sqlHelp.findIdsByParentIdList(parentIdList, t, excludeDetailIds);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Object> findIdsByParentId(Object parentId, Transaction t, List<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(false, "");
|
||||
|
||||
EbeanServer server = server();
|
||||
Query<?> q = server.find(getPropertyType());
|
||||
bindParentIdEq(rawWhere, parentId, q);
|
||||
|
||||
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
|
||||
q.where().not(q.getExpressionFactory().idIn(excludeDetailIds));
|
||||
}
|
||||
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exclude many properties from bean cache data.
|
||||
*/
|
||||
@@ -359,88 +321,10 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
// assumes the ManyToOne property is included
|
||||
query.where().in(childMasterIdProperty, parentIds);
|
||||
} else {
|
||||
addWhereParentIdIn(query, parentIds);
|
||||
sqlHelp.addWhereParentIdIn(query, parentIds);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a where clause to the query for a given list of parent Id's.
|
||||
*/
|
||||
private void addWhereParentIdIn(SpiQuery<?> query, List<Object> parentIds) {
|
||||
|
||||
String tableAlias = hasJoinTable() ? "int_." : "t0.";
|
||||
if (hasJoinTable()) {
|
||||
query.setM2MIncludeJoin(inverseJoin);
|
||||
}
|
||||
String rawWhere = deriveWhereParentIdSql(true, tableAlias);
|
||||
String expr = descriptor.getParentIdInExpr(parentIds.size(), rawWhere);
|
||||
|
||||
bindParentIdsIn(expr, parentIds, query);
|
||||
}
|
||||
|
||||
private List<Object> findIdsByParentIdList(List<Object> parentIds, Transaction t, List<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(true, "");
|
||||
String inClause = buildInClauseBinding(parentIds.size(), exportedPropertyBindProto);
|
||||
|
||||
String expr = rawWhere + inClause;
|
||||
|
||||
EbeanServer server = descriptor.getEbeanServer();
|
||||
Query<?> q = server.find(propertyType);
|
||||
bindParentIdsIn(expr, parentIds, q);
|
||||
|
||||
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
|
||||
q.where().not(q.getExpressionFactory().idIn(excludeDetailIds));
|
||||
}
|
||||
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
private SqlUpdate deleteByParentIdList(List<Object> parentIds) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(100);
|
||||
sb.append(deleteByParentIdInSql);
|
||||
sb.append(buildInClauseBinding(parentIds.size(), exportedPropertyBindProto));
|
||||
|
||||
DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString());
|
||||
bindParentIds(delete, parentIds);
|
||||
return delete;
|
||||
}
|
||||
|
||||
private String deriveExportedPropertyBindProto() {
|
||||
if (exportedProperties.length == 1) {
|
||||
return "?";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("?");
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String buildInClauseBinding(int size, String bindProto) {
|
||||
|
||||
if (descriptor.isSimpleId()) {
|
||||
return descriptor.getIdBinder().getIdInValueExpr(false, size);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(10 + (size * (bindProto.length() + 1)));
|
||||
sb.append(" in");
|
||||
sb.append(" (");
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(bindProto);
|
||||
}
|
||||
sb.append(") ");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the lazy load server to help create reference collections (that lazy
|
||||
* load on demand).
|
||||
@@ -476,7 +360,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object readSet(DbReadContext ctx, EntityBean bean) throws SQLException {
|
||||
public Object readSet(DbReadContext ctx, EntityBean bean) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -616,6 +500,10 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
return manyToMany;
|
||||
}
|
||||
|
||||
public boolean isElementCollection() {
|
||||
return elementCollection;
|
||||
}
|
||||
|
||||
/**
|
||||
* ManyToMany only, join from local table to intersection table.
|
||||
*/
|
||||
@@ -705,30 +593,6 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
private String deriveWhereParentIdSql(boolean inClause, String tableAlias) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (inClause) {
|
||||
sb.append("(");
|
||||
}
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
String fkColumn = exportedProperties[i].getForeignDbColumn();
|
||||
if (i > 0) {
|
||||
String s = inClause ? "," : " and ";
|
||||
sb.append(s);
|
||||
}
|
||||
sb.append(tableAlias).append(fkColumn);
|
||||
if (!inClause) {
|
||||
sb.append("=? ");
|
||||
}
|
||||
}
|
||||
if (inClause) {
|
||||
sb.append(")");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the array of ExportedProperty used to build reference objects.
|
||||
*/
|
||||
@@ -741,12 +605,9 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
if (idProp != null && idProp.isEmbedded()) {
|
||||
|
||||
BeanPropertyAssocOne<?> one = (BeanPropertyAssocOne<?>) idProp;
|
||||
BeanDescriptor<?> targetDesc = one.getTargetDescriptor();
|
||||
BeanProperty[] emIds = targetDesc.propertiesBaseScalar();
|
||||
try {
|
||||
for (BeanProperty emId : emIds) {
|
||||
ExportedProperty expProp = findMatch(true, emId);
|
||||
list.add(expProp);
|
||||
for (BeanProperty emId : one.getTargetDescriptor().propertiesBaseScalar()) {
|
||||
list.add(findMatch(true, emId));
|
||||
}
|
||||
} catch (PersistenceException e) {
|
||||
// not found as individual scalar properties
|
||||
@@ -755,8 +616,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
|
||||
} else {
|
||||
if (idProp != null) {
|
||||
ExportedProperty expProp = findMatch(false, idProp);
|
||||
list.add(expProp);
|
||||
list.add(findMatch(false, idProp));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -768,32 +628,12 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
*/
|
||||
private ExportedProperty findMatch(boolean embedded, BeanProperty prop) {
|
||||
|
||||
String matchColumn = prop.getDbColumn();
|
||||
|
||||
String searchTable;
|
||||
TableJoinColumn[] columns;
|
||||
if (hasJoinTable()) {
|
||||
// look for column going to intersection
|
||||
columns = intersectionJoin.columns();
|
||||
searchTable = intersectionJoin.getTable();
|
||||
|
||||
return findMatch(embedded, prop, prop.getDbColumn(), intersectionJoin);
|
||||
} else {
|
||||
columns = tableJoin.columns();
|
||||
searchTable = tableJoin.getTable();
|
||||
return findMatch(embedded, prop, prop.getDbColumn(), tableJoin);
|
||||
}
|
||||
for (TableJoinColumn column : columns) {
|
||||
String matchTo = column.getLocalDbColumn();
|
||||
|
||||
if (matchColumn.equalsIgnoreCase(matchTo)) {
|
||||
String foreignCol = column.getForeignDbColumn();
|
||||
return new ExportedProperty(embedded, foreignCol, prop);
|
||||
}
|
||||
}
|
||||
|
||||
String msg = "Error with the Join on [" + getFullBeanName()
|
||||
+ "]. Could not find the matching foreign key for [" + matchColumn + "] in table[" + searchTable + "]?"
|
||||
+ " Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?";
|
||||
throw new PersistenceException(msg);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -813,8 +653,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
Class<?> beanType = descriptor.getBeanType();
|
||||
BeanDescriptor<?> targetDesc = getTargetDescriptor();
|
||||
|
||||
BeanPropertyAssocOne<?>[] ones = targetDesc.propertiesOne();
|
||||
for (BeanPropertyAssocOne<?> prop : ones) {
|
||||
for (BeanPropertyAssocOne<?> prop : targetDesc.propertiesOne()) {
|
||||
if (mappedBy != null) {
|
||||
// match using mappedBy as property name
|
||||
if (mappedBy.equalsIgnoreCase(prop.getName())) {
|
||||
@@ -880,9 +719,9 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
/**
|
||||
* Register the mapping of intersection table to associated draft table.
|
||||
*/
|
||||
public void registerDraftIntersectionTable(Map<String, String> draftTableMap) {
|
||||
public void registerDraftIntersectionTable(BeanDescriptorInitContext initContext) {
|
||||
if (hasDraftIntersection()) {
|
||||
draftTableMap.put(intersectionPublishTable, intersectionDraftTable);
|
||||
initContext.addDraftIntersection(intersectionPublishTable, intersectionDraftTable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -928,7 +767,7 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
* Skip JSON write value for ToMany property.
|
||||
*/
|
||||
@Override
|
||||
public void jsonWriteValue(SpiJsonWriter writeJson, Object value) throws IOException {
|
||||
public void jsonWriteValue(SpiJsonWriter writeJson, Object value) {
|
||||
// do nothing, exclude ToMany properties
|
||||
}
|
||||
|
||||
@@ -1030,4 +869,16 @@ public class BeanPropertyAssocMany<T> extends BeanPropertyAssoc<T> {
|
||||
public boolean isIncludeCascadeDelete() {
|
||||
return cascadeInfo.isDelete() || o2mJoinTable || ModifyListenMode.REMOVALS == modifyListenMode;
|
||||
}
|
||||
|
||||
public String insertElementCollection() {
|
||||
return sqlHelp.insertElementCollection();
|
||||
}
|
||||
|
||||
public boolean isTargetDocStoreMapped() {
|
||||
return targetDescriptor.isDocStoreMapped();
|
||||
}
|
||||
|
||||
public BeanCollectionHelp<T> getHelp() {
|
||||
return help;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.io.IOException;
|
||||
/**
|
||||
* Help BeanPropertyAssocMany with JSON processing.
|
||||
*/
|
||||
public class BeanPropertyAssocManyJsonHelp {
|
||||
class BeanPropertyAssocManyJsonHelp {
|
||||
|
||||
/**
|
||||
* The associated many property.
|
||||
@@ -28,7 +28,7 @@ public class BeanPropertyAssocManyJsonHelp {
|
||||
/**
|
||||
* Construct for the owning many property.
|
||||
*/
|
||||
public BeanPropertyAssocManyJsonHelp(BeanPropertyAssocMany<?> many) {
|
||||
BeanPropertyAssocManyJsonHelp(BeanPropertyAssocMany<?> many) {
|
||||
this.many = many;
|
||||
boolean objectMapperPresent = many.getBeanDescriptor().getServerConfig().getClassLoadConfig().isJacksonObjectMapperPresent();
|
||||
this.jsonTransient = !objectMapperPresent ? null : new BeanPropertyAssocManyJsonTransient();
|
||||
|
||||
+2
-2
@@ -14,12 +14,12 @@ import java.util.LinkedHashMap;
|
||||
/**
|
||||
* Helper used to read transient many properties using Jackson ObjectMapper.
|
||||
*/
|
||||
public class BeanPropertyAssocManyJsonTransient {
|
||||
class BeanPropertyAssocManyJsonTransient {
|
||||
|
||||
/**
|
||||
* Use Jackson ObjectMapper to read the transient 'many' property.
|
||||
*/
|
||||
public void jsonReadUsingObjectMapper(BeanPropertyAssocMany<?> many, ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
void jsonReadUsingObjectMapper(BeanPropertyAssocMany<?> many, ReadJson readJson, EntityBean parentBean) throws IOException {
|
||||
|
||||
ObjectMapper mapper = readJson.getObjectMapper();
|
||||
|
||||
|
||||
@@ -0,0 +1,258 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.dbmigration.model.visitor.BaseTablePropertyVisitor;
|
||||
import io.ebeaninternal.dbmigration.model.visitor.VisitAllUsing;
|
||||
import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class BeanPropertyAssocManySqlHelp<T> {
|
||||
|
||||
private final BeanPropertyAssocMany<T> many;
|
||||
private final ExportedProperty[] exportedProperties;
|
||||
private final boolean hasJoinTable;
|
||||
private final BeanDescriptor<?> descriptor;
|
||||
private final String exportedPropertyBindProto;
|
||||
private final String deleteByParentIdSql;
|
||||
private final String deleteByParentIdInSql;
|
||||
private final String elementCollectionInsertSql;
|
||||
|
||||
BeanPropertyAssocManySqlHelp(BeanPropertyAssocMany<T> many, ExportedProperty[] exportedProperties) {
|
||||
this.many = many;
|
||||
this.exportedProperties = exportedProperties;
|
||||
this.hasJoinTable = many.hasJoinTable();
|
||||
this.descriptor = many.getBeanDescriptor();
|
||||
this.exportedPropertyBindProto = deriveExportedPropertyBindProto();
|
||||
|
||||
String delStmt;
|
||||
if (hasJoinTable) {
|
||||
delStmt = "delete from " + many.inverseJoin.getTable() + " where ";
|
||||
} else {
|
||||
delStmt = "delete from " + many.targetTable() + " where ";
|
||||
}
|
||||
deleteByParentIdSql = delStmt + deriveWhereParentIdSql(false, "");
|
||||
deleteByParentIdInSql = delStmt + deriveWhereParentIdSql(true, "");
|
||||
if (many.isElementCollection()) {
|
||||
elementCollectionInsertSql = elementCollectionInsert();
|
||||
} else {
|
||||
elementCollectionInsertSql = null;
|
||||
}
|
||||
}
|
||||
|
||||
private String elementCollectionInsert() {
|
||||
|
||||
StringBuilder sb = new StringBuilder(200);
|
||||
sb.append("insert into ").append(many.targetTable()).append(" (");
|
||||
append(sb, "", ",", "");
|
||||
|
||||
Cols cols = new Cols(sb);
|
||||
VisitAllUsing.visitOne(many.targetDescriptor, cols);
|
||||
sb.append(") values (");
|
||||
appendBind(sb, exportedProperties.length, true);
|
||||
appendBind(sb, cols.colCount, false);
|
||||
sb.append(")");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public String insertElementCollection() {
|
||||
return elementCollectionInsertSql;
|
||||
}
|
||||
|
||||
private static class Cols extends BaseTablePropertyVisitor {
|
||||
|
||||
int colCount;
|
||||
|
||||
private final StringBuilder sb;
|
||||
|
||||
private Cols(StringBuilder sb) {
|
||||
this.sb = sb;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEmbeddedScalar(BeanProperty p, BeanPropertyAssocOne<?> embedded) {
|
||||
sb.append(",").append(p.getDbColumn());
|
||||
colCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitOneImported(BeanPropertyAssocOne<?> p) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitScalar(BeanProperty p) {
|
||||
sb.append(",").append(p.getDbColumn());
|
||||
colCount++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
String lazyFetchOrderBy(String fetchOrderBy) {
|
||||
|
||||
// derive lazyFetchOrderBy
|
||||
StringBuilder sb = new StringBuilder(50);
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
// these fk columns are either on the intersection (int_) or base table (t0)
|
||||
String fkTableAlias = hasJoinTable ? "int_" : "t0";
|
||||
sb.append(fkTableAlias).append(".").append(exportedProperties[i].getForeignDbColumn());
|
||||
}
|
||||
sb.append(", ").append(fetchOrderBy);
|
||||
return sb.toString().trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a where clause to the query for a given list of parent Id's.
|
||||
*/
|
||||
void addWhereParentIdIn(SpiQuery<?> query, List<Object> parentIds) {
|
||||
|
||||
String tableAlias = hasJoinTable ? "int_." : "t0.";
|
||||
if (hasJoinTable) {
|
||||
query.setM2MIncludeJoin(many.inverseJoin);
|
||||
}
|
||||
String rawWhere = deriveWhereParentIdSql(true, tableAlias);
|
||||
String expr = descriptor.getParentIdInExpr(parentIds.size(), rawWhere);
|
||||
|
||||
many.bindParentIdsIn(expr, parentIds, query);
|
||||
}
|
||||
|
||||
List<Object> findIdsByParentId(Object parentId, Transaction t, List<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(false, "");
|
||||
|
||||
SpiEbeanServer server = descriptor.getEbeanServer();
|
||||
SpiQuery<?> q = many.newQuery(server);
|
||||
many.bindParentIdEq(rawWhere, parentId, q);
|
||||
|
||||
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
|
||||
q.where().not(q.getExpressionFactory().idIn(excludeDetailIds));
|
||||
}
|
||||
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
List<Object> findIdsByParentIdList(List<Object> parentIds, Transaction t, List<Object> excludeDetailIds) {
|
||||
|
||||
String rawWhere = deriveWhereParentIdSql(true, "");
|
||||
String inClause = buildInClauseBinding(parentIds.size(), exportedPropertyBindProto);
|
||||
|
||||
String expr = rawWhere + inClause;
|
||||
|
||||
SpiEbeanServer server = descriptor.getEbeanServer();
|
||||
SpiQuery<?> q = many.newQuery(server);
|
||||
//Query<?> q = server.find(propertyType);
|
||||
many.bindParentIdsIn(expr, parentIds, q);
|
||||
|
||||
if (excludeDetailIds != null && !excludeDetailIds.isEmpty()) {
|
||||
q.where().not(q.getExpressionFactory().idIn(excludeDetailIds));
|
||||
}
|
||||
|
||||
return server.findIds(q, t);
|
||||
}
|
||||
|
||||
SqlUpdate deleteByParentId(Object parentId) {
|
||||
DefaultSqlUpdate sqlDelete = new DefaultSqlUpdate(deleteByParentIdSql);
|
||||
many.bindParentId(sqlDelete, parentId);
|
||||
return sqlDelete;
|
||||
}
|
||||
|
||||
SqlUpdate deleteByParentIdList(List<Object> parentIds) {
|
||||
|
||||
StringBuilder sb = new StringBuilder(100);
|
||||
sb.append(deleteByParentIdInSql);
|
||||
sb.append(buildInClauseBinding(parentIds.size(), exportedPropertyBindProto));
|
||||
|
||||
DefaultSqlUpdate delete = new DefaultSqlUpdate(sb.toString());
|
||||
many.bindParentIds(delete, parentIds);
|
||||
return delete;
|
||||
}
|
||||
|
||||
private void appendBind(StringBuilder sb, int count, boolean skipComma) {
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!skipComma || i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("?");
|
||||
}
|
||||
}
|
||||
|
||||
private void append(StringBuilder sb, String tableAlias, String prefix, String suffix) {
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
String fkColumn = exportedProperties[i].getForeignDbColumn();
|
||||
if (i > 0) {
|
||||
sb.append(prefix);
|
||||
}
|
||||
sb.append(tableAlias).append(fkColumn);
|
||||
sb.append(suffix);
|
||||
}
|
||||
}
|
||||
private String deriveWhereParentIdSql(boolean inClause, String tableAlias) {
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
if (inClause) {
|
||||
sb.append("(");
|
||||
}
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
String fkColumn = exportedProperties[i].getForeignDbColumn();
|
||||
if (i > 0) {
|
||||
String s = inClause ? "," : " and ";
|
||||
sb.append(s);
|
||||
}
|
||||
sb.append(tableAlias).append(fkColumn);
|
||||
if (!inClause) {
|
||||
sb.append("=? ");
|
||||
}
|
||||
}
|
||||
if (inClause) {
|
||||
sb.append(")");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String buildInClauseBinding(int size, String bindProto) {
|
||||
|
||||
if (descriptor.isSimpleId()) {
|
||||
return descriptor.getIdBinder().getIdInValueExpr(false, size);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(10 + (size * (bindProto.length() + 1)));
|
||||
sb.append(" in");
|
||||
sb.append(" (");
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append(bindProto);
|
||||
}
|
||||
sb.append(") ");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private String deriveExportedPropertyBindProto() {
|
||||
if (exportedProperties.length == 1) {
|
||||
return "?";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("(");
|
||||
for (int i = 0; i < exportedProperties.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(",");
|
||||
}
|
||||
sb.append("?");
|
||||
}
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -16,10 +16,12 @@ import io.ebeaninternal.server.deploy.id.ImportedId;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.el.ElPropertyChainBuilder;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.query.STreePropertyAssocOne;
|
||||
import io.ebeaninternal.server.query.SqlBeanLoad;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.text.json.ReadJson;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.IOException;
|
||||
@@ -32,7 +34,7 @@ import java.util.Map;
|
||||
/**
|
||||
* Property mapped to a joined bean.
|
||||
*/
|
||||
public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> implements STreePropertyAssocOne {
|
||||
|
||||
private final boolean oneToOne;
|
||||
|
||||
@@ -94,8 +96,12 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise() {
|
||||
super.initialise();
|
||||
public void initialise(BeanDescriptorInitContext initContext) {
|
||||
super.initialise(initContext);
|
||||
initialiseAssocOne();
|
||||
}
|
||||
|
||||
private void initialiseAssocOne() {
|
||||
localHelp = createHelp(embedded, oneToOneExported);
|
||||
|
||||
if (!isTransient) {
|
||||
@@ -417,6 +423,11 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalarType<?> getIdScalarType() {
|
||||
return targetDescriptor.getIdProperty().getScalarType();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the Id values from the given bean.
|
||||
*/
|
||||
@@ -539,24 +550,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
|
||||
*/
|
||||
private ExportedProperty findMatch(boolean embeddedProp, BeanProperty prop) {
|
||||
|
||||
String matchColumn = prop.getDbColumn();
|
||||
|
||||
String searchTable = tableJoin.getTable();
|
||||
TableJoinColumn[] columns = tableJoin.columns();
|
||||
|
||||
for (TableJoinColumn column : columns) {
|
||||
String matchTo = column.getLocalDbColumn();
|
||||
|
||||
if (matchColumn.equalsIgnoreCase(matchTo)) {
|
||||
String foreignCol = column.getForeignDbColumn();
|
||||
return new ExportedProperty(embeddedProp, foreignCol, prop);
|
||||
}
|
||||
}
|
||||
|
||||
String msg = "Error with the Join on [" + getFullBeanName()
|
||||
+ "]. Could not find the matching foreign key for [" + matchColumn + "] in table[" + searchTable + "]?"
|
||||
+ " Perhaps using a @JoinColumn with the name/referencedColumnName attributes swapped?";
|
||||
throw new PersistenceException(msg);
|
||||
return findMatch(embeddedProp, prop, prop.getDbColumn(), tableJoin);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -4,8 +4,27 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanPropertySimpleCollection;
|
||||
|
||||
public class BeanPropertySimpleCollection<T> extends BeanPropertyAssocMany<T> {
|
||||
|
||||
private BeanDescriptor<T> elementDescriptor;
|
||||
|
||||
public BeanPropertySimpleCollection(BeanDescriptor<?> descriptor, DeployBeanPropertySimpleCollection<T> deploy) {
|
||||
super(descriptor, deploy);
|
||||
this.elementDescriptor = deploy.getElementDescriptor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialise(BeanDescriptorInitContext initContext) {
|
||||
super.initialise(initContext);
|
||||
if (isElementCollection()) {
|
||||
// initialise all non-id properties (we don't have an Id property)
|
||||
elementDescriptor.initialiseOther(initContext);
|
||||
}
|
||||
}
|
||||
|
||||
void initialiseTargetDescriptor(BeanDescriptorInitContext initContext) {
|
||||
if (isElementCollection()) {
|
||||
targetDescriptor = elementDescriptor;
|
||||
} else {
|
||||
targetDescriptor = descriptor.getBeanDescriptor(targetType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import io.ebean.Query;
|
||||
import io.ebean.Transaction;
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.BeanCollectionAdd;
|
||||
import io.ebean.bean.BeanCollectionLoader;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.common.BeanSet;
|
||||
import io.ebeaninternal.server.text.json.SpiJsonWriter;
|
||||
@@ -19,32 +18,18 @@ import java.util.Set;
|
||||
*/
|
||||
public final class BeanSetHelp<T> extends BaseCollectionHelp<T> {
|
||||
|
||||
private final BeanPropertyAssocMany<T> many;
|
||||
private final BeanDescriptor<T> targetDescriptor;
|
||||
private final String propertyName;
|
||||
private BeanCollectionLoader loader;
|
||||
|
||||
/**
|
||||
* When attached to a specific many property.
|
||||
*/
|
||||
public BeanSetHelp(BeanPropertyAssocMany<T> many) {
|
||||
this.many = many;
|
||||
this.targetDescriptor = many.getTargetDescriptor();
|
||||
this.propertyName = many.getName();
|
||||
BeanSetHelp(BeanPropertyAssocMany<T> many) {
|
||||
super(many);
|
||||
}
|
||||
|
||||
/**
|
||||
* For a query that returns a set.
|
||||
*/
|
||||
public BeanSetHelp() {
|
||||
this.many = null;
|
||||
this.targetDescriptor = null;
|
||||
this.propertyName = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLoader(BeanCollectionLoader loader) {
|
||||
this.loader = loader;
|
||||
BeanSetHelp() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,15 +46,6 @@ public final class BeanSetHelp<T> extends BaseCollectionHelp<T> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
if (withCheck) {
|
||||
collection.internalAddWithCheck(bean);
|
||||
} else {
|
||||
collection.internalAdd(bean);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public BeanCollection<T> createEmptyNoParent() {
|
||||
return new BeanSet<>();
|
||||
@@ -142,13 +118,6 @@ public final class BeanSetHelp<T> extends BaseCollectionHelp<T> {
|
||||
} else {
|
||||
set = (Set<?>) collection;
|
||||
}
|
||||
|
||||
if (!set.isEmpty() || ctx.isIncludeEmpty()) {
|
||||
ctx.beginAssocMany(name);
|
||||
for (Object bean : set) {
|
||||
targetDescriptor.jsonWrite(ctx, (EntityBean) bean);
|
||||
}
|
||||
ctx.endAssocMany();
|
||||
}
|
||||
jsonWriteCollection(ctx, name, set);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,16 @@ public class BeanTable {
|
||||
this.idProperty = mutable.createIdProperty(owner);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for element collection.
|
||||
*/
|
||||
public BeanTable(BeanDescriptorMap owner, String tableName, Class<?> beanType) {
|
||||
this.owner = owner;
|
||||
this.beanType = beanType;
|
||||
this.baseTable = tableName;
|
||||
this.idProperty = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return baseTable;
|
||||
|
||||
@@ -5,6 +5,7 @@ import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.query.STreePropertyAssocMany;
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
|
||||
import java.util.Map;
|
||||
@@ -68,7 +69,7 @@ public interface DbReadContext {
|
||||
* Return the property that is associated with the many. There can only be
|
||||
* one. This can be null.
|
||||
*/
|
||||
BeanPropertyAssocMany<?> getManyProperty();
|
||||
STreePropertyAssocMany getManyProperty();
|
||||
|
||||
/**
|
||||
* Set back the bean that has just been loaded with its id.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebeaninternal.server.query.STreeProperty;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.query.SqlTreeProperty;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.util.List;
|
||||
@@ -9,14 +9,14 @@ import java.util.List;
|
||||
/**
|
||||
* Abstract base for dynamic properties.
|
||||
*/
|
||||
abstract class DynamicPropertyBase implements SqlTreeProperty {
|
||||
abstract class DynamicPropertyBase implements STreeProperty {
|
||||
|
||||
final String name;
|
||||
final String fullName;
|
||||
final String elPrefix;
|
||||
final ScalarType<?> scalarType;
|
||||
|
||||
public DynamicPropertyBase(String name, String fullName, String elPrefix, ScalarType<?> scalarType) {
|
||||
DynamicPropertyBase(String name, String fullName, String elPrefix, ScalarType<?> scalarType) {
|
||||
this.name = name;
|
||||
this.fullName = fullName;
|
||||
this.elPrefix = elPrefix;
|
||||
@@ -43,6 +43,11 @@ abstract class DynamicPropertyBase implements SqlTreeProperty {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFormula() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getElPrefix() {
|
||||
return elPrefix;
|
||||
@@ -67,4 +72,9 @@ abstract class DynamicPropertyBase implements SqlTreeProperty {
|
||||
public void appendFrom(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
// do not add to from usually
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEncryptKeyAsString() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.EntityBeanIntercept;
|
||||
|
||||
class ElementEntityBean implements EntityBean {
|
||||
|
||||
private final String[] properties;
|
||||
|
||||
private Object[] data;
|
||||
|
||||
private EntityBeanIntercept intercept;
|
||||
|
||||
ElementEntityBean(String[] properties) {
|
||||
this.properties = properties;
|
||||
this.intercept = new EntityBeanIntercept(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] _ebean_getPropertyNames() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String _ebean_getPropertyName(int pos) {
|
||||
return properties[pos];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String _ebean_getMarker() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object _ebean_newInstance() {
|
||||
return new ElementEntityBean(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void _ebean_setEmbeddedLoaded() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean _ebean_isEmbeddedNewOrDirty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBeanIntercept _ebean_getIntercept() {
|
||||
return intercept;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBeanIntercept _ebean_intercept() {
|
||||
return intercept;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void _ebean_setField(int fieldIndex, Object value) {
|
||||
if (data == null) {
|
||||
data = new Object[properties.length];
|
||||
}
|
||||
data[fieldIndex] = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void _ebean_setFieldIntercept(int fieldIndex, Object value) {
|
||||
_ebean_setField(fieldIndex, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object _ebean_getField(int fieldIndex) {
|
||||
return data == null ? null : data[fieldIndex];
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object _ebean_getFieldIntercept(int fieldIndex) {
|
||||
return _ebean_getField(fieldIndex);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy;
|
||||
|
||||
import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import io.ebeaninternal.server.query.SqlTreeProperty;
|
||||
import io.ebeaninternal.server.query.STreeProperty;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.sql.Types;
|
||||
@@ -89,7 +89,7 @@ class FormulaPropertyPath {
|
||||
return alias;
|
||||
}
|
||||
|
||||
SqlTreeProperty build() {
|
||||
STreeProperty build() {
|
||||
|
||||
DeployPropertyParser parser = descriptor.parser().setCatchFirst(true);
|
||||
|
||||
|
||||
@@ -184,7 +184,7 @@ public class InheritInfo {
|
||||
|
||||
for (InheritInfo childInfo : children) {
|
||||
// recursively search this child bean descriptor
|
||||
prop = childInfo.desc().findBeanProperty(propertyName);
|
||||
prop = childInfo.desc().findProperty(propertyName);
|
||||
if (prop != null) {
|
||||
return prop;
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import io.ebeaninternal.server.core.DefaultSqlUpdate;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.query.STreeProperty;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
|
||||
import java.io.DataInput;
|
||||
@@ -52,7 +53,7 @@ public interface IdBinder {
|
||||
/**
|
||||
* Return the Id BeanProperty.
|
||||
*/
|
||||
BeanProperty getBeanProperty();
|
||||
STreeProperty getBeanProperty();
|
||||
|
||||
/**
|
||||
* Find a BeanProperty that is mapped to the database column.
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.properties.BeanPropertySetter;
|
||||
|
||||
/**
|
||||
* Setter used for "element beans" with ElementCollection.
|
||||
*/
|
||||
class BeanPropertyElementSetter implements BeanPropertySetter {
|
||||
|
||||
private final int pos;
|
||||
|
||||
BeanPropertyElementSetter(int pos) {
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set(EntityBean bean, Object value) {
|
||||
bean._ebean_setField(pos, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIntercept(EntityBean bean, Object value) {
|
||||
set(bean, value);
|
||||
}
|
||||
}
|
||||
@@ -210,6 +210,8 @@ public class DeployBeanProperty {
|
||||
|
||||
private boolean undirectionalShadow;
|
||||
|
||||
private boolean elementProperty;
|
||||
|
||||
private int sortOrder;
|
||||
|
||||
private boolean excludedFromHistory;
|
||||
@@ -444,7 +446,11 @@ public class DeployBeanProperty {
|
||||
}
|
||||
|
||||
public BeanPropertySetter getSetter() {
|
||||
return setter;
|
||||
if (elementProperty) {
|
||||
return new BeanPropertyElementSetter(sortOrder);
|
||||
} else {
|
||||
return setter;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1081,4 +1087,10 @@ public class DeployBeanProperty {
|
||||
return dbMigrationInfos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set when this property is part of a 'element bean' used with ElementCollection.
|
||||
*/
|
||||
public void setElementProperty() {
|
||||
this.elementProperty = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import io.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.ManyType;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
|
||||
@@ -23,6 +24,8 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
|
||||
|
||||
private boolean o2mJoinTable;
|
||||
|
||||
private boolean elementCollection;
|
||||
|
||||
/**
|
||||
* Flag to indicate this is a unidirectional relationship.
|
||||
*/
|
||||
@@ -46,6 +49,11 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
|
||||
|
||||
private DeployOrderColumn orderColumn;
|
||||
|
||||
/**
|
||||
* Effectively the dynamically created target descriptor (that doesn't have a mapped type/class per say).
|
||||
*/
|
||||
private BeanDescriptor<?> elementDescriptor;
|
||||
|
||||
/**
|
||||
* Create this property.
|
||||
*/
|
||||
@@ -231,5 +239,24 @@ public class DeployBeanPropertyAssocMany<T> extends DeployBeanPropertyAssoc<T> {
|
||||
this.o2mJoinTable = true;
|
||||
setModifyListenMode(ModifyListenMode.ALL);
|
||||
}
|
||||
|
||||
public void setElementCollection() {
|
||||
elementCollection = true;
|
||||
cascadeInfo.setSaveDelete(true, true);
|
||||
setModifyListenMode(ModifyListenMode.ALL);
|
||||
}
|
||||
|
||||
public boolean isElementCollection() {
|
||||
return elementCollection;
|
||||
}
|
||||
|
||||
public void setElementDescriptor(BeanDescriptor<?> elementDescriptor) {
|
||||
this.elementDescriptor = elementDescriptor;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <A> BeanDescriptor<A> getElementDescriptor() {
|
||||
return (BeanDescriptor<A>)elementDescriptor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.ebeaninternal.server.deploy.meta;
|
||||
|
||||
import io.ebeaninternal.server.deploy.PropertyForeignKey;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
|
||||
@@ -162,4 +163,12 @@ public class DeployBeanPropertyAssocOne<T> extends DeployBeanPropertyAssoc<T> {
|
||||
public boolean isOrphanRemoval() {
|
||||
return orphanRemoval;
|
||||
}
|
||||
|
||||
public void setJoinType(boolean outerJoin) {
|
||||
tableJoin.setType(outerJoin ? SqlJoinType.OUTER : SqlJoinType.INNER);
|
||||
}
|
||||
|
||||
public void setJoinColumns(DeployTableJoinColumn[] columns, boolean reverse) {
|
||||
tableJoin.setColumns(columns, reverse);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,18 +7,24 @@ import io.ebean.annotation.Where;
|
||||
import io.ebean.bean.BeanCollection.ModifyListenMode;
|
||||
import io.ebean.config.NamingConvention;
|
||||
import io.ebean.config.TableName;
|
||||
import io.ebean.util.CamelCaseHelper;
|
||||
import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanTable;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployOrderColumn;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoinColumn;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinTable;
|
||||
import javax.persistence.ManyToMany;
|
||||
@@ -38,8 +44,8 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
/**
|
||||
* Create with the DeployInfo.
|
||||
*/
|
||||
AnnotationAssocManys(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
|
||||
super(info, javaxValidationAnnotations);
|
||||
AnnotationAssocManys(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig, BeanDescriptorManager factory) {
|
||||
super(info, readConfig);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@@ -82,6 +88,10 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
if (manyToMany != null) {
|
||||
readToMany(manyToMany, prop);
|
||||
}
|
||||
ElementCollection elementCollection = get(prop, ElementCollection.class);
|
||||
if (elementCollection != null) {
|
||||
readElementCollection(prop, elementCollection);
|
||||
}
|
||||
|
||||
if (get(prop, HistoryExclude.class) != null) {
|
||||
prop.setExcludedFromHistory();
|
||||
@@ -160,6 +170,59 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
}
|
||||
}
|
||||
|
||||
private void readElementCollection(DeployBeanPropertyAssocMany<?> prop, ElementCollection elementCollection) {
|
||||
|
||||
prop.setElementCollection();
|
||||
if (!elementCollection.targetClass().equals(void.class)) {
|
||||
prop.setTargetType(elementCollection.targetClass());
|
||||
}
|
||||
Column column = get(prop, Column.class);
|
||||
if (column != null) {
|
||||
prop.setDbColumn(column.name());
|
||||
prop.setDbLength(column.length());
|
||||
prop.setDbScale(column.scale());
|
||||
}
|
||||
|
||||
CollectionTable collectionTable = get(prop, CollectionTable.class);
|
||||
|
||||
String fullTableName = getFullTableName(collectionTable);
|
||||
if (fullTableName == null) {
|
||||
fullTableName = descriptor.getBaseTable()+"_"+ CamelCaseHelper.toUnderscoreFromCamel(prop.getName());
|
||||
}
|
||||
//namingConvention.
|
||||
BeanTable beanTable = factory.getCollectionBeanTable(fullTableName, prop.getTargetType());
|
||||
prop.setBeanTable(beanTable);
|
||||
|
||||
if (collectionTable != null) {
|
||||
prop.getTableJoin().addJoinColumn(true, collectionTable.joinColumns(), beanTable);
|
||||
}
|
||||
|
||||
Class<?> elementType = prop.getTargetType();
|
||||
|
||||
DeployBeanDescriptor<?> elementDescriptor = factory.createDeployDescriptor(elementType);
|
||||
elementDescriptor.setBaseTable(new TableName(fullTableName), readConfig.getAsOfViewSuffix(), readConfig.getVersionsBetweenSuffix());
|
||||
|
||||
ScalarType<?> scalarType = util.getTypeManager().getScalarType(elementType);
|
||||
DeployBeanProperty elementProp = new DeployBeanProperty(elementDescriptor, elementType, scalarType, null);
|
||||
|
||||
elementProp.setName("value");
|
||||
elementProp.setDbColumn(prop.getDbColumn());
|
||||
elementProp.setNullable(false);
|
||||
elementProp.setDbInsertable(true);
|
||||
elementProp.setDbUpdateable(true);
|
||||
elementProp.setDbRead(true);
|
||||
elementProp.setElementProperty();
|
||||
|
||||
elementDescriptor.addBeanProperty(elementProp);
|
||||
elementDescriptor.setProperties(new String[]{"value"});
|
||||
elementDescriptor.setName(prop.getFullBeanName());
|
||||
|
||||
Class<?> owningType = prop.getOwningType();
|
||||
|
||||
factory.createUnidirectional(elementDescriptor, owningType, beanTable, prop.getTableJoin());
|
||||
prop.setElementDescriptor(factory.createElementDescriptor(elementDescriptor));
|
||||
}
|
||||
|
||||
/**
|
||||
* Define the joins for a ManyToMany relationship.
|
||||
* <p>
|
||||
@@ -204,6 +267,24 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the full table name
|
||||
*/
|
||||
private String getFullTableName(CollectionTable collectionTable) {
|
||||
if (collectionTable == null) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (!StringHelper.isNull(collectionTable.catalog())) {
|
||||
sb.append(collectionTable.catalog()).append(".");
|
||||
}
|
||||
if (!StringHelper.isNull(collectionTable.schema())) {
|
||||
sb.append(collectionTable.schema()).append(".");
|
||||
}
|
||||
sb.append(collectionTable.name());
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Define intersection table and foreign key columns for ManyToMany.
|
||||
* <p>
|
||||
|
||||
@@ -41,8 +41,8 @@ public class AnnotationAssocOnes extends AnnotationParser {
|
||||
/**
|
||||
* Create with the deploy Info.
|
||||
*/
|
||||
AnnotationAssocOnes(DeployBeanInfo<?> info, boolean javaxValidationAnnotations, BeanDescriptorManager factory) {
|
||||
super(info, javaxValidationAnnotations);
|
||||
AnnotationAssocOnes(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig, BeanDescriptorManager factory) {
|
||||
super(info, readConfig);
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ public class AnnotationAssocOnes extends AnnotationParser {
|
||||
}
|
||||
}
|
||||
|
||||
info.setBeanJoinType(prop, prop.isNullable());
|
||||
prop.setJoinType(prop.isNullable());
|
||||
|
||||
if (!prop.getTableJoin().hasJoinColumns() && beanTable != null) {
|
||||
|
||||
|
||||
@@ -42,25 +42,15 @@ public class AnnotationClass extends AnnotationParser {
|
||||
|
||||
private final boolean disableL2Cache;
|
||||
|
||||
/**
|
||||
* Create for normal early parse of class level annotations.
|
||||
*/
|
||||
public AnnotationClass(DeployBeanInfo<?> info, boolean validationAnnotations, String asOfViewSuffix, String versionsBetweenSuffix, boolean disableL2Cache) {
|
||||
super(info, validationAnnotations);
|
||||
this.asOfViewSuffix = asOfViewSuffix;
|
||||
this.versionsBetweenSuffix = versionsBetweenSuffix;
|
||||
this.disableL2Cache = disableL2Cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create to parse AttributeOverride annotations which is run last
|
||||
* after all the properties/fields have been parsed fully.
|
||||
*/
|
||||
public AnnotationClass(DeployBeanInfo<?> info) {
|
||||
super(info, false);
|
||||
this.asOfViewSuffix = null;
|
||||
this.versionsBetweenSuffix = null;
|
||||
this.disableL2Cache = false;
|
||||
public AnnotationClass(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
|
||||
super(info, readConfig);
|
||||
this.asOfViewSuffix = readConfig.getAsOfViewSuffix();
|
||||
this.versionsBetweenSuffix = readConfig.getVersionsBetweenSuffix();
|
||||
this.disableL2Cache = readConfig.isDisableL2Cache();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -91,14 +91,11 @@ public class AnnotationFields extends AnnotationParser {
|
||||
*/
|
||||
private FetchType defaultLobFetchType = FetchType.LAZY;
|
||||
|
||||
AnnotationFields(GeneratedPropertyFactory generatedPropFactory, DeployBeanInfo<?> info,
|
||||
boolean javaxValidationAnnotations, boolean jacksonAnnotationsPresent, boolean eagerFetchLobs) {
|
||||
|
||||
super(info, javaxValidationAnnotations);
|
||||
this.jacksonAnnotationsPresent = jacksonAnnotationsPresent;
|
||||
this.generatedPropFactory = generatedPropFactory;
|
||||
|
||||
if (eagerFetchLobs) {
|
||||
AnnotationFields(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
|
||||
super(info, readConfig);
|
||||
this.jacksonAnnotationsPresent = readConfig.isJacksonAnnotations();
|
||||
this.generatedPropFactory = readConfig.getGeneratedPropFactory();
|
||||
if (readConfig.isEagerFetchLobs()) {
|
||||
defaultLobFetchType = FetchType.EAGER;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +27,12 @@ public abstract class AnnotationParser extends AnnotationBase {
|
||||
|
||||
protected final boolean validationAnnotations;
|
||||
|
||||
public AnnotationParser(DeployBeanInfo<?> info, boolean validationAnnotations) {
|
||||
protected final ReadAnnotationConfig readConfig;
|
||||
|
||||
public AnnotationParser(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
|
||||
super(info.getUtil());
|
||||
this.validationAnnotations = validationAnnotations;
|
||||
this.readConfig = readConfig;
|
||||
this.validationAnnotations = readConfig.isJavaxValidationAnnotations();
|
||||
this.info = info;
|
||||
this.beanType = info.getDescriptor().getBeanType();
|
||||
this.descriptor = info.getDescriptor();
|
||||
|
||||
@@ -7,10 +7,10 @@ import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
/**
|
||||
* Read the class level deployment annotations.
|
||||
*/
|
||||
public class AnnotationSql extends AnnotationParser {
|
||||
class AnnotationSql extends AnnotationParser {
|
||||
|
||||
public AnnotationSql(DeployBeanInfo<?> info, boolean javaxValidationAnnotations) {
|
||||
super(info, javaxValidationAnnotations);
|
||||
AnnotationSql(DeployBeanInfo<?> info, ReadAnnotationConfig readConfig) {
|
||||
super(info, readConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -3,7 +3,6 @@ package io.ebeaninternal.server.deploy.parse;
|
||||
import io.ebean.RawSql;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployTableJoin;
|
||||
import io.ebeaninternal.server.query.SqlJoinType;
|
||||
import io.ebeaninternal.server.rawsql.SpiRawSql;
|
||||
@@ -70,15 +69,6 @@ public class DeployBeanInfo<T> {
|
||||
return tableJoin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a the join alias for a assoc one property.
|
||||
*/
|
||||
public void setBeanJoinType(DeployBeanPropertyAssocOne<?> beanProp, boolean outerJoin) {
|
||||
|
||||
DeployTableJoin tableJoin = beanProp.getTableJoin();
|
||||
tableJoin.setType(outerJoin ? SqlJoinType.OUTER : SqlJoinType.INNER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add named RawSql from ebean.xml.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package io.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
|
||||
|
||||
/**
|
||||
* Configuration used when reading the deployment annotations.
|
||||
*/
|
||||
class ReadAnnotationConfig {
|
||||
|
||||
private final GeneratedPropertyFactory generatedPropFactory;
|
||||
private final String asOfViewSuffix;
|
||||
private final String versionsBetweenSuffix;
|
||||
private final boolean disableL2Cache;
|
||||
private final boolean eagerFetchLobs;
|
||||
private final boolean javaxValidationAnnotations;
|
||||
private final boolean jacksonAnnotations;
|
||||
|
||||
ReadAnnotationConfig(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, ServerConfig serverConfig) {
|
||||
|
||||
this.generatedPropFactory = generatedPropFactory;
|
||||
this.asOfViewSuffix = asOfViewSuffix;
|
||||
this.versionsBetweenSuffix = versionsBetweenSuffix;
|
||||
this.disableL2Cache = serverConfig.isDisableL2Cache();
|
||||
this.eagerFetchLobs = serverConfig.isEagerFetchLobs();
|
||||
|
||||
this.javaxValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJavaxValidationAnnotationsPresent();
|
||||
this.jacksonAnnotations = generatedPropFactory.getClassLoadConfig().isJacksonAnnotationsPresent();
|
||||
}
|
||||
|
||||
GeneratedPropertyFactory getGeneratedPropFactory() {
|
||||
return generatedPropFactory;
|
||||
}
|
||||
|
||||
String getAsOfViewSuffix() {
|
||||
return asOfViewSuffix;
|
||||
}
|
||||
|
||||
String getVersionsBetweenSuffix() {
|
||||
return versionsBetweenSuffix;
|
||||
}
|
||||
|
||||
boolean isDisableL2Cache() {
|
||||
return disableL2Cache;
|
||||
}
|
||||
|
||||
boolean isEagerFetchLobs() {
|
||||
return eagerFetchLobs;
|
||||
}
|
||||
|
||||
boolean isJavaxValidationAnnotations() {
|
||||
return javaxValidationAnnotations;
|
||||
}
|
||||
|
||||
boolean isJacksonAnnotations() {
|
||||
return jacksonAnnotations;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import io.ebean.config.ServerConfig;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
import io.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory;
|
||||
|
||||
@@ -9,38 +10,10 @@ import io.ebeaninternal.server.deploy.generatedproperty.GeneratedPropertyFactory
|
||||
*/
|
||||
public class ReadAnnotations {
|
||||
|
||||
/**
|
||||
* Creates appropriate generated properties - WhenXXX, WhoXXX, Version etc.
|
||||
*/
|
||||
private final GeneratedPropertyFactory generatedPropFactory;
|
||||
private final ReadAnnotationConfig readConfig;
|
||||
|
||||
/**
|
||||
* Typically _with_history and when appended to the base table derives the name of
|
||||
* the view that unions the base table with the history table to support asOf queries.
|
||||
*/
|
||||
private final String asOfViewSuffix;
|
||||
|
||||
private final String versionsBetweenSuffix;
|
||||
|
||||
/**
|
||||
* True if the javax validation annotations are present in the classpath.
|
||||
*/
|
||||
private final boolean javaxValidationAnnotations;
|
||||
|
||||
/**
|
||||
* True if the jackson annotations are present in the classpath.
|
||||
*/
|
||||
private final boolean jacksonAnnotations;
|
||||
|
||||
private final boolean disableL2Cache;
|
||||
|
||||
public ReadAnnotations(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, boolean disableL2Cache) {
|
||||
this.generatedPropFactory = generatedPropFactory;
|
||||
this.asOfViewSuffix = asOfViewSuffix;
|
||||
this.versionsBetweenSuffix = versionsBetweenSuffix;
|
||||
this.javaxValidationAnnotations = generatedPropFactory.getClassLoadConfig().isJavaxValidationAnnotationsPresent();
|
||||
this.jacksonAnnotations = generatedPropFactory.getClassLoadConfig().isJacksonAnnotationsPresent();
|
||||
this.disableL2Cache = disableL2Cache;
|
||||
public ReadAnnotations(GeneratedPropertyFactory generatedPropFactory, String asOfViewSuffix, String versionsBetweenSuffix, ServerConfig serverConfig) {
|
||||
this.readConfig = new ReadAnnotationConfig(generatedPropFactory, asOfViewSuffix, versionsBetweenSuffix, serverConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -50,12 +23,10 @@ public class ReadAnnotations {
|
||||
* to resolve the relationships etc.
|
||||
* </p>
|
||||
*/
|
||||
public void readInitial(DeployBeanInfo<?> info, boolean eagerFetchLobs) {
|
||||
|
||||
public void readInitial(DeployBeanInfo<?> info) {
|
||||
try {
|
||||
new AnnotationClass(info, javaxValidationAnnotations, asOfViewSuffix, versionsBetweenSuffix, disableL2Cache).parse();
|
||||
new AnnotationFields(generatedPropFactory, info, javaxValidationAnnotations, jacksonAnnotations, eagerFetchLobs).parse();
|
||||
|
||||
new AnnotationClass(info, readConfig).parse();
|
||||
new AnnotationFields(info, readConfig).parse();
|
||||
} catch (RuntimeException e) {
|
||||
throw new RuntimeException("Error reading annotations for " + info, e);
|
||||
}
|
||||
@@ -75,14 +46,14 @@ public class ReadAnnotations {
|
||||
|
||||
try {
|
||||
|
||||
new AnnotationAssocOnes(info, javaxValidationAnnotations, factory).parse();
|
||||
new AnnotationAssocManys(info, javaxValidationAnnotations, factory).parse();
|
||||
new AnnotationAssocOnes(info, readConfig, factory).parse();
|
||||
new AnnotationAssocManys(info, readConfig, factory).parse();
|
||||
|
||||
// read the Sql annotations last because they may be
|
||||
// dependent on field level annotations
|
||||
new AnnotationSql(info, javaxValidationAnnotations).parse();
|
||||
new AnnotationSql(info, readConfig).parse();
|
||||
|
||||
new AnnotationClass(info).parseAttributeOverride();
|
||||
new AnnotationClass(info, readConfig).parseAttributeOverride();
|
||||
info.getDescriptor().postAnnotations();
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
|
||||
@@ -5,8 +5,6 @@ import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.meta.DeployBeanPropertyAssocOne;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Mark transient properties.
|
||||
*/
|
||||
@@ -20,29 +18,22 @@ public class TransientProperties {
|
||||
*/
|
||||
public void process(DeployBeanDescriptor<?> desc) {
|
||||
|
||||
List<DeployBeanProperty> props = desc.propertiesBase();
|
||||
for (DeployBeanProperty prop : props) {
|
||||
for (DeployBeanProperty prop : desc.propertiesBase()) {
|
||||
if (!prop.isDbRead() && !prop.isDbInsertable() && !prop.isDbUpdateable()) {
|
||||
// non-transient...
|
||||
prop.setTransient();
|
||||
}
|
||||
}
|
||||
|
||||
List<DeployBeanPropertyAssocOne<?>> ones = desc.propertiesAssocOne();
|
||||
for (DeployBeanPropertyAssocOne<?> prop : ones) {
|
||||
if (prop.getBeanTable() == null) {
|
||||
if (!prop.isEmbedded()) {
|
||||
prop.setTransient();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<DeployBeanPropertyAssocMany<?>> manys = desc.propertiesAssocMany();
|
||||
for (DeployBeanPropertyAssocMany<?> prop : manys) {
|
||||
if (prop.getBeanTable() == null) {
|
||||
for (DeployBeanPropertyAssocOne<?> prop : desc.propertiesAssocOne()) {
|
||||
if (prop.getBeanTable() == null && !prop.isEmbedded()) {
|
||||
prop.setTransient();
|
||||
}
|
||||
}
|
||||
|
||||
for (DeployBeanPropertyAssocMany<?> prop : desc.propertiesAssocMany()) {
|
||||
if (prop.getBeanTable() == null) {
|
||||
prop.setTransient();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,7 +327,7 @@ public class DLoadContext implements LoadContext {
|
||||
}
|
||||
|
||||
private BeanProperty getBeanProperty(BeanDescriptor<?> desc, String path) {
|
||||
return desc.getBeanPropertyFromPath(path);
|
||||
return desc.findPropertyFromPath(path);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,7 @@ public class DLoadManyContext extends DLoadBaseContext implements LoadManyContex
|
||||
super(parent, property.getBeanDescriptor(), path, defaultBatchSize, queryProps);
|
||||
|
||||
this.property = property;
|
||||
this.docStoreMapped = property.getTargetDescriptor().isDocStoreMapped();
|
||||
this.docStoreMapped = property.isTargetDocStoreMapped();
|
||||
// bufferList only required when using query joins (queryFetch)
|
||||
this.bufferList = (!queryFetch) ? null : new ArrayList<>();
|
||||
this.currentBuffer = createBuffer(firstBatchSize);
|
||||
|
||||
@@ -29,6 +29,7 @@ import io.ebeaninternal.server.deploy.BeanManager;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertySimpleCollection;
|
||||
import io.ebeaninternal.server.deploy.IntersectionRow;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -869,8 +870,7 @@ public final class DefaultPersister implements Persister {
|
||||
SpiTransaction t = request.getTransaction();
|
||||
|
||||
// exported ones with cascade save
|
||||
BeanPropertyAssocOne<?>[] expOnes = desc.propertiesOneExportedSave();
|
||||
for (BeanPropertyAssocOne<?> prop : expOnes) {
|
||||
for (BeanPropertyAssocOne<?> prop : desc.propertiesOneExportedSave()) {
|
||||
// check for partial beans
|
||||
if (request.isLoadedProperty(prop)) {
|
||||
EntityBean detailBean = prop.getValueAsEntityBean(parentBean);
|
||||
@@ -886,12 +886,11 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
|
||||
// many's with cascade save
|
||||
BeanPropertyAssocMany<?>[] manys = desc.propertiesManySave();
|
||||
boolean insertedParent = request.isInsertedParent();
|
||||
for (BeanPropertyAssocMany<?> many : manys) {
|
||||
for (BeanPropertyAssocMany<?> many : desc.propertiesManySave()) {
|
||||
// check that property is loaded and collection should be cascaded to
|
||||
if (request.isLoadedProperty(many) && !many.isSkipSaveBeanCollection(parentBean, insertedParent)) {
|
||||
saveMany(new SaveManyPropRequest(insertedParent, many, parentBean, request));
|
||||
saveMany2(insertedParent, many, parentBean, request);
|
||||
if (!insertedParent) {
|
||||
request.addUpdatedManyProperty(many);
|
||||
}
|
||||
@@ -899,6 +898,18 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
}
|
||||
|
||||
private void saveMany2(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
saveMany(saveManyRequest(insertedParent, many, parentBean, request));
|
||||
}
|
||||
|
||||
private SaveManyPropRequest saveManyRequest(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
if (many instanceof BeanPropertySimpleCollection) {
|
||||
return new SaveManySimpleCollection(insertedParent, many, parentBean, request);
|
||||
} else {
|
||||
return new SaveManyPropRequest(insertedParent, many, parentBean, request);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveMany(SaveManyPropRequest saveMany) {
|
||||
|
||||
if (saveMany.getMany().hasJoinTable()) {
|
||||
@@ -1131,8 +1142,7 @@ public final class DefaultPersister implements Persister {
|
||||
}
|
||||
|
||||
// Many's with delete cascade
|
||||
BeanPropertyAssocMany<?>[] manys = desc.propertiesManyDelete();
|
||||
for (BeanPropertyAssocMany<?> many : manys) {
|
||||
for (BeanPropertyAssocMany<?> many : desc.propertiesManyDelete()) {
|
||||
if (many.hasJoinTable()) {
|
||||
if (!softDelete) {
|
||||
// delete associated rows from intersection table (but not during soft delete)
|
||||
|
||||
@@ -21,11 +21,11 @@ import java.util.Map;
|
||||
*/
|
||||
class SaveManyPropRequest {
|
||||
|
||||
private final PersistRequestBean<?> request;
|
||||
final PersistRequestBean<?> request;
|
||||
private final boolean insertedParent;
|
||||
private final BeanPropertyAssocMany<?> many;
|
||||
private final EntityBean parentBean;
|
||||
private final SpiTransaction transaction;
|
||||
final BeanPropertyAssocMany<?> many;
|
||||
final EntityBean parentBean;
|
||||
final SpiTransaction transaction;
|
||||
private final boolean cascade;
|
||||
private final boolean deleteMissingChildren;
|
||||
private final boolean publish;
|
||||
@@ -35,8 +35,8 @@ class SaveManyPropRequest {
|
||||
private final boolean isMap;
|
||||
private final boolean saveRecurseSkippable;
|
||||
|
||||
private Collection<?> collection;
|
||||
private DefaultPersister persister;
|
||||
Collection<?> collection;
|
||||
DefaultPersister persister;
|
||||
private boolean deleteMissing;
|
||||
private int sortOrder;
|
||||
|
||||
@@ -127,7 +127,7 @@ class SaveManyPropRequest {
|
||||
}
|
||||
}
|
||||
|
||||
private void processDetails() {
|
||||
void processDetails() {
|
||||
|
||||
BeanProperty orderColumn = null;
|
||||
boolean hasOrderColumn = many.hasOrderColumn();
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package io.ebeaninternal.server.persist;
|
||||
|
||||
import io.ebean.SqlUpdate;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.SpiEbeanServer;
|
||||
import io.ebeaninternal.server.core.PersistRequestBean;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
|
||||
class SaveManySimpleCollection extends SaveManyPropRequest {
|
||||
|
||||
SaveManySimpleCollection(boolean insertedParent, BeanPropertyAssocMany<?> many, EntityBean parentBean, PersistRequestBean<?> request) {
|
||||
super(insertedParent, many, parentBean, request);
|
||||
}
|
||||
|
||||
@Override
|
||||
void processDetails() {
|
||||
|
||||
Object parentId = request.getBeanId();
|
||||
|
||||
SqlUpdate sqlDelete = many.deleteByParentId(parentId, null);
|
||||
|
||||
SpiEbeanServer server = request.getServer();
|
||||
server.execute(sqlDelete, transaction);
|
||||
|
||||
transaction.depth(+1);
|
||||
|
||||
String insert = many.insertElementCollection();
|
||||
SqlUpdate sqlInsert = server.createSqlUpdate(insert);
|
||||
|
||||
for (Object value : collection) {
|
||||
|
||||
sqlInsert.setParameter(1, parentId);
|
||||
sqlInsert.setParameter(2, value);
|
||||
server.execute(sqlInsert, transaction);
|
||||
}
|
||||
|
||||
transaction.depth(-1);
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,6 @@ import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NoSuchElementException;
|
||||
@@ -56,6 +55,8 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CQuery.class);
|
||||
|
||||
private static final CQueryCollectionAddNoop NOOP_ADD = new CQueryCollectionAddNoop();
|
||||
|
||||
/**
|
||||
* The resultSet rows read.
|
||||
*/
|
||||
@@ -99,7 +100,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
/**
|
||||
* The help for the 'master' collection.
|
||||
*/
|
||||
private final BeanCollectionHelp<T> help;
|
||||
private final CQueryCollectionAdd help;
|
||||
|
||||
/**
|
||||
* The overall find request wrapper object.
|
||||
@@ -147,7 +148,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
/**
|
||||
* For master detail query.
|
||||
*/
|
||||
private final BeanPropertyAssocMany<?> manyProperty;
|
||||
private final STreePropertyAssocMany manyProperty;
|
||||
|
||||
private DataReader dataReader;
|
||||
|
||||
@@ -180,20 +181,26 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
private long executionTimeMicros;
|
||||
|
||||
/**
|
||||
* Flag set when findIterate is being read audited.
|
||||
* Flag set when read auditing.
|
||||
*/
|
||||
private boolean audit;
|
||||
|
||||
/**
|
||||
* Flag set when findIterate is being read audited meaning we log in batches.
|
||||
*/
|
||||
private boolean auditFindIterate;
|
||||
|
||||
/**
|
||||
* A buffer of Ids collected for findIterate auditing.
|
||||
*/
|
||||
private List<Object> auditFindIterateIds;
|
||||
private List<Object> auditIds;
|
||||
|
||||
/**
|
||||
* Create the Sql select based on the request.
|
||||
*/
|
||||
public CQuery(OrmQueryRequest<T> request, CQueryPredicates predicates, CQueryPlan queryPlan) {
|
||||
this.request = request;
|
||||
this.audit = request.isAuditReads();
|
||||
this.queryPlan = queryPlan;
|
||||
this.query = request.getQuery();
|
||||
this.queryMode = query.getMode();
|
||||
@@ -220,7 +227,11 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
this.logWhereSql = queryPlan.getLogWhereSql();
|
||||
this.desc = request.getBeanDescriptor();
|
||||
this.predicates = predicates;
|
||||
this.help = createHelp(request);
|
||||
if (lazyLoadManyProperty != null) {
|
||||
this.help = NOOP_ADD;
|
||||
} else {
|
||||
this.help = createHelp(request);
|
||||
}
|
||||
this.collection = (help != null ? help.createEmptyNoParent() : null);
|
||||
}
|
||||
|
||||
@@ -233,7 +244,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
// subQuery compiled for InQueryExpression
|
||||
return null;
|
||||
}
|
||||
return BeanCollectionHelpFactory.create(request);
|
||||
return BeanCollectionHelpFactory.create(manyType, request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,7 +385,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
*/
|
||||
public void close() {
|
||||
try {
|
||||
if (auditFindIterateIds != null && !auditFindIterateIds.isEmpty()) {
|
||||
if (auditFindIterate && auditIds != null && !auditIds.isEmpty()) {
|
||||
auditIterateLogMessage();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
@@ -510,8 +521,8 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
}
|
||||
|
||||
protected EntityBean next() {
|
||||
if (auditFindIterate) {
|
||||
auditIterateNextBean();
|
||||
if (audit) {
|
||||
auditNextBean();
|
||||
}
|
||||
hasNextCache = false;
|
||||
if (nextBean == null) {
|
||||
@@ -656,7 +667,7 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
* per SqlSelect. This can be null.
|
||||
*/
|
||||
@Override
|
||||
public BeanPropertyAssocMany<?> getManyProperty() {
|
||||
public STreePropertyAssocMany getManyProperty() {
|
||||
return manyProperty;
|
||||
}
|
||||
|
||||
@@ -747,30 +758,25 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
*/
|
||||
void auditFindMany() {
|
||||
|
||||
if (!collection.isEmpty()) {
|
||||
if (auditIds != null && !auditIds.isEmpty()) {
|
||||
// get the id values of the underlying collection
|
||||
List<Object> ids = new ArrayList<>(collection.size());
|
||||
Collection<T> underlyingBeans = collection.getActualDetails();
|
||||
for (T underlyingBean : underlyingBeans) {
|
||||
ids.add(desc.getIdForJson(underlyingBean));
|
||||
}
|
||||
ReadEvent futureReadEvent = query.getFutureFetchAudit();
|
||||
if (futureReadEvent == null) {
|
||||
// normal query execution
|
||||
desc.readAuditMany(queryPlan.getAuditQueryKey(), bindLog, ids);
|
||||
desc.readAuditMany(queryPlan.getAuditQueryKey(), bindLog, auditIds);
|
||||
} else {
|
||||
// this query was executed via findFutureList() and the prepare()
|
||||
// has already been called so set the details and log
|
||||
futureReadEvent.setQueryKey(queryPlan.getAuditQueryKey());
|
||||
futureReadEvent.setBindLog(bindLog);
|
||||
futureReadEvent.setIds(ids);
|
||||
futureReadEvent.setIds(auditIds);
|
||||
desc.readAuditFutureMany(futureReadEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that read auditing is occurring on a this findIterate query.
|
||||
* Indicate that read auditing is occurring on this findIterate query.
|
||||
*/
|
||||
void auditFindIterate() {
|
||||
auditFindIterate = true;
|
||||
@@ -780,21 +786,21 @@ public class CQuery<T> implements DbReadContext, CancelableQuery, SpiProfileTran
|
||||
* Send the current buffer of findIterate collected ids to the audit log.
|
||||
*/
|
||||
private void auditIterateLogMessage() {
|
||||
desc.readAuditMany(queryPlan.getAuditQueryKey(), bindLog, auditFindIterateIds);
|
||||
desc.readAuditMany(queryPlan.getAuditQueryKey(), bindLog, auditIds);
|
||||
// create a new list on demand with the next bean/id
|
||||
auditFindIterateIds = null;
|
||||
auditIds = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the id to the audit id buffer and flush if needed in batches of 100.
|
||||
*/
|
||||
private void auditIterateNextBean() {
|
||||
private void auditNextBean() {
|
||||
|
||||
if (auditFindIterateIds == null) {
|
||||
auditFindIterateIds = new ArrayList<>(100);
|
||||
if (auditIds == null) {
|
||||
auditIds = new ArrayList<>(100);
|
||||
}
|
||||
auditFindIterateIds.add(desc.getIdForJson(nextBean));
|
||||
if (auditFindIterateIds.size() >= 100) {
|
||||
auditIds.add(desc.getIdForJson(nextBean));
|
||||
if (auditFindIterate && auditIds.size() >= 100) {
|
||||
auditIterateLogMessage();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.persist.Binder;
|
||||
@@ -524,20 +523,15 @@ class CQueryBuilder {
|
||||
return rawSqlHandler.buildSql(request, predicates, query.getRawSql().getSql());
|
||||
}
|
||||
|
||||
BeanPropertyAssocMany<?> manyProp = select.getManyProperty();
|
||||
|
||||
boolean useSqlLimiter = false;
|
||||
|
||||
StringBuilder sb = new StringBuilder(500);
|
||||
|
||||
String dbOrderBy = predicates.getDbOrderBy();
|
||||
|
||||
if (selectClause != null) {
|
||||
sb.append(selectClause);
|
||||
|
||||
} else {
|
||||
|
||||
useSqlLimiter = (query.hasMaxRowsOrFirstRow() && manyProp == null);
|
||||
useSqlLimiter = (query.hasMaxRowsOrFirstRow() && select.getManyProperty() == null);
|
||||
|
||||
if (!useSqlLimiter) {
|
||||
sb.append("select ");
|
||||
@@ -686,7 +680,7 @@ class CQueryBuilder {
|
||||
throw new IllegalArgumentException("Illegal enum: "+ orderBy);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Append where or and based on the hasWhere flag.
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* Defines interface for adding beans to the collection which might be a List, Set or Map.
|
||||
*/
|
||||
public interface CQueryCollectionAdd<T> {
|
||||
|
||||
/**
|
||||
* Create an empty collection.
|
||||
*/
|
||||
BeanCollection<T> createEmptyNoParent();
|
||||
|
||||
/**
|
||||
* Add a bean to the List Set or Map.
|
||||
*/
|
||||
void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
|
||||
/**
|
||||
* A NOOP based CQueryCollectionAdd for use with lazy loading many queries where the
|
||||
* beans loaded into the collection are added to the collection(s) of the parent(s).
|
||||
*/
|
||||
class CQueryCollectionAddNoop<T> implements CQueryCollectionAdd<T> {
|
||||
|
||||
/**
|
||||
* Return null as we are not collecting the beans.
|
||||
*/
|
||||
@Override
|
||||
public BeanCollection<T> createEmptyNoParent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do nothing for this case.
|
||||
*/
|
||||
@Override
|
||||
public void add(BeanCollection<?> collection, EntityBean bean, boolean withCheck) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import io.ebeaninternal.metric.MetricFactory;
|
||||
import io.ebeaninternal.metric.TimedMetric;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.core.timezone.DataTimeZone;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.query.CQueryPlanStats.Snapshot;
|
||||
import io.ebeaninternal.server.type.DataBind;
|
||||
import io.ebeaninternal.server.type.DataReader;
|
||||
@@ -74,7 +73,7 @@ public class CQueryPlan {
|
||||
/**
|
||||
* Encrypted properties required additional binding.
|
||||
*/
|
||||
private final BeanProperty[] encryptedProps;
|
||||
private final STreeProperty[] encryptedProps;
|
||||
|
||||
private final CQueryPlanStats stats;
|
||||
|
||||
@@ -177,9 +176,8 @@ public class CQueryPlan {
|
||||
DataBind bindEncryptedProperties(PreparedStatement stmt, Connection conn) throws SQLException {
|
||||
DataBind dataBind = new DataBind(dataTimeZone, stmt, conn);
|
||||
if (encryptedProps != null) {
|
||||
for (BeanProperty encryptedProp : encryptedProps) {
|
||||
String key = encryptedProp.getEncryptKey().getStringValue();
|
||||
dataBind.setString(key);
|
||||
for (STreeProperty encryptedProp : encryptedProps) {
|
||||
dataBind.setString(encryptedProp.getEncryptKeyAsString());
|
||||
}
|
||||
}
|
||||
return dataBind;
|
||||
|
||||
@@ -231,7 +231,7 @@ public class CQueryPredicates {
|
||||
}
|
||||
}
|
||||
|
||||
BeanPropertyAssocMany<?> manyProperty = request.getManyProperty();
|
||||
BeanPropertyAssocMany<?> manyProperty = request.determineMany();
|
||||
if (manyProperty != null) {
|
||||
OrmQueryProperties chunk = query.getDetail().getChunk(manyProperty.getName(), false);
|
||||
SpiExpressionList<?> filterManyExpr = chunk.getFilterMany();
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
public class ExtraJoin {
|
||||
|
||||
private final STreePropertyAssoc property;
|
||||
private final boolean containsMany;
|
||||
|
||||
public ExtraJoin(STreePropertyAssoc property, boolean containsMany) {
|
||||
this.property = property;
|
||||
this.containsMany = containsMany;
|
||||
}
|
||||
|
||||
public STreePropertyAssoc getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
public boolean isContainsMany() {
|
||||
return containsMany;
|
||||
}
|
||||
}
|
||||
+12
-2
@@ -8,10 +8,10 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* A property in the SQL Tree.
|
||||
*
|
||||
* <p>
|
||||
* A BeanProperty or a dynamically created property based on formula.
|
||||
*/
|
||||
public interface SqlTreeProperty {
|
||||
public interface STreeProperty {
|
||||
|
||||
/**
|
||||
* Return the property name.
|
||||
@@ -38,6 +38,16 @@ public interface SqlTreeProperty {
|
||||
*/
|
||||
boolean isAggregation();
|
||||
|
||||
/**
|
||||
* Return true if the property is a formula.
|
||||
*/
|
||||
boolean isFormula();
|
||||
|
||||
/**
|
||||
* Return the encryption key as a string value (when the property is encrypted).
|
||||
*/
|
||||
String getEncryptKeyAsString();
|
||||
|
||||
/**
|
||||
* Return the Expression language prefix (join path).
|
||||
*/
|
||||
@@ -0,0 +1,39 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.id.IdBinder;
|
||||
|
||||
public interface STreePropertyAssoc extends STreeProperty {
|
||||
|
||||
/**
|
||||
* Return the extra where clause if set.
|
||||
*/
|
||||
String getExtraWhere();
|
||||
|
||||
/**
|
||||
* Return the type of the target (other side).
|
||||
*/
|
||||
STreeType target();
|
||||
|
||||
/**
|
||||
* Return the IdBinder of the underlying type.
|
||||
*/
|
||||
IdBinder getIdBinder();
|
||||
|
||||
/**
|
||||
* Add a Join with the given alias.
|
||||
*/
|
||||
SqlJoinType addJoin(SqlJoinType joinType, String alias2, String alias, DbSqlContext ctx);
|
||||
|
||||
/**
|
||||
* Add a Join with the given prefix (determining the alias).
|
||||
*/
|
||||
SqlJoinType addJoin(SqlJoinType joinType, String prefix, DbSqlContext ctx);
|
||||
|
||||
/**
|
||||
* Add a bean to the parent.
|
||||
*/
|
||||
void setValue(EntityBean parentBean, Object contextBean);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.bean.BeanCollection;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
|
||||
public interface STreePropertyAssocMany extends STreePropertyAssoc {
|
||||
|
||||
/**
|
||||
* Append exported columns to the select.
|
||||
*/
|
||||
void addSelectExported(DbSqlContext ctx, String prefix);
|
||||
|
||||
/**
|
||||
* Return true if this is a ManyToMany with history.
|
||||
*/
|
||||
boolean isManyToManyWithHistory();
|
||||
|
||||
/**
|
||||
* Return a reference collection.
|
||||
*/
|
||||
BeanCollection<?> createReferenceIfNull(EntityBean localBean);
|
||||
|
||||
/**
|
||||
* Return true if the property has a join table.
|
||||
*/
|
||||
boolean hasJoinTable();
|
||||
|
||||
/**
|
||||
* Return the intersection table join.
|
||||
*/
|
||||
TableJoin getIntersectionTableJoin();
|
||||
|
||||
/**
|
||||
* Add a bean to the collection.
|
||||
*/
|
||||
void addBeanToCollectionWithCreate(EntityBean contextParent, EntityBean detailBean, boolean withCheck);
|
||||
|
||||
/**
|
||||
* Return true if the property is excluded from history.
|
||||
*/
|
||||
boolean isExcludedFromHistory();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
public interface STreePropertyAssocOne extends STreePropertyAssoc {
|
||||
|
||||
/**
|
||||
* Return true if the property is an Id.
|
||||
*/
|
||||
boolean isAssocId();
|
||||
|
||||
/**
|
||||
* Return the scalar type of the associated id property.
|
||||
*/
|
||||
ScalarType<?> getIdScalarType();
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.bean.PersistenceContext;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.InheritInfo;
|
||||
import io.ebeaninternal.server.deploy.id.IdBinder;
|
||||
|
||||
/**
|
||||
* Bean type interface for Sql query tree.
|
||||
*/
|
||||
public interface STreeType {
|
||||
|
||||
/**
|
||||
* Return the bean short name.
|
||||
*/
|
||||
String getName();
|
||||
|
||||
/**
|
||||
* Return true if the underlying type has an Id property.
|
||||
*/
|
||||
boolean hasId();
|
||||
|
||||
/**
|
||||
* Return true if the type is for ElementCollection (not mapped to an entity type/class).
|
||||
*/
|
||||
boolean isElementType();
|
||||
|
||||
/**
|
||||
* Return true if the type uses soft delete.
|
||||
*/
|
||||
boolean isSoftDelete();
|
||||
|
||||
/**
|
||||
* Return true if the type uses history.
|
||||
*/
|
||||
boolean isHistorySupport();
|
||||
|
||||
/**
|
||||
* Return true if the type is RawSql based.
|
||||
*/
|
||||
boolean isRawSqlBased();
|
||||
|
||||
/**
|
||||
* Return the soft delete predicate using the given table alias.
|
||||
*/
|
||||
String getSoftDeletePredicate(String baseTableAlias);
|
||||
|
||||
/**
|
||||
* Return the scalar properties.
|
||||
*/
|
||||
STreeProperty[] propsBaseScalar();
|
||||
|
||||
/**
|
||||
* Return the embedded bean properties.
|
||||
*/
|
||||
STreePropertyAssoc[] propsEmbedded();
|
||||
|
||||
/**
|
||||
* Return the associated one properties.
|
||||
*/
|
||||
STreePropertyAssocOne[] propsOne();
|
||||
|
||||
/**
|
||||
* Return the associated many properties.
|
||||
*/
|
||||
STreePropertyAssocMany[] propsMany();
|
||||
|
||||
/**
|
||||
* Return the inheritance information for this type.
|
||||
*/
|
||||
InheritInfo getInheritInfo();
|
||||
|
||||
/**
|
||||
* Return the IdBinder for this type.
|
||||
*/
|
||||
IdBinder getIdBinder();
|
||||
|
||||
/**
|
||||
* Create a new entity bean instance.
|
||||
*/
|
||||
EntityBean createEntityBean();
|
||||
|
||||
/**
|
||||
* Put the entity bean into the persistence context.
|
||||
*/
|
||||
Object contextPutIfAbsent(PersistenceContext persistenceContext, Object id, EntityBean localBean);
|
||||
|
||||
/**
|
||||
* Set draft status on the entity bean.
|
||||
*/
|
||||
void setDraft(EntityBean localBean);
|
||||
|
||||
/**
|
||||
* Invoke any post load listeners.
|
||||
*/
|
||||
void postLoad(Object localBean);
|
||||
|
||||
/**
|
||||
* Return the base table to use given the temporalMode.
|
||||
*/
|
||||
String getBaseTable(SpiQuery.TemporalMode temporalMode);
|
||||
|
||||
/**
|
||||
* Return true if the given path is an embedded bean.
|
||||
*/
|
||||
boolean isEmbeddedPath(String propertyPath);
|
||||
|
||||
/**
|
||||
* Return the bean property traversing the object graph and taking into account inheritance.
|
||||
*/
|
||||
STreeProperty findPropertyFromPath(String property);
|
||||
|
||||
/**
|
||||
* Find a known property.
|
||||
*/
|
||||
STreeProperty findProperty(String propName);
|
||||
|
||||
/**
|
||||
* Find and return property allowing for dynamic formula properties.
|
||||
*/
|
||||
STreeProperty findPropertyWithDynamic(String baseName);
|
||||
|
||||
/**
|
||||
* Return an extra join if the property path requires it.
|
||||
*/
|
||||
ExtraJoin extraJoin(String propertyPath);
|
||||
|
||||
/**
|
||||
* Load the property taking into account inheritance.
|
||||
*/
|
||||
void inheritanceLoad(SqlBeanLoad sqlBeanLoad, STreeProperty property, DbReadContext ctx);
|
||||
|
||||
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -18,7 +16,7 @@ class SqlTree {
|
||||
/**
|
||||
* Property if resultSet contains master and detail rows.
|
||||
*/
|
||||
private final BeanPropertyAssocMany<?> manyProperty;
|
||||
private final STreePropertyAssocMany manyProperty;
|
||||
|
||||
private final Set<String> includes;
|
||||
|
||||
@@ -38,7 +36,7 @@ class SqlTree {
|
||||
/**
|
||||
* Encrypted Properties require additional binding.
|
||||
*/
|
||||
private final BeanProperty[] encryptedProps;
|
||||
private final STreeProperty[] encryptedProps;
|
||||
|
||||
/**
|
||||
* Where clause for inheritance.
|
||||
@@ -51,7 +49,7 @@ class SqlTree {
|
||||
* Create the SqlSelectClause.
|
||||
*/
|
||||
SqlTree(String summary, SqlTreeNode rootNode, String distinctOn, String selectSql, String fromSql, String groupBy, String inheritanceWhereSql,
|
||||
BeanProperty[] encryptedProps, BeanPropertyAssocMany<?> manyProperty, Set<String> includes, boolean includeJoins) {
|
||||
STreeProperty[] encryptedProps, STreePropertyAssocMany manyProperty, Set<String> includes, boolean includeJoins) {
|
||||
|
||||
this.summary = summary;
|
||||
this.rootNode = rootNode;
|
||||
@@ -147,11 +145,11 @@ class SqlTree {
|
||||
* Return the property that is associated with the many. There can only be one
|
||||
* per SqlSelect. This can be null.
|
||||
*/
|
||||
BeanPropertyAssocMany<?> getManyProperty() {
|
||||
STreePropertyAssocMany getManyProperty() {
|
||||
return manyProperty;
|
||||
}
|
||||
|
||||
BeanProperty[] getEncryptedProps() {
|
||||
STreeProperty[] getEncryptedProps() {
|
||||
return encryptedProps;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Special Map of the logical property joins to table alias.
|
||||
@@ -61,16 +58,11 @@ class SqlTreeAlias {
|
||||
/**
|
||||
* Add joins.
|
||||
*/
|
||||
public void addJoin(Set<String> propJoins, BeanDescriptor<?> desc) {
|
||||
public void addJoin(Set<String> propJoins, STreeType desc) {
|
||||
if (propJoins != null) {
|
||||
for (String propJoin : propJoins) {
|
||||
ElPropertyDeploy elProp = desc.getElPropertyDeploy(propJoin);
|
||||
if (elProp == null) {
|
||||
throw new PersistenceException("Invalid path " + propJoin + " from " + desc.getFullName());
|
||||
|
||||
} else if (elProp.getBeanProperty().isEmbedded()) {
|
||||
if (desc.isEmbeddedPath(propJoin)) {
|
||||
addEmbeddedPropertyJoin(propJoin);
|
||||
|
||||
} else {
|
||||
addPropertyJoin(propJoin, joinProps);
|
||||
}
|
||||
|
||||
@@ -6,14 +6,8 @@ import io.ebeaninternal.api.PropertyJoin;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiQuery.Type;
|
||||
import io.ebeaninternal.server.core.OrmQueryRequest;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.InheritInfo;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryDetail;
|
||||
import io.ebeaninternal.server.querydefn.OrmQueryProperties;
|
||||
import org.slf4j.Logger;
|
||||
@@ -37,7 +31,7 @@ public final class SqlTreeBuilder {
|
||||
|
||||
private final SpiQuery<?> query;
|
||||
|
||||
private final BeanDescriptor<?> desc;
|
||||
private final STreeType desc;
|
||||
|
||||
private final OrmQueryDetail queryDetail;
|
||||
|
||||
@@ -51,7 +45,7 @@ public final class SqlTreeBuilder {
|
||||
/**
|
||||
* Property if resultSet contains master and detail rows.
|
||||
*/
|
||||
private BeanPropertyAssocMany<?> manyProperty;
|
||||
private STreePropertyAssocMany manyProperty;
|
||||
|
||||
private final SqlTreeAlias alias;
|
||||
|
||||
@@ -141,7 +135,7 @@ public final class SqlTreeBuilder {
|
||||
String fromSql = null;
|
||||
String inheritanceWhereSql = null;
|
||||
String groupBy = null;
|
||||
BeanProperty[] encryptedProps = null;
|
||||
STreeProperty[] encryptedProps = null;
|
||||
if (!rawSql) {
|
||||
selectSql = buildSelectClause();
|
||||
fromSql = buildFromClause();
|
||||
@@ -233,7 +227,7 @@ public final class SqlTreeBuilder {
|
||||
return ctx.getContent();
|
||||
}
|
||||
|
||||
private void buildRoot(BeanDescriptor<?> desc) {
|
||||
private void buildRoot(STreeType desc) {
|
||||
|
||||
rootNode = buildSelectChain(null, null, desc, null);
|
||||
|
||||
@@ -253,26 +247,24 @@ public final class SqlTreeBuilder {
|
||||
* Recursively build the query tree depending on what leaves in the tree
|
||||
* should be included.
|
||||
*/
|
||||
private SqlTreeNode buildSelectChain(String prefix, BeanPropertyAssoc<?> prop,
|
||||
BeanDescriptor<?> desc, List<SqlTreeNode> joinList) {
|
||||
private SqlTreeNode buildSelectChain(String prefix, STreePropertyAssoc prop,
|
||||
STreeType desc, List<SqlTreeNode> joinList) {
|
||||
|
||||
List<SqlTreeNode> myJoinList = new ArrayList<>();
|
||||
|
||||
BeanPropertyAssocOne<?>[] ones = desc.propertiesOne();
|
||||
for (BeanPropertyAssocOne<?> one : ones) {
|
||||
for (STreePropertyAssocOne one : desc.propsOne()) {
|
||||
String propPrefix = SplitName.add(prefix, one.getName());
|
||||
if (isIncludeBean(propPrefix)) {
|
||||
selectIncludes.add(propPrefix);
|
||||
buildSelectChain(propPrefix, one, one.getTargetDescriptor(), myJoinList);
|
||||
buildSelectChain(propPrefix, one, one.target(), myJoinList);
|
||||
}
|
||||
}
|
||||
|
||||
BeanPropertyAssocMany<?>[] manys = desc.propertiesMany();
|
||||
for (BeanPropertyAssocMany<?> many : manys) {
|
||||
for (STreePropertyAssocMany many : desc.propsMany()) {
|
||||
String propPrefix = SplitName.add(prefix, many.getName());
|
||||
if (isIncludeMany(propPrefix, many)) {
|
||||
selectIncludes.add(propPrefix);
|
||||
buildSelectChain(propPrefix, many, many.getTargetDescriptor(), myJoinList);
|
||||
buildSelectChain(propPrefix, many, many.target(), myJoinList);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -304,24 +296,24 @@ public final class SqlTreeBuilder {
|
||||
|
||||
Collection<PropertyJoin> includes = manyWhereJoins.getPropertyJoins();
|
||||
for (PropertyJoin joinProp : includes) {
|
||||
BeanPropertyAssoc<?> beanProperty = (BeanPropertyAssoc<?>) desc.getBeanPropertyFromPath(joinProp.getProperty());
|
||||
STreePropertyAssoc beanProperty = (STreePropertyAssoc) desc.findPropertyFromPath(joinProp.getProperty());
|
||||
SqlTreeNodeManyWhereJoin nodeJoin = new SqlTreeNodeManyWhereJoin(joinProp.getProperty(), beanProperty, joinProp.getSqlJoinType());
|
||||
myJoinList.add(nodeJoin);
|
||||
}
|
||||
}
|
||||
|
||||
private SqlTreeNode buildNode(String prefix, BeanPropertyAssoc<?> prop, BeanDescriptor<?> desc, List<SqlTreeNode> myList, SqlTreeProperties props) {
|
||||
private SqlTreeNode buildNode(String prefix, STreePropertyAssoc prop, STreeType desc, List<SqlTreeNode> myList, SqlTreeProperties props) {
|
||||
|
||||
if (prefix == null) {
|
||||
buildExtraJoins(desc, myList);
|
||||
|
||||
// Optional many property for lazy loading query
|
||||
BeanPropertyAssocMany<?> lazyLoadMany = (query == null) ? null : query.getLazyLoadMany();
|
||||
STreePropertyAssocMany lazyLoadMany = (query == null) ? null : query.getLazyLoadMany();
|
||||
boolean withId = !rawNoId && !subQuery && (query == null || query.isWithId());
|
||||
return new SqlTreeNodeRoot(desc, props, myList, withId, includeJoin, lazyLoadMany, temporalMode, disableLazyLoad);
|
||||
|
||||
} else if (prop instanceof BeanPropertyAssocMany<?>) {
|
||||
return new SqlTreeNodeManyRoot(prefix, (BeanPropertyAssocMany<?>) prop, props, myList, temporalMode, disableLazyLoad);
|
||||
} else if (prop instanceof STreePropertyAssocMany) {
|
||||
return new SqlTreeNodeManyRoot(prefix, (STreePropertyAssocMany) prop, props, myList, temporalMode, disableLazyLoad);
|
||||
|
||||
} else {
|
||||
// do not read Id on child beans (e.g. when used with fetch())
|
||||
@@ -334,7 +326,7 @@ public final class SqlTreeBuilder {
|
||||
* Build extra joins to support properties used in where clause but not
|
||||
* already in select clause.
|
||||
*/
|
||||
private void buildExtraJoins(BeanDescriptor<?> desc, List<SqlTreeNode> myList) {
|
||||
private void buildExtraJoins(STreeType desc, List<SqlTreeNode> myList) {
|
||||
|
||||
if (rawSql) {
|
||||
return;
|
||||
@@ -383,25 +375,25 @@ public final class SqlTreeBuilder {
|
||||
* This means it can included individual properties of an embedded bean.
|
||||
* </p>
|
||||
*/
|
||||
private void addPropertyToSubQuery(SqlTreeProperties selectProps, BeanDescriptor<?> desc, String propName) {
|
||||
private void addPropertyToSubQuery(SqlTreeProperties selectProps, STreeType desc, String propName) {
|
||||
|
||||
BeanProperty p = desc.findBeanProperty(propName);
|
||||
STreeProperty p = desc.findProperty(propName);
|
||||
if (p == null) {
|
||||
logger.error("property [" + propName + "]not found on " + desc + " for query - excluding it.");
|
||||
|
||||
} else if (p instanceof BeanPropertyAssoc<?> && p.isEmbedded()) {
|
||||
} else if (p instanceof STreePropertyAssoc && p.isEmbedded()) {
|
||||
// if the property is embedded we need to lookup the real column name
|
||||
int pos = propName.indexOf('.');
|
||||
if (pos > -1) {
|
||||
String name = propName.substring(pos + 1);
|
||||
p = ((BeanPropertyAssoc<?>) p).getTargetDescriptor().findBeanProperty(name);
|
||||
p = ((STreePropertyAssoc) p).target().findProperty(name);
|
||||
}
|
||||
}
|
||||
|
||||
selectProps.add(p);
|
||||
}
|
||||
|
||||
private void addProperty(SqlTreeProperties selectProps, BeanDescriptor<?> desc,
|
||||
private void addProperty(SqlTreeProperties selectProps, STreeType desc,
|
||||
OrmQueryProperties queryProps, String propName) {
|
||||
|
||||
if (subQuery) {
|
||||
@@ -418,7 +410,7 @@ public final class SqlTreeBuilder {
|
||||
|
||||
// make sure we only included the base/embedded bean once
|
||||
if (!selectProps.containsProperty(baseName)) {
|
||||
SqlTreeProperty p = desc.findSqlTreeProperty(baseName);
|
||||
STreeProperty p = desc.findPropertyWithDynamic(baseName);
|
||||
if (p == null) {
|
||||
logger.error("property [" + propName + "] not found on " + desc + " for query - excluding it.");
|
||||
|
||||
@@ -436,17 +428,17 @@ public final class SqlTreeBuilder {
|
||||
} else {
|
||||
// find the property including searching the
|
||||
// sub class hierarchy if required
|
||||
SqlTreeProperty p = desc.findSqlTreeProperty(propName);
|
||||
STreeProperty p = desc.findPropertyWithDynamic(propName);
|
||||
if (p == null) {
|
||||
logger.error("property [" + propName + "] not found on " + desc + " for query - excluding it.");
|
||||
p = desc.findBeanProperty("id");
|
||||
p = desc.findProperty("id");
|
||||
selectProps.add(p);
|
||||
|
||||
} else if (p.isId() && excludeIdProperty()) {
|
||||
// do not bother to include id for normal queries as the
|
||||
// id is always added (except for subQueries)
|
||||
|
||||
} else if (p instanceof BeanPropertyAssoc<?>) {
|
||||
} else if (p instanceof STreePropertyAssoc) {
|
||||
// need to check if this property should be
|
||||
// excluded. This occurs when this property is
|
||||
// included as a bean join. With a bean join
|
||||
@@ -463,7 +455,7 @@ public final class SqlTreeBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
private SqlTreeProperties getBaseSelectPartial(BeanDescriptor<?> desc, OrmQueryProperties queryProps) {
|
||||
private SqlTreeProperties getBaseSelectPartial(STreeType desc, OrmQueryProperties queryProps) {
|
||||
|
||||
SqlTreeProperties selectProps = new SqlTreeProperties();
|
||||
selectProps.setReadOnly(queryProps.isReadOnly());
|
||||
@@ -484,7 +476,7 @@ public final class SqlTreeBuilder {
|
||||
return selectProps;
|
||||
}
|
||||
|
||||
private SqlTreeProperties getBaseSelect(BeanDescriptor<?> desc, OrmQueryProperties queryProps) {
|
||||
private SqlTreeProperties getBaseSelect(STreeType desc, OrmQueryProperties queryProps) {
|
||||
|
||||
boolean partial = queryProps != null && !queryProps.allProperties();
|
||||
if (partial) {
|
||||
@@ -495,17 +487,16 @@ public final class SqlTreeBuilder {
|
||||
selectProps.setAllProperties();
|
||||
|
||||
// normal simple properties of the bean
|
||||
selectProps.add(desc.propertiesBaseScalar());
|
||||
selectProps.add(desc.propertiesEmbedded());
|
||||
selectProps.add(desc.propsBaseScalar());
|
||||
selectProps.add(desc.propsEmbedded());
|
||||
|
||||
BeanPropertyAssocOne<?>[] propertiesOne = desc.propertiesOne();
|
||||
for (BeanPropertyAssocOne<?> aPropertiesOne : propertiesOne) {
|
||||
for (STreePropertyAssocOne propertyAssocOne : desc.propsOne()) {
|
||||
//noinspection StatementWithEmptyBody
|
||||
if (queryProps != null && queryProps.isIncludedBeanJoin(aPropertiesOne.getName())) {
|
||||
if (queryProps != null && queryProps.isIncludedBeanJoin(propertyAssocOne.getName())) {
|
||||
// if it is a joined bean... then don't add the property
|
||||
// as it will have its own entire Node in the SqlTree
|
||||
} else {
|
||||
selectProps.add(aPropertiesOne);
|
||||
selectProps.add(propertyAssocOne);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,7 +512,7 @@ public final class SqlTreeBuilder {
|
||||
/**
|
||||
* Return true if this many node should be included in the query.
|
||||
*/
|
||||
private boolean isIncludeMany(String propName, BeanPropertyAssocMany<?> manyProp) {
|
||||
private boolean isIncludeMany(String propName, STreePropertyAssocMany manyProp) {
|
||||
|
||||
if (queryDetail.isJoinsEmpty()) {
|
||||
return false;
|
||||
@@ -588,9 +579,9 @@ public final class SqlTreeBuilder {
|
||||
*/
|
||||
private final Map<String, SqlTreeNodeExtraJoin> rootRegister = new HashMap<>();
|
||||
|
||||
private final BeanDescriptor<?> desc;
|
||||
private final STreeType desc;
|
||||
|
||||
private IncludesDistiller(BeanDescriptor<?> desc, Set<String> selectIncludes,
|
||||
private IncludesDistiller(STreeType desc, Set<String> selectIncludes,
|
||||
Set<String> predicateIncludes) {
|
||||
this.desc = desc;
|
||||
this.selectIncludes = selectIncludes;
|
||||
@@ -645,25 +636,14 @@ public final class SqlTreeBuilder {
|
||||
*/
|
||||
private SqlTreeNodeExtraJoin createJoinLeaf(String propertyName) {
|
||||
|
||||
ElPropertyValue elGetValue = desc.getElGetValue(propertyName);
|
||||
|
||||
if (elGetValue == null) {
|
||||
// this can occur for master detail queries
|
||||
// with concatenated keys (so not an error now)
|
||||
ExtraJoin extra = desc.extraJoin(propertyName);
|
||||
if (extra == null) {
|
||||
return null;
|
||||
}
|
||||
BeanProperty beanProperty = elGetValue.getBeanProperty();
|
||||
if (beanProperty instanceof BeanPropertyAssoc<?>) {
|
||||
BeanPropertyAssoc<?> assocProp = (BeanPropertyAssoc<?>) beanProperty;
|
||||
if (assocProp.isEmbedded()) {
|
||||
// no extra join required for embedded beans
|
||||
return null;
|
||||
}
|
||||
SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, assocProp, elGetValue.containsMany());
|
||||
} else {
|
||||
SqlTreeNodeExtraJoin extraJoin = new SqlTreeNodeExtraJoin(propertyName, extra.getProperty(), extra.isContainsMany());
|
||||
joinRegister.put(propertyName, extraJoin);
|
||||
return extraJoin;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -674,10 +654,8 @@ public final class SqlTreeBuilder {
|
||||
* not specified and is implicitly created.
|
||||
* </p>
|
||||
*/
|
||||
private SqlTreeNodeExtraJoin findExtraJoinRoot(String includeProp,
|
||||
SqlTreeNodeExtraJoin childJoin) {
|
||||
private SqlTreeNodeExtraJoin findExtraJoinRoot(String includeProp, SqlTreeNodeExtraJoin childJoin) {
|
||||
while (true) {
|
||||
|
||||
int dotPos = includeProp.lastIndexOf('.');
|
||||
if (dotPos == -1) {
|
||||
// no parent possible(parent is root)
|
||||
|
||||
@@ -9,11 +9,6 @@ import io.ebean.util.SplitName;
|
||||
import io.ebean.util.StringHelper;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.api.SpiQuery.Mode;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanProperty;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.InheritInfo;
|
||||
@@ -34,7 +29,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
private static final SqlTreeNode[] NO_CHILDREN = new SqlTreeNode[0];
|
||||
|
||||
protected final BeanDescriptor<?> desc;
|
||||
protected final STreeType desc;
|
||||
|
||||
protected final IdBinder idBinder;
|
||||
|
||||
@@ -48,14 +43,14 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
*/
|
||||
private final boolean partialObject;
|
||||
|
||||
protected final SqlTreeProperty[] properties;
|
||||
protected final STreeProperty[] properties;
|
||||
|
||||
/**
|
||||
* Extra where clause added by Where annotation on associated many.
|
||||
*/
|
||||
private final String extraWhere;
|
||||
|
||||
private final BeanPropertyAssoc<?> nodeBeanProp;
|
||||
private final STreePropertyAssoc nodeBeanProp;
|
||||
|
||||
/**
|
||||
* False if report bean and has no id property.
|
||||
@@ -70,7 +65,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
private final Map<String, String> pathMap;
|
||||
|
||||
final BeanPropertyAssocMany<?> lazyLoadParent;
|
||||
final STreePropertyAssocMany lazyLoadParent;
|
||||
|
||||
final SpiQuery.TemporalMode temporalMode;
|
||||
|
||||
@@ -92,29 +87,29 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
/**
|
||||
* Construct for leaf node.
|
||||
*/
|
||||
SqlTreeNodeBean(String prefix, BeanPropertyAssoc<?> beanProp, SqlTreeProperties props,
|
||||
SqlTreeNodeBean(String prefix, STreePropertyAssoc beanProp, SqlTreeProperties props,
|
||||
List<SqlTreeNode> myChildren, boolean withId, SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad) {
|
||||
|
||||
this(prefix, beanProp, beanProp.getTargetDescriptor(), props, myChildren, withId, null, temporalMode, disableLazyLoad);
|
||||
this(prefix, beanProp, beanProp.target(), props, myChildren, withId, null, temporalMode, disableLazyLoad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct for root node.
|
||||
*/
|
||||
SqlTreeNodeBean(BeanDescriptor<?> desc, SqlTreeProperties props, List<SqlTreeNode> myList, boolean withId,
|
||||
BeanPropertyAssocMany<?> many, SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad) {
|
||||
SqlTreeNodeBean(STreeType desc, SqlTreeProperties props, List<SqlTreeNode> myList, boolean withId,
|
||||
STreePropertyAssocMany many, SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad) {
|
||||
this(null, null, desc, props, myList, withId, many, temporalMode, disableLazyLoad);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create with the appropriate node.
|
||||
*/
|
||||
private SqlTreeNodeBean(String prefix, BeanPropertyAssoc<?> beanProp, BeanDescriptor<?> desc, SqlTreeProperties props,
|
||||
List<SqlTreeNode> myChildren, boolean withId, BeanPropertyAssocMany<?> lazyLoadParent,
|
||||
private SqlTreeNodeBean(String prefix, STreePropertyAssoc beanProp, STreeType desc, SqlTreeProperties props,
|
||||
List<SqlTreeNode> myChildren, boolean withId, STreePropertyAssocMany lazyLoadParent,
|
||||
SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad) {
|
||||
|
||||
this.lazyLoadParent = lazyLoadParent;
|
||||
this.lazyLoadParentIdBinder = (lazyLoadParent == null) ? null : lazyLoadParent.getBeanDescriptor().getIdBinder();
|
||||
this.lazyLoadParentIdBinder = (lazyLoadParent == null) ? null : lazyLoadParent.getIdBinder();
|
||||
this.prefix = prefix;
|
||||
this.desc = desc;
|
||||
this.inheritInfo = desc.getInheritInfo();
|
||||
@@ -129,7 +124,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
this.aggregationRoot = props.isAggregationRoot();
|
||||
|
||||
// the bean has an Id property and we want to use it
|
||||
this.readId = !aggregationRoot && withId && (desc.getIdProperty() != null);
|
||||
this.readId = !aggregationRoot && withId && desc.hasId();
|
||||
this.disableLazyLoad = disableLazyLoad || !readId || desc.isRawSqlBased() || temporalVersions;
|
||||
|
||||
this.partialObject = props.isPartialObject();
|
||||
@@ -150,25 +145,22 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
// if we have also no children, NPE happens anyway.
|
||||
return children[0].getSingleAttributeScalarType();
|
||||
}
|
||||
if (properties[0] instanceof BeanPropertyAssocOne<?>) {
|
||||
BeanPropertyAssocOne<?> assocOne = (BeanPropertyAssocOne<?>)properties[0];
|
||||
if (properties[0] instanceof STreePropertyAssocOne) {
|
||||
STreePropertyAssocOne assocOne = (STreePropertyAssocOne)properties[0];
|
||||
if (assocOne.isAssocId()) {
|
||||
return assocOne.getTargetDescriptor().getIdProperty().getScalarType();
|
||||
return assocOne.getIdScalarType();
|
||||
}
|
||||
}
|
||||
return properties[0].getScalarType();
|
||||
}
|
||||
|
||||
private Map<String, String> createPathMap(String prefix, BeanDescriptor<?> desc) {
|
||||
|
||||
BeanPropertyAssocMany<?>[] manys = desc.propertiesMany();
|
||||
private Map<String, String> createPathMap(String prefix, STreeType desc) {
|
||||
|
||||
HashMap<String, String> m = new HashMap<>();
|
||||
for (BeanPropertyAssocMany<?> many : manys) {
|
||||
for (STreePropertyAssocMany many : desc.propsMany()) {
|
||||
String name = many.getName();
|
||||
m.put(name, getPath(prefix, name));
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -189,7 +181,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
}
|
||||
idBinder.buildRawSqlSelectChain(prefix, selectChain);
|
||||
}
|
||||
for (SqlTreeProperty property : properties) {
|
||||
for (STreeProperty property : properties) {
|
||||
property.buildRawSqlSelectChain(prefix, selectChain);
|
||||
}
|
||||
// recursively continue reading...
|
||||
@@ -228,7 +220,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
}
|
||||
|
||||
Class<?> localType;
|
||||
BeanDescriptor<?> localDesc;
|
||||
STreeType localDesc;
|
||||
IdBinder localIdBinder;
|
||||
EntityBean localBean;
|
||||
|
||||
@@ -292,21 +284,16 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
if (inheritInfo == null) {
|
||||
// normal behavior with no inheritance
|
||||
for (SqlTreeProperty property : properties) {
|
||||
for (STreeProperty property : properties) {
|
||||
property.load(sqlBeanLoad);
|
||||
}
|
||||
|
||||
} else {
|
||||
// take account of inheritance and due to subclassing approach
|
||||
// need to get a 'local' version of the property
|
||||
for (SqlTreeProperty property : properties) {
|
||||
for (STreeProperty property : properties) {
|
||||
// get a local version of the BeanProperty
|
||||
BeanProperty p = localDesc.getBeanProperty(property.getName());
|
||||
if (p != null) {
|
||||
p.load(sqlBeanLoad);
|
||||
} else {
|
||||
property.loadIgnore(ctx);
|
||||
}
|
||||
localDesc.inheritanceLoad(sqlBeanLoad, property, ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -374,6 +361,9 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
if (!readId || temporalVersions) {
|
||||
// a bean with no Id (never found in context)
|
||||
if (lazyLoadParentId != null && desc.isElementType()) {
|
||||
ctx.setLazyLoadedChildBean(localBean, lazyLoadParentId);
|
||||
}
|
||||
return localBean;
|
||||
|
||||
} else {
|
||||
@@ -388,13 +378,12 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
* Create lazy loading proxies for the Many's except for the one that is
|
||||
* included in the actual query.
|
||||
*/
|
||||
private void createListProxies(BeanDescriptor<?> localDesc, DbReadContext ctx, EntityBean localBean, boolean disableLazyLoad) {
|
||||
private void createListProxies(STreeType localDesc, DbReadContext ctx, EntityBean localBean, boolean disableLazyLoad) {
|
||||
|
||||
BeanPropertyAssocMany<?> fetchedMany = ctx.getManyProperty();
|
||||
STreePropertyAssocMany fetchedMany = ctx.getManyProperty();
|
||||
|
||||
// load the List/Set/Map proxy objects (deferred fetching of lists)
|
||||
BeanPropertyAssocMany<?>[] manys = localDesc.propertiesMany();
|
||||
for (BeanPropertyAssocMany<?> many : manys) {
|
||||
for (STreePropertyAssocMany many : localDesc.propsMany()) {
|
||||
|
||||
if (fetchedMany == null || !fetchedMany.equals(many)) {
|
||||
// create a proxy for the many (deferred fetching)
|
||||
@@ -419,7 +408,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
if (readId) {
|
||||
appendSelectId(ctx, idBinder.getBeanProperty());
|
||||
}
|
||||
for (SqlTreeProperty property : properties) {
|
||||
for (STreeProperty property : properties) {
|
||||
if (!property.isAggregation()) {
|
||||
property.appendSelect(ctx, subQuery);
|
||||
}
|
||||
@@ -486,14 +475,13 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
/**
|
||||
* Append the properties to the buffer.
|
||||
*/
|
||||
private void appendSelect(DbSqlContext ctx, boolean subQuery, SqlTreeProperty[] props) {
|
||||
|
||||
for (SqlTreeProperty prop : props) {
|
||||
private void appendSelect(DbSqlContext ctx, boolean subQuery, STreeProperty[] props) {
|
||||
for (STreeProperty prop : props) {
|
||||
prop.appendSelect(ctx, subQuery);
|
||||
}
|
||||
}
|
||||
|
||||
protected void appendSelectId(DbSqlContext ctx, BeanProperty prop) {
|
||||
protected void appendSelectId(DbSqlContext ctx, STreeProperty prop) {
|
||||
if (prop != null) {
|
||||
prop.appendSelect(ctx, false);
|
||||
}
|
||||
@@ -546,7 +534,7 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
// join and return SqlJoinType to use for child joins
|
||||
joinType = appendFromBaseTable(ctx, joinType);
|
||||
|
||||
for (SqlTreeProperty property : properties) {
|
||||
for (STreeProperty property : properties) {
|
||||
// usually nothing... except for 1-1 Exported
|
||||
property.appendFrom(ctx, joinType);
|
||||
}
|
||||
@@ -601,8 +589,8 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
private SqlJoinType appendFromAsJoin(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
|
||||
if (nodeBeanProp instanceof BeanPropertyAssocMany<?>) {
|
||||
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>) nodeBeanProp;
|
||||
if (nodeBeanProp instanceof STreePropertyAssocMany) {
|
||||
STreePropertyAssocMany manyProp = (STreePropertyAssocMany) nodeBeanProp;
|
||||
if (manyProp.hasJoinTable()) {
|
||||
|
||||
String alias = ctx.getTableAlias(prefix);
|
||||
@@ -619,7 +607,6 @@ class SqlTreeNodeBean implements SqlTreeNode {
|
||||
|
||||
return nodeBeanProp.addJoin(joinType, alias2, alias, ctx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return nodeBeanProp.addJoin(joinType, prefix, ctx);
|
||||
|
||||
@@ -4,14 +4,11 @@ import io.ebean.Version;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -25,7 +22,7 @@ import java.util.List;
|
||||
*/
|
||||
class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
|
||||
private final BeanPropertyAssoc<?> assocBeanProperty;
|
||||
private final STreePropertyAssoc assocBeanProperty;
|
||||
|
||||
private final String prefix;
|
||||
|
||||
@@ -35,11 +32,11 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
|
||||
private List<SqlTreeNodeExtraJoin> children;
|
||||
|
||||
SqlTreeNodeExtraJoin(String prefix, BeanPropertyAssoc<?> assocBeanProperty, boolean pathContainsMany) {
|
||||
SqlTreeNodeExtraJoin(String prefix, STreePropertyAssoc assocBeanProperty, boolean pathContainsMany) {
|
||||
this.prefix = prefix;
|
||||
this.assocBeanProperty = assocBeanProperty;
|
||||
this.pathContainsMany = pathContainsMany;
|
||||
this.manyJoin = assocBeanProperty instanceof BeanPropertyAssocMany<?>;
|
||||
this.manyJoin = assocBeanProperty instanceof STreePropertyAssocMany;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,8 +100,8 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
|
||||
boolean manyToMany = false;
|
||||
|
||||
if (assocBeanProperty instanceof BeanPropertyAssocMany<?>) {
|
||||
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>) assocBeanProperty;
|
||||
if (assocBeanProperty instanceof STreePropertyAssocMany) {
|
||||
STreePropertyAssocMany manyProp = (STreePropertyAssocMany) assocBeanProperty;
|
||||
if (manyProp.hasJoinTable()) {
|
||||
|
||||
manyToMany = true;
|
||||
@@ -127,7 +124,7 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
}
|
||||
if (!manyToMany) {
|
||||
if (assocBeanProperty.isFormula()) {
|
||||
// add joins for formula beans
|
||||
// add joins for formula beans
|
||||
assocBeanProperty.appendFrom(ctx, joinType);
|
||||
}
|
||||
joinType = assocBeanProperty.addJoin(joinType, prefix, ctx);
|
||||
@@ -164,7 +161,7 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
* Does nothing.
|
||||
*/
|
||||
@Override
|
||||
public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) throws SQLException {
|
||||
public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -172,7 +169,7 @@ class SqlTreeNodeExtraJoin implements SqlTreeNode {
|
||||
* Does nothing.
|
||||
*/
|
||||
@Override
|
||||
public <T> Version<T> loadVersion(DbReadContext ctx) throws SQLException {
|
||||
public <T> Version<T> loadVersion(DbReadContext ctx) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
|
||||
@@ -11,9 +10,9 @@ import java.util.List;
|
||||
|
||||
final class SqlTreeNodeManyRoot extends SqlTreeNodeBean {
|
||||
|
||||
private final BeanPropertyAssocMany<?> manyProp;
|
||||
private final STreePropertyAssocMany manyProp;
|
||||
|
||||
SqlTreeNodeManyRoot(String prefix, BeanPropertyAssocMany<?> prop, SqlTreeProperties props, List<SqlTreeNode> myList,
|
||||
SqlTreeNodeManyRoot(String prefix, STreePropertyAssocMany prop, SqlTreeProperties props, List<SqlTreeNode> myList,
|
||||
SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad) {
|
||||
super(prefix, prop, props, myList, true, temporalMode, disableLazyLoad);
|
||||
this.manyProp = prop;
|
||||
|
||||
@@ -4,15 +4,11 @@ import io.ebean.Version;
|
||||
import io.ebean.bean.EntityBean;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocOne;
|
||||
import io.ebeaninternal.server.deploy.DbReadContext;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
import io.ebeaninternal.server.type.ScalarType;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -24,15 +20,14 @@ class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
|
||||
|
||||
private final String prefix;
|
||||
|
||||
private final BeanPropertyAssoc<?> nodeBeanProp;
|
||||
private final STreePropertyAssoc nodeBeanProp;
|
||||
|
||||
/**
|
||||
* The many where join which is either INNER or OUTER.
|
||||
*/
|
||||
private final SqlJoinType manyJoinType;
|
||||
|
||||
SqlTreeNodeManyWhereJoin(String prefix, BeanPropertyAssoc<?> prop, SqlJoinType manyJoinType) {
|
||||
|
||||
SqlTreeNodeManyWhereJoin(String prefix, STreePropertyAssoc prop, SqlJoinType manyJoinType) {
|
||||
this.nodeBeanProp = prop;
|
||||
this.prefix = prefix;
|
||||
this.manyJoinType = manyJoinType;
|
||||
@@ -86,16 +81,16 @@ class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
|
||||
* Join to base table for this node. This includes a join to the
|
||||
* intersection table if this is a ManyToMany node.
|
||||
*/
|
||||
void appendFromBaseTable(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
private void appendFromBaseTable(DbSqlContext ctx, SqlJoinType joinType) {
|
||||
|
||||
String alias = ctx.getTableAliasManyWhere(prefix);
|
||||
String parentAlias = ctx.getTableAliasManyWhere(parentPrefix);
|
||||
|
||||
if (nodeBeanProp instanceof BeanPropertyAssocOne<?>) {
|
||||
if (nodeBeanProp instanceof STreePropertyAssocOne) {
|
||||
nodeBeanProp.addJoin(joinType, parentAlias, alias, ctx);
|
||||
|
||||
} else {
|
||||
BeanPropertyAssocMany<?> manyProp = (BeanPropertyAssocMany<?>) nodeBeanProp;
|
||||
STreePropertyAssocMany manyProp = (STreePropertyAssocMany) nodeBeanProp;
|
||||
if (!manyProp.hasJoinTable()) {
|
||||
manyProp.addJoin(joinType, parentAlias, alias, ctx);
|
||||
|
||||
@@ -125,13 +120,13 @@ class SqlTreeNodeManyWhereJoin implements SqlTreeNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) throws SQLException {
|
||||
public EntityBean load(DbReadContext ctx, EntityBean localBean, EntityBean parentBean) {
|
||||
// nothing to do here
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> Version<T> loadVersion(DbReadContext ctx) throws SQLException {
|
||||
public <T> Version<T> loadVersion(DbReadContext ctx) {
|
||||
// nothing to do here
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package io.ebeaninternal.server.query;
|
||||
|
||||
import io.ebeaninternal.api.SpiQuery;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssocMany;
|
||||
import io.ebeaninternal.server.deploy.DbSqlContext;
|
||||
import io.ebeaninternal.server.deploy.TableJoin;
|
||||
|
||||
@@ -18,8 +16,8 @@ final class SqlTreeNodeRoot extends SqlTreeNodeBean {
|
||||
/**
|
||||
* Specify for SqlSelect to include an Id property or not.
|
||||
*/
|
||||
SqlTreeNodeRoot(BeanDescriptor<?> desc, SqlTreeProperties props, List<SqlTreeNode> myList, boolean withId,
|
||||
TableJoin includeJoin, BeanPropertyAssocMany<?> many, SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad) {
|
||||
SqlTreeNodeRoot(STreeType desc, SqlTreeProperties props, List<SqlTreeNode> myList, boolean withId,
|
||||
TableJoin includeJoin, STreePropertyAssocMany many, SpiQuery.TemporalMode temporalMode, boolean disableLazyLoad) {
|
||||
|
||||
super(desc, props, myList, withId, many, temporalMode, disableLazyLoad);
|
||||
this.includeJoin = includeJoin;
|
||||
|
||||
@@ -20,7 +20,7 @@ public class SqlTreeProperties {
|
||||
/**
|
||||
* The bean properties in order.
|
||||
*/
|
||||
private final List<SqlTreeProperty> propsList = new ArrayList<>();
|
||||
private final List<STreeProperty> propsList = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Maintain a list of property names to detect embedded bean additions.
|
||||
@@ -40,17 +40,17 @@ public class SqlTreeProperties {
|
||||
return propNames.contains(propName);
|
||||
}
|
||||
|
||||
public void add(SqlTreeProperty[] props) {
|
||||
public void add(STreeProperty[] props) {
|
||||
propsList.addAll(Arrays.asList(props));
|
||||
}
|
||||
|
||||
public void add(SqlTreeProperty prop) {
|
||||
public void add(STreeProperty prop) {
|
||||
propsList.add(prop);
|
||||
propNames.add(prop.getName());
|
||||
}
|
||||
|
||||
public SqlTreeProperty[] getProps() {
|
||||
return propsList.toArray(new SqlTreeProperty[propsList.size()]);
|
||||
public STreeProperty[] getProps() {
|
||||
return propsList.toArray(new STreeProperty[propsList.size()]);
|
||||
}
|
||||
|
||||
boolean isPartialObject() {
|
||||
@@ -97,7 +97,7 @@ public class SqlTreeProperties {
|
||||
*/
|
||||
private String aggregationJoin() {
|
||||
if (!allProperties) {
|
||||
for (SqlTreeProperty beanProperty : propsList) {
|
||||
for (STreeProperty beanProperty : propsList) {
|
||||
if (beanProperty.isAggregation()) {
|
||||
aggregation = true;
|
||||
aggregationPath = beanProperty.getElPrefix();
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package io.ebeaninternal.server.querydefn;
|
||||
|
||||
import io.ebean.FetchConfig;
|
||||
import io.ebean.util.SplitName;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptor;
|
||||
import io.ebeaninternal.server.deploy.BeanPropertyAssoc;
|
||||
import io.ebeaninternal.server.el.ElPropertyDeploy;
|
||||
import io.ebeaninternal.server.el.ElPropertyValue;
|
||||
import io.ebean.util.SplitName;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
import java.io.Serializable;
|
||||
@@ -427,7 +427,7 @@ public class OrmQueryDetail implements Serializable {
|
||||
for (OrmQueryProperties joinProps : fetchPaths.values()) {
|
||||
if (!joinProps.hasSelectClause()) {
|
||||
BeanDescriptor<?> assocDesc = desc.getBeanDescriptor(joinProps.getPath());
|
||||
if (assocDesc.hasDefaultSelectClause()) {
|
||||
if (assocDesc != null && assocDesc.hasDefaultSelectClause()) {
|
||||
fetch(joinProps.getPath(), assocDesc.getDefaultSelectClause(), joinProps.getFetchConfig());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public class DocStructure {
|
||||
public <T> void prepareMany(BeanDescriptor<T> desc) {
|
||||
Set<String> strings = embedded.keySet();
|
||||
for (String prop : strings) {
|
||||
BeanPropertyAssoc<?> embProp = (BeanPropertyAssoc<?>) desc.findBeanProperty(prop);
|
||||
BeanPropertyAssoc<?> embProp = (BeanPropertyAssoc<?>) desc.findProperty(prop);
|
||||
if (embProp.isMany()) {
|
||||
prepare(prop, embProp);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user