diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/ExpressionUtils.java b/core/impl/src/main/java/com/blazebit/persistence/impl/ExpressionUtils.java index a2a08c0d69..27646850c3 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/ExpressionUtils.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/ExpressionUtils.java @@ -143,7 +143,7 @@ private static boolean isUnique(EntityMetamodel metamodel, SubqueryExpression ex } private static boolean isUnique(EntityMetamodel metamodel, GeneralCaseExpression expr) { - if (!isUnique(metamodel, expr.getDefaultExpr())) { + if (expr.getDefaultExpr() != null && !isUnique(metamodel, expr.getDefaultExpr())) { return false; } @@ -254,7 +254,7 @@ private static boolean isNullable(EntityMetamodel metamodel, ArithmeticExpressio } private static boolean isNullable(EntityMetamodel metamodel, GeneralCaseExpression expr) { - if (isNullable(metamodel, expr.getDefaultExpr())) { + if (expr.getDefaultExpr() != null && isNullable(metamodel, expr.getDefaultExpr())) { return true; } diff --git a/core/impl/src/main/java/com/blazebit/persistence/impl/GroupByExpressionGatheringVisitor.java b/core/impl/src/main/java/com/blazebit/persistence/impl/GroupByExpressionGatheringVisitor.java index 39529bf252..06c9c758c9 100644 --- a/core/impl/src/main/java/com/blazebit/persistence/impl/GroupByExpressionGatheringVisitor.java +++ b/core/impl/src/main/java/com/blazebit/persistence/impl/GroupByExpressionGatheringVisitor.java @@ -296,14 +296,16 @@ public Boolean visit(GeneralCaseExpression expression) { setCollect(true); collectExpressions(expressions, 0, i); collectExpressions(expressions, i + 1, size); - expression.getDefaultExpr().accept(this); + if (expression.getDefaultExpr() != null) { + expression.getDefaultExpr().accept(this); + } setCollect(oldCollect); return true; } } - if (expression.getDefaultExpr().accept(this)) { + if (expression.getDefaultExpr() != null && expression.getDefaultExpr().accept(this)) { collectExpressions(expressions, 0, size); setCollect(oldCollect); return true; @@ -326,7 +328,9 @@ public Boolean visit(SimpleCaseExpression expression) { if (expression.getCaseOperand().accept(this)) { setCollect(true); collectExpressions(expressions, 0, size); - expression.getDefaultExpr().accept(this); + if (expression.getDefaultExpr() != null) { + expression.getDefaultExpr().accept(this); + } setCollect(oldCollect); return true; @@ -339,14 +343,16 @@ public Boolean visit(SimpleCaseExpression expression) { expression.getCaseOperand().accept(this); collectExpressions(expressions, 0, i); collectExpressions(expressions, i + 1, size); - expression.getDefaultExpr().accept(this); + if (expression.getDefaultExpr() != null) { + expression.getDefaultExpr().accept(this); + } setCollect(oldCollect); return true; } } - if (expression.getDefaultExpr().accept(this)) { + if (expression.getDefaultExpr() != null && expression.getDefaultExpr().accept(this)) { setCollect(true); // Add previous expressions which are non-complex expression.getCaseOperand().accept(this); diff --git a/core/parser/src/main/antlr4/com/blazebit/persistence/parser/JPQLSelectExpression.g4 b/core/parser/src/main/antlr4/com/blazebit/persistence/parser/JPQLSelectExpression.g4 index 76f71964e9..84498640d6 100644 --- a/core/parser/src/main/antlr4/com/blazebit/persistence/parser/JPQLSelectExpression.g4 +++ b/core/parser/src/main/antlr4/com/blazebit/persistence/parser/JPQLSelectExpression.g4 @@ -472,13 +472,13 @@ comparison_operator : equality_comparison_operator # EqOrNeqPredicate | '<=' # LePredicate ; -general_case_expression : caseTerminal=CASE when_clause (when_clause)* elseTerminal=ELSE scalar_expression endTerminal=END +general_case_expression : caseTerminal=CASE when_clause (when_clause)* (elseTerminal=ELSE scalar_expression)? endTerminal=END ; when_clause : whenTerminal=WHEN conditional_expression thenTerminal=THEN scalar_expression ; -simple_case_expression : caseTerminal=CASE case_operand simple_when_clause (simple_when_clause)* elseTerminal=ELSE scalar_expression endTerminal=END +simple_case_expression : caseTerminal=CASE case_operand simple_when_clause (simple_when_clause)* (elseTerminal=ELSE scalar_expression)? endTerminal=END ; simple_when_clause : whenTerminal=WHEN scalar_expression thenTerminal=THEN scalar_expression diff --git a/core/parser/src/main/java/com/blazebit/persistence/impl/PathTargetResolvingExpressionVisitor.java b/core/parser/src/main/java/com/blazebit/persistence/impl/PathTargetResolvingExpressionVisitor.java index 7fa607baf4..11533b4f5c 100644 --- a/core/parser/src/main/java/com/blazebit/persistence/impl/PathTargetResolvingExpressionVisitor.java +++ b/core/parser/src/main/java/com/blazebit/persistence/impl/PathTargetResolvingExpressionVisitor.java @@ -239,11 +239,13 @@ public void visit(GeneralCaseExpression expression) { newPositions.addAll(pathPositions); } - PathPosition position = currentPositions.get(j).copy(); - pathPositions = new ArrayList(); - pathPositions.add(currentPosition = position); - expression.getDefaultExpr().accept(this); - newPositions.addAll(pathPositions); + if (expression.getDefaultExpr() != null) { + PathPosition position = currentPositions.get(j).copy(); + pathPositions = new ArrayList(); + pathPositions.add(currentPosition = position); + expression.getDefaultExpr().accept(this); + newPositions.addAll(pathPositions); + } } currentPosition = null; diff --git a/core/parser/src/main/java/com/blazebit/persistence/impl/SimpleQueryGenerator.java b/core/parser/src/main/java/com/blazebit/persistence/impl/SimpleQueryGenerator.java index f26545b8e7..c15340c3e9 100644 --- a/core/parser/src/main/java/com/blazebit/persistence/impl/SimpleQueryGenerator.java +++ b/core/parser/src/main/java/com/blazebit/persistence/impl/SimpleQueryGenerator.java @@ -629,7 +629,7 @@ private void handleCaseWhen(Expression caseOperand, List w ParameterRenderingMode oldParameterRenderingMode = setParameterRenderingMode(ParameterRenderingMode.PLACEHOLDER); caseOperand.accept(this); setParameterRenderingMode(oldParameterRenderingMode); - sb.append(" "); + sb.append(' '); } // TODO: when a type can be inferred by the results of the WHEN or ELSE clauses, we can set PLACEHOLDER, otherwise we have to render literals for parameters @@ -637,24 +637,28 @@ private void handleCaseWhen(Expression caseOperand, List w int size = whenClauses.size(); for (int i = 0; i < size; i++) { whenClauses.get(i).accept(this); - sb.append(" "); + sb.append(' '); } - sb.append("ELSE "); - BooleanLiteralRenderingContext oldBooleanLiteralRenderingContext = setBooleanLiteralRenderingContext(BooleanLiteralRenderingContext.PLAIN); - final boolean requiresParenthesis = needsParenthesisForCaseResult(defaultExpr); - if (requiresParenthesis) { - sb.append('('); - } + if (defaultExpr != null) { + sb.append("ELSE "); + BooleanLiteralRenderingContext oldBooleanLiteralRenderingContext = setBooleanLiteralRenderingContext(BooleanLiteralRenderingContext.PLAIN); + + final boolean requiresParenthesis = needsParenthesisForCaseResult(defaultExpr); + if (requiresParenthesis) { + sb.append('('); + } - defaultExpr.accept(this); + defaultExpr.accept(this); - if (requiresParenthesis) { - sb.append(')'); + if (requiresParenthesis) { + sb.append(')'); + } + setBooleanLiteralRenderingContext(oldBooleanLiteralRenderingContext); + sb.append(' '); } - sb.append(" END"); - setBooleanLiteralRenderingContext(oldBooleanLiteralRenderingContext); + sb.append("END"); } protected boolean needsParenthesisForCaseResult(Expression expression) { diff --git a/core/parser/src/main/java/com/blazebit/persistence/impl/expression/AbortableVisitorAdapter.java b/core/parser/src/main/java/com/blazebit/persistence/impl/expression/AbortableVisitorAdapter.java index ac6683d732..365483b062 100644 --- a/core/parser/src/main/java/com/blazebit/persistence/impl/expression/AbortableVisitorAdapter.java +++ b/core/parser/src/main/java/com/blazebit/persistence/impl/expression/AbortableVisitorAdapter.java @@ -141,7 +141,12 @@ public Boolean visit(GeneralCaseExpression expression) { return true; } } - return expression.getDefaultExpr().accept(this); + + if (expression.getDefaultExpr() != null) { + return expression.getDefaultExpr().accept(this); + } + + return false; } @Override diff --git a/core/parser/src/main/java/com/blazebit/persistence/impl/expression/ExpressionModifierCollectingResultVisitorAdapter.java b/core/parser/src/main/java/com/blazebit/persistence/impl/expression/ExpressionModifierCollectingResultVisitorAdapter.java index bb3f33916e..53cc448cef 100644 --- a/core/parser/src/main/java/com/blazebit/persistence/impl/expression/ExpressionModifierCollectingResultVisitorAdapter.java +++ b/core/parser/src/main/java/com/blazebit/persistence/impl/expression/ExpressionModifierCollectingResultVisitorAdapter.java @@ -206,7 +206,7 @@ public Boolean visit(GeneralCaseExpression expression) { onModifier(new ExpressionListModifier(expressions, i)); } } - if (Boolean.TRUE == expression.getDefaultExpr().accept(this)) { + if (expression.getDefaultExpr() != null && Boolean.TRUE == expression.getDefaultExpr().accept(this)) { onModifier(new GeneralCaseExpressionDefaultModifier(expression)); } return Boolean.FALSE; diff --git a/core/parser/src/main/java/com/blazebit/persistence/impl/expression/ExpressionOptimizer.java b/core/parser/src/main/java/com/blazebit/persistence/impl/expression/ExpressionOptimizer.java index 94672e7ff4..b0458be941 100644 --- a/core/parser/src/main/java/com/blazebit/persistence/impl/expression/ExpressionOptimizer.java +++ b/core/parser/src/main/java/com/blazebit/persistence/impl/expression/ExpressionOptimizer.java @@ -181,7 +181,9 @@ public Expression visit(GeneralCaseExpression expression) { for (int i = 0; i < expression.getWhenClauses().size(); i++) { expression.getWhenClauses().set(i, (WhenClauseExpression) expression.getWhenClauses().get(i).accept(this)); } - expression.setDefaultExpr(expression.getDefaultExpr().accept(this)); + if (expression.getDefaultExpr() != null) { + expression.setDefaultExpr(expression.getDefaultExpr().accept(this)); + } return expression; } diff --git a/core/parser/src/main/java/com/blazebit/persistence/impl/expression/InplaceModificationResultVisitorAdapter.java b/core/parser/src/main/java/com/blazebit/persistence/impl/expression/InplaceModificationResultVisitorAdapter.java index 1a3cef0fe3..40083e7849 100644 --- a/core/parser/src/main/java/com/blazebit/persistence/impl/expression/InplaceModificationResultVisitorAdapter.java +++ b/core/parser/src/main/java/com/blazebit/persistence/impl/expression/InplaceModificationResultVisitorAdapter.java @@ -139,7 +139,9 @@ public Expression visit(GeneralCaseExpression expression) { for (int i = 0; i < size; i++) { expressions.set(i, (WhenClauseExpression) expressions.get(i).accept(this)); } - expression.setDefaultExpr(expression.getDefaultExpr().accept(this)); + if (expression.getDefaultExpr() != null) { + expression.setDefaultExpr(expression.getDefaultExpr().accept(this)); + } return expression; } diff --git a/core/parser/src/main/java/com/blazebit/persistence/impl/expression/JPQLSelectExpressionVisitorImpl.java b/core/parser/src/main/java/com/blazebit/persistence/impl/expression/JPQLSelectExpressionVisitorImpl.java index 1440902af1..c5415b1bc9 100644 --- a/core/parser/src/main/java/com/blazebit/persistence/impl/expression/JPQLSelectExpressionVisitorImpl.java +++ b/core/parser/src/main/java/com/blazebit/persistence/impl/expression/JPQLSelectExpressionVisitorImpl.java @@ -684,7 +684,8 @@ public Expression visitGeneral_case_expression(JPQLSelectExpressionParser.Genera for (JPQLSelectExpressionParser.When_clauseContext whenClause : ctx.when_clause()) { whenClauses.add((WhenClauseExpression) whenClause.accept(this)); } - return new GeneralCaseExpression(whenClauses, ctx.scalar_expression().accept(this)); + JPQLSelectExpressionParser.Scalar_expressionContext elseExpressionCtx = ctx.scalar_expression(); + return new GeneralCaseExpression(whenClauses, elseExpressionCtx == null ? null : elseExpressionCtx.accept(this)); } @Override @@ -693,7 +694,8 @@ public Expression visitSimple_case_expression(JPQLSelectExpressionParser.Simple_ for (JPQLSelectExpressionParser.Simple_when_clauseContext whenClause : ctx.simple_when_clause()) { whenClauses.add((WhenClauseExpression) whenClause.accept(this)); } - return new SimpleCaseExpression(ctx.case_operand().accept(this), whenClauses, ctx.scalar_expression().accept(this)); + JPQLSelectExpressionParser.Scalar_expressionContext elseExpressionCtx = ctx.scalar_expression(); + return new SimpleCaseExpression(ctx.case_operand().accept(this), whenClauses, elseExpressionCtx == null ? null : elseExpressionCtx.accept(this)); } @Override diff --git a/core/parser/src/main/java/com/blazebit/persistence/impl/expression/VisitorAdapter.java b/core/parser/src/main/java/com/blazebit/persistence/impl/expression/VisitorAdapter.java index 0511e39bc4..056a8bcf40 100644 --- a/core/parser/src/main/java/com/blazebit/persistence/impl/expression/VisitorAdapter.java +++ b/core/parser/src/main/java/com/blazebit/persistence/impl/expression/VisitorAdapter.java @@ -126,7 +126,9 @@ public void visit(GeneralCaseExpression expression) { for (int i = 0; i < size; i++) { expressions.get(i).accept(this); } - expression.getDefaultExpr().accept(this); + if (expression.getDefaultExpr() != null) { + expression.getDefaultExpr().accept(this); + } } @Override diff --git a/entity-view/api/src/main/java/com/blazebit/persistence/view/EntityView.java b/entity-view/api/src/main/java/com/blazebit/persistence/view/EntityView.java index dc7498a277..c116336a27 100644 --- a/entity-view/api/src/main/java/com/blazebit/persistence/view/EntityView.java +++ b/entity-view/api/src/main/java/com/blazebit/persistence/view/EntityView.java @@ -22,7 +22,7 @@ import java.lang.annotation.Target; /** - * Specifies that the class is is an entity view. + * Specifies that the class is an entity view. * * @author Christian Beikov * @since 1.0.0 diff --git a/entity-view/api/src/main/java/com/blazebit/persistence/view/EntityViewInheritance.java b/entity-view/api/src/main/java/com/blazebit/persistence/view/EntityViewInheritance.java new file mode 100644 index 0000000000..b7d12cc15b --- /dev/null +++ b/entity-view/api/src/main/java/com/blazebit/persistence/view/EntityViewInheritance.java @@ -0,0 +1,41 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Specifies that the entity view when queried should consider subtypes of the entity view. + * + * @author Christian Beikov + * @since 1.2.0 + */ +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface EntityViewInheritance { + + /** + * The entity view subtypes that may be considered when querying. + * An empty value has the meaning of all (registered) subtypes. + * + * @return The entity view subtype classes + */ + Class[] value() default {}; +} diff --git a/entity-view/api/src/main/java/com/blazebit/persistence/view/EntityViewInheritanceMapping.java b/entity-view/api/src/main/java/com/blazebit/persistence/view/EntityViewInheritanceMapping.java new file mode 100644 index 0000000000..01f758acdf --- /dev/null +++ b/entity-view/api/src/main/java/com/blazebit/persistence/view/EntityViewInheritanceMapping.java @@ -0,0 +1,40 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Defines the inheritance selection for the entity view when a super type is queried. + * + * @author Christian Beikov + * @since 1.2.0 + */ +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface EntityViewInheritanceMapping { + + /** + * The selection predicate which is used to test whether this entity view type should be used as target type for an entity. + * + * @return The selection predicate + */ + String value(); +} diff --git a/entity-view/api/src/main/java/com/blazebit/persistence/view/MappingInheritance.java b/entity-view/api/src/main/java/com/blazebit/persistence/view/MappingInheritance.java new file mode 100644 index 0000000000..10df7e45fe --- /dev/null +++ b/entity-view/api/src/main/java/com/blazebit/persistence/view/MappingInheritance.java @@ -0,0 +1,47 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Defines the subtype mappings for a subview attribute. + * + * @author Christian Beikov + * @since 1.2.0 + */ +@Target({ ElementType.METHOD, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface MappingInheritance { + + /** + * Specifies that the base type is not considered in the inheritance subtype selection, but only it's subtypes. + * + * @return Whether the base type should be considered for inheritance subtype selection + */ + boolean onlySubtypes() default false; + + /** + * The subtype mappings of this annotated attribute that should be considered for inheritance. + * + * @return The subtype mappings + */ + MappingInheritanceSubtype[] value(); +} diff --git a/entity-view/api/src/main/java/com/blazebit/persistence/view/MappingInheritanceMapKey.java b/entity-view/api/src/main/java/com/blazebit/persistence/view/MappingInheritanceMapKey.java new file mode 100644 index 0000000000..6af5df30df --- /dev/null +++ b/entity-view/api/src/main/java/com/blazebit/persistence/view/MappingInheritanceMapKey.java @@ -0,0 +1,47 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Defines the subtype mappings for a map attribute with a subview key. + * + * @author Christian Beikov + * @since 1.2.0 + */ +@Target({ ElementType.METHOD, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface MappingInheritanceMapKey { + + /** + * Specifies that the base type is not considered in the inheritance subtype selection, but only it's subtypes. + * + * @return Whether the base type should be considered for inheritance subtype selection + */ + boolean onlySubtypes() default false; + + /** + * The subtype mappings of this annotated map attribute's key subview type that should be considered for inheritance. + * + * @return The subtype mappings + */ + MappingInheritanceSubtype[] value(); +} diff --git a/entity-view/api/src/main/java/com/blazebit/persistence/view/MappingInheritanceSubtype.java b/entity-view/api/src/main/java/com/blazebit/persistence/view/MappingInheritanceSubtype.java new file mode 100644 index 0000000000..e555615201 --- /dev/null +++ b/entity-view/api/src/main/java/com/blazebit/persistence/view/MappingInheritanceSubtype.java @@ -0,0 +1,48 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Defines a possible subtype that should be materialized when the selection predicate is satisfied. + * + * @author Christian Beikov + * @since 1.2.0 + */ +@Target({ ElementType.METHOD, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface MappingInheritanceSubtype { + + /** + * The selection predicate which is used to test whether the entity view type denoted by {@link #value()} should be used as target type for an entity. + * The default is to use the predicate defined on the entity view type if it is defined. + * + * @return The selection predicate + */ + String mapping() default ""; + + /** + * The entity view subtype of the annotated subview attribute. + * + * @return The entity view subtype + */ + Class value(); +} diff --git a/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/ManagedViewType.java b/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/ManagedViewType.java index 3e32a5046a..39e5be90a6 100644 --- a/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/ManagedViewType.java +++ b/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/ManagedViewType.java @@ -87,4 +87,21 @@ public interface ManagedViewType extends Type { * @return The constructor of the entity view with the given name */ public MappingConstructor getConstructor(String name); + + /** + * Returns the inheritance mapping that should be used for inheritance subtype selection. + * + * @return The inheritance mapping + * @since 1.2.0 + */ + public String getInheritanceMapping(); + + /** + * Returns the transitive closure of all subtypes that should be considered for inheritance selection. + * + * @return The entity view subtypes for inheritance + * @since 1.2.0 + */ + public Set> getInheritanceSubtypes(); + } diff --git a/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/MapAttribute.java b/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/MapAttribute.java index 9233e0bcc4..88ecc954a1 100644 --- a/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/MapAttribute.java +++ b/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/MapAttribute.java @@ -36,6 +36,15 @@ public interface MapAttribute extends PluralAttribute, V> */ public Type getKeyType(); + /** + * Returns the inheritance subtypes that should be considered for the keys of this map attribute. + * When the key type of the map attribute is not a subview, this returns an empty set. + * + * @return The inheritance subtypes or an empty set + * @since 1.2.0 + */ + public Map, String> getKeyInheritanceSubtypeMappings(); + /** * Returns true if the key of this map attribute is a subview, otherwise false. * diff --git a/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/PluralAttribute.java b/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/PluralAttribute.java index e30da9f521..f34310cdb2 100644 --- a/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/PluralAttribute.java +++ b/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/PluralAttribute.java @@ -17,6 +17,7 @@ package com.blazebit.persistence.view.metamodel; import java.util.Comparator; +import java.util.Map; /** * Instances of the type {@linkplain PluralAttribute} represent collection-valued attributes. @@ -43,6 +44,15 @@ public interface PluralAttribute extends Attribute { * @since 1.2.0 */ public Type getElementType(); + + /** + * Returns the inheritance subtypes that should be considered for the elements of this plural attribute. + * When the element type of the attribute is not a subview, this returns an empty set. + * + * @return The inheritance subtypes or an empty set + * @since 1.2.0 + */ + public Map, String> getElementInheritanceSubtypeMappings(); /** * Returns whether this collection is indexed or not. diff --git a/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/SingularAttribute.java b/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/SingularAttribute.java index 329dcfd450..f0da4900fc 100644 --- a/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/SingularAttribute.java +++ b/entity-view/api/src/main/java/com/blazebit/persistence/view/metamodel/SingularAttribute.java @@ -16,6 +16,8 @@ package com.blazebit.persistence.view.metamodel; +import java.util.Map; + /** * Instances of the type {@linkplain SingularAttribute} represents single-valued properties or fields. * @@ -34,6 +36,15 @@ public interface SingularAttribute extends Attribute { */ public Type getType(); + /** + * Returns the inheritance subtype mappings that should be considered for this attribute. + * When the attribute type is not a subview, this returns an empty map. + * + * @return The inheritance subtype mappings or an empty map + * @since 1.2.0 + */ + public Map, String> getInheritanceSubtypeMappings(); + /** * Returns true if this attribute maps to a query parameter, otherwise false. * diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/EntityViewManagerImpl.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/EntityViewManagerImpl.java index 7104b62a16..9b4c129459 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/EntityViewManagerImpl.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/EntityViewManagerImpl.java @@ -61,16 +61,17 @@ import com.blazebit.persistence.view.impl.filter.StartsWithIgnoreCaseFilterImpl; import com.blazebit.persistence.view.impl.macro.DefaultViewRootJpqlMacro; import com.blazebit.persistence.view.impl.metamodel.ManagedViewTypeImpl; +import com.blazebit.persistence.view.impl.metamodel.MappingConstructorImpl; import com.blazebit.persistence.view.impl.metamodel.MetamodelBuildingContext; import com.blazebit.persistence.view.impl.metamodel.MetamodelBuildingContextImpl; import com.blazebit.persistence.view.impl.metamodel.ViewMetamodelImpl; +import com.blazebit.persistence.view.impl.metamodel.ViewTypeImpl; import com.blazebit.persistence.view.impl.objectbuilder.ViewTypeObjectBuilderTemplate; import com.blazebit.persistence.view.impl.proxy.ProxyFactory; import com.blazebit.persistence.view.impl.proxy.UpdatableProxy; import com.blazebit.persistence.view.impl.update.EntityViewUpdater; import com.blazebit.persistence.view.impl.update.FullEntityViewUpdater; import com.blazebit.persistence.view.impl.update.PartialEntityViewUpdater; -import com.blazebit.persistence.view.metamodel.ManagedViewType; import com.blazebit.persistence.view.metamodel.MappingConstructor; import com.blazebit.persistence.view.metamodel.ViewType; @@ -124,23 +125,23 @@ public EntityViewManagerImpl(EntityViewConfigurationImpl config, CriteriaBuilder this.unsafeDisabled = !Boolean.valueOf(String.valueOf(properties.get(ConfigurationProperties.PROXY_UNSAFE_ALLOWED))); if (Boolean.valueOf(String.valueOf(properties.get(ConfigurationProperties.TEMPLATE_EAGER_LOADING)))) { - for (ViewType view : metamodel.getViews()) { + for (ViewTypeImpl view : metamodel.views()) { // TODO: Might be a good idea to let the view root be overridden or specified via the annotation String probableViewRoot = StringUtils.firstToLower(view.getEntityClass().getSimpleName()); ExpressionFactory macroAwareExpressionFactory = context.createMacroAwareExpressionFactory(probableViewRoot); getTemplate(macroAwareExpressionFactory, view, null, null); for (MappingConstructor constructor : view.getConstructors()) { - getTemplate(macroAwareExpressionFactory, view, (MappingConstructor) constructor, null); + getTemplate(macroAwareExpressionFactory, view, (MappingConstructorImpl) constructor, null); } } } else if (Boolean.valueOf(String.valueOf(properties.get(ConfigurationProperties.PROXY_EAGER_LOADING)))) { // Loading template will always involve also loading the proxies, so we use else if for (ViewType view : metamodel.getViews()) { if (view.getConstructors().isEmpty() || unsafeDisabled) { - proxyFactory.getProxy(view); + proxyFactory.getProxy(view, null); } else { - proxyFactory.getUnsafeProxy(view); + proxyFactory.getUnsafeProxy(view, null); } } } @@ -276,7 +277,7 @@ public String applyObjectBuilder(Class clazz, String mappingConstructorName, if (viewType == null) { throw new IllegalArgumentException("There is no entity view for the class '" + clazz.getName() + "' registered!"); } - MappingConstructor mappingConstructor = viewType.getConstructor(mappingConstructorName); + MappingConstructorImpl mappingConstructor = viewType.getConstructor(mappingConstructorName); String viewName; if (viewType instanceof ViewType) { viewName = ((ViewType) viewType).getName(); @@ -286,24 +287,28 @@ public String applyObjectBuilder(Class clazz, String mappingConstructorName, return applyObjectBuilder(viewType, mappingConstructor, viewName, entityViewRoot, configuration.getCriteriaBuilder(), configuration, 0, true); } - public String applyObjectBuilder(ManagedViewType viewType, MappingConstructor mappingConstructor, String viewName, String entityViewRoot, FullQueryBuilder criteriaBuilder, EntityViewConfiguration configuration, int offset, boolean registerMacro) { + public String applyObjectBuilder(ManagedViewTypeImpl viewType, MappingConstructorImpl mappingConstructor, String viewName, String entityViewRoot, FullQueryBuilder criteriaBuilder, EntityViewConfiguration configuration, int offset, boolean registerMacro) { From root = getFromByViewRoot(criteriaBuilder, entityViewRoot); criteriaBuilder.selectNew(createObjectBuilder(viewType, mappingConstructor, viewName, root, criteriaBuilder, configuration, offset, registerMacro)); return root.getAlias(); } - public ObjectBuilder createObjectBuilder(ManagedViewType viewType, MappingConstructor mappingConstructor, String viewName, String entityViewRoot, FullQueryBuilder criteriaBuilder, EntityViewConfiguration configuration, int offset, boolean registerMacro) { + public ObjectBuilder createObjectBuilder(ManagedViewTypeImpl viewType, MappingConstructorImpl mappingConstructor, String viewName, String entityViewRoot, FullQueryBuilder criteriaBuilder, EntityViewConfiguration configuration, int offset, boolean registerMacro) { From root = getFromByViewRoot(criteriaBuilder, entityViewRoot); return createObjectBuilder(viewType, mappingConstructor, viewName, root, criteriaBuilder, configuration, offset, registerMacro); } - public ObjectBuilder createObjectBuilder(ManagedViewType viewType, MappingConstructor mappingConstructor, String viewName, From root, FullQueryBuilder criteriaBuilder, EntityViewConfiguration configuration, int offset, boolean registerMacro) { + public ObjectBuilder createObjectBuilder(ManagedViewTypeImpl viewType, MappingConstructorImpl mappingConstructor, String viewName, From root, FullQueryBuilder criteriaBuilder, EntityViewConfiguration configuration, int offset, boolean registerMacro) { Class entityClazz = root.getType(); String entityViewRoot = root.getAlias(); ExpressionFactory ef = criteriaBuilder.getService(ExpressionFactory.class); if (!viewType.getEntityClass().isAssignableFrom(entityClazz)) { - throw new IllegalArgumentException("The given view type with the entity type '" + viewType.getEntityClass().getName() - + "' can not be applied to the query builder with result type '" + criteriaBuilder.getResultType().getName() + "'"); + if (entityClazz.isAssignableFrom(viewType.getEntityClass())) { + entityViewRoot = "TREAT(" + entityViewRoot + " AS " + viewType.getJavaType().getName() + ")"; + } else { + throw new IllegalArgumentException("The given view type with the entity type '" + viewType.getEntityClass().getName() + + "' can not be applied to the query builder with result type '" + criteriaBuilder.getResultType().getName() + "'"); + } } if (registerMacro) { @@ -327,11 +332,11 @@ private static From getFromByViewRoot(FullQueryBuilder queryBuilder, Strin } @SuppressWarnings("unchecked") - public ViewTypeObjectBuilderTemplate getTemplate(ExpressionFactory ef, ViewType viewType, MappingConstructor mappingConstructor, String entityViewRoot) { + public ViewTypeObjectBuilderTemplate getTemplate(ExpressionFactory ef, ViewTypeImpl viewType, MappingConstructorImpl mappingConstructor, String entityViewRoot) { return getTemplate(ef, viewType, mappingConstructor, viewType.getName(), entityViewRoot, 0); } - public ViewTypeObjectBuilderTemplate getTemplate(ExpressionFactory ef, ManagedViewType viewType, MappingConstructor mappingConstructor, String name, String entityViewRoot, int offset) { + public ViewTypeObjectBuilderTemplate getTemplate(ExpressionFactory ef, ManagedViewTypeImpl viewType, MappingConstructorImpl mappingConstructor, String name, String entityViewRoot, int offset) { ViewTypeObjectBuilderTemplate.Key key = new ViewTypeObjectBuilderTemplate.Key(ef, viewType, mappingConstructor, name, entityViewRoot, offset); ViewTypeObjectBuilderTemplate value = objectBuilderCache.get(key); diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/PrefixingQueryGenerator.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/PrefixingQueryGenerator.java index 3cdd8e932d..bda07e1e7b 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/PrefixingQueryGenerator.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/PrefixingQueryGenerator.java @@ -19,7 +19,10 @@ import java.util.List; import com.blazebit.persistence.impl.SimpleQueryGenerator; +import com.blazebit.persistence.impl.expression.PathElementExpression; import com.blazebit.persistence.impl.expression.PathExpression; +import com.blazebit.persistence.impl.expression.PropertyExpression; +import com.blazebit.persistence.impl.expression.TreatExpression; /** * @@ -29,7 +32,7 @@ public class PrefixingQueryGenerator extends SimpleQueryGenerator { private final String prefix; - + public PrefixingQueryGenerator(List prefixParts) { StringBuilder prefixSb = new StringBuilder(); @@ -45,8 +48,24 @@ public PrefixingQueryGenerator(List prefixParts) { @Override public void visit(PathExpression expression) { - sb.append(prefix); - super.visit(expression); + final List expressions = expression.getExpressions(); + final PathElementExpression firstElement = expressions.get(0); + // Prefixing will only be done for the inner most path, but not the treat expression + if (!(firstElement instanceof TreatExpression)) { + sb.append(prefix); + } + if (firstElement instanceof PropertyExpression && "this".equalsIgnoreCase(((PropertyExpression) firstElement).getProperty())) { + final int size = expressions.size(); + if (size > 1) { + for (int i = 1; i < size; i++) { + expressions.get(i).accept(this); + } + } else { + sb.setLength(sb.length() - 1); + } + } else { + super.visit(expression); + } } // @Override diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/ScalarTargetResolvingExpressionVisitor.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/ScalarTargetResolvingExpressionVisitor.java index 096e502391..2c66f1202f 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/ScalarTargetResolvingExpressionVisitor.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/ScalarTargetResolvingExpressionVisitor.java @@ -284,7 +284,7 @@ public void visit(GeneralCaseExpression expression) { } } - if (newPositions.isEmpty()) { + if (newPositions.isEmpty() && expression.getDefaultExpr() != null) { PathPosition position = currentPositions.get(j).copy(); pathPositions = new ArrayList<>(); pathPositions.add(currentPosition = position); diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractAttribute.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractAttribute.java index 28e3474138..4efc6f1b11 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractAttribute.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractAttribute.java @@ -16,8 +16,6 @@ package com.blazebit.persistence.view.impl.metamodel; -import java.util.regex.Pattern; - import com.blazebit.persistence.impl.expression.SyntaxErrorException; import com.blazebit.persistence.view.BatchFetch; import com.blazebit.persistence.view.CorrelationProvider; @@ -50,6 +48,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; /** * diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractMethodPluralAttribute.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractMethodPluralAttribute.java index 804b218b5e..6f657e1d22 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractMethodPluralAttribute.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractMethodPluralAttribute.java @@ -17,10 +17,12 @@ package com.blazebit.persistence.view.impl.metamodel; import com.blazebit.persistence.view.CollectionMapping; +import com.blazebit.persistence.view.metamodel.ManagedViewType; import com.blazebit.persistence.view.metamodel.PluralAttribute; import com.blazebit.persistence.view.metamodel.Type; import java.util.Comparator; +import java.util.Map; /** * @@ -30,6 +32,7 @@ public abstract class AbstractMethodPluralAttribute extends AbstractMethodAttribute implements PluralAttribute { private final Type elementType; + private final Map, String> elementInheritanceSubtypes; private final boolean sorted; private final boolean ordered; private final boolean ignoreIndex; @@ -40,6 +43,7 @@ public abstract class AbstractMethodPluralAttribute extends AbstractMet public AbstractMethodPluralAttribute(ManagedViewTypeImpl viewType, MethodAttributeMapping mapping, MetamodelBuildingContext context) { super(viewType, mapping, context); this.elementType = (Type) mapping.getElementType(); + this.elementInheritanceSubtypes = (Map, String>) (Map) mapping.getElementInheritanceSubtypes(); this.sorted = mapping.isSorted(); CollectionMapping collectionMapping = mapping.getCollectionMapping(); @@ -59,6 +63,11 @@ public Type getElementType() { return elementType; } + @Override + public Map, String> getElementInheritanceSubtypeMappings() { + return elementInheritanceSubtypes; + } + @Override public boolean isCollection() { return true; diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractMethodSingularAttribute.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractMethodSingularAttribute.java index 5835b4f367..e8ba30053c 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractMethodSingularAttribute.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractMethodSingularAttribute.java @@ -16,10 +16,13 @@ package com.blazebit.persistence.view.impl.metamodel; +import com.blazebit.persistence.view.metamodel.ManagedViewType; import com.blazebit.persistence.view.metamodel.PluralAttribute; import com.blazebit.persistence.view.metamodel.SingularAttribute; import com.blazebit.persistence.view.metamodel.Type; +import java.util.Map; + /** * * @author Christian Beikov @@ -28,11 +31,13 @@ public abstract class AbstractMethodSingularAttribute extends AbstractMethodAttribute implements SingularAttribute { private final Type type; + private final Map, String> inheritanceSubtypes; @SuppressWarnings("unchecked") public AbstractMethodSingularAttribute(ManagedViewTypeImpl viewType, MethodAttributeMapping mapping, MetamodelBuildingContext context) { super(viewType, mapping, context); this.type = (Type) mapping.getType(); + this.inheritanceSubtypes = (Map, String>) (Map) mapping.getInheritanceSubtypes(); } @Override @@ -65,6 +70,11 @@ protected Type getElementType() { return type; } + @Override + public Map, String> getInheritanceSubtypeMappings() { + return inheritanceSubtypes; + } + @Override public boolean isSubview() { return type.getMappingType() != Type.MappingType.BASIC; diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractParameterPluralAttribute.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractParameterPluralAttribute.java index a84a71b810..6ec90a5160 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractParameterPluralAttribute.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractParameterPluralAttribute.java @@ -17,11 +17,13 @@ package com.blazebit.persistence.view.impl.metamodel; import com.blazebit.persistence.view.CollectionMapping; +import com.blazebit.persistence.view.metamodel.ManagedViewType; import com.blazebit.persistence.view.metamodel.ParameterAttribute; import com.blazebit.persistence.view.metamodel.PluralAttribute; import com.blazebit.persistence.view.metamodel.Type; import java.util.Comparator; +import java.util.Map; /** * @@ -31,6 +33,7 @@ public abstract class AbstractParameterPluralAttribute extends AbstractParameterAttribute implements PluralAttribute, ParameterAttribute { private final Type elementType; + private final Map, String> elementInheritanceSubtypes; private final boolean sorted; private final boolean ordered; private final boolean ignoreIndex; @@ -41,6 +44,7 @@ public abstract class AbstractParameterPluralAttribute extends Abstract public AbstractParameterPluralAttribute(MappingConstructorImpl mappingConstructor, ParameterAttributeMapping mapping, MetamodelBuildingContext context) { super(mappingConstructor, mapping, context); this.elementType = (Type) mapping.getElementType(); + this.elementInheritanceSubtypes = (Map, String>) (Map) mapping.getElementInheritanceSubtypes(); this.sorted = mapping.isSorted(); CollectionMapping collectionMapping = mapping.getCollectionMapping(); @@ -60,6 +64,11 @@ public Type getElementType() { return elementType; } + @Override + public Map, String> getElementInheritanceSubtypeMappings() { + return elementInheritanceSubtypes; + } + @Override public boolean isCollection() { return true; diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractParameterSingularAttribute.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractParameterSingularAttribute.java index a7a579b862..3d2d5b2cc2 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractParameterSingularAttribute.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AbstractParameterSingularAttribute.java @@ -16,10 +16,13 @@ package com.blazebit.persistence.view.impl.metamodel; +import com.blazebit.persistence.view.metamodel.ManagedViewType; import com.blazebit.persistence.view.metamodel.PluralAttribute; import com.blazebit.persistence.view.metamodel.SingularAttribute; import com.blazebit.persistence.view.metamodel.Type; +import java.util.Map; + /** * * @author Christian Beikov @@ -28,11 +31,13 @@ public abstract class AbstractParameterSingularAttribute extends AbstractParameterAttribute implements SingularAttribute { private final Type type; + private final Map, String> inheritanceSubtypes; @SuppressWarnings("unchecked") public AbstractParameterSingularAttribute(MappingConstructorImpl constructor, ParameterAttributeMapping mapping, MetamodelBuildingContext context) { super(constructor, mapping, context); this.type = (Type) mapping.getType(); + this.inheritanceSubtypes = (Map, String>) (Map) mapping.getInheritanceSubtypes(); } @Override @@ -65,6 +70,11 @@ protected Type getElementType() { return type; } + @Override + public Map, String> getInheritanceSubtypeMappings() { + return inheritanceSubtypes; + } + @Override public boolean isSubview() { return type.getMappingType() != Type.MappingType.BASIC; diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AttributeMapping.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AttributeMapping.java index 696dadb45c..3857e33d10 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AttributeMapping.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/AttributeMapping.java @@ -26,6 +26,9 @@ import javax.persistence.metamodel.ManagedType; import java.lang.annotation.Annotation; import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; @@ -47,6 +50,12 @@ public abstract class AttributeMapping { protected ViewMapping typeMapping; protected ViewMapping keyViewMapping; protected ViewMapping elementViewMapping; + protected InheritanceViewMapping inheritanceSubtypeMappings; + protected InheritanceViewMapping keyInheritanceSubtypeMappings; + protected InheritanceViewMapping elementInheritanceSubtypeMappings; + protected Map, String> inheritanceSubtypes; + protected Map, String> keyInheritanceSubtypes; + protected Map, String> elementInheritanceSubtypes; protected AbstractAttribute attribute; public AttributeMapping(Class entityViewClass, ManagedType managedType, Annotation mapping, MetamodelBuildingContext context) { @@ -147,12 +156,63 @@ public Type getElementType() { return elementType = elementViewMapping.getManagedViewType(); } + public Map, String> getInheritanceSubtypes() { + if (inheritanceSubtypes != null) { + return inheritanceSubtypes; + } + return inheritanceSubtypes = initializeInheritanceSubtypes(inheritanceSubtypeMappings, typeMapping); + } + + public Map, String> getKeyInheritanceSubtypes() { + if (keyInheritanceSubtypes != null) { + return keyInheritanceSubtypes; + } + return keyInheritanceSubtypes = initializeInheritanceSubtypes(keyInheritanceSubtypeMappings, keyViewMapping); + } + + public Map, String> getElementInheritanceSubtypes() { + if (elementInheritanceSubtypes != null) { + return elementInheritanceSubtypes; + } + return elementInheritanceSubtypes = initializeInheritanceSubtypes(elementInheritanceSubtypeMappings, elementViewMapping); + } + + @SuppressWarnings("unchecked") + private Map, String> initializeInheritanceSubtypes(InheritanceViewMapping inheritanceSubtypeMappings, ViewMapping viewMapping) { + if (inheritanceSubtypeMappings == null || inheritanceSubtypeMappings.getInheritanceSubtypeMappings().isEmpty()) { + return Collections.emptyMap(); + } + Map, String> map = new LinkedHashMap<>(inheritanceSubtypeMappings.getInheritanceSubtypeMappings().size()); + for (Map.Entry mappingEntry : inheritanceSubtypeMappings.getInheritanceSubtypeMappings().entrySet()) { + String mapping = mappingEntry.getValue(); + if (mapping == null) { + mapping = mappingEntry.getKey().getInheritanceMapping(); + // An empty inheritance mapping signals that a subtype should actually be considered. If it was null it wouldn't be considered + if (mapping == null) { + mapping = ""; + } + } + map.put(mappingEntry.getKey().getManagedViewType(), mapping); + } + if (map.equals(viewMapping.getManagedViewType().getInheritanceSubtypeConfiguration())) { + return (Map, String>) (Map) viewMapping.getManagedViewType().getInheritanceSubtypeConfiguration(); + } else { + return Collections.unmodifiableMap(map); + } + } + protected abstract Class resolveType(); protected abstract Class resolveKeyType(); protected abstract Class resolveElementType(); + protected abstract Map, String> resolveInheritanceSubtypeMappings(); + + protected abstract Map, String> resolveKeyInheritanceSubtypeMappings(); + + protected abstract Map, String> resolveElementInheritanceSubtypeMappings(); + public void initializeViewMappings(Class entityViewRootClass, Map, ViewMapping> viewMappings, Set> dependencies) { Class type = resolveType(); Class keyType = resolveKeyType(); @@ -160,32 +220,63 @@ public void initializeViewMappings(Class entityViewRootClass, Map, V if (context.isEntityView(type)) { typeMapping = initializeDependentMapping(entityViewRootClass, type, viewMappings, dependencies); + inheritanceSubtypeMappings = initializedInheritanceViewMappings(typeMapping, resolveInheritanceSubtypeMappings(), entityViewRootClass, viewMappings, dependencies); } else { this.type = context.getBasicType(type); } if (context.isEntityView(keyType)) { keyViewMapping = initializeDependentMapping(entityViewRootClass, keyType, viewMappings, dependencies); + keyInheritanceSubtypeMappings = initializedInheritanceViewMappings(keyViewMapping, resolveKeyInheritanceSubtypeMappings(), entityViewRootClass, viewMappings, dependencies); } else { this.keyType = context.getBasicType(keyType); } if (context.isEntityView(elementType)) { elementViewMapping = initializeDependentMapping(entityViewRootClass, elementType, viewMappings, dependencies); + elementInheritanceSubtypeMappings = initializedInheritanceViewMappings(elementViewMapping, resolveElementInheritanceSubtypeMappings(), entityViewRootClass, viewMappings, dependencies); } else { this.elementType = context.getBasicType(elementType); } } + private InheritanceViewMapping initializedInheritanceViewMappings(ViewMapping attributeMapping, Map, String> inheritanceMapping, Class entityViewRootClass, Map, ViewMapping> viewMappings, Set> dependencies) { + InheritanceViewMapping inheritanceViewMapping; + Map subtypeMappings = new HashMap<>(); + if (attributeMapping != null) { + if (inheritanceMapping == null) { + inheritanceViewMapping = attributeMapping.getDefaultInheritanceViewMapping(); + } else { + subtypeMappings = new HashMap<>(inheritanceMapping.size() + 1); + + for (Map.Entry, String> mappingEntry : inheritanceMapping.entrySet()) { + subtypeMappings.put(initializeDependentMapping(entityViewRootClass, mappingEntry.getKey(), viewMappings, dependencies), mappingEntry.getValue()); + } + + inheritanceViewMapping = new InheritanceViewMapping(subtypeMappings); + attributeMapping.getInheritanceViewMappings().add(inheritanceViewMapping); + return inheritanceViewMapping; + } + } else { + inheritanceViewMapping = new InheritanceViewMapping(subtypeMappings); + } + + return inheritanceViewMapping; + } + private ViewMapping initializeDependentMapping(Class entityViewRootClass, Class subviewClass, Map, ViewMapping> viewMappings, Set> dependencies) { if (dependencies.contains(subviewClass)) { - context.addError("A circular dependency is introduced at the " + getErrorLocation() + " in the following dependency set: " + Arrays.deepToString(dependencies.toArray())); + circularDependencyError(dependencies); return null; } dependencies.add(subviewClass); // This will initialize all subviews and populate the viewMappings map accordingly - ViewMapping mapping = ViewMapping.initializeViewMappings(entityViewRootClass, subviewClass, context, viewMappings, dependencies); + ViewMapping mapping = ViewMapping.initializeViewMappings(entityViewRootClass, subviewClass, context, viewMappings, dependencies, this); dependencies.remove(subviewClass); return mapping; } + + public void circularDependencyError(Set> dependencies) { + context.addError("A circular dependency is introduced at the " + getErrorLocation() + " in the following dependency set: " + Arrays.deepToString(dependencies.toArray())); + } } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/CollectionMappingLiteral.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/CollectionMappingLiteral.java index daea250c7d..7f731e3578 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/CollectionMappingLiteral.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/CollectionMappingLiteral.java @@ -16,11 +16,11 @@ package com.blazebit.persistence.view.impl.metamodel; +import com.blazebit.persistence.view.CollectionMapping; + import java.lang.annotation.Annotation; import java.util.Comparator; -import com.blazebit.persistence.view.CollectionMapping; - /** * * @author Christian Beikov diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ConstrainedAttribute.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ConstrainedAttribute.java new file mode 100644 index 0000000000..86c1464faa --- /dev/null +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ConstrainedAttribute.java @@ -0,0 +1,58 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.impl.metamodel; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +public class ConstrainedAttribute> { + + private final T attribute; + private final List> selectionConstrainedAttributes; + + @SuppressWarnings("unchecked") + public ConstrainedAttribute(String constraint, T attribute) { + this.attribute = attribute; + this.selectionConstrainedAttributes = new ArrayList<>(); + addSelectionConstraint(constraint, attribute); + } + + public T getAttribute() { + return attribute; + } + + public boolean requiresCaseWhen() { + return selectionConstrainedAttributes.size() > 1; + } + + public Collection> getSelectionConstrainedAttributes() { + return selectionConstrainedAttributes; + } + + public void addSelectionConstraint(String constraint, T attribute) { + this.selectionConstrainedAttributes.add(new AbstractMap.SimpleEntry<>(constraint, attribute)); + } + +} \ No newline at end of file diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/InheritanceViewMapping.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/InheritanceViewMapping.java new file mode 100644 index 0000000000..4c704bb689 --- /dev/null +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/InheritanceViewMapping.java @@ -0,0 +1,89 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.impl.metamodel; + +import java.util.Comparator; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +/** + * @author Christian Beikov + * @since 1.2.0 + */ +public class InheritanceViewMapping { + + private final Map inheritanceSubtypeMappings; + + public InheritanceViewMapping(ViewMapping baseType, Set inheritanceSubtypes) { + Map sortedMap = createSortedMap(); + sortedMap.put(baseType, null); + for (ViewMapping subtypeMapping : inheritanceSubtypes) { + sortedMap.put(subtypeMapping, null); + } + this.inheritanceSubtypeMappings = sortedMap; + } + + public InheritanceViewMapping(Map inheritanceSubtypeMappings) { + Map sortedMap = createSortedMap(); + sortedMap.putAll(inheritanceSubtypeMappings); + this.inheritanceSubtypeMappings = sortedMap; + } + + private Map createSortedMap() { + // Order from abstract to concrete, left to right + return new TreeMap<>(new Comparator() { + @Override + public int compare(ViewMapping o1, ViewMapping o2) { + if (o1.getEntityViewClass() == o2.getEntityViewClass()) { + return 0; + } + if (o1.getEntityViewClass().isAssignableFrom(o2.getEntityViewClass())) { + return -1; + } + if (o2.getEntityViewClass().isAssignableFrom(o1.getEntityViewClass())) { + return 1; + } + + return o1.getEntityViewClass().getName().compareTo(o2.getEntityViewClass().getName()); + } + }); + } + + public Map getInheritanceSubtypeMappings() { + return inheritanceSubtypeMappings; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof InheritanceViewMapping)) { + return false; + } + + InheritanceViewMapping that = (InheritanceViewMapping) o; + + return getInheritanceSubtypeMappings() != null ? getInheritanceSubtypeMappings().equals(that.getInheritanceSubtypeMappings()) : that.getInheritanceSubtypeMappings() == null; + } + + @Override + public int hashCode() { + return getInheritanceSubtypeMappings() != null ? getInheritanceSubtypeMappings().hashCode() : 0; + } +} diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ManagedViewTypeImpl.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ManagedViewTypeImpl.java index 76db68d3ac..791b47bd06 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ManagedViewTypeImpl.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ManagedViewTypeImpl.java @@ -25,11 +25,15 @@ import javax.persistence.metamodel.ManagedType; import java.lang.reflect.Modifier; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.SortedMap; import java.util.TreeMap; /** @@ -39,13 +43,16 @@ */ public abstract class ManagedViewTypeImpl implements ManagedViewType { - protected final Class javaType; - protected final Class entityClass; - protected final int defaultBatchSize; - protected final Map> attributes; - protected final Map> constructors; - protected final Map> constructorIndex; - protected final boolean hasJoinFetchedCollections; + private final Class javaType; + private final Class entityClass; + private final int defaultBatchSize; + private final Map> attributes; + private final Map> constructors; + private final Map> constructorIndex; + private final String inheritanceMapping; + private final InheritanceSubtypeConfiguration defaultInheritanceSubtypeConfiguration; + private final Map, String>, InheritanceSubtypeConfiguration> inheritanceSubtypeConfigurations; + private final boolean hasJoinFetchedCollections; @SuppressWarnings("unchecked") public ManagedViewTypeImpl(ViewMapping viewMapping, MetamodelBuildingContext context) { @@ -67,7 +74,7 @@ public ManagedViewTypeImpl(ViewMapping viewMapping, MetamodelBuildingContext con } // We use a tree map to get a deterministic attribute order - Map> attributes = new TreeMap>(); + Map> attributes = new TreeMap<>(); boolean hasJoinFetchedCollections = false; for (MethodAttributeMapping mapping : viewMapping.getAttributes().values()) { @@ -77,9 +84,18 @@ public ManagedViewTypeImpl(ViewMapping viewMapping, MetamodelBuildingContext con } this.attributes = Collections.unmodifiableMap(attributes); + this.defaultInheritanceSubtypeConfiguration = new InheritanceSubtypeConfiguration<>(this, viewMapping.getDefaultInheritanceViewMapping()); + Map, String>, InheritanceSubtypeConfiguration> inheritanceSubtypeConfigurations = new HashMap<>(); + inheritanceSubtypeConfigurations.put(defaultInheritanceSubtypeConfiguration.inheritanceSubtypeConfiguration, defaultInheritanceSubtypeConfiguration); + for (InheritanceViewMapping inheritanceViewMapping : viewMapping.getInheritanceViewMappings()) { + InheritanceSubtypeConfiguration subtypeConfiguration = new InheritanceSubtypeConfiguration<>(this, inheritanceViewMapping); + inheritanceSubtypeConfigurations.put(subtypeConfiguration.inheritanceSubtypeConfiguration, subtypeConfiguration); + } + + this.inheritanceSubtypeConfigurations = Collections.unmodifiableMap(inheritanceSubtypeConfigurations); - Map> constructors = new HashMap>(); - Map> constructorIndex = new HashMap>(); + Map> constructors = new HashMap<>(); + Map> constructorIndex = new HashMap<>(); for (Map.Entry entry : viewMapping.getConstructors().entrySet()) { ConstructorMapping constructor = entry.getValue(); @@ -94,7 +110,22 @@ public ManagedViewTypeImpl(ViewMapping viewMapping, MetamodelBuildingContext con this.constructors = Collections.unmodifiableMap(constructors); this.constructorIndex = Collections.unmodifiableMap(constructorIndex); + this.inheritanceMapping = viewMapping.getInheritanceMapping(); this.hasJoinFetchedCollections = hasJoinFetchedCollections; + + if (viewMapping.getInheritanceSupertypes().isEmpty()) { + if (inheritanceMapping != null) { + context.addError("Entity view type '" + javaType.getName() + "' has a @EntityViewInheritanceMapping but is never used as subtype which is not allowed!"); + } + } else { + if (inheritanceMapping == null || inheritanceMapping.isEmpty()) { + List> classes = new ArrayList<>(); + for (ViewMapping mapping : viewMapping.getInheritanceSupertypes()) { + classes.add(mapping.getEntityViewClass()); + } + context.addError("Entity view type '" + javaType.getName() + "' has no @EntityViewInheritanceMapping but is used as inheritance subtype in: " + classes); + } + } } public void checkAttributes(MetamodelBuildingContext context) { @@ -224,11 +255,275 @@ public Set getConstructorNames() { } @Override - public MappingConstructor getConstructor(String name) { + public MappingConstructorImpl getConstructor(String name) { return constructorIndex.get(name); } + @Override + public String getInheritanceMapping() { + return inheritanceMapping; + } + + @Override + @SuppressWarnings("unchecked") + public Set> getInheritanceSubtypes() { + return (Set>) (Set) defaultInheritanceSubtypeConfiguration.inheritanceSubtypes; + } + + public Map, String> getInheritanceSubtypeConfiguration() { + return defaultInheritanceSubtypeConfiguration.inheritanceSubtypeConfiguration; + } + public boolean hasJoinFetchedCollections() { return hasJoinFetchedCollections; } + + public boolean hasSubtypes() { + return defaultInheritanceSubtypeConfiguration.inheritanceSubtypes.size() > 1 || !defaultInheritanceSubtypeConfiguration.inheritanceSubtypes.contains(this); + } + + public InheritanceSubtypeConfiguration getInheritanceSubtypeConfiguration(Map, String> inheritanceSubtypeMapping) { + if (inheritanceSubtypeMapping == null || inheritanceSubtypeMapping.isEmpty() || defaultInheritanceSubtypeConfiguration.getInheritanceSubtypeConfiguration() == inheritanceSubtypeMapping) { + return defaultInheritanceSubtypeConfiguration; + } + return inheritanceSubtypeConfigurations.get(inheritanceSubtypeMapping); + } + + public InheritanceSubtypeConfiguration getDefaultInheritanceSubtypeConfiguration() { + return defaultInheritanceSubtypeConfiguration; + } + + public Map, String>, InheritanceSubtypeConfiguration> getInheritanceSubtypeConfigurations() { + return inheritanceSubtypeConfigurations; + } + + public static final class AttributeKey { + final int subtypeIndex; + final String attributeName; + + public AttributeKey(int subtypeIndex, String attributeName) { + this.subtypeIndex = subtypeIndex; + this.attributeName = attributeName; + } + + public int getSubtypeIndex() { + return subtypeIndex; + } + + public String getAttributeName() { + return attributeName; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof AttributeKey)) { + return false; + } + + AttributeKey that = (AttributeKey) o; + + if (subtypeIndex != -1 && that.subtypeIndex != -1 && subtypeIndex != that.subtypeIndex) { + return false; + } + return attributeName.equals(that.attributeName); + } + + @Override + public int hashCode() { + return attributeName.hashCode(); + } + } + + public static class InheritanceSubtypeConfiguration { + private final ManagedViewTypeImpl baseType; + private final Map, String> inheritanceSubtypeConfiguration; + private final Set> inheritanceSubtypes; + private final String inheritanceDiscriminatorMapping; + private final Map>> attributesClosure; + + public InheritanceSubtypeConfiguration(ManagedViewTypeImpl baseType, InheritanceViewMapping inheritanceViewMapping) { + this.baseType = baseType; + ManagedViewTypeImpl[] orderedInheritanceSubtypes = createOrderedSubtypes(inheritanceViewMapping); + this.inheritanceSubtypeConfiguration = createInheritanceSubtypeConfiguration(inheritanceViewMapping); + this.inheritanceSubtypes = Collections.unmodifiableSet(inheritanceSubtypeConfiguration.keySet()); + this.inheritanceDiscriminatorMapping = createInheritanceDiscriminatorMapping(orderedInheritanceSubtypes); + this.attributesClosure = createSubtypeAttributesClosure(orderedInheritanceSubtypes); + } + + public ManagedViewTypeImpl getBaseType() { + return baseType; + } + + public Set> getInheritanceSubtypes() { + return inheritanceSubtypes; + } + + public Map, String> getInheritanceSubtypeConfiguration() { + return inheritanceSubtypeConfiguration; + } + + public String getInheritanceDiscriminatorMapping() { + return inheritanceDiscriminatorMapping; + } + + public Map>> getAttributesClosure() { + return attributesClosure; + } + + @SuppressWarnings("unchecked") + private ManagedViewTypeImpl[] createOrderedSubtypes(InheritanceViewMapping inheritanceViewMapping) { + ManagedViewTypeImpl[] orderedSubtypes = new ManagedViewTypeImpl[inheritanceViewMapping.getInheritanceSubtypeMappings().size()]; + int i = 0; + for (ViewMapping mapping : inheritanceViewMapping.getInheritanceSubtypeMappings().keySet()) { + if (mapping.getEntityViewClass() == baseType.javaType) { + orderedSubtypes[i++] = baseType; + } else { + orderedSubtypes[i++] = (ManagedViewTypeImpl) mapping.getManagedViewType(); + } + } + + return orderedSubtypes; + } + + @SuppressWarnings("unchecked") + private Map, String> createInheritanceSubtypeConfiguration(InheritanceViewMapping inheritanceViewMapping) { + Map, String> configuration = new LinkedHashMap<>(inheritanceViewMapping.getInheritanceSubtypeMappings().size()); + + for (Map.Entry mappingEntry : inheritanceViewMapping.getInheritanceSubtypeMappings().entrySet()) { + String mapping = mappingEntry.getValue(); + if (mapping == null) { + mapping = mappingEntry.getKey().getInheritanceMapping(); + // An empty inheritance mapping signals that a subtype should actually be considered. If it was null it wouldn't be considered + if (mapping == null) { + mapping = ""; + } + } + if (mappingEntry.getKey().getEntityViewClass() == baseType.javaType) { + configuration.put(baseType, mapping); + } else { + configuration.put((ManagedViewTypeImpl) mappingEntry.getKey().getManagedViewType(), mapping); + } + } + + return configuration; + } + + @SuppressWarnings("unchecked") + private String createInheritanceDiscriminatorMapping(ManagedViewTypeImpl[] subtypes) { + SortedMap, Integer> concreteToGeneralViewToDiscriminatorMap = new TreeMap<>(new Comparator>() { + @Override + public int compare(ManagedViewTypeImpl o1, ManagedViewTypeImpl o2) { + if (o1.javaType == o2.javaType) { + return 0; + } + if (o1.javaType.isAssignableFrom(o2.javaType)) { + return 1; + } + if (o2.javaType.isAssignableFrom(o1.javaType)) { + return -1; + } + + return o1.javaType.getName().compareTo(o2.javaType.getName()); + } + }); + + int subtypeIndex = 0; + for (ManagedViewTypeImpl subtype : subtypes) { + concreteToGeneralViewToDiscriminatorMap.put(subtype, subtypeIndex++); + } + + // Build the discriminator mapping expression + StringBuilder sb = new StringBuilder(); + sb.append("CASE"); + for (Map.Entry, Integer> entry : concreteToGeneralViewToDiscriminatorMap.entrySet()) { + ManagedViewTypeImpl subtype = entry.getKey(); + subtypeIndex = entry.getValue(); + if (subtype == baseType) { + continue; + } + String inheritanceMapping = inheritanceSubtypeConfiguration.get(subtype); + + if (inheritanceMapping != null && !inheritanceMapping.isEmpty()) { + sb.append(" WHEN "); + sb.append(inheritanceMapping); + + for (int i = subtypeIndex - 1; i >= 0; i--) { + inheritanceMapping = inheritanceSubtypeConfiguration.get(subtypes[i]); + if (subtypes[i].javaType.isAssignableFrom(subtype.javaType) && inheritanceMapping != null && !inheritanceMapping.isEmpty()) { + sb.append(" AND ").append(inheritanceMapping); + } + } + + sb.append(" THEN "); + sb.append(subtypeIndex); + } + } + + // We only consider mappings that are non-null + String defaultMapping = inheritanceSubtypeConfiguration.get(baseType); + if (defaultMapping != null) { + if (defaultMapping.isEmpty()) { + sb.append(" ELSE 0"); + } else { + sb.append(" WHEN ").append(defaultMapping).append(" THEN 0"); + } + } + + sb.append(" END"); + return sb.toString(); + } + + @SuppressWarnings("unchecked") + private Map>> createSubtypeAttributesClosure(ManagedViewTypeImpl[] subtypes) { + int subtypeIndex = 0; + // Collect all attributes from all subtypes in separate maps + Map>> subtypesAttributesClosure = new LinkedHashMap<>(); + + for (AbstractMethodAttribute attribute : baseType.attributes.values()) { + subtypesAttributesClosure.put(new AttributeKey(0, attribute.getName()), new ConstrainedAttribute>(null, attribute)); + } + + if (subtypes.length > 0) { + // Go through the subtype attributes and put them in the attribute closure maps + for (ManagedViewTypeImpl subtype : subtypes) { + subtypeIndex++; + + for (AbstractMethodAttribute attribute : (Collection>) (Collection) subtype.attributes.values()) { + // Try to find the attribute on some of the super types to see if it is specialized + ConstrainedAttribute> superTypeAttribute = findAttribute(subtypesAttributesClosure, subtypes, subtypeIndex, attribute.getName()); + if (superTypeAttribute != null) { + if (!attribute.getJavaMethod().equals(superTypeAttribute.getAttribute().getJavaMethod())) { + // This attribute was overridden/specialized in a subtype + superTypeAttribute.addSelectionConstraint(inheritanceSubtypeConfiguration.get(subtype), attribute); + } + // Otherwise the attribute came from the parent so we ignore it + } else { + subtypesAttributesClosure.put(new AttributeKey(subtypeIndex, attribute.getName()), new ConstrainedAttribute>(inheritanceSubtypeConfiguration.get(subtype), attribute)); + } + } + } + } + + return subtypesAttributesClosure; + } + + @SuppressWarnings("unchecked") + private ConstrainedAttribute> findAttribute(Map>> subtypesAttributesClosure, ManagedViewTypeImpl[] subtypes, int subtypeIndex, String name) { + for (int i = subtypeIndex - 1; i >= 0; i--) { + // Must be a proper subtype and contain an attribute with that name + if (subtypes[i].javaType.isAssignableFrom(subtypes[i].javaType) && subtypes[i].getAttribute(name) != null) { + // Only then we will try to find the constrained attribute + ConstrainedAttribute attribute = subtypesAttributesClosure.get(new AttributeKey(i, name)); + if (attribute != null) { + return (ConstrainedAttribute>) attribute; + } + } + } + + return null; + } + } } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MappingConstructorImpl.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MappingConstructorImpl.java index a9f2f03d9a..c1e1139ec7 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MappingConstructorImpl.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MappingConstructorImpl.java @@ -17,6 +17,7 @@ package com.blazebit.persistence.view.impl.metamodel; import com.blazebit.persistence.view.ViewConstructor; +import com.blazebit.persistence.view.metamodel.ManagedViewType; import com.blazebit.persistence.view.metamodel.MappingConstructor; import com.blazebit.persistence.view.metamodel.ParameterAttribute; @@ -24,6 +25,7 @@ import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; @@ -38,6 +40,8 @@ public class MappingConstructorImpl implements MappingConstructor { private final ManagedViewTypeImpl declaringType; private final Constructor javaConstructor; private final List> parameters; + private final List> defaultInheritanceParametersAttributesClosureConfiguration; + private final Map, String>, List>> inheritanceSubtypeParameterAttributesClosureConfigurations; private final boolean hasJoinFetchedCollections; @SuppressWarnings("unchecked") @@ -58,9 +62,58 @@ public MappingConstructorImpl(ManagedViewTypeImpl viewType, String name, Cons } this.parameters = Collections.unmodifiableList(parameters); + this.defaultInheritanceParametersAttributesClosureConfiguration = createParameterAttributesClosure(viewType.getDefaultInheritanceSubtypeConfiguration().getInheritanceSubtypeConfiguration(), context); + + Map, String>, List>> inheritanceSubtypeParameterAttributesClosureConfigurations = new HashMap<>(); + + for (Map, String> subtypes : viewType.getInheritanceSubtypeConfigurations().keySet()) { + inheritanceSubtypeParameterAttributesClosureConfigurations.put(subtypes, createParameterAttributesClosure(subtypes, context)); + } + + this.inheritanceSubtypeParameterAttributesClosureConfigurations = Collections.unmodifiableMap(inheritanceSubtypeParameterAttributesClosureConfigurations); this.hasJoinFetchedCollections = hasJoinFetchedCollections; } + @SuppressWarnings("unchecked") + private List> createParameterAttributesClosure(Map, String> subtypes, MetamodelBuildingContext context) { + List> parametersAttributeClosure = new ArrayList>(parameters.size()); + parametersAttributeClosure.addAll(parameters); + + // Go through the subtype parameter attributes of the same named constructor and put them in the attribute closure list + for (ManagedViewType subtype : subtypes.keySet()) { + if (subtype == declaringType) { + continue; + } + + MappingConstructorImpl constructor = (MappingConstructorImpl) subtype.getConstructor(name); + + if (constructor == null) { + context.addError("Could not find required mapping constructor with name '" + name + "' in inheritance subtype '" + subtype.getJavaType().getName() + "'!"); + continue; + } + + parametersAttributeClosure.addAll((List>) (List) constructor.getParameterAttributes()); + } + + return Collections.unmodifiableList(parametersAttributeClosure); + } + + public List> getSubtypeParameterAttributesClosure(Map, String> inheritanceSubtypeMappings) { + if (inheritanceSubtypeMappings == null || inheritanceSubtypeMappings.isEmpty() || declaringType.getDefaultInheritanceSubtypeConfiguration().getInheritanceSubtypeConfiguration() == inheritanceSubtypeMappings) { + return defaultInheritanceParametersAttributesClosureConfiguration; + } + + return inheritanceSubtypeParameterAttributesClosureConfigurations.get(inheritanceSubtypeMappings); + } + + public List> getDefaultInheritanceParametersAttributesClosureConfiguration() { + return defaultInheritanceParametersAttributesClosureConfiguration; + } + + public Map, String>, List>> getInheritanceSubtypeParameterAttributesClosureConfigurations() { + return inheritanceSubtypeParameterAttributesClosureConfigurations; + } + public void checkParameters(ManagedType managedType, Map> collectionMappings, MetamodelBuildingContext context) { for (AbstractParameterAttribute parameter : parameters) { parameter.checkAttribute(managedType, context); diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MappingLiteral.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MappingLiteral.java index 68aca2e831..2016db96bb 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MappingLiteral.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MappingLiteral.java @@ -16,11 +16,11 @@ package com.blazebit.persistence.view.impl.metamodel; -import java.lang.annotation.Annotation; - import com.blazebit.persistence.view.FetchStrategy; import com.blazebit.persistence.view.Mapping; +import java.lang.annotation.Annotation; + /** * * @author Christian Beikov diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MetamodelBuildingContext.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MetamodelBuildingContext.java index 28531cc5cd..31fdd76491 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MetamodelBuildingContext.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MetamodelBuildingContext.java @@ -23,6 +23,7 @@ import com.blazebit.persistence.view.metamodel.Type; import java.util.Map; +import java.util.Set; /** * @@ -50,4 +51,6 @@ public interface MetamodelBuildingContext { public boolean hasErrors(); public boolean isEntityView(Class clazz); + + public Set> findSubtypes(Class entityViewClass); } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MetamodelBuildingContextImpl.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MetamodelBuildingContextImpl.java index b167ed351d..83f00e25db 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MetamodelBuildingContextImpl.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MetamodelBuildingContextImpl.java @@ -30,6 +30,7 @@ import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -121,4 +122,15 @@ public boolean isEntityView(Class clazz) { return entityViewClasses.contains(clazz); } + @Override + public Set> findSubtypes(Class entityViewClass) { + Set> subtypes = new HashSet<>(); + for (Class clazz : entityViewClasses) { + if (entityViewClass.isAssignableFrom(clazz) && entityViewClass != clazz) { + subtypes.add(clazz); + } + } + + return subtypes; + } } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MetamodelUtils.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MetamodelUtils.java index 7679fbbf55..4cd00421e8 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MetamodelUtils.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MetamodelUtils.java @@ -16,6 +16,17 @@ package com.blazebit.persistence.view.impl.metamodel; +import com.blazebit.annotation.AnnotationUtils; +import com.blazebit.persistence.impl.EntityMetamodel; +import com.blazebit.persistence.impl.PathTargetResolvingExpressionVisitor; +import com.blazebit.persistence.impl.expression.ExpressionFactory; +import com.blazebit.persistence.view.CollectionMapping; +import com.blazebit.persistence.view.metamodel.MappingConstructor; +import com.blazebit.reflection.ReflectionUtils; + +import javax.persistence.OrderColumn; +import javax.persistence.metamodel.Attribute; +import javax.persistence.metamodel.ListAttribute; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; @@ -29,18 +40,6 @@ import java.util.SortedMap; import java.util.SortedSet; -import javax.persistence.OrderColumn; -import javax.persistence.metamodel.Attribute; -import javax.persistence.metamodel.ListAttribute; - -import com.blazebit.annotation.AnnotationUtils; -import com.blazebit.persistence.impl.EntityMetamodel; -import com.blazebit.persistence.impl.expression.ExpressionFactory; -import com.blazebit.persistence.view.CollectionMapping; -import com.blazebit.persistence.impl.PathTargetResolvingExpressionVisitor; -import com.blazebit.persistence.view.metamodel.MappingConstructor; -import com.blazebit.reflection.ReflectionUtils; - /** * * @author Christian Beikov diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MethodAttributeMapping.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MethodAttributeMapping.java index 18283cbe10..d6bd72e194 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MethodAttributeMapping.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/MethodAttributeMapping.java @@ -21,6 +21,9 @@ import com.blazebit.persistence.view.CollectionMapping; import com.blazebit.persistence.view.MappingCorrelated; import com.blazebit.persistence.view.MappingCorrelatedSimple; +import com.blazebit.persistence.view.MappingInheritance; +import com.blazebit.persistence.view.MappingInheritanceMapKey; +import com.blazebit.persistence.view.MappingInheritanceSubtype; import com.blazebit.persistence.view.MappingParameter; import com.blazebit.persistence.view.MappingSingular; import com.blazebit.persistence.view.MappingSubquery; @@ -40,6 +43,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NavigableMap; @@ -209,4 +213,69 @@ protected Class resolveElementType() { return typeArguments[typeArguments.length - 1]; } + @Override + protected Map, String> resolveInheritanceSubtypeMappings() { + MappingInheritance inheritance = AnnotationUtils.findAnnotation(method, MappingInheritance.class); + if (inheritance != null) { + Class baseType = null; + if (!inheritance.onlySubtypes()) { + baseType = resolveType(); + } + return resolveInheritanceSubtypeMappings(baseType, inheritance.value()); + } + return resolveInheritanceSubtypeMappings(null, null); + } + + @Override + protected Map, String> resolveKeyInheritanceSubtypeMappings() { + MappingInheritanceMapKey inheritance = AnnotationUtils.findAnnotation(method, MappingInheritanceMapKey.class); + if (inheritance != null) { + Class baseType = null; + if (!inheritance.onlySubtypes()) { + baseType = resolveKeyType(); + } + return resolveInheritanceSubtypeMappings(baseType, inheritance.value()); + } + return null; + } + + @Override + protected Map, String> resolveElementInheritanceSubtypeMappings() { + MappingInheritance inheritance = AnnotationUtils.findAnnotation(method, MappingInheritance.class); + if (inheritance != null) { + Class baseType = null; + if (!inheritance.onlySubtypes()) { + baseType = resolveElementType(); + } + return resolveInheritanceSubtypeMappings(baseType, inheritance.value()); + } + return resolveInheritanceSubtypeMappings(null, null); + } + + private Map, String> resolveInheritanceSubtypeMappings(Class baseType, MappingInheritanceSubtype[] subtypes) { + if (subtypes == null) { + MappingInheritanceSubtype subtype = AnnotationUtils.findAnnotation(method, MappingInheritanceSubtype.class); + if (subtype == null) { + return null; + } else { + subtypes = new MappingInheritanceSubtype[]{ subtype }; + } + } + + Map, String> mappings = new HashMap<>(subtypes.length); + + if (baseType != null) { + mappings.put(baseType, null); + } + + for (MappingInheritanceSubtype subtype : subtypes) { + String mapping = subtype.mapping(); + if (mapping.isEmpty()) { + mapping = null; + } + mappings.put(subtype.value(), mapping); + } + + return mappings; + } } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ParameterAttributeMapping.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ParameterAttributeMapping.java index c5e44e9e66..a4e086d5c8 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ParameterAttributeMapping.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ParameterAttributeMapping.java @@ -20,6 +20,9 @@ import com.blazebit.persistence.view.CollectionMapping; import com.blazebit.persistence.view.MappingCorrelated; import com.blazebit.persistence.view.MappingCorrelatedSimple; +import com.blazebit.persistence.view.MappingInheritance; +import com.blazebit.persistence.view.MappingInheritanceMapKey; +import com.blazebit.persistence.view.MappingInheritanceSubtype; import com.blazebit.persistence.view.MappingParameter; import com.blazebit.persistence.view.MappingSingular; import com.blazebit.persistence.view.MappingSubquery; @@ -41,6 +44,7 @@ import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; import java.util.Collection; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NavigableMap; @@ -203,6 +207,72 @@ protected Class resolveElementType() { return typeArguments[typeArguments.length - 1]; } + @Override + protected Map, String> resolveInheritanceSubtypeMappings() { + MappingInheritance inheritance = findAnnotation(MappingInheritance.class); + if (inheritance != null) { + Class baseType = null; + if (!inheritance.onlySubtypes()) { + baseType = resolveType(); + } + return resolveInheritanceSubtypeMappings(baseType, inheritance.value()); + } + return resolveInheritanceSubtypeMappings(null, null); + } + + @Override + protected Map, String> resolveKeyInheritanceSubtypeMappings() { + MappingInheritanceMapKey inheritance = findAnnotation(MappingInheritanceMapKey.class); + if (inheritance != null) { + Class baseType = null; + if (!inheritance.onlySubtypes()) { + baseType = resolveKeyType(); + } + return resolveInheritanceSubtypeMappings(baseType, inheritance.value()); + } + return null; + } + + @Override + protected Map, String> resolveElementInheritanceSubtypeMappings() { + MappingInheritance inheritance = findAnnotation(MappingInheritance.class); + if (inheritance != null) { + Class baseType = null; + if (!inheritance.onlySubtypes()) { + baseType = resolveElementType(); + } + return resolveInheritanceSubtypeMappings(baseType, inheritance.value()); + } + return resolveInheritanceSubtypeMappings(null, null); + } + + private Map, String> resolveInheritanceSubtypeMappings(Class baseType, MappingInheritanceSubtype[] subtypes) { + if (subtypes == null) { + MappingInheritanceSubtype subtype = findAnnotation(MappingInheritanceSubtype.class); + if (subtype == null) { + return null; + } else { + subtypes = new MappingInheritanceSubtype[]{ subtype }; + } + } + + Map, String> mappings = new HashMap<>(subtypes.length); + + if (baseType != null) { + mappings.put(baseType, null); + } + + for (MappingInheritanceSubtype subtype : subtypes) { + String mapping = subtype.mapping(); + if (mapping.isEmpty()) { + mapping = null; + } + mappings.put(subtype.value(), mapping); + } + + return mappings; + } + private T findAnnotation(Class annotationType) { return MetamodelUtils.findAnnotation(constructor, index, annotationType); } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ViewMapping.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ViewMapping.java index 52337fd0e0..bbdf67e3f8 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ViewMapping.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ViewMapping.java @@ -18,6 +18,8 @@ import com.blazebit.annotation.AnnotationUtils; import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; import com.blazebit.reflection.ReflectionUtils; import javax.persistence.metamodel.ManagedType; @@ -25,17 +27,21 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Map; import java.util.Set; import java.util.TreeMap; +import java.util.TreeSet; /** * * @author Christian Beikov * @since 1.2.0 */ -public class ViewMapping { +public class ViewMapping implements Comparable { private final Class entityViewClass; private final EntityView mapping; @@ -43,15 +49,29 @@ public class ViewMapping { private final MethodAttributeMapping idAttribute; private final Map attributes; private final Map constructors; + private final String inheritanceMapping; + private final Set inheritanceSubtypes; + private final Set inheritanceSupertypes; + private final InheritanceViewMapping defaultInheritanceViewMapping; + private final Set inheritanceViewMappings; private ManagedViewTypeImpl viewType; - public ViewMapping(Class entityViewClass, EntityView mapping, MetamodelBuildingContext context, MethodAttributeMapping idAttribute, Map attributes, Map constructors) { + public ViewMapping(Class entityViewClass, EntityView mapping, MetamodelBuildingContext context, MethodAttributeMapping idAttribute, Map attributes, Map constructors, String inheritanceMapping, Set inheritanceSubtypes) { this.entityViewClass = entityViewClass; this.mapping = mapping; this.context = context; this.idAttribute = idAttribute; this.attributes = attributes; this.constructors = constructors; + this.inheritanceMapping = inheritanceMapping; + this.inheritanceSubtypes = inheritanceSubtypes; + this.inheritanceSupertypes = new HashSet<>(); + this.inheritanceViewMappings = new HashSet<>(); + inheritanceViewMappings.add(defaultInheritanceViewMapping = new InheritanceViewMapping(this, inheritanceSubtypes)); + } + + public InheritanceViewMapping getDefaultInheritanceViewMapping() { + return defaultInheritanceViewMapping; } public Class getEntityViewClass() { @@ -74,6 +94,37 @@ public Map getConstructors() { return constructors; } + public String getInheritanceMapping() { + if (inheritanceMapping == null && !inheritanceSupertypes.isEmpty()) { + Class entityClass = mapping.value(); + // Check all super type inheritance mappings. If we encounter that a super type uses + // an entity class that is not a proper super type of our entity class, we can't infer a type inheritance mapping + for (ViewMapping supertypeMapping : inheritanceSupertypes) { + Class supertypeEntityClass = supertypeMapping.getMapping().value(); + if (!supertypeEntityClass.isAssignableFrom(entityClass) || supertypeEntityClass == entityClass) { + return inheritanceMapping; + } + } + + // If we get here, we know that our entity class type is a proper subtype of all super type inheritance mappings + return "TYPE(this) = " + entityClass.getName(); + } + + return inheritanceMapping; + } + + public Set getInheritanceSubtypes() { + return inheritanceSubtypes; + } + + public Set getInheritanceSupertypes() { + return inheritanceSupertypes; + } + + public Set getInheritanceViewMappings() { + return inheritanceViewMappings; + } + public ManagedViewTypeImpl getManagedViewType() { if (viewType == null) { if (idAttribute != null) { @@ -86,7 +137,7 @@ public ManagedViewTypeImpl getManagedViewType() { return viewType; } - public static ViewMapping initializeViewMappings(Class entityViewRootClass, Class entityViewClass, MetamodelBuildingContext context, Map, ViewMapping> viewMappings, Set> dependencies) { + public static ViewMapping initializeViewMappings(Class entityViewRootClass, Class entityViewClass, MetamodelBuildingContext context, Map, ViewMapping> viewMappings, Set> dependencies, AttributeMapping originatingAttributeMapping) { ViewMapping existingMapping = viewMappings.get(entityViewClass); if (existingMapping != null) { return existingMapping; @@ -96,6 +147,32 @@ public static ViewMapping initializeViewMappings(Class entityViewRootClass, C Class entityClass = entityView.value(); ManagedType managedType = context.getEntityMetamodel().managedType(entityClass); + // Inheritance + EntityViewInheritance inheritanceAnnotation = entityViewClass.getAnnotation(EntityViewInheritance.class); + EntityViewInheritanceMapping inheritanceMappingAnnotation = entityViewClass.getAnnotation(EntityViewInheritanceMapping.class); + String inheritanceMapping; + + if (inheritanceMappingAnnotation != null) { + inheritanceMapping = inheritanceMappingAnnotation.value(); + } else { + inheritanceMapping = null; + } + + Set inheritanceSubtypes; + Set> subtypeClasses; + + if (inheritanceAnnotation == null) { + inheritanceSubtypes = Collections.emptySet(); + subtypeClasses = Collections.emptySet(); + } else if (inheritanceAnnotation.value().length == 0) { + inheritanceSubtypes = new TreeSet<>(); + subtypeClasses = initializeSubtypes(entityViewRootClass, entityViewClass, context, viewMappings, dependencies, originatingAttributeMapping, inheritanceSubtypes, context.findSubtypes(entityViewClass), false); + } else { + inheritanceSubtypes = new LinkedHashSet<>(); + subtypeClasses = initializeSubtypes(entityViewRootClass, entityViewClass, context, viewMappings, dependencies, originatingAttributeMapping, inheritanceSubtypes, new HashSet<>(Arrays.asList(inheritanceAnnotation.value())), true); + } + + // Attributes MethodAttributeMapping idAttribute = null; // We use a tree map to get a deterministic attribute order Map attributes = new TreeMap<>(); @@ -162,7 +239,10 @@ public static ViewMapping initializeViewMappings(Class entityViewRootClass, C constructors.put(new ParametersKey(constructor.getParameterTypes()), constructorMapping); } - ViewMapping viewMapping = new ViewMapping(entityViewClass, entityView, context, idAttribute, attributes, constructors); + ViewMapping viewMapping = new ViewMapping(entityViewClass, entityView, context, idAttribute, attributes, constructors, inheritanceMapping, inheritanceSubtypes); + for (ViewMapping subtype : inheritanceSubtypes) { + subtype.inheritanceSupertypes.add(viewMapping); + } viewMappings.put(entityViewClass, viewMapping); for (MethodAttributeMapping attributeMapping : attributes.values()) { attributeMapping.initializeViewMappings(entityViewRootClass, viewMappings, dependencies); @@ -171,6 +251,42 @@ public static ViewMapping initializeViewMappings(Class entityViewRootClass, C constructorMapping.initializeViewMappings(entityViewRootClass, viewMappings, dependencies); } + // Cleanup dependencies after constructing the view type + dependencies.removeAll(subtypeClasses); + return viewMapping; } + + private static Set> initializeSubtypes(Class entityViewRootClass, Class entityViewClass, MetamodelBuildingContext context, Map, ViewMapping> viewMappings, Set> dependencies, AttributeMapping originatingAttributeMapping, Set inheritanceSubtypes, Set> subtypeClasses, boolean explicit) { + for (Class subtypeClass : subtypeClasses) { + if (!dependencies.add(subtypeClass) && subtypeClass != entityViewClass) { + originatingAttributeMapping.circularDependencyError(dependencies); + return subtypeClasses; + } + } + for (Class subtypeClass : subtypeClasses) { + if (entityViewClass == subtypeClass) { + if (explicit) { + context.addError("Entity view type '" + entityViewClass.getName() + "' declared itself in @EntityViewInheritance as subtype which is not allowed!"); + } + continue; + } + if (explicit) { + if (!entityViewClass.isAssignableFrom(subtypeClass)) { + context.addError("Entity view subtype '" + subtypeClass.getName() + "' was explicitly declared as subtype in '" + entityViewClass.getName() + "' but isn't a Java subtype!"); + } + } + + ViewMapping subtypeMapping = ViewMapping.initializeViewMappings(entityViewRootClass, subtypeClass, context, viewMappings, dependencies, originatingAttributeMapping); + inheritanceSubtypes.add(subtypeMapping); + inheritanceSubtypes.addAll(subtypeMapping.getInheritanceSubtypes()); + } + + return subtypeClasses; + } + + @Override + public int compareTo(ViewMapping o) { + return getEntityViewClass().getName().compareTo(o.getEntityViewClass().getName()); + } } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ViewMetamodelImpl.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ViewMetamodelImpl.java index f0a9bd73b9..94d54d6769 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ViewMetamodelImpl.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ViewMetamodelImpl.java @@ -24,6 +24,7 @@ import com.blazebit.persistence.view.metamodel.ViewType; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.IdentityHashMap; @@ -39,16 +40,16 @@ public class ViewMetamodelImpl implements ViewMetamodel { private final EntityMetamodel metamodel; - private final Map, ViewType> views; - private final Map, FlatViewType> flatViews; + private final Map, ViewTypeImpl> views; + private final Map, FlatViewTypeImpl> flatViews; private final Map, ManagedViewTypeImpl> managedViews; public ViewMetamodelImpl(Set> entityViews, ServiceProvider serviceProvider, MetamodelBuildingContext context, boolean validateExpressions) { this.metamodel = serviceProvider.getService(EntityMetamodel.class); - Map, ViewType> views = new HashMap, ViewType>(entityViews.size()); - Map, FlatViewType> flatViews = new HashMap, FlatViewType>(entityViews.size()); - Map, ManagedViewTypeImpl> managedViews = new HashMap, ManagedViewTypeImpl>(entityViews.size()); + Map, ViewTypeImpl> views = new HashMap<>(entityViews.size()); + Map, FlatViewTypeImpl> flatViews = new HashMap<>(entityViews.size()); + Map, ManagedViewTypeImpl> managedViews = new HashMap<>(entityViews.size()); // For every entity view class, we keep the mapping and the dependent mappings // The dependencies are evaluated during initialization and done recursively which is possible because we require no cycles @@ -57,7 +58,7 @@ public ViewMetamodelImpl(Set> entityViews, ServiceProvider serviceProvi for (Class entityViewClass : entityViews) { // Check for circular dependencies while initializing subview attribute mappings with view mappings dependencies.add(entityViewClass); - ViewMapping.initializeViewMappings(entityViewClass, entityViewClass, context, viewMappings, dependencies); + ViewMapping.initializeViewMappings(entityViewClass, entityViewClass, context, viewMappings, dependencies, null); dependencies.remove(entityViewClass); } // Similarly we initialize dependent view types first and cache keep the objects in the mappings @@ -67,9 +68,9 @@ public ViewMetamodelImpl(Set> entityViews, ServiceProvider serviceProvi managedViews.put(viewMapping.getEntityViewClass(), managedView); if (managedView instanceof FlatViewType) { - flatViews.put(viewMapping.getEntityViewClass(), (FlatViewType) managedView); + flatViews.put(viewMapping.getEntityViewClass(), (FlatViewTypeImpl) managedView); } else { - views.put(viewMapping.getEntityViewClass(), (ViewType) managedView); + views.put(viewMapping.getEntityViewClass(), (ViewTypeImpl) managedView); } } @@ -103,6 +104,10 @@ public Set> getViews() { return new SetView>(views.values()); } + public Collection> views() { + return views.values(); + } + @Override @SuppressWarnings("unchecked") public ManagedViewTypeImpl managedView(Class clazz) { diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ViewTypeImpl.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ViewTypeImpl.java index ab238d78dd..a45e946053 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ViewTypeImpl.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/ViewTypeImpl.java @@ -49,12 +49,12 @@ public ViewTypeImpl(ViewMapping viewMapping, MetamodelBuildingContext context) { EntityView entityViewAnnot = viewMapping.getMapping(); if (entityViewAnnot.name().isEmpty()) { - this.name = javaType.getSimpleName(); + this.name = getJavaType().getSimpleName(); } else { this.name = entityViewAnnot.name(); } - UpdatableEntityView updatableEntityView = AnnotationUtils.findAnnotation(javaType, UpdatableEntityView.class); + UpdatableEntityView updatableEntityView = AnnotationUtils.findAnnotation(getJavaType(), UpdatableEntityView.class); if (updatableEntityView != null) { this.updatable = true; this.partiallyUpdatable = updatableEntityView.partial(); @@ -65,12 +65,12 @@ public ViewTypeImpl(ViewMapping viewMapping, MetamodelBuildingContext context) { Map viewFilters = new HashMap(); - ViewFilter filterMapping = AnnotationUtils.findAnnotation(javaType, ViewFilter.class); - ViewFilters filtersMapping = AnnotationUtils.findAnnotation(javaType, ViewFilters.class); + ViewFilter filterMapping = AnnotationUtils.findAnnotation(getJavaType(), ViewFilter.class); + ViewFilters filtersMapping = AnnotationUtils.findAnnotation(getJavaType(), ViewFilters.class); if (filterMapping != null) { if (filtersMapping != null) { - context.addError("Illegal occurrences of @ViewFilter and @ViewFilters on the class '" + javaType.getName() + "'!"); + context.addError("Illegal occurrences of @ViewFilter and @ViewFilters on the class '" + getJavaType().getName() + "'!"); } else { addFilterMapping(filterMapping, viewFilters, context); } @@ -85,7 +85,7 @@ public ViewTypeImpl(ViewMapping viewMapping, MetamodelBuildingContext context) { if (updatable) { if (idAttribute.isUpdatable()) { - context.addError("Id attribute in entity view '" + javaType.getName() + "' is updatable which is not allowed!"); + context.addError("Id attribute in entity view '" + getJavaType().getName() + "' is updatable which is not allowed!"); } } } @@ -104,7 +104,7 @@ private void addFilterMapping(ViewFilter filterMapping, Map extends AbstractMethodPluralAttribute, V> implements MapAttribute { private final Type keyType; + private final Map, String> keyInheritanceSubtypes; @SuppressWarnings("unchecked") public AbstractMethodMapAttribute(ManagedViewTypeImpl viewType, MethodAttributeMapping mapping, MetamodelBuildingContext context) { super(viewType, mapping, context); this.keyType = (Type) mapping.getKeyType(); + this.keyInheritanceSubtypes = (Map, String>) (Map) mapping.getKeyInheritanceSubtypes(); if (isIgnoreIndex()) { context.addError("Illegal ignoreIndex mapping for the " + mapping.getErrorLocation()); } @@ -48,6 +51,11 @@ public Type getKeyType() { return keyType; } + @Override + public Map, String> getKeyInheritanceSubtypeMappings() { + return keyInheritanceSubtypes; + } + @Override public boolean isKeySubview() { return keyType.getMappingType() != Type.MappingType.BASIC; diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/attribute/AbstractParameterMapAttribute.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/attribute/AbstractParameterMapAttribute.java index 37704e1f7f..54c2bdcf76 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/attribute/AbstractParameterMapAttribute.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/metamodel/attribute/AbstractParameterMapAttribute.java @@ -20,6 +20,7 @@ import com.blazebit.persistence.view.impl.metamodel.MappingConstructorImpl; import com.blazebit.persistence.view.impl.metamodel.MetamodelBuildingContext; import com.blazebit.persistence.view.impl.metamodel.ParameterAttributeMapping; +import com.blazebit.persistence.view.metamodel.ManagedViewType; import com.blazebit.persistence.view.metamodel.MapAttribute; import com.blazebit.persistence.view.metamodel.Type; @@ -33,11 +34,13 @@ public abstract class AbstractParameterMapAttribute extends AbstractParameterPluralAttribute, V> implements MapAttribute { private final Type keyType; + private final Map, String> keyInheritanceSubtypes; @SuppressWarnings("unchecked") public AbstractParameterMapAttribute(MappingConstructorImpl mappingConstructor, ParameterAttributeMapping mapping, MetamodelBuildingContext context) { super(mappingConstructor, mapping, context); this.keyType = (Type) mapping.getKeyType(); + this.keyInheritanceSubtypes = (Map, String>) (Map) mapping.getKeyInheritanceSubtypes(); if (isIgnoreIndex()) { context.addError("Illegal ignoreIndex mapping for the " + mapping.getErrorLocation()); } @@ -48,6 +51,11 @@ public Type getKeyType() { return keyType; } + @Override + public Map, String> getKeyInheritanceSubtypeMappings() { + return keyInheritanceSubtypes; + } + @Override public boolean isKeySubview() { return keyType.getMappingType() != Type.MappingType.BASIC; diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/DelegatingObjectBuilder.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/DelegatingObjectBuilder.java index b3139fc8bc..47a983b7bf 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/DelegatingObjectBuilder.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/DelegatingObjectBuilder.java @@ -28,7 +28,7 @@ */ public class DelegatingObjectBuilder implements ObjectBuilder { - private final ObjectBuilder delegate; + protected final ObjectBuilder delegate; public DelegatingObjectBuilder(ObjectBuilder delegate) { this.delegate = delegate; diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/InheritanceReducerViewTypeObjectBuilder.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/InheritanceReducerViewTypeObjectBuilder.java new file mode 100644 index 0000000000..7910595f8a --- /dev/null +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/InheritanceReducerViewTypeObjectBuilder.java @@ -0,0 +1,47 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.impl.objectbuilder; + +import com.blazebit.persistence.ObjectBuilder; +import com.blazebit.persistence.view.impl.proxy.ObjectInstantiator; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +public class InheritanceReducerViewTypeObjectBuilder extends ReducerViewTypeObjectBuilder { + + private final int subtypeDiscriminatorIndex; + private final ObjectInstantiator[] subtypeInstantiators; + + public InheritanceReducerViewTypeObjectBuilder(ObjectBuilder delegate, int subtypeDiscriminatorIndex, int length, boolean keepTuplePrefix, ObjectInstantiator[] subtypeInstantiators) { + super(delegate, subtypeDiscriminatorIndex + 1, length - 1, keepTuplePrefix); + this.subtypeDiscriminatorIndex = subtypeDiscriminatorIndex; + this.subtypeInstantiators = subtypeInstantiators; + } + + @Override + protected T buildObject(Object[] originalTuple, Object[] tuple) { + Integer index = (Integer) originalTuple[subtypeDiscriminatorIndex]; + if (index == null) { + return null; + } else { + return subtypeInstantiators[index].newInstance(tuple); + } + } +} diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/ReducerViewTypeObjectBuilder.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/ReducerViewTypeObjectBuilder.java index 90d3db5aff..38fe07e02b 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/ReducerViewTypeObjectBuilder.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/ReducerViewTypeObjectBuilder.java @@ -51,7 +51,7 @@ public T build(Object[] tuple) { // We can return the actual array here because we know that the only possible delegate // is the ViewTypeObjectBuilder which consumes the elements of the array - T result = super.build(newTuple.getArray()); + T result = buildObject(tuple, newTuple.getArray()); if (keepTuplePrefix) { // Create a new array and put in the prefix parts as well as the result into it Object[] tupleWithPrefix = new Object[start + 1]; @@ -63,6 +63,10 @@ public T build(Object[] tuple) { return result; } + protected T buildObject(Object[] originalTuple, Object[] tuple) { + return delegate.build(tuple); + } + private static class FastArrayList { /** diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/ViewTypeObjectBuilderTemplate.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/ViewTypeObjectBuilderTemplate.java index e413138735..6122c41e84 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/ViewTypeObjectBuilderTemplate.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/ViewTypeObjectBuilderTemplate.java @@ -16,26 +16,28 @@ package com.blazebit.persistence.view.impl.objectbuilder; -import com.blazebit.lang.StringUtils; import com.blazebit.persistence.FullQueryBuilder; import com.blazebit.persistence.ObjectBuilder; import com.blazebit.persistence.impl.EntityMetamodel; import com.blazebit.persistence.impl.PathTargetResolvingExpressionVisitor; -import com.blazebit.persistence.impl.SimpleQueryGenerator; -import com.blazebit.persistence.impl.expression.Expression; import com.blazebit.persistence.impl.expression.ExpressionFactory; import com.blazebit.persistence.view.FetchStrategy; import com.blazebit.persistence.view.impl.CorrelationProviderFactory; import com.blazebit.persistence.view.impl.CorrelationProviderHelper; import com.blazebit.persistence.view.impl.EntityViewConfiguration; import com.blazebit.persistence.view.impl.EntityViewManagerImpl; -import com.blazebit.persistence.view.impl.PrefixingQueryGenerator; import com.blazebit.persistence.view.impl.SubqueryProviderFactory; import com.blazebit.persistence.view.impl.SubqueryProviderHelper; import com.blazebit.persistence.view.impl.metamodel.AbstractAttribute; +import com.blazebit.persistence.view.impl.metamodel.AbstractMethodAttribute; +import com.blazebit.persistence.view.impl.metamodel.AbstractParameterAttribute; +import com.blazebit.persistence.view.impl.metamodel.ConstrainedAttribute; +import com.blazebit.persistence.view.impl.metamodel.ManagedViewTypeImpl; +import com.blazebit.persistence.view.impl.metamodel.MappingConstructorImpl; import com.blazebit.persistence.view.impl.objectbuilder.mapper.AliasExpressionSubqueryTupleElementMapper; import com.blazebit.persistence.view.impl.objectbuilder.mapper.AliasExpressionTupleElementMapper; import com.blazebit.persistence.view.impl.objectbuilder.mapper.AliasSubqueryTupleElementMapper; +import com.blazebit.persistence.view.impl.objectbuilder.mapper.ConstrainedTupleElementMapper; import com.blazebit.persistence.view.impl.objectbuilder.mapper.ExpressionCorrelationJoinTupleElementMapper; import com.blazebit.persistence.view.impl.objectbuilder.mapper.ExpressionSubqueryTupleElementMapper; import com.blazebit.persistence.view.impl.objectbuilder.mapper.ExpressionTupleElementMapper; @@ -44,8 +46,9 @@ import com.blazebit.persistence.view.impl.objectbuilder.mapper.ParameterizedExpressionCorrelationJoinTupleElementMapper; import com.blazebit.persistence.view.impl.objectbuilder.mapper.ParameterizedExpressionSubqueryTupleElementMapper; import com.blazebit.persistence.view.impl.objectbuilder.mapper.ParameterizedSubqueryTupleElementMapper; -import com.blazebit.persistence.view.impl.objectbuilder.mapper.SubqueryTupleElementMapper; +import com.blazebit.persistence.view.impl.objectbuilder.mapper.SimpleSubqueryTupleElementMapper; import com.blazebit.persistence.view.impl.objectbuilder.mapper.TupleElementMapper; +import com.blazebit.persistence.view.impl.objectbuilder.mapper.TupleElementMapperBuilder; import com.blazebit.persistence.view.impl.objectbuilder.mapper.TupleParameterMapper; import com.blazebit.persistence.view.impl.objectbuilder.transformator.TupleTransformatorFactory; import com.blazebit.persistence.view.impl.objectbuilder.transformer.IndexedListTupleListTransformer; @@ -96,16 +99,16 @@ import com.blazebit.persistence.view.metamodel.SubqueryAttribute; import com.blazebit.persistence.view.metamodel.ViewType; +import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.IdentifiableType; import javax.persistence.metamodel.ManagedType; +import java.util.AbstractMap; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.LinkedHashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Set; /** * @@ -120,29 +123,28 @@ public class ViewTypeObjectBuilderTemplate { private static final int FEATURE_SUBVIEWS = 2; private final ObjectInstantiator objectInstantiator; + private final ObjectInstantiator[] subtypeInstantiators; private final TupleElementMapper[] mappers; private final TupleParameterMapper parameterMapper; private final int effectiveTupleSize; private final boolean hasParameters; private final boolean hasIndexedCollections; private final boolean hasSubviews; + private final boolean hasSubtypes; - private final ManagedViewType viewRoot; + private final ManagedViewTypeImpl viewRoot; private final String viewRootAlias; private final Class managedTypeClass; - private final String aliasPrefix; - private final List mappingPrefix; - private final String idPrefix; private final int[] idPositions; private final int tupleOffset; private final EntityViewManagerImpl evm; private final ExpressionFactory ef; private final ProxyFactory proxyFactory; - private final TupleTransformatorFactory tupleTransformatorFactory = new TupleTransformatorFactory(); + private final TupleTransformatorFactory tupleTransformatorFactory; @SuppressWarnings("unchecked") - private ViewTypeObjectBuilderTemplate(ManagedViewType viewRoot, String viewRootAlias, String attributePath, String aliasPrefix, List mappingPrefix, String idPrefix, int[] idPositions, int tupleOffset, - EntityViewManagerImpl evm, ExpressionFactory ef, ManagedViewType managedViewType, MappingConstructor mappingConstructor, ProxyFactory proxyFactory) { + private ViewTypeObjectBuilderTemplate(ManagedViewTypeImpl viewRoot, String viewRootAlias, String attributePath, String aliasPrefix, String mappingPrefix, String idPrefix, int[] idPositions, int tupleOffset, + Map, String> inheritanceSubtypeMappings, EntityViewManagerImpl evm, ExpressionFactory ef, ManagedViewTypeImpl managedViewType, MappingConstructorImpl mappingConstructor, ProxyFactory proxyFactory) { ViewType viewType; if (managedViewType instanceof ViewType) { viewType = (ViewType) managedViewType; @@ -154,93 +156,173 @@ private ViewTypeObjectBuilderTemplate(ManagedViewType viewRoot, String viewRo if (managedViewType.getConstructors().size() > 1) { throw new IllegalArgumentException("The given view type '" + managedViewType.getJavaType().getName() + "' has multiple constructors but the given constructor was null."); } else if (managedViewType.getConstructors().size() == 1) { - mappingConstructor = (MappingConstructor) managedViewType.getConstructors().toArray()[0]; + mappingConstructor = (MappingConstructorImpl) managedViewType.getConstructors().toArray()[0]; } } this.viewRoot = viewRoot; this.viewRootAlias = viewRootAlias; this.managedTypeClass = managedViewType.getEntityClass(); - this.aliasPrefix = aliasPrefix; - this.mappingPrefix = mappingPrefix; - this.idPrefix = idPrefix; this.idPositions = idPositions; this.tupleOffset = tupleOffset; this.evm = evm; this.ef = ef; this.proxyFactory = proxyFactory; - Set> attributeSet = new LinkedHashSet<>(managedViewType.getAttributes()); - int attributeCount = attributeSet.size(); + ManagedViewTypeImpl.InheritanceSubtypeConfiguration inheritanceSubtypeConfiguration = managedViewType.getInheritanceSubtypeConfiguration(inheritanceSubtypeMappings); + Map>> attributeMap = new LinkedHashMap<>(inheritanceSubtypeConfiguration.getAttributesClosure()); + + int attributeCount = attributeMap.size(); // We have special handling for the id attribute since we need to know it's position in advance // Therefore we have to remove it so that it doesn't get processed as normal attribute if (viewType != null) { - attributeSet.remove(viewType.getIdAttribute()); + attributeMap.remove(new ManagedViewTypeImpl.AttributeKey(0, viewType.getIdAttribute().getName())); } - MethodAttribute[] attributes = attributeSet.toArray(new MethodAttribute[attributeSet.size()]); - ParameterAttribute[] parameterAttributes; + List> parameterAttributeList; if (mappingConstructor == null) { - parameterAttributes = new ParameterAttribute[0]; + parameterAttributeList = Collections.emptyList(); } else { - List> parameterAttributeList = mappingConstructor.getParameterAttributes(); - parameterAttributes = parameterAttributeList.toArray(new ParameterAttribute[parameterAttributeList.size()]); + parameterAttributeList = mappingConstructor.getSubtypeParameterAttributesClosure(inheritanceSubtypeMappings); } - attributeCount += parameterAttributes.length; + attributeCount += parameterAttributeList.size(); - List mappingList = new ArrayList(attributeCount); - List parameterMappingList = new ArrayList(attributeCount); - Class[] parameterTypes = new Class[attributeCount]; + List mappingList = new ArrayList<>(attributeCount); + List parameterMappingList = new ArrayList<>(attributeCount); + List> parameterTypes = new ArrayList<>(attributeCount); boolean[] featuresFound = new boolean[3]; - int parameterOffset = 0; + + final TupleTransformatorFactory tupleTransformatorFactory = new TupleTransformatorFactory(); + final EntityMetamodel metamodel = evm.getMetamodel().getEntityMetamodel(); + TupleElementMapperBuilder mainMapperBuilder = new TupleElementMapperBuilder(0, null, aliasPrefix, mappingPrefix, idPrefix, null, metamodel, ef, mappingList, parameterMappingList, tupleTransformatorFactory); + + // Add inheritance type extraction + if (managedViewType.hasSubtypes()) { + String mapping = inheritanceSubtypeConfiguration.getInheritanceDiscriminatorMapping(); + mainMapperBuilder.addMapper(createMapper(mainMapperBuilder.getMapping(mapping), mainMapperBuilder.getAlias("class"), EMPTY)); + } if (viewType != null) { MethodAttribute idAttribute = viewType.getIdAttribute(); MappingAttribute mappingAttribute = (MappingAttribute) idAttribute; + parameterTypes.add(idAttribute.getJavaType()); + // An id mapping can only be basic or a flat subview if (idAttribute.isSubview()) { - ManagedViewType subViewType = (ManagedViewType) ((SingularAttribute) mappingAttribute).getType(); - applySubviewMapping(mappingAttribute, attributePath, idPositions, subViewType, mappingList, parameterMappingList, false); + ManagedViewTypeImpl subViewType = (ManagedViewTypeImpl) ((SingularAttribute) mappingAttribute).getType(); + applySubviewMapping(mappingAttribute, attributePath, idPositions, subViewType, mainMapperBuilder, false); } else { - applyBasicIdMapping(mappingAttribute, mappingList, parameterMappingList); + applyBasicIdMapping(mappingAttribute, mainMapperBuilder); } + } - parameterTypes[0] = idAttribute.getJavaType(); - parameterOffset = 1; + // Add tuple element mappers for attributes + for (Map.Entry>> attributeEntry : attributeMap.entrySet()) { + ConstrainedAttribute> constrainedAttribute = attributeEntry.getValue(); + parameterTypes.add(constrainedAttribute.getAttribute().getJavaType()); + if (constrainedAttribute.requiresCaseWhen()) { + // Collect all mappers for all constraints + List> builders = new ArrayList<>(constrainedAttribute.getSelectionConstrainedAttributes().size()); + for (Map.Entry> entry : constrainedAttribute.getSelectionConstrainedAttributes()) { + String constraint = entry.getKey(); + AbstractMethodAttribute attribute = entry.getValue(); + EntityType treatType = getTreatType(metamodel, managedViewType, attribute.getDeclaringType()); + TupleElementMapperBuilder mapperBuilder = new TupleElementMapperBuilder(mappingList.size(), constraint, aliasPrefix, mappingPrefix, idPrefix, treatType, metamodel, ef); + applyMapping(constrainedAttribute.getAttribute(), attributePath, mapperBuilder, featuresFound); + builders.add(new AbstractMap.SimpleEntry<>(constraint, mapperBuilder)); + } + ConstrainedTupleElementMapper.addMappers(mappingList, parameterMappingList, tupleTransformatorFactory, builders); + } else { + AbstractMethodAttribute attribute = constrainedAttribute.getAttribute(); + EntityType treatType = getTreatType(metamodel, managedViewType, attribute.getDeclaringType()); + TupleElementMapperBuilder mapperBuilder = new TupleElementMapperBuilder(0, null, aliasPrefix, mappingPrefix, idPrefix, treatType, metamodel, ef, mappingList, parameterMappingList, tupleTransformatorFactory); + applyMapping(attribute, attributePath, mapperBuilder, featuresFound); + } } - for (int i = 0; i < attributes.length; i++) { - parameterTypes[i + parameterOffset] = attributes[i].getJavaType(); + int subtypeIndex = -1; + MappingConstructor lastConstructor; + if (managedViewType.hasSubtypes()) { + lastConstructor = null; + } else { + lastConstructor = mappingConstructor; } - for (int i = 0; i < parameterAttributes.length; i++) { - parameterTypes[i + attributes.length + parameterOffset] = parameterAttributes[i].getJavaType(); + + // Add tuple element mappers for constructor parameters + for (ParameterAttribute parameterAttribute : parameterAttributeList) { + String paramAliasPrefix; + if (lastConstructor == parameterAttribute.getDeclaringConstructor()) { + paramAliasPrefix = aliasPrefix; + } else { + lastConstructor = parameterAttribute.getDeclaringConstructor(); + paramAliasPrefix = aliasPrefix + "_" + (++subtypeIndex) + "_" + lastConstructor.getDeclaringType().getJavaType().getSimpleName(); + } + parameterTypes.add(parameterAttribute.getJavaType()); + EntityType treatType = getTreatType(metamodel, managedViewType, parameterAttribute.getDeclaringType()); + TupleElementMapperBuilder mapperBuilder = new TupleElementMapperBuilder(0, null, paramAliasPrefix, mappingPrefix, idPrefix, treatType, metamodel, ef, mappingList, parameterMappingList, tupleTransformatorFactory); + applyMapping(parameterAttribute, attributePath, mapperBuilder, featuresFound); } - if (managedViewType.getConstructors().isEmpty() || evm.isUnsafeDisabled()) { - this.objectInstantiator = new ReflectionInstantiator(mappingConstructor, proxyFactory, managedViewType, parameterTypes); - } else { - this.objectInstantiator = new UnsafeInstantiator(mappingConstructor, proxyFactory, managedViewType, parameterTypes); + ManagedViewTypeImpl viewTypeBase = null; + if (this.hasSubtypes = managedViewType.hasSubtypes()) { + viewTypeBase = managedViewType; } - - for (int i = 0; i < attributes.length; i++) { - String newAttributePath = getAttributePath(attributePath, attributes[i], false); - applyMapping(attributes[i], newAttributePath, mappingList, parameterMappingList, featuresFound); + Class[] constructorParameterTypes = parameterTypes.toArray(new Class[parameterTypes.size()]); + // This can only happen for subview mappings + if (!inheritanceSubtypeConfiguration.getInheritanceSubtypes().contains(managedViewType)) { + this.objectInstantiator = null; + } else { + this.objectInstantiator = createInstantiator(managedViewType, viewTypeBase, mappingConstructor, constructorParameterTypes); } - for (int i = 0; i < parameterAttributes.length; i++) { - String newAttributePath = getAttributePath(attributePath, parameterAttributes[i], false); - applyMapping(parameterAttributes[i], newAttributePath, mappingList, parameterMappingList, featuresFound); + + List> subtypeInstantiators = new ArrayList<>(inheritanceSubtypeConfiguration.getInheritanceSubtypes().size()); + mappingConstructor = null; + + for (ManagedViewTypeImpl subtype : inheritanceSubtypeConfiguration.getInheritanceSubtypes()) { + if (subtype == managedViewType) { + subtypeInstantiators.add(0, objectInstantiator); + } else { + ObjectInstantiator instantiator = createInstantiator((ManagedViewType) subtype, managedViewType, mappingConstructor, constructorParameterTypes); + subtypeInstantiators.add(instantiator); + } } this.hasParameters = featuresFound[FEATURE_PARAMETERS]; this.hasIndexedCollections = featuresFound[FEATURE_INDEXED_COLLECTIONS]; this.hasSubviews = featuresFound[FEATURE_SUBVIEWS]; + this.subtypeInstantiators = subtypeInstantiators.toArray(new ObjectInstantiator[subtypeInstantiators.size()]); this.effectiveTupleSize = attributeCount; this.mappers = mappingList.toArray(new TupleElementMapper[mappingList.size()]); this.parameterMapper = new TupleParameterMapper(parameterMappingList, tupleOffset); + this.tupleTransformatorFactory = tupleTransformatorFactory; + } + + private EntityType getTreatType(EntityMetamodel metamodel, ManagedViewTypeImpl managedViewType, ManagedViewType subtype) { + if (managedViewType == subtype) { + return null; + } + return getTreatType(metamodel, managedViewType.getEntityClass(), subtype.getEntityClass()); + } + + private EntityType getTreatType(EntityMetamodel metamodel, Class baseType, Class subtype) { + if (baseType == subtype) { + return null; + } + + return metamodel.entity(subtype); + } + + @SuppressWarnings("unchecked") + private ObjectInstantiator createInstantiator(ManagedViewType managedViewType, ManagedViewTypeImpl viewTypeBase, MappingConstructorImpl mappingConstructor, Class[] constructorParameterTypes) { + if (managedViewType.getConstructors().isEmpty() || evm.isUnsafeDisabled()) { + return new ReflectionInstantiator<>((MappingConstructorImpl) mappingConstructor, proxyFactory, (ManagedViewTypeImpl) managedViewType, viewTypeBase, constructorParameterTypes); + } else { + return new UnsafeInstantiator<>((MappingConstructorImpl) mappingConstructor, proxyFactory, (ManagedViewTypeImpl) managedViewType, viewTypeBase, constructorParameterTypes); + } } private TupleElementMapper createMapper(String expression, String[] fetches) { @@ -265,7 +347,8 @@ private TupleElementMapper createMapper(String expression, String alias, String[ } @SuppressWarnings("unchecked") - private void applyMapping(Attribute attribute, String attributePath, List mappingList, List parameterMappingList, boolean[] featuresFound) { + private void applyMapping(Attribute attribute, String parentAttributePath, TupleElementMapperBuilder mapperBuilder, boolean[] featuresFound) { + String attributePath = getAttributePath(parentAttributePath, attribute, false); int batchSize = attribute.getBatchSize(); if (batchSize == -1) { @@ -273,13 +356,13 @@ private void applyMapping(Attribute attribute, String attributePath, List< } if (attribute.isSubquery()) { - applySubqueryMapping((SubqueryAttribute) attribute, mappingList, parameterMappingList); + applySubqueryMapping((SubqueryAttribute) attribute, mapperBuilder); } else { if (attribute.isCollection()) { PluralAttribute pluralAttribute = (PluralAttribute) attribute; boolean listKey = pluralAttribute.isIndexed() && pluralAttribute instanceof ListAttribute; boolean mapKey = pluralAttribute.isIndexed() && pluralAttribute instanceof MapAttribute; - int startIndex = tupleOffset + mappingList.size(); + int startIndex = tupleOffset + mapperBuilder.mapperIndex(); int mapValueStartIndex = startIndex + 1; if (listKey) { @@ -288,7 +371,7 @@ private void applyMapping(Attribute attribute, String attributePath, List< } MappingAttribute mappingAttribute = (MappingAttribute) attribute; featuresFound[FEATURE_INDEXED_COLLECTIONS] = true; - applyCollectionFunctionMapping("INDEX", "_KEY", mappingAttribute, mappingList, parameterMappingList, EMPTY); + applyCollectionFunctionMapping("INDEX", "_KEY", mappingAttribute, mapperBuilder, EMPTY); } else if (mapKey) { if (pluralAttribute.isCorrelated()) { throw new IllegalArgumentException("Correlated mappings can't be indexed!"); @@ -298,11 +381,11 @@ private void applyMapping(Attribute attribute, String attributePath, List< MapAttribute mapAttribute = (MapAttribute) pluralAttribute; if (mapAttribute.isKeySubview()) { featuresFound[FEATURE_SUBVIEWS] = true; - ManagedViewType managedViewType = (ManagedViewType) mapAttribute.getKeyType(); - applySubviewMapping(mappingAttribute, attributePath, idPositions, managedViewType, mappingList, parameterMappingList, true); - mapValueStartIndex = tupleOffset + (mappingList.size() - startIndex) + 1; + ManagedViewTypeImpl managedViewType = (ManagedViewTypeImpl) mapAttribute.getKeyType(); + applySubviewMapping(mappingAttribute, attributePath, idPositions, managedViewType, mapperBuilder, true); + mapValueStartIndex = tupleOffset + (mapperBuilder.mapperIndex() - startIndex) + 1; } else { - applyCollectionFunctionMapping("KEY", "_KEY", mappingAttribute, mappingList, parameterMappingList, EMPTY); + applyCollectionFunctionMapping("KEY", "_KEY", mappingAttribute, mapperBuilder, EMPTY); } } @@ -321,23 +404,23 @@ private void applyMapping(Attribute attribute, String attributePath, List< if (pluralAttribute.isCorrelated()) { CorrelatedAttribute correlatedAttribute = (CorrelatedAttribute) attribute; - ManagedViewType managedViewType = (ManagedViewType) pluralAttribute.getElementType(); - applyCorrelatedSubviewMapping(correlatedAttribute, attributePath, newIdPositions, managedViewType, mappingList, parameterMappingList, batchSize); + ManagedViewTypeImpl managedViewType = (ManagedViewTypeImpl) pluralAttribute.getElementType(); + applyCorrelatedSubviewMapping(correlatedAttribute, attributePath, newIdPositions, (ManagedViewTypeImpl) (ManagedViewTypeImpl) managedViewType, mapperBuilder, batchSize); } else { MappingAttribute mappingAttribute = (MappingAttribute) attribute; - ManagedViewType managedViewType = (ManagedViewType) pluralAttribute.getElementType(); - applySubviewMapping(mappingAttribute, attributePath, newIdPositions, managedViewType, mappingList, parameterMappingList, false); + ManagedViewTypeImpl managedViewType = (ManagedViewTypeImpl) pluralAttribute.getElementType(); + applySubviewMapping(mappingAttribute, attributePath, newIdPositions, managedViewType, mapperBuilder, false); } } else if (mapKey) { MappingAttribute mappingAttribute = (MappingAttribute) attribute; - applyCollectionFunctionMapping("VALUE", "", mappingAttribute, mappingList, parameterMappingList, mappingAttribute.getFetches()); + applyCollectionFunctionMapping("VALUE", "", mappingAttribute, mapperBuilder, mappingAttribute.getFetches()); } else { if (pluralAttribute.isCorrelated()) { CorrelatedAttribute correlatedAttribute = (CorrelatedAttribute) attribute; - applyBasicCorrelatedMapping(correlatedAttribute, attributePath, mappingList, parameterMappingList, batchSize); + applyBasicCorrelatedMapping(correlatedAttribute, attributePath, mapperBuilder, batchSize); } else { MappingAttribute mappingAttribute = (MappingAttribute) attribute; - applyBasicMapping(mappingAttribute, mappingList, parameterMappingList); + applyBasicMapping(mappingAttribute, mapperBuilder); } } @@ -346,29 +429,29 @@ private void applyMapping(Attribute attribute, String attributePath, List< throw new IllegalArgumentException("The list attribute '" + pluralAttribute + "' can not be sorted!"); } else { if (pluralAttribute instanceof MethodAttribute && ((MethodAttribute) pluralAttribute).isUpdatable()) { - tupleTransformatorFactory.add(new UpdatableIndexedListTupleListTransformer(idPositions, startIndex)); + mapperBuilder.setTupleListTransformer(new UpdatableIndexedListTupleListTransformer(idPositions, startIndex)); } else { - tupleTransformatorFactory.add(new IndexedListTupleListTransformer(idPositions, startIndex)); + mapperBuilder.setTupleListTransformer(new IndexedListTupleListTransformer(idPositions, startIndex)); } } } else if (mapKey) { if (pluralAttribute.isSorted()) { if (pluralAttribute instanceof MethodAttribute && ((MethodAttribute) pluralAttribute).isUpdatable()) { - tupleTransformatorFactory.add(new UpdatableSortedMapTupleListTransformer(idPositions, startIndex, mapValueStartIndex, pluralAttribute.getComparator())); + mapperBuilder.setTupleListTransformer(new UpdatableSortedMapTupleListTransformer(idPositions, startIndex, mapValueStartIndex, pluralAttribute.getComparator())); } else { - tupleTransformatorFactory.add(new SortedMapTupleListTransformer(idPositions, startIndex, mapValueStartIndex, pluralAttribute.getComparator())); + mapperBuilder.setTupleListTransformer(new SortedMapTupleListTransformer(idPositions, startIndex, mapValueStartIndex, pluralAttribute.getComparator())); } } else if (pluralAttribute.isOrdered()) { if (pluralAttribute instanceof MethodAttribute && ((MethodAttribute) pluralAttribute).isUpdatable()) { - tupleTransformatorFactory.add(new UpdatableOrderedMapTupleListTransformer(idPositions, startIndex, mapValueStartIndex)); + mapperBuilder.setTupleListTransformer(new UpdatableOrderedMapTupleListTransformer(idPositions, startIndex, mapValueStartIndex)); } else { - tupleTransformatorFactory.add(new OrderedMapTupleListTransformer(idPositions, startIndex, mapValueStartIndex)); + mapperBuilder.setTupleListTransformer(new OrderedMapTupleListTransformer(idPositions, startIndex, mapValueStartIndex)); } } else { if (pluralAttribute instanceof MethodAttribute && ((MethodAttribute) pluralAttribute).isUpdatable()) { - tupleTransformatorFactory.add(new UpdatableMapTupleListTransformer(idPositions, startIndex, mapValueStartIndex)); + mapperBuilder.setTupleListTransformer(new UpdatableMapTupleListTransformer(idPositions, startIndex, mapValueStartIndex)); } else { - tupleTransformatorFactory.add(new MapTupleListTransformer(idPositions, startIndex, mapValueStartIndex)); + mapperBuilder.setTupleListTransformer(new MapTupleListTransformer(idPositions, startIndex, mapValueStartIndex)); } } } else if (!pluralAttribute.isCorrelated() || pluralAttribute.getFetchStrategy() == FetchStrategy.JOIN) { @@ -378,9 +461,9 @@ private void applyMapping(Attribute attribute, String attributePath, List< throw new IllegalArgumentException("The collection attribute '" + pluralAttribute + "' can not be sorted!"); } else { if (pluralAttribute instanceof MethodAttribute && ((MethodAttribute) pluralAttribute).isUpdatable()) { - tupleTransformatorFactory.add(new UpdatableOrderedListTupleListTransformer(idPositions, startIndex)); + mapperBuilder.setTupleListTransformer(new UpdatableOrderedListTupleListTransformer(idPositions, startIndex)); } else { - tupleTransformatorFactory.add(new OrderedListTupleListTransformer(idPositions, startIndex)); + mapperBuilder.setTupleListTransformer(new OrderedListTupleListTransformer(idPositions, startIndex)); } } break; @@ -389,30 +472,30 @@ private void applyMapping(Attribute attribute, String attributePath, List< throw new IllegalArgumentException("The list attribute '" + pluralAttribute + "' can not be sorted!"); } else { if (pluralAttribute instanceof MethodAttribute && ((MethodAttribute) pluralAttribute).isUpdatable()) { - tupleTransformatorFactory.add(new UpdatableOrderedListTupleListTransformer(idPositions, startIndex)); + mapperBuilder.setTupleListTransformer(new UpdatableOrderedListTupleListTransformer(idPositions, startIndex)); } else { - tupleTransformatorFactory.add(new OrderedListTupleListTransformer(idPositions, startIndex)); + mapperBuilder.setTupleListTransformer(new OrderedListTupleListTransformer(idPositions, startIndex)); } } break; case SET: if (pluralAttribute.isSorted()) { if (pluralAttribute instanceof MethodAttribute && ((MethodAttribute) pluralAttribute).isUpdatable()) { - tupleTransformatorFactory.add(new UpdatableSortedSetTupleListTransformer(idPositions, startIndex, pluralAttribute.getComparator())); + mapperBuilder.setTupleListTransformer(new UpdatableSortedSetTupleListTransformer(idPositions, startIndex, pluralAttribute.getComparator())); } else { - tupleTransformatorFactory.add(new SortedSetTupleListTransformer(idPositions, startIndex, pluralAttribute.getComparator())); + mapperBuilder.setTupleListTransformer(new SortedSetTupleListTransformer(idPositions, startIndex, pluralAttribute.getComparator())); } } else if (pluralAttribute.isOrdered()) { if (pluralAttribute instanceof MethodAttribute && ((MethodAttribute) pluralAttribute).isUpdatable()) { - tupleTransformatorFactory.add(new UpdatableOrderedSetTupleListTransformer(idPositions, startIndex)); + mapperBuilder.setTupleListTransformer(new UpdatableOrderedSetTupleListTransformer(idPositions, startIndex)); } else { - tupleTransformatorFactory.add(new OrderedSetTupleListTransformer(idPositions, startIndex)); + mapperBuilder.setTupleListTransformer(new OrderedSetTupleListTransformer(idPositions, startIndex)); } } else { if (pluralAttribute instanceof MethodAttribute && ((MethodAttribute) pluralAttribute).isUpdatable()) { - tupleTransformatorFactory.add(new UpdatableSetTupleListTransformer(idPositions, startIndex)); + mapperBuilder.setTupleListTransformer(new UpdatableSetTupleListTransformer(idPositions, startIndex)); } else { - tupleTransformatorFactory.add(new SetTupleListTransformer(idPositions, startIndex)); + mapperBuilder.setTupleListTransformer(new SetTupleListTransformer(idPositions, startIndex)); } } break; @@ -425,73 +508,80 @@ private void applyMapping(Attribute attribute, String attributePath, List< } else if (((SingularAttribute) attribute).isQueryParameter()) { MappingAttribute mappingAttribute = (MappingAttribute) attribute; featuresFound[FEATURE_PARAMETERS] = true; - applyQueryParameterMapping(mappingAttribute, mappingList, parameterMappingList); + applyQueryParameterMapping(mappingAttribute, mapperBuilder); } else if (attribute.isSubview()) { featuresFound[FEATURE_SUBVIEWS] = true; if (attribute.isCorrelated()) { CorrelatedAttribute correlatedAttribute = (CorrelatedAttribute) attribute; - ManagedViewType managedViewType = (ManagedViewType) ((SingularAttribute) attribute).getType(); - applyCorrelatedSubviewMapping(correlatedAttribute, attributePath, idPositions, managedViewType, mappingList, parameterMappingList, batchSize); + ManagedViewTypeImpl managedViewType = (ManagedViewTypeImpl) ((SingularAttribute) attribute).getType(); + applyCorrelatedSubviewMapping(correlatedAttribute, attributePath, idPositions, (ManagedViewTypeImpl) (ManagedViewTypeImpl) managedViewType, mapperBuilder, batchSize); } else { MappingAttribute mappingAttribute = (MappingAttribute) attribute; - ManagedViewType managedViewType = (ManagedViewType) ((SingularAttribute) attribute).getType(); - applySubviewMapping(mappingAttribute, attributePath, idPositions, managedViewType, mappingList, parameterMappingList, false); + ManagedViewTypeImpl managedViewType = (ManagedViewTypeImpl) ((SingularAttribute) attribute).getType(); + applySubviewMapping(mappingAttribute, attributePath, idPositions, managedViewType, mapperBuilder, false); } } else { if (attribute.isCorrelated()) { CorrelatedAttribute correlatedAttribute = (CorrelatedAttribute) attribute; - applyBasicCorrelatedMapping(correlatedAttribute, attributePath, mappingList, parameterMappingList, batchSize); + applyBasicCorrelatedMapping(correlatedAttribute, attributePath, mapperBuilder, batchSize); } else { MappingAttribute mappingAttribute = (MappingAttribute) attribute; - applyBasicMapping(mappingAttribute, mappingList, parameterMappingList); + applyBasicMapping(mappingAttribute, mapperBuilder); } } } } - private void applyCollectionFunctionMapping(String function, String aliasSuffix, MappingAttribute mappingAttribute, List mappingList, List parameterMappingList, String[] fetches) { - String expression = function + "(" + getMapping(mappingPrefix, mappingAttribute) + ")"; - String alias = getAlias(aliasPrefix, mappingAttribute, false); + private void applyCollectionFunctionMapping(String function, String aliasSuffix, MappingAttribute mappingAttribute, TupleElementMapperBuilder mapperBuilder, String[] fetches) { + String expression = function + "(" + mapperBuilder.getMapping(mappingAttribute) + ")"; + String alias = mapperBuilder.getAlias(mappingAttribute, false); TupleElementMapper mapper; if (alias == null) { mapper = createMapper(expression, fetches); } else { mapper = createMapper(expression, alias + aliasSuffix, fetches); } - mappingList.add(mapper); - parameterMappingList.add(null); + mapperBuilder.addMapper(mapper); } - private void applySubviewMapping(MappingAttribute mappingAttribute, String attributePath, int[] idPositions, ManagedViewType managedViewType, List mappingList, List parameterMappingList, boolean isKey) { + @SuppressWarnings("unchecked") + private void applySubviewMapping(MappingAttribute mappingAttribute, String attributePath, int[] idPositions, ManagedViewTypeImpl managedViewType, TupleElementMapperBuilder mapperBuilder, boolean isKey) { String subviewAttributePath = getAttributePath(attributePath, mappingAttribute, isKey); - String subviewAliasPrefix = getAlias(aliasPrefix, mappingAttribute, isKey); - List subviewMappingPrefix = createSubviewMappingPrefix(mappingPrefix, mappingAttribute, isKey); - String subviewIdPrefix = getMapping(idPrefix, mappingAttribute, isKey); + String subviewAliasPrefix = mapperBuilder.getAlias(mappingAttribute, isKey); + String subviewMappingPrefix = mapperBuilder.createSubviewMappingPrefix(mappingAttribute, isKey); + String subviewIdPrefix = mapperBuilder.createSubviewMappingPrefix(mappingAttribute, isKey); int[] subviewIdPositions; int startIndex; if (managedViewType instanceof ViewType) { subviewIdPositions = new int[idPositions.length + 1]; System.arraycopy(idPositions, 0, subviewIdPositions, 0, idPositions.length); - subviewIdPositions[idPositions.length] = tupleOffset + mappingList.size(); - startIndex = tupleOffset + mappingList.size(); + subviewIdPositions[idPositions.length] = tupleOffset + mapperBuilder.mapperIndex(); + startIndex = tupleOffset + mapperBuilder.mapperIndex(); } else { subviewIdPositions = idPositions; - startIndex = tupleOffset + mappingList.size(); + startIndex = tupleOffset + mapperBuilder.mapperIndex(); } - ViewTypeObjectBuilderTemplate template = new ViewTypeObjectBuilderTemplate(viewRoot, viewRootAlias, subviewAttributePath, subviewAliasPrefix, subviewMappingPrefix, subviewIdPrefix, subviewIdPositions, - startIndex, evm, ef, managedViewType, null, proxyFactory); - Collections.addAll(mappingList, template.mappers); - // We do not copy because the subview object builder will populate the subview's parameters - for (int i = 0; i < template.mappers.length; i++) { - parameterMappingList.add(null); + Map, String> inheritanceSubtypeMappings; + + if (isKey) { + inheritanceSubtypeMappings = (Map, String>) (Map) ((MapAttribute) mappingAttribute).getKeyInheritanceSubtypeMappings(); + } else if (mappingAttribute instanceof PluralAttribute) { + inheritanceSubtypeMappings = (Map, String>) (Map) ((PluralAttribute) mappingAttribute).getElementInheritanceSubtypeMappings(); + } else { + inheritanceSubtypeMappings = (Map, String>) (Map) ((SingularAttribute) mappingAttribute).getInheritanceSubtypeMappings(); } - tupleTransformatorFactory.add(template.tupleTransformatorFactory); - tupleTransformatorFactory.add(new SubviewTupleTransformerFactory(template)); + + ViewTypeObjectBuilderTemplate template = new ViewTypeObjectBuilderTemplate(viewRoot, viewRootAlias, subviewAttributePath, subviewAliasPrefix, subviewMappingPrefix, subviewIdPrefix, subviewIdPositions, + startIndex, inheritanceSubtypeMappings, evm, ef, managedViewType, null, proxyFactory); + mapperBuilder.addMappers(template.mappers); + mapperBuilder.addTupleTransformatorFactory(template.tupleTransformatorFactory); + mapperBuilder.addTupleTransformerFactory(new SubviewTupleTransformerFactory(template)); } - private void applyCorrelatedSubviewMapping(CorrelatedAttribute correlatedAttribute, String attributePath, int[] idPositions, ManagedViewType managedViewType, List mappingList, List parameterMappingList, int batchSize) { + @SuppressWarnings("unchecked") + private void applyCorrelatedSubviewMapping(CorrelatedAttribute correlatedAttribute, String attributePath, int[] idPositions, ManagedViewTypeImpl managedViewType, TupleElementMapperBuilder mapperBuilder, int batchSize) { Class subviewClass = managedViewType.getJavaType(); String subviewAttributePath = getAttributePath(attributePath, correlatedAttribute, false); CorrelationProviderFactory factory = CorrelationProviderHelper.getFactory(correlatedAttribute.getCorrelationProvider()); @@ -499,53 +589,57 @@ private void applyCorrelatedSubviewMapping(CorrelatedAttribute cor if (correlatedAttribute.getFetchStrategy() == FetchStrategy.JOIN) { @SuppressWarnings("unchecked") - String subviewAliasPrefix = getAlias(aliasPrefix, correlatedAttribute, false); - String correlationBasis = getMapping(mappingPrefix, AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis())); + String subviewAliasPrefix = mapperBuilder.getAlias(correlatedAttribute, false); + String correlationBasis = mapperBuilder.getMapping(AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis())); String subviewIdPrefix; if (correlationResult.isEmpty()) { subviewIdPrefix = CorrelationProviderHelper.getDefaultCorrelationAlias(attributePath); } else { subviewIdPrefix = correlationResult; } - List subviewMappingPrefix = Collections.singletonList(subviewIdPrefix); + String subviewMappingPrefix = subviewIdPrefix; int[] subviewIdPositions; int startIndex; if (managedViewType instanceof ViewType) { subviewIdPositions = new int[idPositions.length + 1]; System.arraycopy(idPositions, 0, subviewIdPositions, 0, idPositions.length); - subviewIdPositions[idPositions.length] = tupleOffset + mappingList.size(); - startIndex = tupleOffset + mappingList.size(); + subviewIdPositions[idPositions.length] = tupleOffset + mapperBuilder.mapperIndex(); + startIndex = tupleOffset + mapperBuilder.mapperIndex(); } else { subviewIdPositions = idPositions; - startIndex = tupleOffset + mappingList.size(); + startIndex = tupleOffset + mapperBuilder.mapperIndex(); + } + + Map, String> inheritanceSubtypeMappings; + + if (correlatedAttribute instanceof PluralAttribute) { + inheritanceSubtypeMappings = (Map, String>) (Map) ((PluralAttribute) correlatedAttribute).getElementInheritanceSubtypeMappings(); + } else { + inheritanceSubtypeMappings = (Map, String>) (Map) ((SingularAttribute) correlatedAttribute).getInheritanceSubtypeMappings(); } @SuppressWarnings("unchecked") ViewTypeObjectBuilderTemplate template = new ViewTypeObjectBuilderTemplate(viewRoot, viewRootAlias, subviewAttributePath, subviewAliasPrefix, subviewMappingPrefix, subviewIdPrefix, subviewIdPositions, - startIndex, evm, ef, (ManagedViewType) (ManagedViewType) managedViewType, null, proxyFactory); - Collections.addAll(mappingList, template.mappers); - // We do not copy because the subview object builder will populate the subview's parameters - for (int i = 0; i < template.mappers.length; i++) { - parameterMappingList.add(null); - } - tupleTransformatorFactory.add(template.tupleTransformatorFactory); - tupleTransformatorFactory.add(new CorrelatedSubviewJoinTupleTransformerFactory(template, factory, correlationBasis, correlationResult, attributePath, correlatedAttribute.getFetches())); + startIndex, inheritanceSubtypeMappings, evm, ef, managedViewType, null, proxyFactory); + mapperBuilder.addMappers(template.mappers); + + mapperBuilder.addTupleTransformatorFactory(template.tupleTransformatorFactory); + mapperBuilder.addTupleTransformerFactory(new CorrelatedSubviewJoinTupleTransformerFactory(template, factory, correlationBasis, correlationResult, attributePath, correlatedAttribute.getFetches())); } else if (correlatedAttribute.getFetchStrategy() == FetchStrategy.SELECT) { - String subviewAliasPrefix = getAlias(aliasPrefix, correlatedAttribute, false); - int startIndex = tupleOffset + mappingList.size(); + String subviewAliasPrefix = mapperBuilder.getAlias(correlatedAttribute, false); + int startIndex = tupleOffset + mapperBuilder.mapperIndex(); Class correlationBasisType = getCorrelationBasisType(AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis())); Class correlationBasisEntity = getCorrelationBasisEntityType(AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis()), correlationBasisType); - mappingList.add(createMapper(getMapping(mappingPrefix, AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis()), correlationBasisEntity), subviewAliasPrefix, correlatedAttribute.getFetches())); - parameterMappingList.add(null); + mapperBuilder.addMapper(createMapper(mapperBuilder.getMapping(AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis()), correlationBasisEntity), subviewAliasPrefix, correlatedAttribute.getFetches())); if (batchSize == -1) { batchSize = 1; } if (!correlatedAttribute.isCollection()) { - tupleTransformatorFactory.add(new CorrelatedSingularBatchTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedSingularBatchTupleListTransformerFactory( new SubviewCorrelator(managedViewType, evm, subviewAliasPrefix), subviewClass, viewRoot, correlationResult, factory, subviewAttributePath, correlatedAttribute.getFetches(), startIndex, batchSize, correlationBasisType, correlationBasisEntity )); @@ -556,7 +650,7 @@ private void applyCorrelatedSubviewMapping(CorrelatedAttribute cor if (pluralAttribute.isSorted()) { throw new IllegalArgumentException("The collection attribute '" + pluralAttribute + "' can not be sorted!"); } else { - tupleTransformatorFactory.add(new CorrelatedSingularBatchTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedSingularBatchTupleListTransformerFactory( new SubviewCorrelator(managedViewType, evm, subviewAliasPrefix), subviewClass, viewRoot, correlationResult, factory, subviewAttributePath, correlatedAttribute.getFetches(), startIndex, batchSize, correlationBasisType, correlationBasisEntity )); @@ -566,7 +660,7 @@ private void applyCorrelatedSubviewMapping(CorrelatedAttribute cor if (pluralAttribute.isSorted()) { throw new IllegalArgumentException("The list attribute '" + pluralAttribute + "' can not be sorted!"); } else { - tupleTransformatorFactory.add(new CorrelatedListBatchTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedListBatchTupleListTransformerFactory( new SubviewCorrelator(managedViewType, evm, subviewAliasPrefix), subviewClass, viewRoot, correlationResult, factory, subviewAttributePath, correlatedAttribute.getFetches(), startIndex, batchSize, correlationBasisType, correlationBasisEntity )); @@ -574,17 +668,17 @@ private void applyCorrelatedSubviewMapping(CorrelatedAttribute cor break; case SET: if (pluralAttribute.isSorted()) { - tupleTransformatorFactory.add(new CorrelatedSortedSetBatchTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedSortedSetBatchTupleListTransformerFactory( new SubviewCorrelator(managedViewType, evm, subviewAliasPrefix), subviewClass, viewRoot, correlationResult, factory, subviewAttributePath, correlatedAttribute.getFetches(), startIndex, batchSize, correlationBasisType, correlationBasisEntity, pluralAttribute.getComparator() )); } else if (pluralAttribute.isOrdered()) { - tupleTransformatorFactory.add(new CorrelatedOrderedSetBatchTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedOrderedSetBatchTupleListTransformerFactory( new SubviewCorrelator(managedViewType, evm, subviewAliasPrefix), subviewClass, viewRoot, correlationResult, factory, subviewAttributePath, correlatedAttribute.getFetches(), startIndex, batchSize, correlationBasisType, correlationBasisEntity )); } else { - tupleTransformatorFactory.add(new CorrelatedSetBatchTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedSetBatchTupleListTransformerFactory( new SubviewCorrelator(managedViewType, evm, subviewAliasPrefix), subviewClass, viewRoot, correlationResult, factory, subviewAttributePath, correlatedAttribute.getFetches(), startIndex, batchSize, correlationBasisType, correlationBasisEntity )); @@ -597,17 +691,16 @@ private void applyCorrelatedSubviewMapping(CorrelatedAttribute cor } } } else if (correlatedAttribute.getFetchStrategy() == FetchStrategy.SUBSELECT) { - String subviewAliasPrefix = getAlias(aliasPrefix, correlatedAttribute, false); - int startIndex = tupleOffset + mappingList.size(); + String subviewAliasPrefix = mapperBuilder.getAlias(correlatedAttribute, false); + int startIndex = tupleOffset + mapperBuilder.mapperIndex(); Class correlationBasisType = getCorrelationBasisType(AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis())); Class correlationBasisEntity = getCorrelationBasisEntityType(AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis()), correlationBasisType); - String correlationKeyExpression = getMapping(mappingPrefix, AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis())); + String correlationKeyExpression = mapperBuilder.getMapping(AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis())); - mappingList.add(createMapper(correlationKeyExpression, subviewAliasPrefix, correlatedAttribute.getFetches())); - parameterMappingList.add(null); + mapperBuilder.addMapper(createMapper(correlationKeyExpression, subviewAliasPrefix, correlatedAttribute.getFetches())); if (!correlatedAttribute.isCollection()) { - tupleTransformatorFactory.add(new CorrelatedSingularSubselectTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedSingularSubselectTupleListTransformerFactory( new SubviewCorrelator(managedViewType, evm, subviewAliasPrefix), subviewClass, viewRoot, viewRootAlias, correlationResult, correlationKeyExpression, factory, attributePath, correlatedAttribute.getFetches(), startIndex, correlationBasisType, correlationBasisEntity )); @@ -618,7 +711,7 @@ private void applyCorrelatedSubviewMapping(CorrelatedAttribute cor if (pluralAttribute.isSorted()) { throw new IllegalArgumentException("The collection attribute '" + pluralAttribute + "' can not be sorted!"); } else { - tupleTransformatorFactory.add(new CorrelatedListSubselectTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedListSubselectTupleListTransformerFactory( new SubviewCorrelator(managedViewType, evm, subviewAliasPrefix), subviewClass, viewRoot, viewRootAlias, correlationResult, correlationKeyExpression, factory, attributePath, correlatedAttribute.getFetches(), startIndex, correlationBasisType, correlationBasisEntity )); @@ -628,7 +721,7 @@ private void applyCorrelatedSubviewMapping(CorrelatedAttribute cor if (pluralAttribute.isSorted()) { throw new IllegalArgumentException("The list attribute '" + pluralAttribute + "' can not be sorted!"); } else { - tupleTransformatorFactory.add(new CorrelatedListSubselectTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedListSubselectTupleListTransformerFactory( new SubviewCorrelator(managedViewType, evm, subviewAliasPrefix), subviewClass, viewRoot, viewRootAlias, correlationResult, correlationKeyExpression, factory, attributePath, correlatedAttribute.getFetches(), startIndex, correlationBasisType, correlationBasisEntity )); @@ -636,17 +729,17 @@ private void applyCorrelatedSubviewMapping(CorrelatedAttribute cor break; case SET: if (pluralAttribute.isSorted()) { - tupleTransformatorFactory.add(new CorrelatedSortedSetSubselectTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedSortedSetSubselectTupleListTransformerFactory( new SubviewCorrelator(managedViewType, evm, subviewAliasPrefix), subviewClass, viewRoot, viewRootAlias, correlationResult, correlationKeyExpression, factory, attributePath, correlatedAttribute.getFetches(), startIndex, correlationBasisType, correlationBasisEntity, pluralAttribute.getComparator() )); } else if (pluralAttribute.isOrdered()) { - tupleTransformatorFactory.add(new CorrelatedOrderedSetSubselectTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedOrderedSetSubselectTupleListTransformerFactory( new SubviewCorrelator(managedViewType, evm, subviewAliasPrefix), subviewClass, viewRoot, viewRootAlias, correlationResult, correlationKeyExpression, factory, attributePath, correlatedAttribute.getFetches(), startIndex, correlationBasisType, correlationBasisEntity )); } else { - tupleTransformatorFactory.add(new CorrelatedSetSubselectTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedSetSubselectTupleListTransformerFactory( new SubviewCorrelator(managedViewType, evm, subviewAliasPrefix), subviewClass, viewRoot, viewRootAlias, correlationResult, correlationKeyExpression, factory, attributePath, correlatedAttribute.getFetches(), startIndex, correlationBasisType, correlationBasisEntity )); @@ -663,25 +756,22 @@ private void applyCorrelatedSubviewMapping(CorrelatedAttribute cor } } - private void applyBasicIdMapping(MappingAttribute mappingAttribute, List mappingList, List parameterMappingList) { - mappingList.add(createMapper(getMapping(idPrefix, mappingAttribute, false), getAlias(aliasPrefix, mappingAttribute, false), mappingAttribute.getFetches())); - parameterMappingList.add(null); + private void applyBasicIdMapping(MappingAttribute mappingAttribute, TupleElementMapperBuilder mapperBuilder) { + mapperBuilder.addMapper(createMapper(mapperBuilder.getIdMapping(mappingAttribute, false), mapperBuilder.getAlias(mappingAttribute, false), mappingAttribute.getFetches())); } - private void applyBasicMapping(MappingAttribute mappingAttribute, List mappingList, List parameterMappingList) { - mappingList.add(createMapper(getMapping(mappingPrefix, mappingAttribute), getAlias(aliasPrefix, mappingAttribute, false), mappingAttribute.getFetches())); - parameterMappingList.add(null); + private void applyBasicMapping(MappingAttribute mappingAttribute, TupleElementMapperBuilder mapperBuilder) { + mapperBuilder.addMapper(createMapper(mapperBuilder.getMapping(mappingAttribute), mapperBuilder.getAlias(mappingAttribute, false), mappingAttribute.getFetches())); } - private void applyQueryParameterMapping(MappingAttribute mappingAttribute, List mappingList, List parameterMappingList) { - mappingList.add(createMapper("NULL", EMPTY)); - parameterMappingList.add(mappingAttribute.getMapping()); + private void applyQueryParameterMapping(MappingAttribute mappingAttribute, TupleElementMapperBuilder mapperBuilder) { + mapperBuilder.addQueryParam(mappingAttribute.getMapping()); } - private void applySubqueryMapping(SubqueryAttribute attribute, List mappingList, List parameterMappingList) { + private void applySubqueryMapping(SubqueryAttribute attribute, TupleElementMapperBuilder mapperBuilder) { @SuppressWarnings("unchecked") SubqueryProviderFactory factory = SubqueryProviderHelper.getFactory(attribute.getSubqueryProvider()); - String alias = getAlias(aliasPrefix, attribute, false); + String alias = mapperBuilder.getAlias(attribute, false); String subqueryAlias = attribute.getSubqueryAlias(); String subqueryExpression = attribute.getSubqueryExpression(); @@ -697,7 +787,7 @@ private void applySubqueryMapping(SubqueryAttribute attribute, List attribute, List correlatedAttribute, String attributePath, List mappingList, List parameterMappingList, int batchSize) { + private void applyBasicCorrelatedMapping(CorrelatedAttribute correlatedAttribute, String attributePath, TupleElementMapperBuilder mapperBuilder, int batchSize) { String correlationResult = correlatedAttribute.getCorrelationResult(); if (correlatedAttribute.getFetchStrategy() == FetchStrategy.JOIN) { @SuppressWarnings("unchecked") CorrelationProviderFactory factory = CorrelationProviderHelper.getFactory(correlatedAttribute.getCorrelationProvider()); - String alias = getAlias(aliasPrefix, correlatedAttribute, false); - String correlationBasis = getMapping(mappingPrefix, AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis())); + String alias = mapperBuilder.getAlias(correlatedAttribute, false); + String correlationBasis = mapperBuilder.getMapping(AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis())); TupleElementMapper mapper; if (factory.isParameterized()) { @@ -733,17 +822,15 @@ private void applyBasicCorrelatedMapping(CorrelatedAttribute correlatedAtt } else { mapper = new ExpressionCorrelationJoinTupleElementMapper(factory.create(null, null), correlationBasis, correlationResult, alias, attributePath, correlatedAttribute.getFetches()); } - mappingList.add(mapper); - parameterMappingList.add(null); + mapperBuilder.addMapper(mapper); } else if (correlatedAttribute.getFetchStrategy() == FetchStrategy.SELECT) { - String subviewAliasPrefix = getAlias(aliasPrefix, correlatedAttribute, false); - int startIndex = tupleOffset + mappingList.size(); + String subviewAliasPrefix = mapperBuilder.getAlias(correlatedAttribute, false); + int startIndex = tupleOffset + mapperBuilder.mapperIndex(); Class correlationBasisType = getCorrelationBasisType(AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis())); Class correlationBasisEntity = getCorrelationBasisEntityType(AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis()), correlationBasisType); - String correlationKeyExpression = getMapping(mappingPrefix, AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis()), correlationBasisEntity); + String correlationKeyExpression = mapperBuilder.getMapping(AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis()), correlationBasisEntity); - mappingList.add(createMapper(correlationKeyExpression, subviewAliasPrefix, correlatedAttribute.getFetches())); - parameterMappingList.add(null); + mapperBuilder.addMapper(createMapper(correlationKeyExpression, subviewAliasPrefix, correlatedAttribute.getFetches())); Class resultType; CorrelationProviderFactory factory = CorrelationProviderHelper.getFactory(correlatedAttribute.getCorrelationProvider()); @@ -756,7 +843,7 @@ private void applyBasicCorrelatedMapping(CorrelatedAttribute correlatedAtt if (!correlatedAttribute.isCollection()) { // TODO: shouldn't we embed this query no matter what strategy is used? resultType = correlatedAttribute.getJavaType(); - tupleTransformatorFactory.add(new CorrelatedSingularBatchTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedSingularBatchTupleListTransformerFactory( new BasicCorrelator(), resultType, viewRoot, correlationResult, factory, basicAttributePath, correlatedAttribute.getFetches(), startIndex, batchSize, correlationBasisType, correlationBasisEntity )); @@ -768,7 +855,7 @@ private void applyBasicCorrelatedMapping(CorrelatedAttribute correlatedAtt if (pluralAttribute.isSorted()) { throw new IllegalArgumentException("The collection attribute '" + pluralAttribute + "' can not be sorted!"); } else { - tupleTransformatorFactory.add(new CorrelatedListBatchTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedListBatchTupleListTransformerFactory( new BasicCorrelator(), resultType, viewRoot, correlationResult, factory, basicAttributePath, correlatedAttribute.getFetches(), startIndex, batchSize, correlationBasisType, correlationBasisEntity )); @@ -778,7 +865,7 @@ private void applyBasicCorrelatedMapping(CorrelatedAttribute correlatedAtt if (pluralAttribute.isSorted()) { throw new IllegalArgumentException("The list attribute '" + pluralAttribute + "' can not be sorted!"); } else { - tupleTransformatorFactory.add(new CorrelatedListBatchTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedListBatchTupleListTransformerFactory( new BasicCorrelator(), resultType, viewRoot, correlationResult, factory, basicAttributePath, correlatedAttribute.getFetches(), startIndex, batchSize, correlationBasisType, correlationBasisEntity )); @@ -786,17 +873,17 @@ private void applyBasicCorrelatedMapping(CorrelatedAttribute correlatedAtt break; case SET: if (pluralAttribute.isSorted()) { - tupleTransformatorFactory.add(new CorrelatedSortedSetBatchTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedSortedSetBatchTupleListTransformerFactory( new BasicCorrelator(), resultType, viewRoot, correlationResult, factory, basicAttributePath, correlatedAttribute.getFetches(), startIndex, batchSize, correlationBasisType, correlationBasisEntity, pluralAttribute.getComparator() )); } else if (pluralAttribute.isOrdered()) { - tupleTransformatorFactory.add(new CorrelatedOrderedSetBatchTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedOrderedSetBatchTupleListTransformerFactory( new BasicCorrelator(), resultType, viewRoot, correlationResult, factory, basicAttributePath, correlatedAttribute.getFetches(), startIndex, batchSize, correlationBasisType, correlationBasisEntity )); } else { - tupleTransformatorFactory.add(new CorrelatedSetBatchTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedSetBatchTupleListTransformerFactory( new BasicCorrelator(), resultType, viewRoot, correlationResult, factory, basicAttributePath, correlatedAttribute.getFetches(), startIndex, batchSize, correlationBasisType, correlationBasisEntity )); @@ -809,21 +896,20 @@ private void applyBasicCorrelatedMapping(CorrelatedAttribute correlatedAtt } } } else if (correlatedAttribute.getFetchStrategy() == FetchStrategy.SUBSELECT) { - String subviewAliasPrefix = getAlias(aliasPrefix, correlatedAttribute, false); - int startIndex = tupleOffset + mappingList.size(); + String subviewAliasPrefix = mapperBuilder.getAlias(correlatedAttribute, false); + int startIndex = tupleOffset + mapperBuilder.mapperIndex(); Class correlationBasisType = getCorrelationBasisType(AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis())); Class correlationBasisEntity = getCorrelationBasisEntityType(AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis()), correlationBasisType); - String correlationKeyExpression = getMapping(mappingPrefix, AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis())); + String correlationKeyExpression = mapperBuilder.getMapping(AbstractAttribute.stripThisFromMapping(correlatedAttribute.getCorrelationBasis())); - mappingList.add(createMapper(correlationKeyExpression, subviewAliasPrefix, correlatedAttribute.getFetches())); - parameterMappingList.add(null); + mapperBuilder.addMapper(createMapper(correlationKeyExpression, subviewAliasPrefix, correlatedAttribute.getFetches())); Class resultType; CorrelationProviderFactory factory = CorrelationProviderHelper.getFactory(correlatedAttribute.getCorrelationProvider()); if (!correlatedAttribute.isCollection()) { resultType = correlatedAttribute.getJavaType(); - tupleTransformatorFactory.add(new CorrelatedSingularSubselectTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedSingularSubselectTupleListTransformerFactory( new BasicCorrelator(), resultType, viewRoot, viewRootAlias, correlationResult, correlationKeyExpression, factory, attributePath, correlatedAttribute.getFetches(), startIndex, correlationBasisType, correlationBasisEntity )); @@ -835,7 +921,7 @@ private void applyBasicCorrelatedMapping(CorrelatedAttribute correlatedAtt if (pluralAttribute.isSorted()) { throw new IllegalArgumentException("The collection attribute '" + pluralAttribute + "' can not be sorted!"); } else { - tupleTransformatorFactory.add(new CorrelatedListSubselectTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedListSubselectTupleListTransformerFactory( new BasicCorrelator(), resultType, viewRoot, viewRootAlias, correlationResult, correlationKeyExpression, factory, attributePath, correlatedAttribute.getFetches(), startIndex, correlationBasisType, correlationBasisEntity )); @@ -845,7 +931,7 @@ private void applyBasicCorrelatedMapping(CorrelatedAttribute correlatedAtt if (pluralAttribute.isSorted()) { throw new IllegalArgumentException("The list attribute '" + pluralAttribute + "' can not be sorted!"); } else { - tupleTransformatorFactory.add(new CorrelatedListSubselectTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedListSubselectTupleListTransformerFactory( new BasicCorrelator(), resultType, viewRoot, viewRootAlias, correlationResult, correlationKeyExpression, factory, attributePath, correlatedAttribute.getFetches(), startIndex, correlationBasisType, correlationBasisEntity )); @@ -853,17 +939,17 @@ private void applyBasicCorrelatedMapping(CorrelatedAttribute correlatedAtt break; case SET: if (pluralAttribute.isSorted()) { - tupleTransformatorFactory.add(new CorrelatedSortedSetSubselectTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedSortedSetSubselectTupleListTransformerFactory( new BasicCorrelator(), resultType, viewRoot, viewRootAlias, correlationResult, correlationKeyExpression, factory, attributePath, correlatedAttribute.getFetches(), startIndex, correlationBasisType, correlationBasisEntity, pluralAttribute.getComparator() )); } else if (pluralAttribute.isOrdered()) { - tupleTransformatorFactory.add(new CorrelatedOrderedSetSubselectTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedOrderedSetSubselectTupleListTransformerFactory( new BasicCorrelator(), resultType, viewRoot, viewRootAlias, correlationResult, correlationKeyExpression, factory, attributePath, correlatedAttribute.getFetches(), startIndex, correlationBasisType, correlationBasisEntity )); } else { - tupleTransformatorFactory.add(new CorrelatedSetSubselectTupleListTransformerFactory( + mapperBuilder.setTupleListTransformerFactory(new CorrelatedSetSubselectTupleListTransformerFactory( new BasicCorrelator(), resultType, viewRoot, viewRootAlias, correlationResult, correlationKeyExpression, factory, attributePath, correlatedAttribute.getFetches(), startIndex, correlationBasisType, correlationBasisEntity )); @@ -880,38 +966,6 @@ private void applyBasicCorrelatedMapping(CorrelatedAttribute correlatedAtt } } - private List createSubviewMappingPrefix(List prefixParts, String mapping) { - if (prefixParts == null || prefixParts.isEmpty()) { - if (mapping.isEmpty()) { - return Collections.emptyList(); - } - return Collections.singletonList(mapping); - } - - if (mapping.isEmpty()) { - return new ArrayList<>(prefixParts); - } - - List subviewMappingPrefix = new ArrayList(prefixParts.size() + 1); - subviewMappingPrefix.addAll(prefixParts); - subviewMappingPrefix.add(mapping); - return subviewMappingPrefix; - } - - private List createSubviewMappingPrefix(List prefixParts, MappingAttribute mappingAttribute, boolean isKey) { - List prefix = createSubviewMappingPrefix(prefixParts, AbstractAttribute.stripThisFromMapping(mappingAttribute.getMapping())); - if (isKey) { - List subviewMappingPrefix = new ArrayList(); - StringBuilder sb = new StringBuilder(); - sb.append("KEY("); - StringUtils.join(sb, ".", prefix); - sb.append(')'); - subviewMappingPrefix.add(sb.toString()); - prefix = subviewMappingPrefix; - } - return prefix; - } - private Class getCorrelationBasisType(String correlationBasis) { if (correlationBasis.isEmpty()) { return managedTypeClass; @@ -946,95 +1000,6 @@ private Class getCorrelationBasisEntityType(String correlationBasis, Class throw new IllegalArgumentException("The correlation basis '" + correlationBasis + "' in the context of the managed type '" + managedTypeClass.getName() + "' resolved to the non-identifiable type '" + entityClazz.getName() + "'!"); } - private String getMapping(List prefixParts, String mapping, Class expressionType) { - if (expressionType == null) { - return getMapping(prefixParts, mapping); - } - - ManagedType managedType = evm.getMetamodel().getEntityMetamodel().getManagedType(expressionType); - if (managedType == null || !(managedType instanceof IdentifiableType)) { - return getMapping(prefixParts, mapping); - } - - IdentifiableType identifiableType = (IdentifiableType) managedType; - javax.persistence.metamodel.SingularAttribute idAttr = identifiableType.getId(identifiableType.getIdType().getJavaType()); - if (mapping.isEmpty()) { - return getMapping(prefixParts, idAttr.getName()); - } else { - return getMapping(prefixParts, mapping + '.' + idAttr.getName()); - } - } - - private String getMapping(List prefixParts, String mapping) { - if (mapping.isEmpty()) { - if (prefixParts != null && prefixParts.size() > 0) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < prefixParts.size(); i++) { - String s = AbstractAttribute.stripThisFromMapping(prefixParts.get(i)); - if (!s.isEmpty()) { - sb.append(s); - sb.append('.'); - } - } - if (sb.length() != 0) { - return sb.substring(0, sb.length() - 1); - } - } - - return ""; - } - if (prefixParts != null && prefixParts.size() > 0) { - Expression expr = ef.createSimpleExpression(mapping, false); - SimpleQueryGenerator generator = new PrefixingQueryGenerator(prefixParts); - StringBuilder sb = new StringBuilder(); - generator.setQueryBuffer(sb); - expr.accept(generator); - return sb.toString(); - } - - return mapping; - } - - private String getMapping(List prefixParts, MappingAttribute mappingAttribute) { - return getMapping(prefixParts, AbstractAttribute.stripThisFromMapping(mappingAttribute.getMapping())); - } - - private String getMapping(String prefix, String mapping) { - if (prefix != null) { - return getMapping(Collections.singletonList(prefix), mapping); - } - - return mapping; - } - - private String getMapping(String prefix, MappingAttribute mappingAttribute, boolean isKey) { - String mapping = getMapping(prefix, AbstractAttribute.stripThisFromMapping(mappingAttribute.getMapping())); - if (isKey) { - return "KEY(" + mapping + ")"; - } - - return mapping; - } - - private static String getAlias(String prefix, String attributeName) { - if (prefix == null) { - return attributeName; - } else { - return prefix + "_" + attributeName; - } - } - - private static String getAlias(String prefix, Attribute attribute, boolean isKey) { - if (isKey) { - prefix = prefix + "_key"; - } - if (attribute instanceof MethodAttribute) { - return getAlias(prefix, ((MethodAttribute) attribute).getName()); - } else { - return getAlias(prefix, "$" + ((ParameterAttribute) attribute).getIndex()); - } - } - private String getAttributePath(String attributePath, Attribute attribute, boolean isKey) { String attributeName; if (attribute instanceof MethodAttribute) { @@ -1068,7 +1033,9 @@ public ObjectBuilder createObjectBuilder(FullQueryBuilder queryBuilder, result = new ViewTypeObjectBuilder(this, queryBuilder, optionalParameters); - if (hasOffset || isSubview || hasIndexedCollections || hasSubviews) { + if (hasSubtypes) { + result = new InheritanceReducerViewTypeObjectBuilder<>(result, tupleOffset, mappers.length, !isSubview && tupleOffset > 0, subtypeInstantiators); + } else if (hasOffset || isSubview || hasIndexedCollections || hasSubviews) { result = new ReducerViewTypeObjectBuilder(result, tupleOffset, mappers.length, !isSubview && tupleOffset > 0); } @@ -1114,16 +1081,16 @@ public int getEffectiveTupleSize() { public static class Key { private final ExpressionFactory ef; - private final ManagedViewType viewType; - private final MappingConstructor constructor; + private final ManagedViewTypeImpl viewType; + private final MappingConstructorImpl constructor; private final String name; private final String entityViewRoot; private final int offset; - public Key(ExpressionFactory ef, ManagedViewType viewType, MappingConstructor constructor, String name, String entityViewRoot, int offset) { + public Key(ExpressionFactory ef, ManagedViewTypeImpl viewType, MappingConstructorImpl constructor, String name, String entityViewRoot, int offset) { this.ef = ef; - this.viewType = (ManagedViewType) viewType; - this.constructor = (MappingConstructor) constructor; + this.viewType = (ManagedViewTypeImpl) viewType; + this.constructor = (MappingConstructorImpl) constructor; this.name = name; this.entityViewRoot = entityViewRoot; this.offset = offset; @@ -1131,14 +1098,11 @@ public Key(ExpressionFactory ef, ManagedViewType viewType, MappingConstructor public ViewTypeObjectBuilderTemplate createValue(EntityViewManagerImpl evm, ProxyFactory proxyFactory) { int[] idPositions = new int[offset + 1]; + // If it has subtype, the first value is the type discriminator for (int i = 0; i <= offset; i++) { - idPositions[i] = i; - } - List mappingPrefixes = null; - if (entityViewRoot != null && entityViewRoot.length() > 0) { - mappingPrefixes = Arrays.asList(entityViewRoot); + idPositions[i] = i + (viewType.hasSubtypes() ? 1 : 0); } - return new ViewTypeObjectBuilderTemplate(viewType, entityViewRoot, "", name, mappingPrefixes, entityViewRoot, idPositions, offset, evm, ef, viewType, constructor, proxyFactory); + return new ViewTypeObjectBuilderTemplate(viewType, entityViewRoot, "", name, entityViewRoot, entityViewRoot, idPositions, offset, null, evm, ef, viewType, constructor, proxyFactory); } @Override diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/AliasSubqueryTupleElementMapper.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/AliasSubqueryTupleElementMapper.java index 20b232e1a2..c7c8f7905d 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/AliasSubqueryTupleElementMapper.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/AliasSubqueryTupleElementMapper.java @@ -27,7 +27,7 @@ * @author Christian Beikov * @since 1.0 */ -public class AliasSubqueryTupleElementMapper extends SubqueryTupleElementMapper { +public class AliasSubqueryTupleElementMapper extends SimpleSubqueryTupleElementMapper { private final String alias; diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ConstrainedTupleElementMapper.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ConstrainedTupleElementMapper.java new file mode 100644 index 0000000000..6d494348ef --- /dev/null +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ConstrainedTupleElementMapper.java @@ -0,0 +1,249 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.impl.objectbuilder.mapper; + +import com.blazebit.persistence.CaseWhenStarterBuilder; +import com.blazebit.persistence.CommonQueryBuilder; +import com.blazebit.persistence.FullQueryBuilder; +import com.blazebit.persistence.MultipleSubqueryInitiator; +import com.blazebit.persistence.SelectBuilder; +import com.blazebit.persistence.SimpleCaseWhenStarterBuilder; +import com.blazebit.persistence.SubqueryBuilder; +import com.blazebit.persistence.SubqueryInitiator; +import com.blazebit.persistence.view.impl.objectbuilder.transformator.TupleTransformatorFactory; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +public class ConstrainedTupleElementMapper implements TupleElementMapper { + + private final Map.Entry[] mappers; + private final Map.Entry[] subqueryMappers; + private final String alias; + + @SuppressWarnings("unchecked") + private ConstrainedTupleElementMapper(List> mappers, List> subqueryMappers, String alias) { + this.mappers = mappers.toArray(new Map.Entry[mappers.size()]); + this.subqueryMappers = subqueryMappers.toArray(new Map.Entry[subqueryMappers.size()]); + this.alias = alias; + } + + @Override + public void applyMapping(SelectBuilder queryBuilder, CommonQueryBuilder parameterSource, Map optionalParameters) { + StringBuilder sb = new StringBuilder(); + StringBuilderSelectBuilder selectBuilder = new StringBuilderSelectBuilder(sb); + + sb.append("CASE"); + for (Map.Entry entry : mappers) { + if (entry.getKey() != null) { + sb.append(" WHEN "); + sb.append(entry.getKey()); + sb.append(" THEN "); + } else { + sb.append(" ELSE "); + } + entry.getValue().applyMapping(selectBuilder, parameterSource, optionalParameters); + } + sb.append(" END"); + + if (subqueryMappers.length == 0) { + if (alias != null) { + queryBuilder.select(sb.toString(), alias); + } else { + queryBuilder.select(sb.toString()); + } + } else { + MultipleSubqueryInitiator initiator; + if (alias != null) { + initiator = queryBuilder.selectSubqueries(sb.toString(), alias); + } else { + initiator = queryBuilder.selectSubqueries(sb.toString()); + } + + for (Map.Entry entry : subqueryMappers) { + selectBuilder.setInitiator(initiator.with(entry.getKey())); + entry.getValue().applyMapping(selectBuilder, parameterSource, optionalParameters); + } + + initiator.end(); + } + } + + public static void addMappers(List mappingList, List parameterMappingList, TupleTransformatorFactory tupleTransformatorFactory, List> builders) { + List>> subtypeMappersPerAttribute = new ArrayList<>(); + boolean first = true; + + // Transpose the subtype grouped attribute lists to attribute grouped subtype lists + for (Map.Entry builderEntry : builders) { + String constraint = builderEntry.getKey(); + TupleElementMapperBuilder mapperBuilder = builderEntry.getValue(); + + // NOTE: we assume that constrained attributes have the same attribute types so we only use the transformators of the first mapper builder + if (first) { + tupleTransformatorFactory.add(mapperBuilder.getTupleTransformatorFactory()); + first = false; + } + + int attributeIndex = 0; + for (TupleElementMapper mapper : mapperBuilder.getMappers()) { + List> list = subtypeMappersPerAttribute.size() > attributeIndex ? subtypeMappersPerAttribute.get(attributeIndex) : null; + if (list == null) { + list = new ArrayList<>(); + subtypeMappersPerAttribute.add(attributeIndex, list); + } + + list.add(new AbstractMap.SimpleEntry<>(constraint, mapper)); + attributeIndex++; + } + } + + // Split up subquery mappers from the rest since they need special handling + for (List> attributeEntry : subtypeMappersPerAttribute) { + List> mappers = new ArrayList<>(); + List> subqueryMappers = new ArrayList<>(); + + for (Map.Entry subtypeEntry : attributeEntry) { + String constraint = subtypeEntry.getKey(); + TupleElementMapper mapper = subtypeEntry.getValue(); + if (mapper instanceof SubqueryTupleElementMapper) { + SubqueryTupleElementMapper subqueryMapper = (SubqueryTupleElementMapper) mapper; + String subqueryAlias = "_inheritance_subquery_" + subqueryMappers.size(); + String subqueryExpression; + if (subqueryMapper.getSubqueryAlias() == null) { + subqueryExpression = subqueryAlias; + } else { + subqueryExpression = subqueryMapper.getSubqueryExpression().replaceAll(subqueryMapper.getSubqueryAlias(), subqueryAlias); + } + + mappers.add(new AbstractMap.SimpleEntry(constraint, new ExpressionTupleElementMapper(subqueryExpression))); + subqueryMappers.add(new AbstractMap.SimpleEntry<>(subqueryAlias, mapper)); + } else { + mappers.add(new AbstractMap.SimpleEntry<>(constraint, mapper)); + } + } + + // TODO: determine alias + String alias = null; + mappingList.add(new ConstrainedTupleElementMapper(mappers, subqueryMappers, alias)); + parameterMappingList.add(null); + } + } + + private static class StringBuilderSelectBuilder implements SelectBuilder { + + private final StringBuilder sb; + private SubqueryInitiator initiator; + + public StringBuilderSelectBuilder(StringBuilder sb) { + this.sb = sb; + } + + @SuppressWarnings("unchecked") + public void setInitiator(SubqueryInitiator initiator) { + this.initiator = (SubqueryInitiator) initiator; + } + + @Override + public CaseWhenStarterBuilder selectCase() { + throw new UnsupportedOperationException(); + } + + @Override + public CaseWhenStarterBuilder selectCase(String alias) { + throw new UnsupportedOperationException(); + } + + @Override + public SimpleCaseWhenStarterBuilder selectSimpleCase(String caseOperand) { + throw new UnsupportedOperationException(); + } + + @Override + public SimpleCaseWhenStarterBuilder selectSimpleCase(String caseOperand, String alias) { + throw new UnsupportedOperationException(); + } + + @Override + public SubqueryInitiator selectSubquery() { + return initiator; + } + + @Override + public SubqueryInitiator selectSubquery(String alias) { + return initiator; + } + + @Override + public SubqueryInitiator selectSubquery(String subqueryAlias, String expression, String selectAlias) { + return initiator; + } + + @Override + public SubqueryInitiator selectSubquery(String subqueryAlias, String expression) { + return initiator; + } + + @Override + public MultipleSubqueryInitiator selectSubqueries(String expression, String selectAlias) { + throw new UnsupportedOperationException(); + } + + @Override + public MultipleSubqueryInitiator selectSubqueries(String expression) { + throw new UnsupportedOperationException(); + } + + @Override + public SubqueryBuilder selectSubquery(FullQueryBuilder criteriaBuilder) { + throw new UnsupportedOperationException(); + } + + @Override + public SubqueryBuilder selectSubquery(String alias, FullQueryBuilder criteriaBuilder) { + throw new UnsupportedOperationException(); + } + + @Override + public SubqueryBuilder selectSubquery(String subqueryAlias, String expression, String selectAlias, FullQueryBuilder criteriaBuilder) { + throw new UnsupportedOperationException(); + } + + @Override + public SubqueryBuilder selectSubquery(String subqueryAlias, String expression, FullQueryBuilder criteriaBuilder) { + throw new UnsupportedOperationException(); + } + + @Override + public Object select(String expression) { + sb.append(expression); + return this; + } + + @Override + public Object select(String expression, String alias) { + sb.append(expression); + return this; + } + } +} diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ExpressionSubqueryTupleElementMapper.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ExpressionSubqueryTupleElementMapper.java index f8fb7e2fa7..03a77010dc 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ExpressionSubqueryTupleElementMapper.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ExpressionSubqueryTupleElementMapper.java @@ -27,7 +27,7 @@ * @author Christian Beikov * @since 1.0 */ -public class ExpressionSubqueryTupleElementMapper implements TupleElementMapper { +public class ExpressionSubqueryTupleElementMapper implements SubqueryTupleElementMapper { protected final SubqueryProvider provider; protected final String subqueryExpression; @@ -44,4 +44,13 @@ public void applyMapping(SelectBuilder queryBuilder, CommonQueryBuilder pa provider.createSubquery(queryBuilder.selectSubquery(subqueryAlias, subqueryExpression)); } + @Override + public String getSubqueryAlias() { + return subqueryAlias; + } + + @Override + public String getSubqueryExpression() { + return subqueryExpression; + } } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ExpressionTupleElementMapper.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ExpressionTupleElementMapper.java index cfe840be0f..b09a4d19f6 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ExpressionTupleElementMapper.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ExpressionTupleElementMapper.java @@ -29,9 +29,16 @@ */ public class ExpressionTupleElementMapper implements TupleElementMapper { + private static final String[] EMPTY = new String[0]; + protected final String expression; protected final String[] fetches; + public ExpressionTupleElementMapper(String expression) { + this.expression = expression; + this.fetches = EMPTY; + } + public ExpressionTupleElementMapper(String expression, String[] fetches) { this.expression = expression; this.fetches = fetches; diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ParameterizedExpressionSubqueryTupleElementMapper.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ParameterizedExpressionSubqueryTupleElementMapper.java index 323edff839..69dd641736 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ParameterizedExpressionSubqueryTupleElementMapper.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ParameterizedExpressionSubqueryTupleElementMapper.java @@ -27,7 +27,7 @@ * @author Christian Beikov * @since 1.0 */ -public class ParameterizedExpressionSubqueryTupleElementMapper implements TupleElementMapper { +public class ParameterizedExpressionSubqueryTupleElementMapper implements SubqueryTupleElementMapper { protected final SubqueryProviderFactory providerFactory; protected final String subqueryExpression; @@ -44,4 +44,13 @@ public void applyMapping(SelectBuilder queryBuilder, CommonQueryBuilder pa providerFactory.create(parameterSource, optionalParameters).createSubquery(queryBuilder.selectSubquery(subqueryAlias, subqueryExpression)); } + @Override + public String getSubqueryAlias() { + return subqueryAlias; + } + + @Override + public String getSubqueryExpression() { + return subqueryExpression; + } } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ParameterizedSubqueryTupleElementMapper.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ParameterizedSubqueryTupleElementMapper.java index 56ca2d6a05..9a524c0ca0 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ParameterizedSubqueryTupleElementMapper.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/ParameterizedSubqueryTupleElementMapper.java @@ -16,18 +16,18 @@ package com.blazebit.persistence.view.impl.objectbuilder.mapper; -import java.util.Map; - import com.blazebit.persistence.CommonQueryBuilder; import com.blazebit.persistence.SelectBuilder; import com.blazebit.persistence.view.impl.SubqueryProviderFactory; +import java.util.Map; + /** * * @author Christian Beikov * @since 1.0 */ -public class ParameterizedSubqueryTupleElementMapper implements TupleElementMapper { +public class ParameterizedSubqueryTupleElementMapper implements SubqueryTupleElementMapper { protected final SubqueryProviderFactory providerFactory; @@ -40,4 +40,13 @@ public void applyMapping(SelectBuilder queryBuilder, CommonQueryBuilder pa providerFactory.create(parameterSource, optionalParameters).createSubquery(queryBuilder.selectSubquery()); } + @Override + public String getSubqueryAlias() { + return null; + } + + @Override + public String getSubqueryExpression() { + return null; + } } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/SimpleSubqueryTupleElementMapper.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/SimpleSubqueryTupleElementMapper.java new file mode 100644 index 0000000000..1ccc51adb5 --- /dev/null +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/SimpleSubqueryTupleElementMapper.java @@ -0,0 +1,52 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.impl.objectbuilder.mapper; + +import com.blazebit.persistence.CommonQueryBuilder; +import com.blazebit.persistence.SelectBuilder; +import com.blazebit.persistence.view.SubqueryProvider; + +import java.util.Map; + +/** + * + * @author Christian Beikov + * @since 1.0 + */ +public class SimpleSubqueryTupleElementMapper implements SubqueryTupleElementMapper { + + protected final SubqueryProvider provider; + + public SimpleSubqueryTupleElementMapper(SubqueryProvider provider) { + this.provider = provider; + } + + @Override + public void applyMapping(SelectBuilder queryBuilder, CommonQueryBuilder parameterSource, Map optionalParameters) { + provider.createSubquery(queryBuilder.selectSubquery()); + } + + @Override + public String getSubqueryAlias() { + return null; + } + + @Override + public String getSubqueryExpression() { + return null; + } +} diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/SubqueryTupleElementMapper.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/SubqueryTupleElementMapper.java index 90d146c925..4156d5e9c5 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/SubqueryTupleElementMapper.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/SubqueryTupleElementMapper.java @@ -16,28 +16,15 @@ package com.blazebit.persistence.view.impl.objectbuilder.mapper; -import java.util.Map; - -import com.blazebit.persistence.CommonQueryBuilder; -import com.blazebit.persistence.SelectBuilder; -import com.blazebit.persistence.view.SubqueryProvider; - /** + * Just a marker interface for element mappers that use subqueries * * @author Christian Beikov - * @since 1.0 + * @since 1.2.0 */ -public class SubqueryTupleElementMapper implements TupleElementMapper { - - protected final SubqueryProvider provider; - - public SubqueryTupleElementMapper(SubqueryProvider provider) { - this.provider = provider; - } +public interface SubqueryTupleElementMapper extends TupleElementMapper { - @Override - public void applyMapping(SelectBuilder queryBuilder, CommonQueryBuilder parameterSource, Map optionalParameters) { - provider.createSubquery(queryBuilder.selectSubquery()); - } + public String getSubqueryAlias(); + public String getSubqueryExpression(); } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/TupleElementMapperBuilder.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/TupleElementMapperBuilder.java new file mode 100644 index 0000000000..0c4b93f019 --- /dev/null +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/mapper/TupleElementMapperBuilder.java @@ -0,0 +1,244 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.impl.objectbuilder.mapper; + +import com.blazebit.persistence.impl.EntityMetamodel; +import com.blazebit.persistence.impl.SimpleQueryGenerator; +import com.blazebit.persistence.impl.expression.Expression; +import com.blazebit.persistence.impl.expression.ExpressionFactory; +import com.blazebit.persistence.view.impl.PrefixingQueryGenerator; +import com.blazebit.persistence.view.impl.metamodel.AbstractAttribute; +import com.blazebit.persistence.view.impl.objectbuilder.transformator.TupleTransformatorFactory; +import com.blazebit.persistence.view.impl.objectbuilder.transformer.TupleListTransformer; +import com.blazebit.persistence.view.impl.objectbuilder.transformer.TupleListTransformerFactory; +import com.blazebit.persistence.view.impl.objectbuilder.transformer.TupleTransformerFactory; +import com.blazebit.persistence.view.metamodel.Attribute; +import com.blazebit.persistence.view.metamodel.MappingAttribute; +import com.blazebit.persistence.view.metamodel.MethodAttribute; +import com.blazebit.persistence.view.metamodel.ParameterAttribute; + +import javax.persistence.metamodel.EntityType; +import javax.persistence.metamodel.IdentifiableType; +import javax.persistence.metamodel.ManagedType; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +public class TupleElementMapperBuilder { + + private static final ExpressionTupleElementMapper NULL_MAPPER = new ExpressionTupleElementMapper("NULL", new String[0]); + private final int mapperIndex; + private final String aliasPrefix; + private final String mappingPrefix; + private final String idPrefix; + private final EntityType treatType; + private final EntityMetamodel metamodel; + private final ExpressionFactory ef; + private final String constraint; + private final List mappers; + private final List parameterMappings; + private final TupleTransformatorFactory tupleTransformatorFactory; + + public TupleElementMapperBuilder(int mapperIndex, String constraint, String aliasPrefix, String mappingPrefix, String idPrefix, EntityType treatType, EntityMetamodel metamodel, ExpressionFactory ef) { + this(mapperIndex, constraint, aliasPrefix, mappingPrefix, idPrefix, treatType, metamodel, ef, new ArrayList(), new ArrayList(), new TupleTransformatorFactory()); + } + + public TupleElementMapperBuilder(int mapperIndex, String constraint, String aliasPrefix, String mappingPrefix, String idPrefix, EntityType treatType, EntityMetamodel metamodel, ExpressionFactory ef, List mappers, List parameterMappings, TupleTransformatorFactory tupleTransformatorFactory) { + this.mapperIndex = mapperIndex; + this.constraint = constraint; + this.aliasPrefix = aliasPrefix; + if (treatType != null) { + this.mappingPrefix = "TREAT(" + mappingPrefix + " AS " + treatType.getName() + ")"; + } else { + this.mappingPrefix = mappingPrefix; + } + this.idPrefix = idPrefix; + this.treatType = treatType; + this.metamodel = metamodel; + this.ef = ef; + this.mappers = mappers; + this.parameterMappings = parameterMappings; + this.tupleTransformatorFactory = tupleTransformatorFactory; + } + + public String constraint() { + return constraint; + } + + public int mapperIndex() { + return mapperIndex + mappers.size(); + } + + public void addMapper(TupleElementMapper mapper) { + mappers.add(mapper); + parameterMappings.add(null); + } + + public void addMappers(TupleElementMapper[] mappers) { + Collections.addAll(this.mappers, mappers); + for (int i = 0; i < mappers.length; i++) { + parameterMappings.add(null); + } + } + + public List getMappers() { + return mappers; + } + + public void addQueryParam(String paramName) { + mappers.add(NULL_MAPPER); + parameterMappings.add(paramName); + } + + public String getAlias(String attributeName) { + return getAlias(aliasPrefix, attributeName); + } + + private String getAlias(String prefix, String attributeName) { + if (prefix == null) { + return attributeName; + } else { + return prefix + "_" + attributeName; + } + } + + public String getAlias(Attribute attribute, boolean isKey) { + return getAlias(aliasPrefix, attribute, isKey); + } + + private String getAlias(String prefix, Attribute attribute, boolean isKey) { + if (isKey) { + prefix = prefix + "_key"; + } + if (attribute instanceof MethodAttribute) { + return getAlias(prefix, ((MethodAttribute) attribute).getName()); + } else { + return getAlias(prefix, "$" + ((ParameterAttribute) attribute).getIndex()); + } + } + + public String getMapping(String mapping, Class expressionType) { + return getMapping(mappingPrefix, mapping, expressionType); + } + + private String getMapping(String prefixParts, String mapping, Class expressionType) { + if (expressionType == null) { + return getMapping(prefixParts, mapping); + } + + ManagedType managedType = metamodel.getManagedType(expressionType); + if (managedType == null || !(managedType instanceof IdentifiableType)) { + return getMapping(prefixParts, mapping); + } + + IdentifiableType identifiableType = (IdentifiableType) managedType; + javax.persistence.metamodel.SingularAttribute idAttr = identifiableType.getId(identifiableType.getIdType().getJavaType()); + if (mapping.isEmpty()) { + return getMapping(prefixParts, idAttr.getName()); + } else { + return getMapping(prefixParts, mapping + '.' + idAttr.getName()); + } + } + + public String getMapping(String mapping) { + return getMapping(mappingPrefix, mapping); + } + + private String getMapping(String prefixParts, String mapping) { + if (mapping.isEmpty()) { + if (prefixParts != null && !prefixParts.isEmpty()) { + return AbstractAttribute.stripThisFromMapping(prefixParts); + } + + return ""; + } + if (prefixParts != null && !prefixParts.isEmpty()) { + Expression expr = ef.createSimpleExpression(mapping, false); + SimpleQueryGenerator generator = new PrefixingQueryGenerator(Collections.singletonList(prefixParts)); + StringBuilder sb = new StringBuilder(); + generator.setQueryBuffer(sb); + expr.accept(generator); + return sb.toString(); + } + + return mapping; + } + + public String getMapping(MappingAttribute mappingAttribute) { + return getMapping(mappingPrefix, mappingAttribute); + } + + private String getMapping(String prefixParts, MappingAttribute mappingAttribute) { + return getMapping(prefixParts, AbstractAttribute.stripThisFromMapping(mappingAttribute.getMapping())); + } + + public String getIdMapping(MappingAttribute mappingAttribute, boolean isKey) { + String mapping = getMapping(idPrefix, AbstractAttribute.stripThisFromMapping(mappingAttribute.getMapping())); + if (isKey) { + return "KEY(" + mapping + ")"; + } + + return mapping; + } + + public String createSubviewMappingPrefix(MappingAttribute mappingAttribute, boolean isKey) { + StringBuilder sb = new StringBuilder(); + String mapping = AbstractAttribute.stripThisFromMapping(mappingAttribute.getMapping()); + if (isKey) { + sb.append("KEY("); + } + + if (mappingPrefix != null && !mappingPrefix.isEmpty()) { + sb.append(mappingPrefix); + if (mapping != null && !mapping.isEmpty()) { + sb.append('.').append(mapping); + } + } else if (mapping != null && !mapping.isEmpty()) { + sb.append(mapping); + } + + if (isKey) { + sb.append(')'); + } + return sb.toString(); + } + + public TupleTransformatorFactory getTupleTransformatorFactory() { + return tupleTransformatorFactory; + } + + public void setTupleListTransformer(TupleListTransformer tupleListTransformer) { + tupleTransformatorFactory.add(tupleListTransformer); + } + + public void setTupleListTransformerFactory(TupleListTransformerFactory tupleListTransformerFactory) { + tupleTransformatorFactory.add(tupleListTransformerFactory); + } + + public void addTupleTransformerFactory(TupleTransformerFactory tupleTransformerFactory) { + tupleTransformatorFactory.add(tupleTransformerFactory); + } + + public void addTupleTransformatorFactory(TupleTransformatorFactory tupleTransformatorFactory) { + this.tupleTransformatorFactory.add(tupleTransformatorFactory); + } +} diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/transformer/correlation/SubviewCorrelator.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/transformer/correlation/SubviewCorrelator.java index da8d636888..acbbf83086 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/transformer/correlation/SubviewCorrelator.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/objectbuilder/transformer/correlation/SubviewCorrelator.java @@ -20,8 +20,7 @@ import com.blazebit.persistence.ObjectBuilder; import com.blazebit.persistence.view.impl.EntityViewConfiguration; import com.blazebit.persistence.view.impl.EntityViewManagerImpl; -import com.blazebit.persistence.view.metamodel.ManagedViewType; -import com.blazebit.persistence.view.metamodel.ViewType; +import com.blazebit.persistence.view.impl.metamodel.ManagedViewTypeImpl; /** * @@ -30,11 +29,11 @@ */ public final class SubviewCorrelator implements Correlator { - private final ManagedViewType managedViewType; + private final ManagedViewTypeImpl managedViewType; private final EntityViewManagerImpl evm; private final String viewName; - public SubviewCorrelator(ManagedViewType managedViewType, EntityViewManagerImpl evm, String viewName) { + public SubviewCorrelator(ManagedViewTypeImpl managedViewType, EntityViewManagerImpl evm, String viewName) { this.managedViewType = managedViewType; this.evm = evm; this.viewName = viewName; @@ -44,7 +43,7 @@ public SubviewCorrelator(ManagedViewType managedViewType, EntityViewManagerIm @SuppressWarnings({"rawtypes", "unchecked"}) public void finish(FullQueryBuilder criteriaBuilder, EntityViewConfiguration entityViewConfiguration, int tupleOffset, String correlationRoot) { // TODO: actually we need a new entity view configuration for this - ObjectBuilder builder = evm.createObjectBuilder((ViewType) managedViewType, null, viewName, correlationRoot, criteriaBuilder, entityViewConfiguration, tupleOffset, false); + ObjectBuilder builder = evm.createObjectBuilder(managedViewType, null, viewName, correlationRoot, criteriaBuilder, entityViewConfiguration, tupleOffset, false); criteriaBuilder.selectNew(builder); } } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/ProxyFactory.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/ProxyFactory.java index e3b728e727..218ac91de8 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/ProxyFactory.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/ProxyFactory.java @@ -18,8 +18,10 @@ import java.lang.reflect.Method; import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -28,6 +30,11 @@ import com.blazebit.persistence.view.CorrelationProvider; import com.blazebit.persistence.view.impl.CorrelationProviderProxyBase; +import com.blazebit.persistence.view.impl.metamodel.AbstractMethodAttribute; +import com.blazebit.persistence.view.impl.metamodel.AbstractParameterAttribute; +import com.blazebit.persistence.view.impl.metamodel.ConstrainedAttribute; +import com.blazebit.persistence.view.impl.metamodel.ManagedViewTypeImpl; +import com.blazebit.persistence.view.impl.metamodel.MappingConstructorImpl; import com.blazebit.persistence.view.metamodel.ManagedViewType; import com.blazebit.persistence.view.metamodel.MappingConstructor; import com.blazebit.persistence.view.metamodel.MethodAttribute; @@ -63,21 +70,48 @@ public class ProxyFactory { private static final Logger LOG = Logger.getLogger(ProxyFactory.class.getName()); // This has to be static since runtime generated correlation providers can't be matched in a later run, so we always create a new one with a unique name private static final ConcurrentMap, AtomicInteger> CORRELATION_PROVIDER_CLASS_COUNT = new ConcurrentHashMap<>(); - private final ConcurrentMap, Class> proxyClasses = new ConcurrentHashMap, Class>(); - private final ConcurrentMap, Class> unsafeProxyClasses = new ConcurrentHashMap, Class>(); + private final ConcurrentMap> proxyClasses = new ConcurrentHashMap<>(); + private final ConcurrentMap> unsafeProxyClasses = new ConcurrentHashMap<>(); private final Object proxyLock = new Object(); private final ClassPool pool; + private static final class ProxyClassKey { + private final Class viewTypeClass; + private final Class inheritanceBaseClass; + + public ProxyClassKey(Class viewTypeClass, Class inheritanceBaseClass) { + this.viewTypeClass = viewTypeClass; + this.inheritanceBaseClass = inheritanceBaseClass; + } + + @Override + public boolean equals(Object o) { + ProxyClassKey that = (ProxyClassKey) o; + + if (!viewTypeClass.equals(that.viewTypeClass)) { + return false; + } + return inheritanceBaseClass != null ? inheritanceBaseClass.equals(that.inheritanceBaseClass) : that.inheritanceBaseClass == null; + } + + @Override + public int hashCode() { + int result = viewTypeClass.hashCode(); + result = 31 * result + (inheritanceBaseClass != null ? inheritanceBaseClass.hashCode() : 0); + return result; + } + } + public ProxyFactory() { this.pool = new ClassPool(ClassPool.getDefault()); } - public Class getProxy(ManagedViewType viewType) { - return getProxy(viewType, false); + public Class getProxy(ManagedViewType viewType, ManagedViewTypeImpl inheritanceBase) { + return getProxy(viewType, inheritanceBase, false); } - public Class getUnsafeProxy(ManagedViewType viewType) { - return getProxy(viewType, true); + public Class getUnsafeProxy(ManagedViewType viewType, ManagedViewTypeImpl inheritanceBase) { + return getProxy(viewType, inheritanceBase, true); } public Class getCorrelationProviderProxy(Class correlated, String correlationKeyAlias, String correlationExpression) { @@ -137,18 +171,27 @@ public Class getCorrelationProviderProxy(Class } @SuppressWarnings("unchecked") - private Class getProxy(ManagedViewType viewType, boolean unsafe) { + private Class getProxy(ManagedViewType viewType, ManagedViewTypeImpl inheritanceBase, boolean unsafe) { Class clazz = viewType.getJavaType(); - ConcurrentMap, Class> classes = unsafe ? unsafeProxyClasses : proxyClasses; - Class proxyClass = (Class) classes.get(clazz); + Class baseClazz; + + if (inheritanceBase == null) { + baseClazz = null; + } else { + baseClazz = inheritanceBase.getJavaType(); + } + + final ConcurrentMap> classes = unsafe ? unsafeProxyClasses : proxyClasses; + final ProxyClassKey key = new ProxyClassKey(clazz, baseClazz); + Class proxyClass = (Class) classes.get(key); // Double checked locking since we can only define the class once if (proxyClass == null) { synchronized (proxyLock) { - proxyClass = (Class) classes.get(clazz); + proxyClass = (Class) classes.get(key); if (proxyClass == null) { - proxyClass = createProxyClass(viewType, unsafe); - classes.put(clazz, proxyClass); + proxyClass = createProxyClass(viewType, inheritanceBase, unsafe); + classes.put(key, proxyClass); } } } @@ -157,11 +200,27 @@ private Class getProxy(ManagedViewType viewType, boolean uns } @SuppressWarnings("unchecked") - private Class createProxyClass(ManagedViewType managedViewType, boolean unsafe) { + private Class createProxyClass(ManagedViewType managedViewType, ManagedViewTypeImpl inheritanceBase, boolean unsafe) { ViewType viewType = managedViewType instanceof ViewType ? (ViewType) managedViewType : null; Class clazz = managedViewType.getJavaType(); String suffix = unsafe ? "unsafe_" : ""; - String proxyClassName = clazz.getName() + "_$$_javassist_entityview_" + suffix; + String baseName; + int subtypeIndex = 0; + + if (inheritanceBase != null) { + for (ManagedViewType subtype : inheritanceBase.getInheritanceSubtypes()) { + if (subtype == managedViewType) { + break; + } + subtypeIndex++; + } + baseName = inheritanceBase.getJavaType().getName(); + baseName += "_" + subtypeIndex + "_" + managedViewType.getJavaType().getSimpleName(); + } else { + baseName = clazz.getName(); + } + + String proxyClassName = baseName + "_$$_javassist_entityview_" + suffix; CtClass cc = pool.makeClass(proxyClassName); CtClass superCc; @@ -201,6 +260,7 @@ private Class createProxyClass(ManagedViewType managedViewTy Set> attributes = new LinkedHashSet<>(managedViewType.getAttributes()); CtField[] attributeFields = new CtField[attributes.size()]; CtClass[] attributeTypes = new CtClass[attributes.size()]; + Map fieldMap = new HashMap<>(attributes.size()); int twoStackSlotCount = 0; int i = 0; @@ -212,6 +272,7 @@ private Class createProxyClass(ManagedViewType managedViewTy i = 1; idAttribute = viewType.getIdAttribute(); idField = addMembersForAttribute(idAttribute, clazz, cc, null, -1); + fieldMap.put(idAttribute.getName(), idField); attributeFields[0] = idField; attributeTypes[0] = idField.getType(); attributes.remove(idAttribute); @@ -236,6 +297,7 @@ private Class createProxyClass(ManagedViewType managedViewTy } CtField attributeField = addMembersForAttribute(attribute, clazz, cc, dirtyStateField, dirtyStateIndex); + fieldMap.put(attribute.getName(), attributeField); attributeFields[i] = attributeField; attributeTypes[i] = attributeField.getType(); i++; @@ -248,65 +310,44 @@ private Class createProxyClass(ManagedViewType managedViewTy dirtyStateIndex++; } } - - CtClass equalsDeclaringClass = superCc.getMethod("equals", getEqualsDesc()).getDeclaringClass(); - CtClass hashCodeDeclaringClass = superCc.getMethod("hashCode", getHashCodeDesc()).getDeclaringClass(); - boolean hasCustomEqualsHashCode = false; - - if (!"java.lang.Object".equals(equalsDeclaringClass.getName())) { - hasCustomEqualsHashCode = true; - LOG.warning("The class '" + equalsDeclaringClass.getName() + "' declares 'boolean equals(java.lang.Object)'! Hopefully you implemented it based on a unique key!"); - } - if (!"java.lang.Object".equals(hashCodeDeclaringClass.getName())) { - hasCustomEqualsHashCode = true; - LOG.warning("The class '" + hashCodeDeclaringClass.getName() + "' declares 'int hashCode()'! Hopefully you implemented it based on a unique key!"); - } - if (!hasCustomEqualsHashCode) { - if (viewType != null) { - cc.addMethod(createEquals(cc, idField)); - cc.addMethod(createHashCode(cc, idField)); - } else { - cc.addMethod(createEquals(cc, attributeFields)); - cc.addMethod(createHashCode(cc, attributeFields)); - } - } + createEqualsHashCodeMethods(viewType, cc, superCc, attributeFields, idField); // Add the default constructor only for interfaces since abstract classes may omit it if (clazz.isInterface()) { cc.addConstructor(createConstructor(cc, attributeFields, attributeTypes, twoStackSlotCount, initialStateField, dirtyStateField, dirtyStateIndex, unsafe)); } - Set> constructors = managedViewType.getConstructors(); + Set> constructors = (Set>) (Set) managedViewType.getConstructors(); - // EmbeddableEntityView does not have an id #219 - int idParameterCount = viewType != null ? 1 : 0; - - for (MappingConstructor constructor : constructors) { + for (MappingConstructorImpl constructor : constructors) { // Copy default constructor parameters - int constructorParameterCount = idParameterCount + attributes.size() + constructor.getParameterAttributes().size(); + int constructorParameterCount = attributeFields.length + constructor.getParameterAttributes().size(); CtClass[] constructorAttributeTypes = new CtClass[constructorParameterCount]; - System.arraycopy(attributeTypes, 0, constructorAttributeTypes, 0, idParameterCount + attributes.size()); + System.arraycopy(attributeTypes, 0, constructorAttributeTypes, 0, attributeFields.length); // Append super constructor parameters to default constructor parameters CtConstructor superConstructor = findConstructor(superCc, constructor); - System.arraycopy(superConstructor.getParameterTypes(), 0, constructorAttributeTypes, idParameterCount + attributes.size(), superConstructor.getParameterTypes().length); + System.arraycopy(superConstructor.getParameterTypes(), 0, constructorAttributeTypes, attributeFields.length, superConstructor.getParameterTypes().length); cc.addConstructor(createConstructor(cc, attributeFields, constructorAttributeTypes, twoStackSlotCount, initialStateField, dirtyStateField, dirtyStateIndex, unsafe)); } + createInheritanceConstructors(constructors, inheritanceBase, managedViewType, subtypeIndex, unsafe, cc, initialStateField, dirtyStateField, fieldMap, dirtyStateIndex); + try { if (unsafe) { return (Class) UnsafeHelper.define(cc.getName(), cc.toBytecode(), clazz); } else { return cc.toClass(clazz.getClassLoader(), null); } - } catch (CannotCompileException ex) { + } catch (CannotCompileException | LinkageError ex) { // If there are multiple proxy factories for the same class loader // we could end up in defining a class multiple times, so we check if the classloader // actually has something to offer - if (ex.getCause() instanceof LinkageError) { - LinkageError error = (LinkageError) ex.getCause(); + LinkageError error; + if (ex instanceof LinkageError && (error = (LinkageError) ex) != null + || ex.getCause() instanceof LinkageError && (error = (LinkageError) ex.getCause()) != null) { try { return (Class) pool.getClassLoader().loadClass(proxyClassName); } catch (ClassNotFoundException cnfe) { @@ -316,16 +357,6 @@ private Class createProxyClass(ManagedViewType managedViewTy } else { throw ex; } - } catch (LinkageError error) { - // If there are multiple proxy factories for the same class loader - // we could end up in defining a class multiple times, so we check if the classloader - // actually has something to offer - try { - return (Class) pool.getClassLoader().loadClass(proxyClassName); - } catch (ClassNotFoundException cnfe) { - // Something we can't handle happened - throw error; - } } } catch (Exception ex) { throw new RuntimeException("Probably we did something wrong, please contact us if you see this message.", ex); @@ -333,7 +364,102 @@ private Class createProxyClass(ManagedViewType managedViewTy pool.removeClassPath(classPath); } } - + + @SuppressWarnings("unchecked") + private void createInheritanceConstructors(Set> constructors, ManagedViewTypeImpl inheritanceBase, ManagedViewType managedView, int subtypeIndex, boolean unsafe, CtClass cc, CtField initialStateField, CtField dirtyStateField, Map fieldMap, int dirtyStateIndex) throws NotFoundException, CannotCompileException { + int twoStackSlotCount; + if (inheritanceBase != null) { + // Go through all configurations that contain this entity view and create an inheritance constructor for it + Map, String>, ManagedViewTypeImpl.InheritanceSubtypeConfiguration> inheritanceSubtypeConfigurationMap = (Map, String>, ManagedViewTypeImpl.InheritanceSubtypeConfiguration>) (Map) inheritanceBase.getInheritanceSubtypeConfigurations(); + for (Map.Entry, String>, ManagedViewTypeImpl.InheritanceSubtypeConfiguration> configurationEntry : inheritanceSubtypeConfigurationMap.entrySet()) { + if (!configurationEntry.getKey().containsKey(managedView)) { + continue; + } + + ManagedViewTypeImpl.InheritanceSubtypeConfiguration inheritanceSubtypeConfiguration = configurationEntry.getValue(); + Map>> subtypeAttributesClosure = (Map>>) (Map) inheritanceSubtypeConfiguration.getAttributesClosure(); + CtField[] fields = new CtField[subtypeAttributesClosure.size()]; + CtClass[] parameterTypes = new CtClass[subtypeAttributesClosure.size()]; + twoStackSlotCount = 0; + + int j = 0; + for (Map.Entry>> entry : subtypeAttributesClosure.entrySet()) { + CtField field = fieldMap.get(entry.getKey().getAttributeName()); + CtClass type; + if (field != null) { + type = field.getType(); + } else { + type = pool.get(entry.getValue().getAttribute().getJavaType().getName()); + } + if (needsTwoStackSlots(type)) { + twoStackSlotCount++; + } + fields[j] = field; + parameterTypes[j] = type; + j++; + } + + cc.addConstructor(createConstructor(cc, fields, parameterTypes, twoStackSlotCount, initialStateField, dirtyStateField, dirtyStateIndex, unsafe)); + + for (MappingConstructorImpl constructor : constructors) { + MappingConstructorImpl baseConstructor = (MappingConstructorImpl) inheritanceBase.getConstructor(constructor.getName()); + @SuppressWarnings("unchecked") + List> parameterAttributes = (List>) (List) baseConstructor.getSubtypeParameterAttributesClosure((Map, String>) (Map) configurationEntry.getKey()); + // Copy default constructor parameters + int constructorParameterCount = fields.length + parameterAttributes.size(); + CtClass[] constructorAttributeTypes = new CtClass[constructorParameterCount]; + System.arraycopy(parameterTypes, 0, constructorAttributeTypes, 0, parameterTypes.length); + + // Find the start position in the parameters for passing through to this constructor + int constructorParameterStartPosition = fields.length; + int i = 0; + for (ManagedViewType subtype : inheritanceBase.getInheritanceSubtypes()) { + if (i == subtypeIndex) { + break; + } + + constructorParameterStartPosition += subtype.getConstructor(constructor.getName()).getParameterAttributes().size(); + i++; + } + + // Copy constructor parameter types + i = fields.length; + for (AbstractParameterAttribute paramAttr : parameterAttributes) { + constructorAttributeTypes[i++] = pool.get(paramAttr.getJavaType().getName()); + } + + int superConstructorParameterEndPosition = constructorParameterStartPosition + constructor.getParameterAttributes().size(); + cc.addConstructor(createConstructor(cc, constructorParameterStartPosition, superConstructorParameterEndPosition, fields, constructorAttributeTypes, twoStackSlotCount, initialStateField, dirtyStateField, dirtyStateIndex, unsafe)); + } + } + } + } + + private void createEqualsHashCodeMethods(ViewType viewType, CtClass cc, CtClass superCc, CtField[] attributeFields, CtField idField) throws NotFoundException, CannotCompileException { + CtClass equalsDeclaringClass = superCc.getMethod("equals", getEqualsDesc()).getDeclaringClass(); + CtClass hashCodeDeclaringClass = superCc.getMethod("hashCode", getHashCodeDesc()).getDeclaringClass(); + boolean hasCustomEqualsHashCode = false; + + if (!"java.lang.Object".equals(equalsDeclaringClass.getName())) { + hasCustomEqualsHashCode = true; + LOG.warning("The class '" + equalsDeclaringClass.getName() + "' declares 'boolean equals(java.lang.Object)'! Hopefully you implemented it based on a unique key!"); + } + if (!"java.lang.Object".equals(hashCodeDeclaringClass.getName())) { + hasCustomEqualsHashCode = true; + LOG.warning("The class '" + hashCodeDeclaringClass.getName() + "' declares 'int hashCode()'! Hopefully you implemented it based on a unique key!"); + } + + if (!hasCustomEqualsHashCode) { + if (viewType != null) { + cc.addMethod(createEquals(cc, idField)); + cc.addMethod(createHashCode(cc, idField)); + } else { + cc.addMethod(createEquals(cc, attributeFields)); + cc.addMethod(createHashCode(cc, attributeFields)); + } + } + } + private void addGetEntityViewClass(CtClass cc, Class entityViewClass) throws CannotCompileException { ConstPool cp = cc.getClassFile2().getConstPool(); MethodInfo minfo = new MethodInfo(cp, "$$_getEntityViewClass", "()Ljava/lang/Class;"); @@ -671,16 +797,20 @@ private CtMethod createSetterBridge(CtClass cc, Method setter, CtMethod attribut } private CtConstructor createConstructor(CtClass cc, CtField[] attributeFields, CtClass[] attributeTypes, int primitives, CtField initialStateField, CtField dirtyStateField, int dirtyStateSize, boolean unsafe) throws CannotCompileException, NotFoundException { + return createConstructor(cc, attributeFields.length, attributeTypes.length, attributeFields, attributeTypes, primitives, initialStateField, dirtyStateField, dirtyStateSize, unsafe); + } + + private CtConstructor createConstructor(CtClass cc, int superConstructorStart, int superConstructorEnd, CtField[] attributeFields, CtClass[] attributeTypes, int primitives, CtField initialStateField, CtField dirtyStateField, int dirtyStateSize, boolean unsafe) throws CannotCompileException, NotFoundException { CtConstructor ctConstructor = new CtConstructor(attributeTypes, cc); ctConstructor.setModifiers(Modifier.PUBLIC); Bytecode bytecode = new Bytecode(cc.getClassFile().getConstPool(), 3, attributeTypes.length + 1 + primitives); if (unsafe) { - renderFieldInitialization(attributeFields, initialStateField, dirtyStateField, dirtyStateSize, bytecode); - renderSuperCall(cc, attributeFields, attributeTypes, bytecode); + renderFieldInitialization(attributeFields, attributeTypes, initialStateField, dirtyStateField, dirtyStateSize, bytecode); + renderSuperCall(cc, superConstructorStart, superConstructorEnd, attributeTypes, bytecode); } else { - renderSuperCall(cc, attributeFields, attributeTypes, bytecode); - renderFieldInitialization(attributeFields, initialStateField, dirtyStateField, dirtyStateSize, bytecode); + renderSuperCall(cc, superConstructorStart, superConstructorEnd, attributeTypes, bytecode); + renderFieldInitialization(attributeFields, attributeTypes, initialStateField, dirtyStateField, dirtyStateSize, bytecode); } bytecode.add(Bytecode.RETURN); @@ -688,7 +818,7 @@ private CtConstructor createConstructor(CtClass cc, CtField[] attributeFields, C return ctConstructor; } - private void renderFieldInitialization(CtField[] attributeFields, CtField initialStateField, CtField dirtyStateField, int dirtyStateSize, Bytecode bytecode) throws NotFoundException { + private void renderFieldInitialization(CtField[] attributeFields, CtClass[] parameterTypes, CtField initialStateField, CtField dirtyStateField, int dirtyStateSize, Bytecode bytecode) throws NotFoundException { if (initialStateField != null) { // this.initialState = new Object[dirtyStateSize] bytecode.addAload(0); @@ -708,13 +838,19 @@ private void renderFieldInitialization(CtField[] attributeFields, CtField initia int j = 0; int fieldSlot = 0; for (int i = 0; i < attributeFields.length; i++) { - bytecode.addAload(0); - bytecode.addLoad(fieldSlot + 1, attributeFields[i].getType()); - if (needsTwoStackSlots(attributeFields[i].getType())) { + boolean needsTwoStackSlots = needsTwoStackSlots(parameterTypes[i]); + if (needsTwoStackSlots) { fieldSlot += 2; } else { fieldSlot++; } + + if (attributeFields[i] == null) { + continue; + } + + bytecode.addAload(0); + bytecode.addLoad(fieldSlot - (needsTwoStackSlots ? 1 : 0), attributeFields[i].getType()); bytecode.addPutfield(attributeFields[i].getDeclaringClass(), attributeFields[i].getName(), Descriptor.of(attributeFields[i].getType())); if ((attributeFields[i].getModifiers() & Modifier.FINAL) == 0) { @@ -750,13 +886,13 @@ private boolean needsTwoStackSlots(String name) { return "long".equals(name) || "double".equals(name); } - private void renderSuperCall(CtClass cc, CtField[] attributeFields, CtClass[] attributeTypes, Bytecode bytecode) throws NotFoundException { + private void renderSuperCall(CtClass cc, int superStart, int superEnd, CtClass[] attributeTypes, Bytecode bytecode) throws NotFoundException { bytecode.addAload(0); - CtClass[] superArguments = new CtClass[attributeTypes.length - attributeFields.length]; + CtClass[] superArguments = new CtClass[superEnd - superStart]; int size = 0; - for (int i = attributeFields.length; i < attributeTypes.length; i++) { + for (int i = superStart; i < superEnd; i++) { superArguments[size++] = attributeTypes[i]; bytecode.addAload(i + 1); } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/ReflectionInstantiator.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/ReflectionInstantiator.java index 6e04d5d097..46a5ad4d09 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/ReflectionInstantiator.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/ReflectionInstantiator.java @@ -19,6 +19,7 @@ import java.lang.reflect.Constructor; import java.util.Arrays; +import com.blazebit.persistence.view.impl.metamodel.ManagedViewTypeImpl; import com.blazebit.persistence.view.metamodel.ManagedViewType; import com.blazebit.persistence.view.metamodel.MappingConstructor; @@ -31,25 +32,22 @@ public class ReflectionInstantiator implements ObjectInstantiator { private final Constructor constructor; - public ReflectionInstantiator(MappingConstructor mappingConstructor, ProxyFactory proxyFactory, ManagedViewType viewType, Class[] parameterTypes) { - Class proxyClazz = getProxyClass(proxyFactory, viewType); - Constructor javaConstructor = null; - + public ReflectionInstantiator(MappingConstructor mappingConstructor, ProxyFactory proxyFactory, ManagedViewType viewType, ManagedViewTypeImpl viewTypeBase, Class[] parameterTypes) { + Class proxyClazz = getProxyClass(proxyFactory, viewType, viewTypeBase); + Constructor javaConstructor; + try { javaConstructor = proxyClazz.getDeclaredConstructor(parameterTypes); - } catch (NoSuchMethodException ex) { - throw new IllegalArgumentException("The given mapping constructor '" + mappingConstructor + "' does not map to a constructor of the proxy class: " + proxyClazz - .getName(), ex); - } catch (SecurityException ex) { + } catch (NoSuchMethodException | SecurityException ex) { throw new IllegalArgumentException("The given mapping constructor '" + mappingConstructor + "' does not map to a constructor of the proxy class: " + proxyClazz .getName(), ex); } - + if (javaConstructor == null) { throw new IllegalArgumentException("The given mapping constructor '" + mappingConstructor + "' does not map to a constructor of the proxy class: " + proxyClazz .getName()); } - + this.constructor = javaConstructor; } @@ -70,10 +68,10 @@ public T newInstance(Object[] tuple) { throw new RuntimeException("Could not invoke the proxy constructor '" + constructor + "' with the given tuple: " + Arrays.toString(tuple) + " with the types: " + Arrays.toString(types), ex); } } - + @SuppressWarnings("unchecked") - protected Class getProxyClass(ProxyFactory proxyFactory, ManagedViewType viewType) { - return (Class) proxyFactory.getProxy(viewType); + protected Class getProxyClass(ProxyFactory proxyFactory, ManagedViewType viewType, ManagedViewTypeImpl viewTypeBase) { + return (Class) proxyFactory.getProxy(viewType, viewTypeBase); } } diff --git a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/UnsafeInstantiator.java b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/UnsafeInstantiator.java index 88c5261ade..2b7373d773 100644 --- a/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/UnsafeInstantiator.java +++ b/entity-view/impl/src/main/java/com/blazebit/persistence/view/impl/proxy/UnsafeInstantiator.java @@ -16,6 +16,7 @@ package com.blazebit.persistence.view.impl.proxy; +import com.blazebit.persistence.view.impl.metamodel.ManagedViewTypeImpl; import com.blazebit.persistence.view.metamodel.ManagedViewType; import com.blazebit.persistence.view.metamodel.MappingConstructor; @@ -26,14 +27,14 @@ */ public class UnsafeInstantiator extends ReflectionInstantiator { - public UnsafeInstantiator(MappingConstructor mappingConstructor, ProxyFactory proxyFactory, ManagedViewType viewType, Class[] parameterTypes) { - super(mappingConstructor, proxyFactory, viewType, parameterTypes); + public UnsafeInstantiator(MappingConstructor mappingConstructor, ProxyFactory proxyFactory, ManagedViewType viewType, ManagedViewTypeImpl viewTypeBase, Class[] parameterTypes) { + super(mappingConstructor, proxyFactory, viewType, viewTypeBase, parameterTypes); } @Override @SuppressWarnings("unchecked") - protected Class getProxyClass(ProxyFactory proxyFactory, ManagedViewType viewType) { - return (Class) proxyFactory.getUnsafeProxy(viewType); + protected Class getProxyClass(ProxyFactory proxyFactory, ManagedViewType viewType, ManagedViewTypeImpl viewTypeBase) { + return (Class) proxyFactory.getUnsafeProxy(viewType, viewTypeBase); } } diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/entity/Document.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/entity/Document.java index c565a7b748..b8321daf3a 100644 --- a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/entity/Document.java +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/entity/Document.java @@ -27,6 +27,7 @@ import java.util.Map; import java.util.Set; +import javax.persistence.CascadeType; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @@ -74,7 +75,10 @@ public Document(String name) { public Document(String name, Person owner, Version... versions) { this.name = name; this.owner = owner; - this.versions.addAll(Arrays.asList(versions)); + for (Version v : versions) { + v.setDocument(this); + this.versions.add(v); + } } @Id @@ -104,7 +108,7 @@ public void setSomeTransientField(Object someTransientField) { this.someTransientField = someTransientField; } - @OneToMany(mappedBy = "document") + @OneToMany(mappedBy = "document", cascade = {CascadeType.PERSIST}) public Set getVersions() { return versions; } @@ -130,7 +134,7 @@ public void setPartners(Set partners) { this.partners = partners; } - @ManyToOne(optional = false) + @ManyToOne(optional = false, cascade = { CascadeType.PERSIST }) public Person getOwner() { return owner; } diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/entity/Person.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/entity/Person.java index 8dcfad07c1..0510f197da 100644 --- a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/entity/Person.java +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/entity/Person.java @@ -50,6 +50,11 @@ public Person(String name) { this.name = name; } + public Person(String name, long age) { + this.name = name; + this.age = age; + } + @Id @GeneratedValue public Long getId() { diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/InheritanceTest.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/InheritanceTest.java new file mode 100644 index 0000000000..926c3fdf19 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/InheritanceTest.java @@ -0,0 +1,318 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.basic; + +import com.blazebit.persistence.CriteriaBuilder; +import com.blazebit.persistence.testsuite.tx.TxVoidWork; +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.EntityViewSetting; +import com.blazebit.persistence.view.EntityViews; +import com.blazebit.persistence.view.metamodel.ManagedViewType; +import com.blazebit.persistence.view.spi.EntityViewConfiguration; +import com.blazebit.persistence.view.testsuite.AbstractEntityViewTest; +import com.blazebit.persistence.view.testsuite.entity.Document; +import com.blazebit.persistence.view.testsuite.entity.Person; +import com.blazebit.persistence.view.testsuite.entity.Version; +import com.blazebit.persistence.view.testsuite.inheritance.basic.model.DocumentBaseView; +import com.blazebit.persistence.view.testsuite.inheritance.basic.model.NewDocumentView; +import com.blazebit.persistence.view.testsuite.inheritance.basic.model.NewSub1DocumentView; +import com.blazebit.persistence.view.testsuite.inheritance.basic.model.NewSub1Sub1DocumentView; +import com.blazebit.persistence.view.testsuite.inheritance.basic.model.NewSub2DocumentView; +import com.blazebit.persistence.view.testsuite.inheritance.basic.model.OldDocumentView; +import com.blazebit.persistence.view.testsuite.inheritance.basic.model.SimplePersonSubView; +import com.blazebit.persistence.view.testsuite.inheritance.basic.model.UnusedOldDocumentView; +import com.blazebit.persistence.view.testsuite.inheritance.basic.model.UsedOldDocumentView; +import com.blazebit.persistence.view.testsuite.inheritance.basic.model.UsedOldSub1DocumentView; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import javax.persistence.EntityManager; +import java.sql.Timestamp; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.*; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +public class InheritanceTest extends AbstractEntityViewTest { + + private Document doc1; + private Document doc2; + private Document doc3; + private Document doc4; + private Document doc5; + private Document doc6; + private EntityViewManager evm; + + @Override + public void setUpOnce() { + cleanDatabase(); + transactional(new TxVoidWork() { + @Override + public void work(EntityManager em) { + doc1 = new Document("doc1", new Person("owner1"), new Version(), new Version()); + doc2 = new Document("doc2", new Person("owner2")); + doc3 = new Document("doc3", new Person("owner3")); + doc4 = new Document("doc4", new Person("owner4"), new Version(), new Version()); + doc5 = new Document("doc5", new Person("owner5")); + doc6 = new Document("doc6", new Person("owner6")); + + // New + doc1.setAge(1); + doc2.setAge(1); + doc3.setAge(1); + + // Base + doc4.setAge(15); + + // Old + doc5.setAge(16); + doc6.setAge(16); + + // NewSub1 + doc1.setDefaultContact(null); + doc2.setDefaultContact(null); + // NewSub2 + doc3.setDefaultContact(1); + + // Base + doc4.setDefaultContact(1); + doc4.setLastModified(Timestamp.valueOf("2000-01-01 00:00:00")); + + // UsedOld + doc5.setLastModified(Timestamp.valueOf("2000-01-01 00:00:00")); + // Old + doc6.setLastModified(null); + + Person o1 = new Person("pers1"); + Person o2 = new Person("pers2"); + Person o3 = new Person("pers3"); + Person o4 = new Person("pers4"); + Person o5 = new Person("pers5"); + Person o6 = new Person("pers6"); + + doc2.getContacts().put(1, o1); + doc2.getContacts().put(1, o2); + doc5.getContacts().put(1, o3); + doc5.getContacts().put(1, o4); + doc6.getContacts().put(1, o5); + doc6.getContacts().put(1, o6); + + em.persist(o1); + em.persist(o2); + em.persist(o3); + em.persist(o4); + em.persist(o5); + em.persist(o6); + + o1.setPartnerDocument(doc3); + o2.setPartnerDocument(doc3); + o3.setPartnerDocument(doc4); + o4.setPartnerDocument(doc4); + o5.setPartnerDocument(doc5); + o6.setPartnerDocument(doc5); + + doc1.getPersonList().add(o1); + doc2.getPersonList().add(o2); + doc3.getPersonList().add(o3); + doc4.getPersonList().add(o4); + doc5.getPersonList().add(o5); + doc6.getPersonList().add(o6); + + em.persist(doc1); + em.persist(doc2); + em.persist(doc3); + em.persist(doc4); + em.persist(doc5); + em.persist(doc6); + } + }); + } + + @Before + public void setUp() { + doc1 = cbf.create(em, Document.class).where("name").eq("doc1").getSingleResult(); + doc2 = cbf.create(em, Document.class).where("name").eq("doc2").getSingleResult(); + doc3 = cbf.create(em, Document.class).where("name").eq("doc3").getSingleResult(); + doc4 = cbf.create(em, Document.class).where("name").eq("doc4").getSingleResult(); + doc5 = cbf.create(em, Document.class).where("name").eq("doc5").getSingleResult(); + doc6 = cbf.create(em, Document.class).where("name").eq("doc6").getSingleResult(); + + EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); + cfg.addEntityView(SimplePersonSubView.class); + cfg.addEntityView(DocumentBaseView.class); + cfg.addEntityView(NewDocumentView.class); + cfg.addEntityView(NewSub1DocumentView.class); + cfg.addEntityView(NewSub2DocumentView.class); + cfg.addEntityView(OldDocumentView.class); + cfg.addEntityView(UnusedOldDocumentView.class); + cfg.addEntityView(UsedOldDocumentView.class); + cfg.addEntityView(NewSub1Sub1DocumentView.class); + cfg.addEntityView(UsedOldSub1DocumentView.class); + this.evm = cfg.createEntityViewManager(cbf); + } + + @Test + public void inheritanceMetamodel() { + ManagedViewType baseViewType = evm.getMetamodel().managedView(DocumentBaseView.class); + ManagedViewType newViewType = evm.getMetamodel().managedView(NewDocumentView.class); + ManagedViewType newSub1ViewType = evm.getMetamodel().managedView(NewSub1DocumentView.class); + ManagedViewType newSub1Sub1ViewType = evm.getMetamodel().managedView(NewSub1Sub1DocumentView.class); + ManagedViewType newSub2ViewType = evm.getMetamodel().managedView(NewSub2DocumentView.class); + ManagedViewType oldViewType = evm.getMetamodel().managedView(OldDocumentView.class); + ManagedViewType usedOldViewType = evm.getMetamodel().managedView(UsedOldDocumentView.class); + ManagedViewType usedOldSubViewType = evm.getMetamodel().managedView(UsedOldSub1DocumentView.class); + ManagedViewType unusedOldViewType = evm.getMetamodel().managedView(UnusedOldDocumentView.class); + + assertEquals(null, baseViewType.getInheritanceMapping()); + assertEquals(6, baseViewType.getInheritanceSubtypes().size()); + assertTrue(baseViewType.getInheritanceSubtypes().contains(baseViewType)); + assertTrue(baseViewType.getInheritanceSubtypes().contains(newViewType)); + assertTrue(baseViewType.getInheritanceSubtypes().contains(newSub1ViewType)); + assertFalse(baseViewType.getInheritanceSubtypes().contains(newSub1Sub1ViewType)); + assertTrue(baseViewType.getInheritanceSubtypes().contains(newSub2ViewType)); + assertTrue(baseViewType.getInheritanceSubtypes().contains(oldViewType)); + assertTrue(baseViewType.getInheritanceSubtypes().contains(usedOldViewType)); + assertFalse(baseViewType.getInheritanceSubtypes().contains(usedOldSubViewType)); + assertFalse(baseViewType.getInheritanceSubtypes().contains(unusedOldViewType)); + + assertEquals("age < 15", newViewType.getInheritanceMapping()); + assertEquals(3, newViewType.getInheritanceSubtypes().size()); + assertTrue(newViewType.getInheritanceSubtypes().contains(newViewType)); + assertTrue(newViewType.getInheritanceSubtypes().contains(newSub1ViewType)); + assertTrue(newViewType.getInheritanceSubtypes().contains(newSub2ViewType)); + + assertEquals("defaultContact IS NULL", newSub1ViewType.getInheritanceMapping()); + assertEquals(1, newSub1ViewType.getInheritanceSubtypes().size()); + assertTrue(newSub1ViewType.getInheritanceSubtypes().contains(newSub1ViewType)); + + assertEquals("defaultContact IS NOT NULL", newSub2ViewType.getInheritanceMapping()); + assertEquals(1, newSub2ViewType.getInheritanceSubtypes().size()); + assertTrue(newSub2ViewType.getInheritanceSubtypes().contains(newSub2ViewType)); + + assertEquals("age > 15", oldViewType.getInheritanceMapping()); + assertEquals(2, oldViewType.getInheritanceSubtypes().size()); + assertTrue(oldViewType.getInheritanceSubtypes().contains(oldViewType)); + assertTrue(oldViewType.getInheritanceSubtypes().contains(usedOldViewType)); + + assertEquals("lastModified IS NOT NULL", usedOldViewType.getInheritanceMapping()); + assertEquals(1, usedOldViewType.getInheritanceSubtypes().size()); + assertTrue(usedOldViewType.getInheritanceSubtypes().contains(usedOldViewType)); + } + + @Test + public void inheritanceQuery() { + CriteriaBuilder criteria = cbf.create(em, Document.class, "d") + .orderByAsc("id"); + CriteriaBuilder cb = evm.applySetting(EntityViewSetting.create(DocumentBaseView.class), criteria); + List results = cb.getResultList(); + + assertEquals(6, results.size()); + assertTypeMatches(results.get(0), evm, DocumentBaseView.class, NewSub1DocumentView.class); + assertTypeMatches(results.get(1), evm, DocumentBaseView.class, NewSub1DocumentView.class); + assertTypeMatches(results.get(2), evm, DocumentBaseView.class, NewSub2DocumentView.class); + assertTypeMatches(results.get(3), evm, DocumentBaseView.class, DocumentBaseView.class); + assertTypeMatches(results.get(4), evm, DocumentBaseView.class, UsedOldDocumentView.class); + assertTypeMatches(results.get(5), evm, DocumentBaseView.class, OldDocumentView.class); + + NewSub1DocumentView docView1 = (NewSub1DocumentView) results.get(0); + NewSub1DocumentView docView2 = (NewSub1DocumentView) results.get(1); + NewSub2DocumentView docView3 = (NewSub2DocumentView) results.get(2); + DocumentBaseView docView4 = results.get(3); + UsedOldDocumentView docView5 = (UsedOldDocumentView) results.get(4); + OldDocumentView docView6 = (OldDocumentView) results.get(5); + + assertDocumentEquals(doc1, docView1); + assertDocumentEquals(doc2, docView2); + assertDocumentEquals(doc3, docView3); + assertDocumentEquals(doc4, docView4); + assertDocumentEquals(doc5, docView5); + assertDocumentEquals(doc6, docView6); + + assertSubviewEquals(doc1.getContacts().values(), docView1.getContacts()); + assertVersionsEquals(doc1.getVersions(), docView1.getVersionIds()); + assertSubviewEquals(doc2.getContacts().values(), docView2.getContacts()); + assertVersionsEquals(doc2.getVersions(), docView2.getVersionIds()); + + assertEquals(doc3.getDefaultContact(), docView3.getDefaultContact()); + + assertSubviewEquals(doc5.getContacts().values(), docView5.getContacts()); + assertSubviewEquals(doc5.getPartners(), docView5.getPartners()); + assertSubviewEquals(doc6.getPartners(), docView6.getPartners()); + } + + public static void assertTypeMatches(T o, EntityViewManager evm, Class baseType, Class subtype) { + int index = 0; + for (ManagedViewType t : evm.getMetamodel().managedView(baseType).getInheritanceSubtypes()) { + if (t.getJavaType() == subtype) { + break; + } + index++; + } + + assertEquals(baseType.getName() + "_" + index + "_" + subtype.getSimpleName() + "_$$_javassist_entityview_", o.getClass().getName()); + } + + public static void assertDocumentEquals(Document doc, DocumentBaseView view) { + assertEquals(doc.getId(), view.getId()); + assertEquals(doc.getName(), view.getName()); + } + + public static void assertVersionsEquals(Set versions, Set versionIds) { + if (versions == null) { + assertNull(versionIds); + return; + } + + assertNotNull(versionIds); + assertEquals(versions.size(), versionIds.size()); + for (Version v : versions) { + if (!versionIds.contains(v.getId())) { + Assert.fail("Could not find a version id: " + v.getId()); + } + } + } + + public static void assertSubviewEquals(Collection persons, Set personSubviews) { + if (persons == null) { + assertNull(personSubviews); + return; + } + + assertNotNull(personSubviews); + assertEquals(persons.size(), personSubviews.size()); + for (Person p : persons) { + boolean found = false; + for (SimplePersonSubView pSub : personSubviews) { + if (p.getName().equals(pSub.getName())) { + found = true; + break; + } + } + + if (!found) { + Assert.fail("Could not find a SimplePersonSubView with the name: " + p.getName()); + } + } + } +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/InheritanceValidationTest.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/InheritanceValidationTest.java new file mode 100644 index 0000000000..41c26450e2 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/InheritanceValidationTest.java @@ -0,0 +1,203 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.basic; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; +import com.blazebit.persistence.view.EntityViews; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.spi.EntityViewConfiguration; +import com.blazebit.persistence.view.testsuite.AbstractEntityViewTest; +import com.blazebit.persistence.view.testsuite.entity.Document; +import com.blazebit.persistence.view.testsuite.entity.Person; +import org.junit.Assert; +import org.junit.Test; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +public class InheritanceValidationTest extends AbstractEntityViewTest { + + @EntityView(Document.class) + @EntityViewInheritance({ DocBase1Sub1.class }) + interface DocBase1 { + @IdMapping Long getId(); + } + + @EntityView(Document.class) + interface DocBase1Sub1 extends DocBase1 { + } + + @Test + public void missingInheritanceMappingValidation() { + EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); + cfg.addEntityView(DocBase1.class); + cfg.addEntityView(DocBase1Sub1.class); + + try { + cfg.createEntityViewManager(cbf); + Assert.fail("Expected validation exception!"); + } catch (IllegalArgumentException ex) { + if (!ex.getMessage().contains("@EntityViewInheritanceMapping")) { + throw ex; + } + } + } + + @EntityView(Document.class) + @EntityViewInheritance({ DocBase2Sub1.class }) + interface DocBase2 { + @IdMapping Long getId(); + } + + @EntityView(Document.class) + @EntityViewInheritanceMapping("1 = 1") + interface DocBase2Sub1 { + } + + @Test + public void notASubtypeValidation() { + EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); + cfg.addEntityView(DocBase2.class); + cfg.addEntityView(DocBase2Sub1.class); + + try { + cfg.createEntityViewManager(cbf); + Assert.fail("Expected validation exception!"); + } catch (IllegalArgumentException ex) { + if (!ex.getMessage().contains("Java subtype")) { + throw ex; + } + } + } + + @EntityView(Document.class) + @EntityViewInheritance({ DocBase3.class }) + interface DocBase3 { + @IdMapping Long getId(); + } + + @Test + public void selfDeclarationValidation() { + EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); + cfg.addEntityView(DocBase3.class); + + try { + cfg.createEntityViewManager(cbf); + Assert.fail("Expected validation exception!"); + } catch (IllegalArgumentException ex) { + if (!ex.getMessage().contains("declared itself in @EntityViewInheritance")) { + throw ex; + } + } + } + + @EntityView(Document.class) + @EntityViewInheritance + @EntityViewInheritanceMapping("1 = 1") + interface DocBase4 { + @IdMapping Long getId(); + } + + @Test + public void notUsedInInheritanceShouldHaveMappingValidation() { + EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); + cfg.addEntityView(DocBase4.class); + + try { + cfg.createEntityViewManager(cbf); + Assert.fail("Expected validation exception!"); + } catch (IllegalArgumentException ex) { + if (!ex.getMessage().contains("@EntityViewInheritanceMapping but is never used as subtype")) { + throw ex; + } + } + } + + @EntityView(Document.class) + @EntityViewInheritance + interface DocBase5 { + @IdMapping Long getId(); + } + + @EntityView(Document.class) + @EntityViewInheritanceMapping("") + interface DocBase5Sub1 extends DocBase5 { + @IdMapping Long getId(); + } + + @Test + public void usedInInheritanceShouldHaveMappingValidation() { + EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); + cfg.addEntityView(DocBase5.class); + cfg.addEntityView(DocBase5Sub1.class); + + try { + cfg.createEntityViewManager(cbf); + Assert.fail("Expected validation exception!"); + } catch (IllegalArgumentException ex) { + if (!ex.getMessage().contains("no @EntityViewInheritanceMapping but is used as inheritance subtype")) { + throw ex; + } + } + } + + @EntityView(Person.class) + interface PersBase6 { + DocBase6 getPartnerDocument(); + } + + @EntityView(Document.class) + @EntityViewInheritance + interface DocBase6 { + @IdMapping Long getId(); + } + + @EntityView(Document.class) + @EntityViewInheritanceMapping("age < 15") + interface DocBase6Sub1 extends DocBase6 { + String getName(); + } + + @EntityView(Document.class) + @EntityViewInheritanceMapping("age > 15") + interface DocBase6Sub2 extends DocBase6 { + PersBase6 getOwner(); + } + + @Test + public void inheritanceIntroducedCycleValidation() { + EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); + cfg.addEntityView(PersBase6.class); + cfg.addEntityView(DocBase6.class); + cfg.addEntityView(DocBase6Sub1.class); + cfg.addEntityView(DocBase6Sub2.class); + + try { + cfg.createEntityViewManager(cbf); + Assert.fail("Expected validation exception!"); + } catch (IllegalArgumentException ex) { + if (!ex.getMessage().contains("circular dependency")) { + throw ex; + } + } + } + +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/DocumentBaseView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/DocumentBaseView.java new file mode 100644 index 0000000000..f4a21b38cf --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/DocumentBaseView.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.basic.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.testsuite.entity.Document; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +@EntityViewInheritance({ OldDocumentView.class, NewDocumentView.class }) +public interface DocumentBaseView { + + @IdMapping("id") + public Long getId(); + + public String getName(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/NewDocumentView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/NewDocumentView.java new file mode 100644 index 0000000000..1926c68e19 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/NewDocumentView.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.basic.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; +import com.blazebit.persistence.view.testsuite.entity.Document; + +import java.util.Set; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +@EntityViewInheritance({ NewSub1DocumentView.class, NewSub2DocumentView.class }) +@EntityViewInheritanceMapping("age < 15") +public interface NewDocumentView extends DocumentBaseView { + + public Set getContacts(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/NewSub1DocumentView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/NewSub1DocumentView.java new file mode 100644 index 0000000000..85f98c2d99 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/NewSub1DocumentView.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.basic.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; +import com.blazebit.persistence.view.Mapping; +import com.blazebit.persistence.view.testsuite.entity.Document; + +import java.util.Set; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +@EntityViewInheritanceMapping("defaultContact IS NULL") +public interface NewSub1DocumentView extends NewDocumentView { + + @Mapping("versions.id") + public Set getVersionIds(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/NewSub1Sub1DocumentView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/NewSub1Sub1DocumentView.java new file mode 100644 index 0000000000..5012040a97 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/NewSub1Sub1DocumentView.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.basic.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.testsuite.entity.Document; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * This is just a dummy to make sure this type is not accidentally considered for subtype selection. + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +public interface NewSub1Sub1DocumentView extends NewSub1DocumentView { + + public Person getOwner(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/NewSub2DocumentView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/NewSub2DocumentView.java new file mode 100644 index 0000000000..27d8aef7b4 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/NewSub2DocumentView.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.basic.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; +import com.blazebit.persistence.view.testsuite.entity.Document; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +@EntityViewInheritanceMapping("defaultContact IS NOT NULL") +public interface NewSub2DocumentView extends NewDocumentView { + + public Integer getDefaultContact(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/OldDocumentView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/OldDocumentView.java new file mode 100644 index 0000000000..c6c92b46e6 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/OldDocumentView.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.basic.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; +import com.blazebit.persistence.view.testsuite.entity.Document; + +import java.util.Set; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +@EntityViewInheritance({ UsedOldDocumentView.class }) +@EntityViewInheritanceMapping("age > 15") +public interface OldDocumentView extends DocumentBaseView { + + public Set getPartners(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/SimplePersonSubView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/SimplePersonSubView.java new file mode 100644 index 0000000000..2e5559f8ff --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/SimplePersonSubView.java @@ -0,0 +1,35 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.basic.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Person.class) +public interface SimplePersonSubView { + + @IdMapping("id") + public Long getId(); + + public String getName(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/UnusedOldDocumentView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/UnusedOldDocumentView.java new file mode 100644 index 0000000000..e264710dd2 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/UnusedOldDocumentView.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.basic.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.testsuite.entity.Document; + +import java.util.Set; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +public interface UnusedOldDocumentView extends OldDocumentView { + + public Set getPersonList(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/UsedOldDocumentView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/UsedOldDocumentView.java new file mode 100644 index 0000000000..e2c01e900d --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/UsedOldDocumentView.java @@ -0,0 +1,35 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.basic.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; +import com.blazebit.persistence.view.testsuite.entity.Document; + +import java.util.Set; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +@EntityViewInheritanceMapping("lastModified IS NOT NULL") +public interface UsedOldDocumentView extends OldDocumentView { + + public Set getContacts(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/UsedOldSub1DocumentView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/UsedOldSub1DocumentView.java new file mode 100644 index 0000000000..cd5f8aa4eb --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/basic/model/UsedOldSub1DocumentView.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.basic.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.testsuite.entity.Document; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * This is just a dummy to make sure this type is not accidentally considered for subtype selection. + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +public interface UsedOldSub1DocumentView extends UsedOldDocumentView { + + public Person getOwner(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/ConstructorInheritanceTest.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/ConstructorInheritanceTest.java new file mode 100644 index 0000000000..6383f09aa0 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/ConstructorInheritanceTest.java @@ -0,0 +1,159 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.constructor; + +import com.blazebit.persistence.CriteriaBuilder; +import com.blazebit.persistence.testsuite.tx.TxVoidWork; +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.EntityViewSetting; +import com.blazebit.persistence.view.EntityViews; +import com.blazebit.persistence.view.metamodel.ManagedViewType; +import com.blazebit.persistence.view.spi.EntityViewConfiguration; +import com.blazebit.persistence.view.testsuite.AbstractEntityViewTest; +import com.blazebit.persistence.view.testsuite.entity.Document; +import com.blazebit.persistence.view.testsuite.entity.Person; +import com.blazebit.persistence.view.testsuite.inheritance.constructor.model.DocumentBaseView; +import com.blazebit.persistence.view.testsuite.inheritance.constructor.model.NewDocumentView; +import com.blazebit.persistence.view.testsuite.inheritance.constructor.model.OldDocumentView; +import com.blazebit.persistence.view.testsuite.inheritance.constructor.model.SimplePersonSubView; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import javax.persistence.EntityManager; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +public class ConstructorInheritanceTest extends AbstractEntityViewTest { + + private Document doc1; + private Document doc2; + private Document doc3; + private EntityViewManager evm; + + @Override + public void setUpOnce() { + cleanDatabase(); + transactional(new TxVoidWork() { + @Override + public void work(EntityManager em) { + doc1 = new Document("doc1", new Person("owner1")); + doc2 = new Document("doc2", new Person("owner2")); + doc3 = new Document("doc3", new Person("owner3")); + + Person o1 = new Person("pers1"); + Person o2 = new Person("pers2"); + Person o3 = new Person("pers3"); + Person o4 = new Person("pers4"); + + // New + doc1.setAge(1); + doc1.getContacts().put(1, o1); + doc1.getContacts().put(1, o2); + + // Base + doc2.setAge(15); + // Old + doc3.setAge(16); + + em.persist(o1); + em.persist(o2); + em.persist(o3); + em.persist(o4); + + o3.setPartnerDocument(doc3); + o4.setPartnerDocument(doc3); + + em.persist(doc1); + em.persist(doc2); + em.persist(doc3); + } + }); + } + + @Before + public void setUp() { + doc1 = cbf.create(em, Document.class).where("name").eq("doc1").getSingleResult(); + doc2 = cbf.create(em, Document.class).where("name").eq("doc2").getSingleResult(); + doc3 = cbf.create(em, Document.class).where("name").eq("doc3").getSingleResult(); + + EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); + cfg.addEntityView(SimplePersonSubView.class); + cfg.addEntityView(DocumentBaseView.class); + cfg.addEntityView(NewDocumentView.class); + cfg.addEntityView(OldDocumentView.class); + this.evm = cfg.createEntityViewManager(cbf); + } + + @Test + public void inheritanceQuery() { + CriteriaBuilder criteria = cbf.create(em, Document.class, "d") + .orderByAsc("id"); + CriteriaBuilder cb = evm.applySetting(EntityViewSetting.create(DocumentBaseView.class), criteria); + List results = cb.getResultList(); + + assertEquals(3, results.size()); + + NewDocumentView docView1 = (NewDocumentView) results.get(0); + DocumentBaseView docView2 = (DocumentBaseView) results.get(1); + OldDocumentView docView3 = (OldDocumentView) results.get(2); + + assertDocumentEquals(doc1, docView1); + assertDocumentEquals(doc2, docView2); + assertDocumentEquals(doc3, docView3); + + assertSubviewEquals(doc1.getContacts().values(), docView1.getPeople()); + assertSubviewEquals(Collections.singleton(doc2.getOwner()), docView2.getPeople()); + assertSubviewEquals(doc3.getPartners(), docView3.getPeople()); + } + + public static void assertDocumentEquals(Document doc, DocumentBaseView view) { + assertEquals(doc.getId(), view.getId()); + assertEquals(doc.getName(), view.getName()); + } + + public static void assertSubviewEquals(Collection persons, Collection personSubviews) { + if (persons == null) { + assertNull(personSubviews); + return; + } + + assertNotNull(personSubviews); + assertEquals(persons.size(), personSubviews.size()); + for (Person p : persons) { + boolean found = false; + for (SimplePersonSubView pSub : personSubviews) { + if (p.getName().equals(pSub.getName())) { + found = true; + break; + } + } + + if (!found) { + Assert.fail("Could not find a SimplePersonSubView with the name: " + p.getName()); + } + } + } +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/model/DocumentBaseView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/model/DocumentBaseView.java new file mode 100644 index 0000000000..027339054a --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/model/DocumentBaseView.java @@ -0,0 +1,50 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.constructor.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.Mapping; +import com.blazebit.persistence.view.testsuite.entity.Document; + +import java.util.Collection; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +@EntityViewInheritance +public abstract class DocumentBaseView { + + private final Collection people; + + public DocumentBaseView(@Mapping("owner") Collection owners) { + this.people = owners; + } + + @IdMapping("id") + public abstract Long getId(); + + public abstract String getName(); + + public Collection getPeople() { + return people; + } +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/model/NewDocumentView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/model/NewDocumentView.java new file mode 100644 index 0000000000..8cc1601091 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/model/NewDocumentView.java @@ -0,0 +1,40 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.constructor.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; +import com.blazebit.persistence.view.Mapping; +import com.blazebit.persistence.view.testsuite.entity.Document; + +import java.util.Collection; +import java.util.Set; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +@EntityViewInheritanceMapping("age < 15") +public abstract class NewDocumentView extends DocumentBaseView { + + public NewDocumentView(@Mapping("contacts") Set contacts) { + super(contacts); + } +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/model/OldDocumentView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/model/OldDocumentView.java new file mode 100644 index 0000000000..1d87124855 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/model/OldDocumentView.java @@ -0,0 +1,40 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.constructor.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; +import com.blazebit.persistence.view.Mapping; +import com.blazebit.persistence.view.testsuite.entity.Document; + +import java.util.Set; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +@EntityViewInheritanceMapping("age > 15") +public abstract class OldDocumentView extends DocumentBaseView { + + public OldDocumentView(@Mapping("partners") Set partners) { + super(partners); + } + +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/model/SimplePersonSubView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/model/SimplePersonSubView.java new file mode 100644 index 0000000000..d8858cb4d7 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/constructor/model/SimplePersonSubView.java @@ -0,0 +1,35 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.constructor.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Person.class) +public interface SimplePersonSubView { + + @IdMapping("id") + public Long getId(); + + public String getName(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/EmbeddedInheritanceTest.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/EmbeddedInheritanceTest.java new file mode 100644 index 0000000000..09276d9389 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/EmbeddedInheritanceTest.java @@ -0,0 +1,177 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.embedded; + +import com.blazebit.persistence.CriteriaBuilder; +import com.blazebit.persistence.testsuite.treat.entity.IntIdEntity; +import com.blazebit.persistence.testsuite.treat.entity.IntValueEmbeddable; +import com.blazebit.persistence.testsuite.treat.entity.SingleTableBase; +import com.blazebit.persistence.testsuite.treat.entity.SingleTableEmbeddable; +import com.blazebit.persistence.testsuite.treat.entity.SingleTableEmbeddableSub1; +import com.blazebit.persistence.testsuite.treat.entity.SingleTableEmbeddableSub2; +import com.blazebit.persistence.testsuite.treat.entity.SingleTableSub1; +import com.blazebit.persistence.testsuite.treat.entity.SingleTableSub2; +import com.blazebit.persistence.testsuite.tx.TxVoidWork; +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.EntityViewSetting; +import com.blazebit.persistence.view.EntityViews; +import com.blazebit.persistence.view.metamodel.ManagedViewType; +import com.blazebit.persistence.view.metamodel.SingularAttribute; +import com.blazebit.persistence.view.spi.EntityViewConfiguration; +import com.blazebit.persistence.view.testsuite.AbstractEntityViewTest; +import com.blazebit.persistence.view.testsuite.inheritance.embedded.model.IntIdEntityView; +import com.blazebit.persistence.view.testsuite.inheritance.embedded.model.SingleTableDetailsView; +import com.blazebit.persistence.view.testsuite.inheritance.embedded.model.SingleTableSub1DetailsView; +import com.blazebit.persistence.view.testsuite.inheritance.embedded.model.SingleTableSub2DetailsView; +import com.blazebit.persistence.view.testsuite.inheritance.embedded.model.SingleTableView; +import org.junit.Before; +import org.junit.Test; + +import javax.persistence.EntityManager; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +public class EmbeddedInheritanceTest extends AbstractEntityViewTest { + + private SingleTableSub1 base1; + private SingleTableSub2 base2; + private EntityViewManager evm; + + @Override + protected Class[] getEntityClasses() { + return new Class[] { + IntIdEntity.class, + IntValueEmbeddable.class, + SingleTableBase.class, + SingleTableSub1.class, + SingleTableSub2.class, + SingleTableEmbeddable.class, + SingleTableEmbeddableSub1.class, + SingleTableEmbeddableSub2.class + }; + } + + @Override + public void setUpOnce() { + cleanDatabase(); + transactional(new TxVoidWork() { + @Override + public void work(EntityManager em) { + IntIdEntity i1 = new IntIdEntity("i1", 1); + base1 = new SingleTableSub1("st1"); + base2 = new SingleTableSub2("st2"); + base1.setSub1Value(123); + base2.setSub2Value(456); + base2.setRelation2(i1); + em.persist(i1); + em.persist(base1); + em.persist(base2); + base1.setParent(base2); + base2.setParent(base1); + } + }); + } + + @Before + public void setUp() { + base1 = cbf.create(em, SingleTableSub1.class).where("name").eq("st1").getSingleResult(); + base2 = cbf.create(em, SingleTableSub2.class).where("name").eq("st2").getSingleResult(); + + EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); + cfg.addEntityView(IntIdEntityView.class); + cfg.addEntityView(SingleTableView.class); + cfg.addEntityView(SingleTableDetailsView.class); + cfg.addEntityView(SingleTableSub1DetailsView.class); + cfg.addEntityView(SingleTableSub2DetailsView.class); + this.evm = cfg.createEntityViewManager(cbf); + } + + @Test + public void inheritanceMetamodel() { + ManagedViewType baseViewType = evm.getMetamodel().managedView(SingleTableView.class); + ManagedViewType detailsViewType = evm.getMetamodel().managedView(SingleTableDetailsView.class); + ManagedViewType detailsViewType1 = evm.getMetamodel().managedView(SingleTableSub1DetailsView.class); + ManagedViewType detailsViewType2 = evm.getMetamodel().managedView(SingleTableSub2DetailsView.class); + SingularAttribute detailsAttribute = (SingularAttribute) baseViewType.getAttribute("details"); + + assertEquals(3, detailsViewType.getInheritanceSubtypes().size()); + assertTrue(detailsViewType.getInheritanceSubtypes().contains(detailsViewType)); + assertTrue(detailsViewType.getInheritanceSubtypes().contains(detailsViewType1)); + assertTrue(detailsViewType.getInheritanceSubtypes().contains(detailsViewType2)); + + assertEquals("", detailsAttribute.getInheritanceSubtypeMappings().get(detailsViewType)); + assertEquals("TYPE(this) = " + SingleTableSub1.class.getName(), detailsAttribute.getInheritanceSubtypeMappings().get(detailsViewType1)); + assertEquals("TYPE(this) = " + SingleTableSub2.class.getName(), detailsAttribute.getInheritanceSubtypeMappings().get(detailsViewType2)); + } + + @Test + public void inheritanceQuery() { + CriteriaBuilder criteria = cbf.create(em, SingleTableBase.class, "d") + .orderByAsc("id"); + CriteriaBuilder cb = evm.applySetting(EntityViewSetting.create(SingleTableView.class), criteria); + List results = cb.getResultList(); + + assertEquals(2, results.size()); + assertTypeMatches(results.get(0).getDetails(), evm, SingleTableDetailsView.class, SingleTableSub1DetailsView.class); + assertTypeMatches(results.get(1).getDetails(), evm, SingleTableDetailsView.class, SingleTableSub2DetailsView.class); + + assertViewEquals(base1, results.get(0)); + assertViewEquals(base2, results.get(1)); + + SingleTableSub1DetailsView view1 = (SingleTableSub1DetailsView) results.get(0).getDetails(); + SingleTableSub2DetailsView view2 = (SingleTableSub2DetailsView) results.get(1).getDetails(); + + assertViewEquals(base1, view1); + assertViewEquals(base2, view2); + + assertEquals(base1.getSub1Value(), view1.getSub1Value()); + assertViewEquals(base2.getRelation2(), view2.getRelation2()); + } + + public static void assertViewEquals(SingleTableBase doc, SingleTableView view) { + assertEquals(doc.getId(), view.getId()); + } + + public static void assertViewEquals(SingleTableBase doc, SingleTableDetailsView view) { + assertEquals(doc.getName(), view.getName()); + } + + public static void assertViewEquals(IntIdEntity intIdEntity, IntIdEntityView view) { + assertEquals(intIdEntity.getId(), view.getId()); + assertEquals(intIdEntity.getName(), view.getName()); + } + + public static void assertTypeMatches(T o, EntityViewManager evm, Class baseType, Class subtype) { + int index = 0; + for (ManagedViewType t : evm.getMetamodel().managedView(baseType).getInheritanceSubtypes()) { + if (t.getJavaType() == subtype) { + break; + } + index++; + } + + assertEquals(baseType.getName() + "_" + index + "_" + subtype.getSimpleName() + "_$$_javassist_entityview_", o.getClass().getName()); + } +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/IntIdEntityView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/IntIdEntityView.java new file mode 100644 index 0000000000..5019d42dd5 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/IntIdEntityView.java @@ -0,0 +1,36 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.embedded.model; + +import com.blazebit.persistence.testsuite.treat.entity.IntIdEntity; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(IntIdEntity.class) +public interface IntIdEntityView { + + @IdMapping + public Integer getId(); + + public String getName(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/SingleTableDetailsView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/SingleTableDetailsView.java new file mode 100644 index 0000000000..8728f22b94 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/SingleTableDetailsView.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.embedded.model; + +import com.blazebit.persistence.testsuite.treat.entity.SingleTableBase; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(SingleTableBase.class) +@EntityViewInheritance +public interface SingleTableDetailsView { + + public String getName(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/SingleTableSub1DetailsView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/SingleTableSub1DetailsView.java new file mode 100644 index 0000000000..6f339655f8 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/SingleTableSub1DetailsView.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.embedded.model; + +import com.blazebit.persistence.testsuite.treat.entity.SingleTableSub1; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.testsuite.entity.Document; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(SingleTableSub1.class) +public interface SingleTableSub1DetailsView extends SingleTableDetailsView { + + public Integer getSub1Value(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/SingleTableSub2DetailsView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/SingleTableSub2DetailsView.java new file mode 100644 index 0000000000..51b27e77f8 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/SingleTableSub2DetailsView.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.embedded.model; + +import com.blazebit.persistence.testsuite.treat.entity.SingleTableSub2; +import com.blazebit.persistence.view.EntityView; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(SingleTableSub2.class) +public interface SingleTableSub2DetailsView extends SingleTableDetailsView { + + public IntIdEntityView getRelation2(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/SingleTableView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/SingleTableView.java new file mode 100644 index 0000000000..d0bfe7d4a7 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/embedded/model/SingleTableView.java @@ -0,0 +1,39 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.embedded.model; + +import com.blazebit.persistence.testsuite.treat.entity.SingleTableBase; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.Mapping; +import com.blazebit.persistence.view.MappingInheritance; +import com.blazebit.persistence.view.MappingInheritanceSubtype; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(SingleTableBase.class) +public interface SingleTableView { + + @IdMapping + public Long getId(); + + @Mapping("this") + public SingleTableDetailsView getDetails(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/PolymorphicInheritanceTest.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/PolymorphicInheritanceTest.java new file mode 100644 index 0000000000..e561491f53 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/PolymorphicInheritanceTest.java @@ -0,0 +1,181 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.polymorphic; + +import com.blazebit.persistence.CriteriaBuilder; +import com.blazebit.persistence.testsuite.treat.entity.IntIdEntity; +import com.blazebit.persistence.testsuite.treat.entity.IntValueEmbeddable; +import com.blazebit.persistence.testsuite.treat.entity.SingleTableBase; +import com.blazebit.persistence.testsuite.treat.entity.SingleTableEmbeddable; +import com.blazebit.persistence.testsuite.treat.entity.SingleTableEmbeddableSub1; +import com.blazebit.persistence.testsuite.treat.entity.SingleTableEmbeddableSub2; +import com.blazebit.persistence.testsuite.treat.entity.SingleTableSub1; +import com.blazebit.persistence.testsuite.treat.entity.SingleTableSub2; +import com.blazebit.persistence.testsuite.tx.TxVoidWork; +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.EntityViewSetting; +import com.blazebit.persistence.view.EntityViews; +import com.blazebit.persistence.view.metamodel.ManagedViewType; +import com.blazebit.persistence.view.spi.EntityViewConfiguration; +import com.blazebit.persistence.view.testsuite.AbstractEntityViewTest; +import com.blazebit.persistence.view.testsuite.inheritance.polymorphic.model.SingleTableBaseView; +import com.blazebit.persistence.view.testsuite.inheritance.polymorphic.model.SingleTableSimpleBaseView; +import com.blazebit.persistence.view.testsuite.inheritance.polymorphic.model.SingleTableSimpleSub1View; +import com.blazebit.persistence.view.testsuite.inheritance.polymorphic.model.SingleTableSimpleSub2View; +import com.blazebit.persistence.view.testsuite.inheritance.polymorphic.model.SingleTableSub1View; +import com.blazebit.persistence.view.testsuite.inheritance.polymorphic.model.SingleTableSub2View; +import org.junit.Before; +import org.junit.Test; + +import javax.persistence.EntityManager; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +public class PolymorphicInheritanceTest extends AbstractEntityViewTest { + + private SingleTableSub1 base1; + private SingleTableSub2 base2; + private EntityViewManager evm; + + @Override + protected Class[] getEntityClasses() { + return new Class[] { + IntIdEntity.class, + IntValueEmbeddable.class, + SingleTableBase.class, + SingleTableSub1.class, + SingleTableSub2.class, + SingleTableEmbeddable.class, + SingleTableEmbeddableSub1.class, + SingleTableEmbeddableSub2.class + }; + } + + @Override + public void setUpOnce() { + cleanDatabase(); + transactional(new TxVoidWork() { + @Override + public void work(EntityManager em) { + base1 = new SingleTableSub1("st1"); + base2 = new SingleTableSub2("st2"); + base1.setSub1Value(123); + base2.setSub2Value(456); + em.persist(base1); + em.persist(base2); + base1.setParent(base2); + base2.setParent(base1); + } + }); + } + + @Before + public void setUp() { + base1 = cbf.create(em, SingleTableSub1.class).where("name").eq("st1").getSingleResult(); + base2 = cbf.create(em, SingleTableSub2.class).where("name").eq("st2").getSingleResult(); + + EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); + cfg.addEntityView(SingleTableBaseView.class); + cfg.addEntityView(SingleTableSub1View.class); + cfg.addEntityView(SingleTableSub2View.class); + cfg.addEntityView(SingleTableSimpleBaseView.class); + cfg.addEntityView(SingleTableSimpleSub1View.class); + cfg.addEntityView(SingleTableSimpleSub2View.class); + this.evm = cfg.createEntityViewManager(cbf); + } + + @Test + public void inheritanceMetamodel() { + ManagedViewType baseViewType = evm.getMetamodel().managedView(SingleTableBaseView.class); + ManagedViewType sub1ViewType = evm.getMetamodel().managedView(SingleTableSub1View.class); + ManagedViewType sub2ViewType = evm.getMetamodel().managedView(SingleTableSub2View.class); + + assertEquals(null, baseViewType.getInheritanceMapping()); + assertEquals(3, baseViewType.getInheritanceSubtypes().size()); + assertTrue(baseViewType.getInheritanceSubtypes().contains(baseViewType)); + assertTrue(baseViewType.getInheritanceSubtypes().contains(sub1ViewType)); + assertTrue(baseViewType.getInheritanceSubtypes().contains(sub2ViewType)); + + assertEquals("TYPE(this) = " + SingleTableSub1.class.getName(), sub1ViewType.getInheritanceMapping()); + assertEquals(1, sub1ViewType.getInheritanceSubtypes().size()); + assertTrue(sub1ViewType.getInheritanceSubtypes().contains(sub1ViewType)); + + assertEquals("TYPE(this) = " + SingleTableSub2.class.getName(), sub2ViewType.getInheritanceMapping()); + assertEquals(1, sub2ViewType.getInheritanceSubtypes().size()); + assertTrue(sub2ViewType.getInheritanceSubtypes().contains(sub2ViewType)); + } + + @Test + public void inheritanceQuery() { + CriteriaBuilder criteria = cbf.create(em, SingleTableBase.class, "d") + .orderByAsc("id"); + CriteriaBuilder cb = evm.applySetting(EntityViewSetting.create(SingleTableBaseView.class), criteria); + List results = cb.getResultList(); + + assertEquals(2, results.size()); + assertTypeMatches(results.get(0), evm, SingleTableBaseView.class, SingleTableSub1View.class); + assertTypeMatches(results.get(1), evm, SingleTableBaseView.class, SingleTableSub2View.class); + + SingleTableSub1View view1 = (SingleTableSub1View) results.get(0); + SingleTableSub2View view2 = (SingleTableSub2View) results.get(1); + + assertBaseEquals(base1, view1); + assertBaseEquals(base2, view2); + + assertEquals(base1.getSub1Value(), view1.getSub1ValueOrNull()); + assertNull(view2.getSub1ValueOrNull()); + + assertEquals(base1.getSub1Value(), view1.getSub1Value()); + assertEquals(base2.getSub2Value(), view2.getSub2Value()); + + assertTypeMatches(view1.getParent(), evm, SingleTableSimpleBaseView.class, SingleTableSimpleSub2View.class); + assertTypeMatches(view2.getParent(), evm, SingleTableSimpleBaseView.class, SingleTableSimpleSub1View.class); + + assertBaseEquals(base2, view1.getParent()); + assertBaseEquals(base1, view2.getParent()); + } + + public static void assertBaseEquals(SingleTableBase doc, SingleTableBaseView view) { + assertEquals(doc.getId(), view.getId()); + assertEquals(doc.getName(), view.getName()); + } + + public static void assertBaseEquals(SingleTableBase doc, SingleTableSimpleBaseView view) { + assertEquals(doc.getId(), view.getId()); + assertEquals(doc.getName(), view.getName()); + } + + public static void assertTypeMatches(T o, EntityViewManager evm, Class baseType, Class subtype) { + int index = 0; + for (ManagedViewType t : evm.getMetamodel().managedView(baseType).getInheritanceSubtypes()) { + if (t.getJavaType() == subtype) { + break; + } + index++; + } + + assertEquals(baseType.getName() + "_" + index + "_" + subtype.getSimpleName() + "_$$_javassist_entityview_", o.getClass().getName()); + } +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableBaseView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableBaseView.java new file mode 100644 index 0000000000..be97ac9419 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableBaseView.java @@ -0,0 +1,43 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.polymorphic.model; + +import com.blazebit.persistence.testsuite.treat.entity.SingleTableBase; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.Mapping; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(SingleTableBase.class) +@EntityViewInheritance +public interface SingleTableBaseView { + + @IdMapping + public Long getId(); + + public String getName(); + + @Mapping("TREAT(this AS SingleTableSub1).sub1Value") + public Integer getSub1ValueOrNull(); + + public SingleTableSimpleBaseView getParent(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSimpleBaseView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSimpleBaseView.java new file mode 100644 index 0000000000..15ead47968 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSimpleBaseView.java @@ -0,0 +1,37 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.polymorphic.model; + +import com.blazebit.persistence.testsuite.treat.entity.SingleTableBase; +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.IdMapping; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(SingleTableBase.class) +@EntityViewInheritance +public interface SingleTableSimpleBaseView { + + @IdMapping + public Long getId(); + + public String getName(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSimpleSub1View.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSimpleSub1View.java new file mode 100644 index 0000000000..723282c1e4 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSimpleSub1View.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.polymorphic.model; + +import com.blazebit.persistence.testsuite.treat.entity.SingleTableSub1; +import com.blazebit.persistence.view.EntityView; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(SingleTableSub1.class) +public interface SingleTableSimpleSub1View extends SingleTableSimpleBaseView { + + public Integer getSub1Value(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSimpleSub2View.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSimpleSub2View.java new file mode 100644 index 0000000000..1af11045d0 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSimpleSub2View.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.polymorphic.model; + +import com.blazebit.persistence.testsuite.treat.entity.SingleTableSub2; +import com.blazebit.persistence.view.EntityView; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(SingleTableSub2.class) +public interface SingleTableSimpleSub2View extends SingleTableSimpleBaseView { + + public Integer getSub2Value(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSub1View.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSub1View.java new file mode 100644 index 0000000000..0b97d64006 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSub1View.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.polymorphic.model; + +import com.blazebit.persistence.testsuite.treat.entity.SingleTableSub1; +import com.blazebit.persistence.view.EntityView; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(SingleTableSub1.class) +public interface SingleTableSub1View extends SingleTableBaseView { + + public Integer getSub1Value(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSub2View.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSub2View.java new file mode 100644 index 0000000000..0645971a75 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/polymorphic/model/SingleTableSub2View.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.polymorphic.model; + +import com.blazebit.persistence.testsuite.treat.entity.SingleTableSub2; +import com.blazebit.persistence.view.EntityView; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(SingleTableSub2.class) +public interface SingleTableSub2View extends SingleTableBaseView { + + public Integer getSub2Value(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/SubviewInheritanceTest.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/SubviewInheritanceTest.java new file mode 100644 index 0000000000..a44d5bfe8c --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/SubviewInheritanceTest.java @@ -0,0 +1,300 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview; + +import com.blazebit.persistence.CriteriaBuilder; +import com.blazebit.persistence.testsuite.tx.TxVoidWork; +import com.blazebit.persistence.view.EntityViewManager; +import com.blazebit.persistence.view.EntityViewSetting; +import com.blazebit.persistence.view.EntityViews; +import com.blazebit.persistence.view.metamodel.ManagedViewType; +import com.blazebit.persistence.view.spi.EntityViewConfiguration; +import com.blazebit.persistence.view.testsuite.AbstractEntityViewTest; +import com.blazebit.persistence.view.testsuite.entity.Document; +import com.blazebit.persistence.view.testsuite.entity.Person; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.DocumentView1; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.DocumentView3; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.OldPersonView1; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.OldPersonView2; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.OldPersonView3; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.PersonBaseView; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.PersonBaseView1; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.PersonBaseView2; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.PersonBaseView3; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.SimpleDocumentView; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.SimplePersonSubView; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.DocumentView2; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.YoungPersonView1; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.YoungPersonView2; +import com.blazebit.persistence.view.testsuite.inheritance.subview.model.YoungPersonView3; +import org.junit.Before; +import org.junit.Test; + +import javax.persistence.EntityManager; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +public class SubviewInheritanceTest extends AbstractEntityViewTest { + + private Document doc1; + private Document doc2; + private Document doc3; + private Document doc4; + private Document doc5; + private Document doc6; + private EntityViewManager evm; + + @Override + public void setUpOnce() { + cleanDatabase(); + transactional(new TxVoidWork() { + @Override + public void work(EntityManager em) { + Person o1 = new Person("pers1", 1); + Person o2 = new Person("pers2", 1); + Person o3 = new Person("pers3", 15); + Person o4 = new Person("pers4", 15); + Person o5 = new Person("pers5", 16); + Person o6 = new Person("pers6", 16); + + doc1 = new Document("doc1", o1); + doc2 = new Document("doc2", o2); + doc3 = new Document("doc3", o3); + doc4 = new Document("doc4", o4); + doc5 = new Document("doc5", o5); + doc6 = new Document("doc6", o6); + + em.persist(o1); + em.persist(o2); + em.persist(o3); + em.persist(o4); + em.persist(o5); + em.persist(o6); + + em.persist(doc1); + em.persist(doc2); + em.persist(doc3); + em.persist(doc4); + em.persist(doc5); + em.persist(doc6); + + o1.setFriend(o2); + o2.setPartnerDocument(doc1); + o3.setFriend(o4); + o4.setPartnerDocument(doc3); + o5.setFriend(o6); + o6.setPartnerDocument(doc5); + } + }); + } + + @Before + public void setUp() { + doc1 = cbf.create(em, Document.class).where("name").eq("doc1").getSingleResult(); + doc2 = cbf.create(em, Document.class).where("name").eq("doc2").getSingleResult(); + doc3 = cbf.create(em, Document.class).where("name").eq("doc3").getSingleResult(); + doc4 = cbf.create(em, Document.class).where("name").eq("doc4").getSingleResult(); + doc5 = cbf.create(em, Document.class).where("name").eq("doc5").getSingleResult(); + doc6 = cbf.create(em, Document.class).where("name").eq("doc6").getSingleResult(); + } + + @Test + public void inheritanceQuery() { + EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); + cfg.addEntityView(DocumentView1.class); + cfg.addEntityView(SimpleDocumentView.class); + cfg.addEntityView(SimplePersonSubView.class); + cfg.addEntityView(PersonBaseView1.class); + cfg.addEntityView(OldPersonView1.class); + cfg.addEntityView(YoungPersonView1.class); + this.evm = cfg.createEntityViewManager(cbf); + + CriteriaBuilder criteria = cbf.create(em, Document.class, "d") + .orderByAsc("id"); + CriteriaBuilder cb = evm.applySetting(EntityViewSetting.create(DocumentView1.class), criteria); + List results = cb.getResultList(); + + assertEquals(6, results.size()); + + assertDocumentEquals(doc1, results.get(0)); + assertDocumentEquals(doc2, results.get(1)); + assertDocumentEquals(doc3, results.get(2)); + assertDocumentEquals(doc4, results.get(3)); + assertDocumentEquals(doc5, results.get(4)); + assertDocumentEquals(doc6, results.get(5)); + + assertTypeMatches(results.get(0).getOwner(), evm, PersonBaseView1.class, YoungPersonView1.class); + assertTypeMatches(results.get(1).getOwner(), evm, PersonBaseView1.class, YoungPersonView1.class); + assertTypeMatches(results.get(2).getOwner(), evm, PersonBaseView1.class, PersonBaseView1.class); + assertTypeMatches(results.get(3).getOwner(), evm, PersonBaseView1.class, PersonBaseView1.class); + assertTypeMatches(results.get(4).getOwner(), evm, PersonBaseView1.class, OldPersonView1.class); + assertTypeMatches(results.get(5).getOwner(), evm, PersonBaseView1.class, OldPersonView1.class); + + YoungPersonView1 persView1 = (YoungPersonView1) results.get(0).getOwner(); + YoungPersonView1 persView2 = (YoungPersonView1) results.get(1).getOwner(); + PersonBaseView persView3 = results.get(2).getOwner(); + PersonBaseView persView4 = results.get(3).getOwner(); + OldPersonView1 persView5 = (OldPersonView1) results.get(4).getOwner(); + OldPersonView1 persView6 = (OldPersonView1) results.get(5).getOwner(); + + assertPersonEquals(doc1.getOwner(), persView1); + assertPersonEquals(doc2.getOwner(), persView2); + assertPersonEquals(doc3.getOwner(), persView3); + assertPersonEquals(doc4.getOwner(), persView4); + assertPersonEquals(doc5.getOwner(), persView5); + assertPersonEquals(doc6.getOwner(), persView6); + + assertPersonEquals(doc1.getOwner().getFriend(), persView1.getFriend()); + assertNull(persView2.getFriend()); + + assertNull(persView5.getPartnerDocument()); + assertDocumentEquals(doc5, persView6.getPartnerDocument()); + } + + @Test + public void inheritanceQuerySubviewInheritanceMapping() { + EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); + cfg.addEntityView(DocumentView2.class); + cfg.addEntityView(SimpleDocumentView.class); + cfg.addEntityView(SimplePersonSubView.class); + cfg.addEntityView(PersonBaseView2.class); + cfg.addEntityView(OldPersonView2.class); + cfg.addEntityView(YoungPersonView2.class); + this.evm = cfg.createEntityViewManager(cbf); + + CriteriaBuilder criteria = cbf.create(em, Document.class, "d") + .orderByAsc("id"); + CriteriaBuilder cb = evm.applySetting(EntityViewSetting.create(DocumentView2.class), criteria); + List results = cb.getResultList(); + + assertEquals(6, results.size()); + + assertDocumentEquals(doc1, results.get(0)); + assertDocumentEquals(doc2, results.get(1)); + assertDocumentEquals(doc3, results.get(2)); + assertDocumentEquals(doc4, results.get(3)); + assertDocumentEquals(doc5, results.get(4)); + assertDocumentEquals(doc6, results.get(5)); + + assertTypeMatches(results.get(0).getOwner(), evm, PersonBaseView2.class, YoungPersonView2.class); + assertTypeMatches(results.get(1).getOwner(), evm, PersonBaseView2.class, YoungPersonView2.class); + assertTypeMatches(results.get(2).getOwner(), evm, PersonBaseView2.class, PersonBaseView2.class); + assertTypeMatches(results.get(3).getOwner(), evm, PersonBaseView2.class, PersonBaseView2.class); + assertTypeMatches(results.get(4).getOwner(), evm, PersonBaseView2.class, PersonBaseView2.class); + assertTypeMatches(results.get(5).getOwner(), evm, PersonBaseView2.class, PersonBaseView2.class); + + YoungPersonView2 persView1 = (YoungPersonView2) results.get(0).getOwner(); + YoungPersonView2 persView2 = (YoungPersonView2) results.get(1).getOwner(); + PersonBaseView persView3 = results.get(2).getOwner(); + PersonBaseView persView4 = results.get(3).getOwner(); + PersonBaseView persView5 = results.get(4).getOwner(); + PersonBaseView persView6 = results.get(5).getOwner(); + + assertPersonEquals(doc1.getOwner(), persView1); + assertPersonEquals(doc2.getOwner(), persView2); + assertPersonEquals(doc3.getOwner(), persView3); + assertPersonEquals(doc4.getOwner(), persView4); + assertPersonEquals(doc5.getOwner(), persView5); + assertPersonEquals(doc6.getOwner(), persView6); + + assertPersonEquals(doc1.getOwner().getFriend(), persView1.getFriend()); + assertNull(persView2.getFriend()); + } + + @Test + public void inheritanceQuerySubviewInheritanceMappingWithoutBaseType() { + EntityViewConfiguration cfg = EntityViews.createDefaultConfiguration(); + cfg.addEntityView(DocumentView3.class); + cfg.addEntityView(SimpleDocumentView.class); + cfg.addEntityView(SimplePersonSubView.class); + cfg.addEntityView(PersonBaseView3.class); + cfg.addEntityView(OldPersonView3.class); + cfg.addEntityView(YoungPersonView3.class); + this.evm = cfg.createEntityViewManager(cbf); + + CriteriaBuilder criteria = cbf.create(em, Document.class, "d") + .orderByAsc("id"); + CriteriaBuilder cb = evm.applySetting(EntityViewSetting.create(DocumentView3.class), criteria); + List results = cb.getResultList(); + + assertEquals(6, results.size()); + + assertDocumentEquals(doc1, results.get(0)); + assertDocumentEquals(doc2, results.get(1)); + assertDocumentEquals(doc3, results.get(2)); + assertDocumentEquals(doc4, results.get(3)); + assertDocumentEquals(doc5, results.get(4)); + assertDocumentEquals(doc6, results.get(5)); + + assertTypeMatches(results.get(0).getOwner(), evm, PersonBaseView3.class, YoungPersonView3.class); + assertTypeMatches(results.get(1).getOwner(), evm, PersonBaseView3.class, YoungPersonView3.class); + assertTypeMatches(results.get(2).getOwner(), evm, PersonBaseView3.class, YoungPersonView3.class); + assertTypeMatches(results.get(3).getOwner(), evm, PersonBaseView3.class, YoungPersonView3.class); + assertNull(results.get(4).getOwner()); + assertNull(results.get(5).getOwner()); + + YoungPersonView3 persView1 = (YoungPersonView3) results.get(0).getOwner(); + YoungPersonView3 persView2 = (YoungPersonView3) results.get(1).getOwner(); + YoungPersonView3 persView3 = (YoungPersonView3) results.get(2).getOwner(); + YoungPersonView3 persView4 = (YoungPersonView3) results.get(3).getOwner(); + + assertPersonEquals(doc1.getOwner(), persView1); + assertPersonEquals(doc2.getOwner(), persView2); + assertPersonEquals(doc3.getOwner(), persView3); + assertPersonEquals(doc4.getOwner(), persView4); + + assertPersonEquals(doc1.getOwner().getFriend(), persView1.getFriend()); + assertNull(persView2.getFriend()); + assertPersonEquals(doc3.getOwner().getFriend(), persView3.getFriend()); + assertNull(persView4.getFriend()); + } + + public static void assertTypeMatches(T o, EntityViewManager evm, Class baseType, Class subtype) { + int index = 0; + for (ManagedViewType t : evm.getMetamodel().managedView(baseType).getInheritanceSubtypes()) { + if (t.getJavaType() == subtype) { + break; + } + index++; + } + + assertEquals(baseType.getName() + "_" + index + "_" + subtype.getSimpleName() + "_$$_javassist_entityview_", o.getClass().getName()); + } + + public static void assertDocumentEquals(Document doc, SimpleDocumentView view) { + if (doc == null) { + assertNull(view); + } + assertEquals(doc.getId(), view.getId()); + assertEquals(doc.getName(), view.getName()); + } + + public static void assertPersonEquals(Person pers, PersonBaseView view) { + if (pers == null) { + assertNull(view); + } + assertEquals(pers.getId(), view.getId()); + assertEquals(pers.getName(), view.getName()); + } +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/DocumentView1.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/DocumentView1.java new file mode 100644 index 0000000000..3e7ce18c0b --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/DocumentView1.java @@ -0,0 +1,36 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.Mapping; +import com.blazebit.persistence.view.testsuite.entity.Document; + +import java.util.Collection; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +public interface DocumentView1 extends SimpleDocumentView { + + public PersonBaseView1 getOwner(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/DocumentView2.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/DocumentView2.java new file mode 100644 index 0000000000..cde8b7e591 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/DocumentView2.java @@ -0,0 +1,36 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.MappingInheritance; +import com.blazebit.persistence.view.MappingInheritanceSubtype; +import com.blazebit.persistence.view.testsuite.entity.Document; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +public interface DocumentView2 extends SimpleDocumentView { + + @MappingInheritance({ + @MappingInheritanceSubtype(YoungPersonView2.class) + }) + public PersonBaseView2 getOwner(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/DocumentView3.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/DocumentView3.java new file mode 100644 index 0000000000..b0ff644b0f --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/DocumentView3.java @@ -0,0 +1,36 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.MappingInheritance; +import com.blazebit.persistence.view.MappingInheritanceSubtype; +import com.blazebit.persistence.view.testsuite.entity.Document; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +public interface DocumentView3 extends SimpleDocumentView { + + @MappingInheritance(onlySubtypes = true, value = { + @MappingInheritanceSubtype(mapping = "age < 16", value = YoungPersonView3.class) + }) + public PersonBaseView3 getOwner(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/OldPersonView1.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/OldPersonView1.java new file mode 100644 index 0000000000..7fa46d3fb9 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/OldPersonView1.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Person.class) +@EntityViewInheritanceMapping("age > 15") +public interface OldPersonView1 extends PersonBaseView1 { + + public SimpleDocumentView getPartnerDocument(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/OldPersonView2.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/OldPersonView2.java new file mode 100644 index 0000000000..314db95e22 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/OldPersonView2.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Person.class) +@EntityViewInheritanceMapping("age > 15") +public interface OldPersonView2 extends PersonBaseView2 { + + public SimpleDocumentView getPartnerDocument(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/OldPersonView3.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/OldPersonView3.java new file mode 100644 index 0000000000..e3bb569c41 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/OldPersonView3.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Person.class) +@EntityViewInheritanceMapping("age > 15") +public interface OldPersonView3 extends PersonBaseView3 { + + public SimpleDocumentView getPartnerDocument(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/PersonBaseView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/PersonBaseView.java new file mode 100644 index 0000000000..167478bd24 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/PersonBaseView.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.IdMapping; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +public interface PersonBaseView { + + @IdMapping + public Long getId(); + + public String getName(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/PersonBaseView1.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/PersonBaseView1.java new file mode 100644 index 0000000000..a430e949dd --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/PersonBaseView1.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Person.class) +@EntityViewInheritance({ YoungPersonView1.class, OldPersonView1.class }) +public interface PersonBaseView1 extends PersonBaseView { +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/PersonBaseView2.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/PersonBaseView2.java new file mode 100644 index 0000000000..e95c118ced --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/PersonBaseView2.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Person.class) +@EntityViewInheritance({ YoungPersonView2.class, OldPersonView2.class }) +public interface PersonBaseView2 extends PersonBaseView { + +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/PersonBaseView3.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/PersonBaseView3.java new file mode 100644 index 0000000000..ee42c4424a --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/PersonBaseView3.java @@ -0,0 +1,32 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Person.class) +@EntityViewInheritance({ YoungPersonView3.class, OldPersonView3.class }) +public interface PersonBaseView3 extends PersonBaseView { + +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/SimpleDocumentView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/SimpleDocumentView.java new file mode 100644 index 0000000000..501d832fed --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/SimpleDocumentView.java @@ -0,0 +1,35 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.testsuite.entity.Document; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Document.class) +public interface SimpleDocumentView { + + @IdMapping + public Long getId(); + + public String getName(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/SimplePersonSubView.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/SimplePersonSubView.java new file mode 100644 index 0000000000..16f88e5dc4 --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/SimplePersonSubView.java @@ -0,0 +1,31 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Person.class) +public interface SimplePersonSubView extends PersonBaseView { + +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/YoungPersonView1.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/YoungPersonView1.java new file mode 100644 index 0000000000..24cc04c3ab --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/YoungPersonView1.java @@ -0,0 +1,35 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritance; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; +import com.blazebit.persistence.view.IdMapping; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Person.class) +@EntityViewInheritanceMapping("age < 15") +public interface YoungPersonView1 extends PersonBaseView1 { + + public SimplePersonSubView getFriend(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/YoungPersonView2.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/YoungPersonView2.java new file mode 100644 index 0000000000..dd2e6b8c0a --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/YoungPersonView2.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Person.class) +@EntityViewInheritanceMapping("age < 15") +public interface YoungPersonView2 extends PersonBaseView2 { + + public SimplePersonSubView getFriend(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/YoungPersonView3.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/YoungPersonView3.java new file mode 100644 index 0000000000..1a43d9679b --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/inheritance/subview/model/YoungPersonView3.java @@ -0,0 +1,33 @@ +/* + * Copyright 2014 - 2017 Blazebit. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.blazebit.persistence.view.testsuite.inheritance.subview.model; + +import com.blazebit.persistence.view.EntityView; +import com.blazebit.persistence.view.EntityViewInheritanceMapping; +import com.blazebit.persistence.view.testsuite.entity.Person; + +/** + * + * @author Christian Beikov + * @since 1.2.0 + */ +@EntityView(Person.class) +@EntityViewInheritanceMapping("age < 15") +public interface YoungPersonView3 extends PersonBaseView3 { + + public SimplePersonSubView getFriend(); +} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/proxy/ProxyFactoryTest.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/proxy/ProxyFactoryTest.java index 778cf104f9..db7eb471af 100644 --- a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/proxy/ProxyFactoryTest.java +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/proxy/ProxyFactoryTest.java @@ -67,7 +67,7 @@ public void testUnsafeClassProxy() throws Exception { // The parameter order is _id, contacts, firstContactPerson, id, name Class[] parameterTypes = new Class[]{ Long.class, Map.class, Person.class, Person.class, String.class, Long.class, Integer.class}; - ObjectInstantiator instantiator = new UnsafeInstantiator(viewType.getConstructor(parameterTypes), proxyFactory, viewType, parameterTypes); + ObjectInstantiator instantiator = new UnsafeInstantiator(viewType.getConstructor(parameterTypes), proxyFactory, viewType, null, parameterTypes); Map expectedContacts = new HashMap(); Person expectedFirstContactPerson = new Person("pers"); Long expectedId = 1L; @@ -101,7 +101,7 @@ public void testUnsafeClassProxy() throws Exception { @Test public void testInterfaceProxy() throws Exception { ViewType viewType = getViewMetamodel().view(DocumentInterfaceView.class); - Class proxyClass = proxyFactory.getProxy(viewType); + Class proxyClass = proxyFactory.getProxy(viewType, null); // The parameter order is _id, contacts, firstContactPerson, id, name Constructor constructor = proxyClass.getConstructor(Long.class, Map.class, @@ -135,7 +135,7 @@ public void testInterfaceProxy() throws Exception { @Test public void testClassProxy() throws Exception { ViewType viewType = getViewMetamodel().view(DocumentClassView.class); - Class proxyClass = proxyFactory.getProxy(viewType); + Class proxyClass = proxyFactory.getProxy(viewType, null); // The parameter order is _id, contacts, firstContactPerson, id, name Constructor constructor = proxyClass.getConstructor(Long.class, Map.class, Person.class, @@ -175,7 +175,7 @@ public void testClassProxy() throws Exception { @Test public void testInterfaceEqualsHashCode() throws Exception { ViewType viewType = getViewMetamodel().view(DocumentInterfaceView.class); - Class proxyClass = proxyFactory.getProxy(viewType); + Class proxyClass = proxyFactory.getProxy(viewType, null); // The parameter order is _id, contacts, firstContactPerson, id, name Constructor constructor = proxyClass.getConstructor(Long.class, Map.class, @@ -198,7 +198,7 @@ public void testInterfaceEqualsHashCode() throws Exception { @Test public void testClassEqualsHashCode() throws Exception { ViewType viewType = getViewMetamodel().view(DocumentClassView.class); - Class proxyClass = proxyFactory.getProxy(viewType); + Class proxyClass = proxyFactory.getProxy(viewType, null); // The parameter order is _id, contacts, firstContactPerson, id, name Constructor constructor = proxyClass.getConstructor(Long.class, Map.class, Person.class, @@ -227,7 +227,7 @@ public void testClassEqualsHashCode() throws Exception { @Test public void testInterfaceProxyStructure() throws Exception { ViewType viewType = getViewMetamodel().view(DocumentInterfaceView.class); - Class proxyClass = proxyFactory.getProxy(viewType); + Class proxyClass = proxyFactory.getProxy(viewType, null); assertEquals(1, proxyClass.getDeclaredConstructors().length); assertNotNull(proxyClass.getDeclaredConstructor(Long.class, Map.class, Person.class, Person.class, @@ -247,7 +247,7 @@ public void testInterfaceProxyStructure() throws Exception { @Test public void testClassProxyStructure() throws Exception { ViewType viewType = getViewMetamodel().view(DocumentClassView.class); - Class proxyClass = proxyFactory.getProxy(viewType); + Class proxyClass = proxyFactory.getProxy(viewType, null); assertEquals(1, proxyClass.getDeclaredConstructors().length); assertNotNull(proxyClass.getDeclaredConstructor(Long.class, Map.class, Person.class, Person.class, diff --git a/jpa-criteria/impl/src/main/java/com/blazebit/persistence/criteria/impl/InternalQuery.java b/jpa-criteria/impl/src/main/java/com/blazebit/persistence/criteria/impl/InternalQuery.java index 3b83e0915a..dc4a26b902 100644 --- a/jpa-criteria/impl/src/main/java/com/blazebit/persistence/criteria/impl/InternalQuery.java +++ b/jpa-criteria/impl/src/main/java/com/blazebit/persistence/criteria/impl/InternalQuery.java @@ -230,7 +230,12 @@ public Set> getParameters() { } } - // TODO: on clauses? + for (RootImpl r : roots) { + r.visit(visitor); + } + for (AbstractFrom r : correlationRoots) { + r.visit(visitor); + } visitor.visit(having); if (groupList != null) { diff --git a/jpa-criteria/impl/src/main/java/com/blazebit/persistence/criteria/impl/path/AbstractFrom.java b/jpa-criteria/impl/src/main/java/com/blazebit/persistence/criteria/impl/path/AbstractFrom.java index 6f054e0a78..db6119d370 100644 --- a/jpa-criteria/impl/src/main/java/com/blazebit/persistence/criteria/impl/path/AbstractFrom.java +++ b/jpa-criteria/impl/src/main/java/com/blazebit/persistence/criteria/impl/path/AbstractFrom.java @@ -24,6 +24,7 @@ import com.blazebit.persistence.criteria.BlazeMapJoin; import com.blazebit.persistence.criteria.BlazeSetJoin; import com.blazebit.persistence.criteria.impl.BlazeCriteriaBuilderImpl; +import com.blazebit.persistence.criteria.impl.ParameterVisitor; import com.blazebit.persistence.criteria.impl.RenderContext; import com.blazebit.persistence.criteria.impl.expression.FromSelection; import com.blazebit.persistence.criteria.impl.expression.SubqueryExpression; @@ -63,8 +64,8 @@ public abstract class AbstractFrom extends AbstractPath implements Blaz private AbstractFrom correlationParent; private JoinScope joinScope = new BasicJoinScope(); - private Set> joins; - private Set> fetches; + private Set> joins; + private Set> fetches; private Map, TreatedPath> treatedPaths; public AbstractFrom(BlazeCriteriaBuilderImpl criteriaBuilder, Class javaType) { @@ -96,6 +97,12 @@ protected boolean isDereferencable() { return true; } + public void visit(ParameterVisitor visitor) { + for (AbstractJoin j : joins) { + j.visit(visitor); + } + } + @Override public void prepareAlias(RenderContext context) { if (getAlias() == null) { @@ -672,7 +679,7 @@ protected class BasicJoinScope implements JoinScope { @Override public void addJoin(AbstractJoin join) { if (joins == null) { - joins = new LinkedHashSet>(); + joins = new LinkedHashSet<>(); } joins.add(join); } @@ -682,7 +689,7 @@ public void addFetch(AbstractJoin fetch) { fetch.setFetch(true); addJoin(fetch); if (fetches == null) { - fetches = new LinkedHashSet>(); + fetches = new LinkedHashSet<>(); } fetches.add(fetch); } @@ -693,7 +700,7 @@ protected class CorrelationJoinScope implements JoinScope { @Override public void addJoin(AbstractJoin join) { if (joins == null) { - joins = new LinkedHashSet>(); + joins = new LinkedHashSet<>(); } joins.add(join); } diff --git a/jpa-criteria/impl/src/main/java/com/blazebit/persistence/criteria/impl/path/AbstractJoin.java b/jpa-criteria/impl/src/main/java/com/blazebit/persistence/criteria/impl/path/AbstractJoin.java index 36ab26ec6d..3857561375 100644 --- a/jpa-criteria/impl/src/main/java/com/blazebit/persistence/criteria/impl/path/AbstractJoin.java +++ b/jpa-criteria/impl/src/main/java/com/blazebit/persistence/criteria/impl/path/AbstractJoin.java @@ -20,6 +20,7 @@ import com.blazebit.persistence.criteria.BlazeFrom; import com.blazebit.persistence.criteria.BlazeJoin; import com.blazebit.persistence.criteria.impl.BlazeCriteriaBuilderImpl; +import com.blazebit.persistence.criteria.impl.ParameterVisitor; import com.blazebit.persistence.criteria.impl.expression.SubqueryExpression; import javax.persistence.criteria.Expression; @@ -56,6 +57,12 @@ public AbstractJoin(BlazeCriteriaBuilderImpl criteriaBuilder, Class javaType, this.joinType = joinType; } + @Override + public void visit(ParameterVisitor visitor) { + visitor.visit(suppliedJoinCondition); + super.visit(visitor); + } + @Override @SuppressWarnings("unchecked") public AbstractPath getBasePath() {