mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1181 - ENH: Add @FetchPreference support - Secondary query generation is based on order of fetch statements
This commit is contained in:
@@ -41,8 +41,10 @@ public final class BeanFkeyProperty implements ElPropertyValue {
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getDeployOrder() {
|
||||
return deployOrder;
|
||||
@Override
|
||||
public int getFetchPreference() {
|
||||
// return some decently high value
|
||||
return 1000;
|
||||
}
|
||||
|
||||
private String calcPlaceHolder(String prefix, String dbColumn) {
|
||||
|
||||
@@ -537,6 +537,12 @@ public class BeanProperty implements ElPropertyValue, Property {
|
||||
return dbEncryptFunction.getDecryptSql(tableAlias + "." + this.getDbColumn());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFetchPreference() {
|
||||
// return some decently high value - override on ToMany property
|
||||
return 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add any extra joins required to support this property. Generally a no
|
||||
* operation except for a OneToOne exported.
|
||||
|
||||
@@ -73,6 +73,8 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
|
||||
final String extraWhere;
|
||||
|
||||
final int fetchPreference;
|
||||
|
||||
boolean saveRecurseSkippable;
|
||||
|
||||
/**
|
||||
@@ -88,6 +90,7 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
|
||||
this.targetType = deploy.getTargetType();
|
||||
this.cascadeInfo = deploy.getCascadeInfo();
|
||||
this.fetchPreference = deploy.getFetchPreference();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,6 +112,11 @@ public abstract class BeanPropertyAssoc<T> extends BeanProperty {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFetchPreference() {
|
||||
return fetchPreference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a ElPropertyValue for a *ToOne or *ToMany.
|
||||
*/
|
||||
|
||||
@@ -40,6 +40,8 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
|
||||
private String docStoreDoc;
|
||||
|
||||
private int fetchPreference = 1000;
|
||||
|
||||
/**
|
||||
* Construct the property.
|
||||
*/
|
||||
@@ -147,4 +149,11 @@ public abstract class DeployBeanPropertyAssoc<T> extends DeployBeanProperty {
|
||||
return docStoreDoc;
|
||||
}
|
||||
|
||||
public int getFetchPreference() {
|
||||
return fetchPreference;
|
||||
}
|
||||
|
||||
public void setFetchPreference(int fetchPreference) {
|
||||
this.fetchPreference = fetchPreference;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import io.ebean.annotation.FetchPreference;
|
||||
import io.ebean.annotation.HistoryExclude;
|
||||
import io.ebean.annotation.PrivateOwned;
|
||||
import io.ebean.annotation.Where;
|
||||
@@ -90,6 +91,11 @@ class AnnotationAssocManys extends AnnotationParser {
|
||||
prop.setExtraWhere(where.clause());
|
||||
}
|
||||
|
||||
FetchPreference fetchPreference = get(prop, FetchPreference.class);
|
||||
if (fetchPreference != null) {
|
||||
prop.setFetchPreference(fetchPreference.value());
|
||||
}
|
||||
|
||||
// check for manually defined joins
|
||||
BeanTable beanTable = prop.getBeanTable();
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.ebeaninternal.server.deploy.parse;
|
||||
|
||||
import io.ebean.annotation.FetchPreference;
|
||||
import io.ebean.annotation.Where;
|
||||
import io.ebean.config.NamingConvention;
|
||||
import io.ebeaninternal.server.deploy.BeanDescriptorManager;
|
||||
@@ -87,6 +88,11 @@ public class AnnotationAssocOnes extends AnnotationParser {
|
||||
prop.setExtraWhere(where.clause());
|
||||
}
|
||||
|
||||
FetchPreference fetchPreference = get(prop, FetchPreference.class);
|
||||
if (fetchPreference != null) {
|
||||
prop.setFetchPreference(fetchPreference.value());
|
||||
}
|
||||
|
||||
if (validationAnnotations) {
|
||||
NotNull notNull = get(prop, NotNull.class);
|
||||
if (notNull != null && isEbeanValidationGroups(notNull.groups())) {
|
||||
|
||||
@@ -84,6 +84,11 @@ public class ElPropertyChain implements ElPropertyValue {
|
||||
return "expr:" + expression + " chain:" + Arrays.toString(chain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFetchPreference() {
|
||||
return chain[0].getFetchPreference();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAggregation() {
|
||||
return false;
|
||||
|
||||
@@ -74,4 +74,10 @@ public interface ElPropertyDeploy {
|
||||
* Return true if this is an aggregation property.
|
||||
*/
|
||||
boolean isAggregation();
|
||||
|
||||
/**
|
||||
* Return the fetch preference. This can be used to control which ToMany relationship
|
||||
* is left as a 'join' and which get converted to query join.
|
||||
*/
|
||||
int getFetchPreference();
|
||||
}
|
||||
|
||||
@@ -318,6 +318,10 @@ public class OrmQueryDetail implements Serializable {
|
||||
*/
|
||||
void markQueryJoins(BeanDescriptor<?> beanDescriptor, String lazyLoadManyPath, boolean allowOne, boolean addIds) {
|
||||
|
||||
if (fetchPaths.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// the name of the many fetch property if there is one
|
||||
String manyFetchProperty = null;
|
||||
|
||||
@@ -325,22 +329,19 @@ public class OrmQueryDetail implements Serializable {
|
||||
boolean fetchJoinFirstMany = allowOne;
|
||||
|
||||
sortFetchPaths(beanDescriptor, addIds);
|
||||
List<FetchEntry> pairs = sortByFetchPreference(beanDescriptor);
|
||||
|
||||
for (String fetchPath : fetchPaths.keySet()) {
|
||||
ElPropertyDeploy elProp = beanDescriptor.getElPropertyDeploy(fetchPath);
|
||||
if (elProp == null) {
|
||||
throw new PersistenceException("Invalid fetch path " + fetchPath + " from " + beanDescriptor.getFullName());
|
||||
}
|
||||
for (FetchEntry pair : pairs) {
|
||||
ElPropertyDeploy elProp = pair.getElProp();
|
||||
if (elProp.containsManySince(manyFetchProperty)) {
|
||||
|
||||
// this is a join to a *ToMany
|
||||
OrmQueryProperties chunk = fetchPaths.get(fetchPath);
|
||||
OrmQueryProperties chunk = pair.getProperties();
|
||||
if (isQueryJoinCandidate(lazyLoadManyPath, chunk)) {
|
||||
// this is a 'fetch join' (included in main query)
|
||||
if (fetchJoinFirstMany) {
|
||||
// letting the first one remain a 'fetch join'
|
||||
fetchJoinFirstMany = false;
|
||||
manyFetchProperty = fetchPath;
|
||||
manyFetchProperty = pair.getPath();
|
||||
} else {
|
||||
// convert this one over to a 'query join'
|
||||
chunk.markForQueryJoin();
|
||||
@@ -350,6 +351,25 @@ public class OrmQueryDetail implements Serializable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the fetch entries taking into account fetchPreference on the path.
|
||||
*/
|
||||
private List<FetchEntry> sortByFetchPreference(BeanDescriptor<?> desc) {
|
||||
|
||||
List<FetchEntry> entries = new ArrayList<>(fetchPaths.size());
|
||||
int idx = 0;
|
||||
for (Map.Entry<String, OrmQueryProperties> entry : fetchPaths.entrySet()) {
|
||||
String fetchPath = entry.getKey();
|
||||
ElPropertyDeploy elProp = desc.getElPropertyDeploy(fetchPath);
|
||||
if (elProp == null) {
|
||||
throw new PersistenceException("Invalid fetch path " + fetchPath + " from " + desc.getFullName());
|
||||
}
|
||||
entries.add(new FetchEntry(idx++, fetchPath, elProp, entry.getValue()));
|
||||
}
|
||||
Collections.sort(entries);
|
||||
return entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this path is a candidate for converting to a query join.
|
||||
*/
|
||||
@@ -483,4 +503,45 @@ public class OrmQueryDetail implements Serializable {
|
||||
public Set<Map.Entry<String, OrmQueryProperties>> entries() {
|
||||
return fetchPaths.entrySet();
|
||||
}
|
||||
|
||||
private static class FetchEntry implements Comparable<FetchEntry> {
|
||||
|
||||
private final int index;
|
||||
private final String path;
|
||||
private final OrmQueryProperties properties;
|
||||
private final ElPropertyDeploy elProp;
|
||||
|
||||
FetchEntry(int index, String path, ElPropertyDeploy elProp, OrmQueryProperties value) {
|
||||
this.index = index;
|
||||
this.path = path;
|
||||
this.elProp = elProp;
|
||||
this.properties = value;
|
||||
}
|
||||
|
||||
String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
OrmQueryProperties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
ElPropertyDeploy getElProp() {
|
||||
return elProp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort by fetchPreference and then by index order.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(FetchEntry other) {
|
||||
int fp = elProp.getFetchPreference();
|
||||
int op = other.elProp.getFetchPreference();
|
||||
if (fp == op) {
|
||||
return Integer.compare(index, other.index);
|
||||
} else {
|
||||
return (fp < op) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user