Skip to content

Commit

Permalink
Implemented rudimental treat support in core and reenabled some tests
Browse files Browse the repository at this point in the history
  • Loading branch information
beikov committed Sep 6, 2016
1 parent 55e4263 commit 1d3fd3c
Show file tree
Hide file tree
Showing 70 changed files with 2,242 additions and 1,232 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
*/
public class CriteriaBuilderFactoryImpl implements CriteriaBuilderFactory {

private final EntityMetamodel metamodel;
private final List<QueryTransformer> queryTransformers;
private final ExtendedQuerySupport extendedQuerySupport;
private final Map<String, DbmsDialect> dbmsDialects;
Expand All @@ -63,12 +64,15 @@ public class CriteriaBuilderFactoryImpl implements CriteriaBuilderFactory {
private final Set<String> configuredRegisteredFunctions;

public CriteriaBuilderFactoryImpl(CriteriaBuilderConfigurationImpl config, EntityManagerFactory entityManagerFactory) {
final boolean compatibleMode = Boolean.valueOf(config.getProperty(ConfigurationProperties.COMPATIBLE_MODE));

this.metamodel = new EntityMetamodel(entityManagerFactory.getMetamodel());
this.queryTransformers = new ArrayList<QueryTransformer>(config.getQueryTransformers());
this.extendedQuerySupport = config.getExtendedQuerySupport();
this.dbmsDialects = new HashMap<String, DbmsDialect>(config.getDbmsDialects());
this.aggregateFunctions = resolveAggregateFunctions(config.getFunctions());
this.expressionFactory = new SimpleCachingExpressionFactory(new ExpressionFactoryImpl(aggregateFunctions));
this.subqueryExpressionFactory = new SubqueryExpressionFactory(aggregateFunctions, expressionFactory);
this.expressionFactory = new SimpleCachingExpressionFactory(new ExpressionFactoryImpl(aggregateFunctions, !compatibleMode));
this.subqueryExpressionFactory = new SubqueryExpressionFactory(aggregateFunctions, !compatibleMode, expressionFactory);
this.properties = copyProperties(config.getProperties());

EntityManagerFactory emf = entityManagerFactory;
Expand Down Expand Up @@ -102,6 +106,10 @@ private static Set<String> resolveAggregateFunctions(Map<String, JpqlFunctionGro
return aggregateFunctions;
}

public EntityMetamodel getMetamodel() {
return metamodel;
}

public List<QueryTransformer> getQueryTransformers() {
return queryTransformers;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package com.blazebit.persistence.impl;

import javax.persistence.metamodel.EmbeddableType;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
* This is a wrapper around the JPA {@link Metamodel} allows additionally efficient access by other attributes than a Class.
*
* @author Christian Beikov
* @since 1.2
*/
public class EntityMetamodel implements Metamodel {

private final Metamodel delegate;
private final Map<String, EntityType<?>> entityNameMap;
private final Map<Class<?>, ManagedType<?>> classMap;

public EntityMetamodel(Metamodel delegate) {
this.delegate = delegate;
Set<ManagedType<?>> managedTypes = delegate.getManagedTypes();
Map<String, EntityType<?>> nameToType = new HashMap<String, EntityType<?>>(managedTypes.size());
Map<Class<?>, ManagedType<?>> classToType = new HashMap<Class<?>, ManagedType<?>>(managedTypes.size());

for (ManagedType<?> t : managedTypes) {
if (t instanceof EntityType<?>) {
EntityType<?> e = (EntityType<?>) t;
nameToType.put(e.getName(), e);
}
classToType.put(t.getJavaType(), t);
}

this.entityNameMap = Collections.unmodifiableMap(nameToType);
this.classMap = Collections.unmodifiableMap(classToType);
}

@Override
public <X> EntityType<X> entity(Class<X> cls) {
return delegate.entity(cls);
}

@Override
public <X> ManagedType<X> managedType(Class<X> cls) {
return delegate.managedType(cls);
}

public ManagedType<?> managedType(String name) {
ManagedType<?> t = entityNameMap.get(name);
if (t == null) {
throw new IllegalStateException("Managed type with name '" + name + "' does not exist!");
}

return t;
}

@SuppressWarnings({ "unchecked" })
public <X> ManagedType<X> getManagedType(Class<X> cls) {
return (ManagedType<X>) classMap.get(cls);
}

@Override
public <X> EmbeddableType<X> embeddable(Class<X> cls) {
return delegate.embeddable(cls);
}

@Override
public Set<ManagedType<?>> getManagedTypes() {
return delegate.getManagedTypes();
}

@Override
public Set<EntityType<?>> getEntities() {
return delegate.getEntities();
}

@Override
public Set<EmbeddableType<?>> getEmbeddables() {
return delegate.getEmbeddables();
}
}
Original file line number Diff line number Diff line change
@@ -1,107 +1,103 @@
package com.blazebit.persistence.impl;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.persistence.FetchType;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;

import com.blazebit.persistence.impl.expression.AggregateExpression;
import com.blazebit.persistence.impl.expression.FunctionExpression;
import com.blazebit.persistence.impl.expression.PathElementExpression;
import com.blazebit.persistence.impl.expression.PathExpression;
import com.blazebit.persistence.impl.expression.SimplePathReference;
import com.blazebit.persistence.impl.expression.VisitorAdapter;

/**
* This visitor resolves entity references to their attributes. This is needed for entity references
* in the select clause when used in combination with aggregate functions. We have to decompose the
* entity and add the components to the group by because all component will end up in the select clause.
*
* @author Christian Beikov
* @since 1.0.5
*/
public class EntitySelectResolveVisitor extends VisitorAdapter {

private final Metamodel m;
private final Set<PathExpression> pathExpressions;

public EntitySelectResolveVisitor(Metamodel m) {
this(m, new LinkedHashSet<PathExpression>());
}

public EntitySelectResolveVisitor(Metamodel m, Set<PathExpression> pathExpressions) {
this.m = m;
this.pathExpressions = pathExpressions;
}

public Set<PathExpression> getPathExpressions() {
return pathExpressions;
}

@Override
public void visit(FunctionExpression expression) {
if (!(expression instanceof AggregateExpression)) {
super.visit(expression);
}
}

@Override
public void visit(PathExpression expression) {
if (expression.getField() == null) {
/**
* We need to resolve entity selects because hibernate will
* select every entity attribute. Since we need every select in
* the group by (because of DB2) we need to resolve such entity
* selects here
*/
JoinNode baseNode = ((JoinNode) expression.getBaseNode());
EntityType<?> entityType;

try {
entityType = m.entity(baseNode.getPropertyClass());
} catch (IllegalArgumentException e) {
// ignore if the expression is not an entity
return;
}

// we need to ensure a deterministic order for testing
SortedSet<Attribute<?, ?>> sortedAttributes = new TreeSet<Attribute<?, ?>>(new Comparator<Attribute<?, ?>>() {

@Override
public int compare(Attribute<?, ?> o1, Attribute<?, ?> o2) {
return o1.getName().compareTo(o2.getName());
}

});
sortedAttributes.addAll(entityType.getAttributes());
for (Attribute<?, ?> attr : sortedAttributes) {
boolean resolve = false;
if (ExpressionUtils.isAssociation(attr) && !attr.isCollection()) {
resolve = true;
} else if (ExpressionUtils.getFetchType(attr) == FetchType.EAGER) {
if (attr.getPersistentAttributeType() == Attribute.PersistentAttributeType.ELEMENT_COLLECTION) {
throw new UnsupportedOperationException("Eager element collections are not supported");
}
resolve = true;
}

if (resolve) {
PathExpression attrPath = new PathExpression(new ArrayList<PathElementExpression>(expression.getExpressions()));
attrPath.setPathReference(new SimplePathReference(baseNode, attr.getName()));
pathExpressions.add(attrPath);
}
}
}
}

public void resolve(JoinNode baseNode) {

}
package com.blazebit.persistence.impl;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.persistence.FetchType;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.Metamodel;

import com.blazebit.persistence.impl.expression.*;

/**
* This visitor resolves entity references to their attributes. This is needed for entity references
* in the select clause when used in combination with aggregate functions. We have to decompose the
* entity and add the components to the group by because all components will end up in the select clause.
*
* @author Christian Beikov
* @since 1.0.5
*/
public class EntitySelectResolveVisitor extends VisitorAdapter {

private final Metamodel m;
private final Set<PathExpression> pathExpressions;

public EntitySelectResolveVisitor(Metamodel m) {
this(m, new LinkedHashSet<PathExpression>());
}

public EntitySelectResolveVisitor(Metamodel m, Set<PathExpression> pathExpressions) {
this.m = m;
this.pathExpressions = pathExpressions;
}

public Set<PathExpression> getPathExpressions() {
return pathExpressions;
}

@Override
public void visit(FunctionExpression expression) {
if (!(expression instanceof AggregateExpression)) {
super.visit(expression);
}
}

@Override
public void visit(PathExpression expression) {
if (expression.getField() == null) {
/**
* We need to resolve entity selects because hibernate will
* select every entity attribute. Since we need every select in
* the group by (because of DB2) we need to resolve such entity
* selects here
*/
JoinNode baseNode = ((JoinNode) expression.getBaseNode());
EntityType<?> entityType;

try {
entityType = m.entity(baseNode.getPropertyClass());
} catch (IllegalArgumentException e) {
// ignore if the expression is not an entity
return;
}

// we need to ensure a deterministic order for testing
SortedSet<Attribute<?, ?>> sortedAttributes = new TreeSet<Attribute<?, ?>>(new Comparator<Attribute<?, ?>>() {

@Override
public int compare(Attribute<?, ?> o1, Attribute<?, ?> o2) {
return o1.getName().compareTo(o2.getName());
}

});
// TODO: a polymorphic query will fail because we don't collect subtype properties
sortedAttributes.addAll(entityType.getAttributes());
for (Attribute<?, ?> attr : sortedAttributes) {
boolean resolve = false;
if (ExpressionUtils.isAssociation(attr) && !attr.isCollection()) {
resolve = true;
} else if (ExpressionUtils.getFetchType(attr) == FetchType.EAGER) {
if (attr.getPersistentAttributeType() == Attribute.PersistentAttributeType.ELEMENT_COLLECTION) {
throw new UnsupportedOperationException("Eager element collections are not supported");
}
resolve = true;
}

if (resolve) {
PathExpression attrPath = new PathExpression(new ArrayList<PathElementExpression>(expression.getExpressions()));
attrPath.setPathReference(new SimplePathReference(baseNode, attr.getName(), null));
pathExpressions.add(attrPath);
}
}
}
}

public void resolve(JoinNode baseNode) {

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ public void visit(ArrayExpression expression) {
throw new IllegalArgumentException("At this point array expressions are not allowed anymore!");
}

@Override
public void visit(TreatExpression expression) {
handleExpression(expression);
}

@Override
public void visit(PropertyExpression expression) {
handleExpression(expression);
Expand Down
Loading

0 comments on commit 1d3fd3c

Please sign in to comment.