HHH-8637 - Downcasting with TREAT operator should also filter results by the specified Type
This commit is contained in:
parent
246ce294a8
commit
9938937fe7
|
@ -217,6 +217,9 @@ tokens
|
|||
protected String unquote(String text) {
|
||||
return text.substring( 1, text.length() - 1 );
|
||||
}
|
||||
|
||||
protected void registerTreat(AST pathToTreat, AST treatAs) {
|
||||
}
|
||||
}
|
||||
|
||||
statement
|
||||
|
@ -369,7 +372,9 @@ joinPath
|
|||
* Uses a validating semantic predicate to make sure the text of the matched first IDENT is the TREAT keyword
|
||||
*/
|
||||
castedJoinPath
|
||||
: i:IDENT! OPEN! p:path AS! path! CLOSE! {i.getText().equalsIgnoreCase("treat") }?
|
||||
: i:IDENT! OPEN! p:path AS! a:path! CLOSE! {i.getText().equalsIgnoreCase("treat") }? {
|
||||
registerTreat( #p, #a );
|
||||
}
|
||||
;
|
||||
|
||||
withClause
|
||||
|
@ -738,7 +743,9 @@ identPrimaryBase
|
|||
;
|
||||
|
||||
castedIdentPrimaryBase
|
||||
: i:IDENT! OPEN! p:path AS! path! CLOSE! { i.getText().equals("treat") }?
|
||||
: i:IDENT! OPEN! p:path AS! a:path! CLOSE! { i.getText().equals("treat") }? {
|
||||
registerTreat( #p, #a );
|
||||
}
|
||||
;
|
||||
|
||||
aggregate
|
||||
|
|
|
@ -25,8 +25,10 @@ package org.hibernate.engine.internal;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
@ -88,6 +90,21 @@ public class JoinSequence {
|
|||
return fromPart;
|
||||
}
|
||||
|
||||
private Set<String> treatAsDeclarations;
|
||||
|
||||
public void applyTreatAsDeclarations(Set<String> treatAsDeclarations) {
|
||||
if ( treatAsDeclarations == null || treatAsDeclarations.isEmpty() ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( this.treatAsDeclarations == null ) {
|
||||
this.treatAsDeclarations = new HashSet<String>();
|
||||
}
|
||||
|
||||
this.treatAsDeclarations.addAll( treatAsDeclarations );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a full, although shallow, copy.
|
||||
*
|
||||
|
@ -142,23 +159,21 @@ public class JoinSequence {
|
|||
* Generate a JoinFragment
|
||||
*
|
||||
* @param enabledFilters The filters associated with the originating session to properly define join conditions
|
||||
* @param includeExtraJoins Should {@link #addExtraJoins} to called. Honestly I do not understand the full
|
||||
* ramifications of this argument
|
||||
* @param includeAllSubclassJoins Should all subclass joins be added to the rendered JoinFragment?
|
||||
*
|
||||
* @return The JoinFragment
|
||||
*
|
||||
* @throws MappingException Indicates a problem access the provided metadata, or incorrect metadata
|
||||
*/
|
||||
public JoinFragment toJoinFragment(Map enabledFilters, boolean includeExtraJoins) throws MappingException {
|
||||
return toJoinFragment( enabledFilters, includeExtraJoins, null, null );
|
||||
public JoinFragment toJoinFragment(Map enabledFilters, boolean includeAllSubclassJoins) throws MappingException {
|
||||
return toJoinFragment( enabledFilters, includeAllSubclassJoins, null, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a JoinFragment
|
||||
*
|
||||
* @param enabledFilters The filters associated with the originating session to properly define join conditions
|
||||
* @param includeExtraJoins Should {@link #addExtraJoins} to called. Honestly I do not understand the full
|
||||
* ramifications of this argument
|
||||
* @param includeAllSubclassJoins Should all subclass joins be added to the rendered JoinFragment?
|
||||
* @param withClauseFragment The with clause (which represents additional join restrictions) fragment
|
||||
* @param withClauseJoinAlias The
|
||||
*
|
||||
|
@ -168,27 +183,29 @@ public class JoinSequence {
|
|||
*/
|
||||
public JoinFragment toJoinFragment(
|
||||
Map enabledFilters,
|
||||
boolean includeExtraJoins,
|
||||
boolean includeAllSubclassJoins,
|
||||
String withClauseFragment,
|
||||
String withClauseJoinAlias) throws MappingException {
|
||||
final QueryJoinFragment joinFragment = new QueryJoinFragment( factory.getDialect(), useThetaStyle );
|
||||
if ( rootJoinable != null ) {
|
||||
joinFragment.addCrossJoin( rootJoinable.getTableName(), rootAlias );
|
||||
final String filterCondition = rootJoinable.filterFragment( rootAlias, enabledFilters );
|
||||
final String filterCondition = rootJoinable.filterFragment( rootAlias, enabledFilters, treatAsDeclarations );
|
||||
// JoinProcessor needs to know if the where clause fragment came from a dynamic filter or not so it
|
||||
// can put the where clause fragment in the right place in the SQL AST. 'hasFilterCondition' keeps track
|
||||
// of that fact.
|
||||
joinFragment.setHasFilterCondition( joinFragment.addCondition( filterCondition ) );
|
||||
if ( includeExtraJoins ) {
|
||||
//TODO: not quite sure about the full implications of this!
|
||||
addExtraJoins( joinFragment, rootAlias, rootJoinable, true );
|
||||
}
|
||||
addSubclassJoins( joinFragment, rootAlias, rootJoinable, true, includeAllSubclassJoins, treatAsDeclarations );
|
||||
}
|
||||
|
||||
Joinable last = rootJoinable;
|
||||
|
||||
for ( Join join : joins ) {
|
||||
final String on = join.getAssociationType().getOnCondition( join.getAlias(), factory, enabledFilters );
|
||||
// technically the treatAsDeclarations should only apply to rootJoinable or to a single Join,
|
||||
// but that is not possible atm given how these JoinSequence and Join objects are built.
|
||||
// However, it is generally ok given how the HQL parser builds these JoinSequences (a HQL join
|
||||
// results in a JoinSequence with an empty rootJoinable and a single Join). So we use that here
|
||||
// as an assumption
|
||||
final String on = join.getAssociationType().getOnCondition( join.getAlias(), factory, enabledFilters, treatAsDeclarations );
|
||||
String condition;
|
||||
if ( last != null
|
||||
&& isManyToManyRoot( last )
|
||||
|
@ -221,20 +238,21 @@ public class JoinSequence {
|
|||
condition
|
||||
);
|
||||
|
||||
//TODO: not quite sure about the full implications of this!
|
||||
if ( includeExtraJoins ) {
|
||||
addExtraJoins(
|
||||
addSubclassJoins(
|
||||
joinFragment,
|
||||
join.getAlias(),
|
||||
join.getJoinable(),
|
||||
join.joinType == JoinType.INNER_JOIN
|
||||
join.joinType == JoinType.INNER_JOIN,
|
||||
includeAllSubclassJoins,
|
||||
// ugh.. this is needed because of how HQL parser (FromElementFactory/SessionFactoryHelper)
|
||||
// builds the JoinSequence for HQL joins
|
||||
treatAsDeclarations
|
||||
);
|
||||
}
|
||||
last = join.getJoinable();
|
||||
}
|
||||
|
||||
if ( next != null ) {
|
||||
joinFragment.addFragment( next.toJoinFragment( enabledFilters, includeExtraJoins ) );
|
||||
joinFragment.addFragment( next.toJoinFragment( enabledFilters, includeAllSubclassJoins ) );
|
||||
}
|
||||
|
||||
joinFragment.addCondition( conditions.toString() );
|
||||
|
@ -254,11 +272,17 @@ public class JoinSequence {
|
|||
return false;
|
||||
}
|
||||
|
||||
private void addExtraJoins(JoinFragment joinFragment, String alias, Joinable joinable, boolean innerJoin) {
|
||||
final boolean include = isIncluded( alias );
|
||||
private void addSubclassJoins(
|
||||
JoinFragment joinFragment,
|
||||
String alias,
|
||||
Joinable joinable,
|
||||
boolean innerJoin,
|
||||
boolean includeSubclassJoins,
|
||||
Set<String> treatAsDeclarations) {
|
||||
final boolean include = includeSubclassJoins && isIncluded( alias );
|
||||
joinFragment.addJoins(
|
||||
joinable.fromJoinFragment( alias, innerJoin, include ),
|
||||
joinable.whereJoinFragment( alias, innerJoin, include )
|
||||
joinable.fromJoinFragment( alias, innerJoin, include, treatAsDeclarations ),
|
||||
joinable.whereJoinFragment( alias, innerJoin, include, treatAsDeclarations )
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -67,12 +67,21 @@ public class EntityGraphQueryHint {
|
|||
}
|
||||
}
|
||||
|
||||
return getFromElements( originEntityGraph.getAttributeNodes(), fromClause.getFromElement(), fromClause,
|
||||
walker, explicitFetches );
|
||||
return getFromElements(
|
||||
originEntityGraph.getAttributeNodes(),
|
||||
fromClause.getFromElement(),
|
||||
fromClause,
|
||||
walker,
|
||||
explicitFetches
|
||||
);
|
||||
}
|
||||
|
||||
private List<FromElement> getFromElements(List attributeNodes, FromElement origin, FromClause fromClause,
|
||||
HqlSqlWalker walker, Map<String, FromElement> explicitFetches) {
|
||||
private List<FromElement> getFromElements(
|
||||
List attributeNodes,
|
||||
FromElement origin,
|
||||
FromClause fromClause,
|
||||
HqlSqlWalker walker,
|
||||
Map<String, FromElement> explicitFetches) {
|
||||
final List<FromElement> fromElements = new ArrayList<FromElement>();
|
||||
|
||||
for (Object obj : attributeNodes) {
|
||||
|
@ -102,8 +111,16 @@ public class EntityGraphQueryHint {
|
|||
attributeName, classAlias, columns, false);
|
||||
final JoinSequence joinSequence = walker.getSessionFactoryHelper().createJoinSequence(
|
||||
false, entityType, tableAlias, JoinType.LEFT_OUTER_JOIN, columns );
|
||||
fromElement = fromElementFactory.createEntityJoin( entityType.getAssociatedEntityName(), tableAlias,
|
||||
joinSequence, true, walker.isInFrom(), entityType, role );
|
||||
fromElement = fromElementFactory.createEntityJoin(
|
||||
entityType.getAssociatedEntityName(),
|
||||
tableAlias,
|
||||
joinSequence,
|
||||
true,
|
||||
walker.isInFrom(),
|
||||
entityType,
|
||||
role,
|
||||
null
|
||||
);
|
||||
}
|
||||
else if ( propertyType.isCollectionType() ) {
|
||||
final String[] columns = origin.toColumns( originTableAlias, attributeName, false );
|
||||
|
|
|
@ -27,6 +27,11 @@ package org.hibernate.hql.internal.ast;
|
|||
import java.io.PrintStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringReader;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import antlr.ASTPair;
|
||||
import antlr.MismatchedTokenException;
|
||||
|
@ -398,6 +403,41 @@ public final class HqlParser extends HqlBaseParser {
|
|||
elementsNode.addChild( p );
|
||||
}
|
||||
|
||||
private Map<String,Set<String>> treatMap;
|
||||
|
||||
@Override
|
||||
protected void registerTreat(AST pathToTreat, AST treatAs) {
|
||||
final String path = toPathText( pathToTreat );
|
||||
final String subclassName = toPathText( treatAs );
|
||||
LOG.debugf( "Registering discovered request to treat(%s as %s)", path, subclassName );
|
||||
|
||||
if ( treatMap == null ) {
|
||||
treatMap = new HashMap<String, Set<String>>();
|
||||
}
|
||||
|
||||
Set<String> subclassNames = treatMap.get( path );
|
||||
if ( subclassNames == null ) {
|
||||
subclassNames = new HashSet<String>();
|
||||
treatMap.put( path, subclassNames );
|
||||
}
|
||||
subclassNames.add( subclassName );
|
||||
}
|
||||
|
||||
private String toPathText(AST node) {
|
||||
final String text = node.getText();
|
||||
if ( text.equals( "." )
|
||||
&& node.getFirstChild() != null
|
||||
&& node.getFirstChild().getNextSibling() != null
|
||||
&& node.getFirstChild().getNextSibling().getNextSibling() == null ) {
|
||||
return toPathText( node.getFirstChild() ) + '.' + toPathText( node.getFirstChild().getNextSibling() );
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public Map<String, Set<String>> getTreatMap() {
|
||||
return treatMap == null ? Collections.<String, Set<String>>emptyMap() : treatMap;
|
||||
}
|
||||
|
||||
static public void panic() {
|
||||
//overriden to avoid System.exit
|
||||
throw new QueryException("Parser: panic");
|
||||
|
|
|
@ -1280,6 +1280,10 @@ public class HqlSqlWalker extends HqlSqlBaseWalker implements ErrorReporter, Par
|
|||
}
|
||||
}
|
||||
|
||||
public Set<String> getTreatAsDeclarationsByPath(String path) {
|
||||
return hqlParser.getTreatMap().get( path );
|
||||
}
|
||||
|
||||
public static void panic() {
|
||||
throw new QueryException( "TreeWalker: panic" );
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
*/
|
||||
package org.hibernate.hql.internal.ast.tree;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import antlr.SemanticException;
|
||||
import antlr.collections.AST;
|
||||
import org.jboss.logging.Logger;
|
||||
|
@ -490,7 +492,8 @@ public class DotNode extends FromReferenceNode implements DisplayableNode, Selec
|
|||
fetch,
|
||||
getWalker().isInFrom(),
|
||||
propertyType,
|
||||
role
|
||||
role,
|
||||
joinPath
|
||||
);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -27,6 +27,7 @@ package org.hibernate.hql.internal.ast.tree;
|
|||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
|
@ -109,11 +110,13 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa
|
|||
this.classAlias = alias;
|
||||
this.tableAlias = origin.getTableAlias();
|
||||
super.initialize( fromClause.getWalker() );
|
||||
|
||||
}
|
||||
|
||||
protected void initializeComponentJoin(FromElementType elementType) {
|
||||
this.elementType = elementType;
|
||||
fromClause.registerFromElement( this );
|
||||
elementType.applyTreatAsDeclarations( getWalker().getTreatAsDeclarationsByPath( classAlias ) );
|
||||
this.elementType = elementType;
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
|
@ -368,6 +371,11 @@ public class FromElement extends HqlSqlWalkerNode implements DisplayableNode, Pa
|
|||
|
||||
public void setRole(String role) {
|
||||
this.role = role;
|
||||
applyTreatAsDeclarations( getWalker().getTreatAsDeclarationsByPath( role ) );
|
||||
}
|
||||
|
||||
public void applyTreatAsDeclarations(Set<String> treatAsDeclarationsByPath) {
|
||||
elementType.applyTreatAsDeclarations( treatAsDeclarationsByPath );
|
||||
}
|
||||
|
||||
public String getRole() {
|
||||
|
|
|
@ -249,9 +249,15 @@ public class FromElementFactory implements SqlTokenTypes {
|
|||
boolean fetchFlag,
|
||||
boolean inFrom,
|
||||
EntityType type,
|
||||
String role) throws SemanticException {
|
||||
String role,
|
||||
String joinPath) throws SemanticException {
|
||||
FromElement elem = createJoin( entityClass, tableAlias, joinSequence, type, false );
|
||||
elem.setFetch( fetchFlag );
|
||||
|
||||
if ( joinPath != null ) {
|
||||
elem.applyTreatAsDeclarations( fromClause.getWalker().getTreatAsDeclarationsByPath( joinPath ) );
|
||||
}
|
||||
|
||||
EntityPersister entityPersister = elem.getEntityPersister();
|
||||
int numberOfTables = entityPersister.getQuerySpaces().length;
|
||||
if ( numberOfTables > 1 && implied && !elem.useFromFragment() ) {
|
||||
|
@ -287,6 +293,7 @@ public class FromElementFactory implements SqlTokenTypes {
|
|||
}
|
||||
|
||||
public FromElement createComponentJoin(ComponentType type) {
|
||||
|
||||
// need to create a "place holder" from-element that can store the component/alias for this
|
||||
// component join
|
||||
return new ComponentJoin( fromClause, origin, classAlias, path, type );
|
||||
|
|
|
@ -24,8 +24,12 @@
|
|||
*/
|
||||
package org.hibernate.hql.internal.ast.tree;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import antlr.SemanticException;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
|
@ -256,8 +260,10 @@ class FromElementType {
|
|||
return fragment.trim();
|
||||
}
|
||||
|
||||
|
||||
public void setJoinSequence(JoinSequence joinSequence) {
|
||||
this.joinSequence = joinSequence;
|
||||
joinSequence.applyTreatAsDeclarations( treatAsDeclarations );
|
||||
}
|
||||
|
||||
public JoinSequence getJoinSequence() {
|
||||
|
@ -268,10 +274,37 @@ class FromElementType {
|
|||
// Class names in the FROM clause result in a JoinSequence (the old FromParser does this).
|
||||
if ( persister instanceof Joinable ) {
|
||||
Joinable joinable = ( Joinable ) persister;
|
||||
return fromElement.getSessionFactoryHelper().createJoinSequence().setRoot( joinable, getTableAlias() );
|
||||
final JoinSequence joinSequence = fromElement.getSessionFactoryHelper().createJoinSequence().setRoot( joinable, getTableAlias() );
|
||||
joinSequence.applyTreatAsDeclarations( treatAsDeclarations );
|
||||
return joinSequence;
|
||||
}
|
||||
else {
|
||||
return null; // TODO: Should this really return null? If not, figure out something better to do here.
|
||||
// TODO: Should this really return null? If not, figure out something better to do here.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> treatAsDeclarations;
|
||||
|
||||
public void applyTreatAsDeclarations(Set<String> treatAsDeclarations) {
|
||||
if ( treatAsDeclarations != null && !treatAsDeclarations.isEmpty() ) {
|
||||
if ( this.treatAsDeclarations == null ) {
|
||||
this.treatAsDeclarations = new HashSet<String>();
|
||||
}
|
||||
|
||||
for ( String treatAsSubclassName : treatAsDeclarations ) {
|
||||
try {
|
||||
EntityPersister subclassPersister = fromElement.getSessionFactoryHelper().requireClassPersister( treatAsSubclassName );
|
||||
this.treatAsDeclarations.add( subclassPersister.getEntityName() );
|
||||
}
|
||||
catch (SemanticException e) {
|
||||
throw new QueryException( "Unable to locate persister for subclass named in TREAT-AS : " + treatAsSubclassName );
|
||||
}
|
||||
}
|
||||
|
||||
if ( joinSequence != null ) {
|
||||
joinSequence.applyTreatAsDeclarations( this.treatAsDeclarations );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -149,12 +149,16 @@ public class IdentNode extends FromReferenceNode implements SelectExpression {
|
|||
}
|
||||
|
||||
private boolean resolveAsAlias() {
|
||||
final String alias = getText();
|
||||
|
||||
// This is not actually a constant, but a reference to FROM element.
|
||||
final FromElement element = getWalker().getCurrentFromClause().getFromElement( getText() );
|
||||
final FromElement element = getWalker().getCurrentFromClause().getFromElement( alias );
|
||||
if ( element == null ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
element.applyTreatAsDeclarations( getWalker().getTreatAsDeclarationsByPath( alias ) );
|
||||
|
||||
setType( SqlTokenTypes.ALIAS_REF );
|
||||
setFromElement( element );
|
||||
|
||||
|
|
|
@ -408,6 +408,16 @@ public final class ArrayHelper {
|
|||
return i;
|
||||
}
|
||||
|
||||
public static String[] reverse(String[] source) {
|
||||
final int length = source.length;
|
||||
final String[] destination = new String[length];
|
||||
for ( int i = 0; i < length; i++ ) {
|
||||
final int x = length - i - 1;
|
||||
destination[x] = source[i];
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
|
||||
public static void main(String... args) {
|
||||
int[] batchSizes = ArrayHelper.getBatchSizes( 32 );
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ import java.util.Arrays;
|
|||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.FetchMode;
|
||||
|
@ -1671,18 +1672,38 @@ public abstract class AbstractCollectionPersister
|
|||
return hasWhere() ? " and " + getSQLWhereString( alias ) : "";
|
||||
}
|
||||
|
||||
public String filterFragment(String alias, Map enabledFilters) throws MappingException {
|
||||
protected String filterFragment(String alias, Set<String> treatAsDeclarations) throws MappingException {
|
||||
return hasWhere() ? " and " + getSQLWhereString( alias ) : "";
|
||||
}
|
||||
|
||||
public String filterFragment(String alias, Map enabledFilters) throws MappingException {
|
||||
StringBuilder sessionFilterFragment = new StringBuilder();
|
||||
filterHelper.render( sessionFilterFragment, getFilterAliasGenerator(alias), enabledFilters );
|
||||
|
||||
return sessionFilterFragment.append( filterFragment( alias ) ).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filterFragment(
|
||||
String alias,
|
||||
Map enabledFilters,
|
||||
Set<String> treatAsDeclarations) {
|
||||
StringBuilder sessionFilterFragment = new StringBuilder();
|
||||
filterHelper.render( sessionFilterFragment, getFilterAliasGenerator(alias), enabledFilters );
|
||||
|
||||
return sessionFilterFragment.append( filterFragment( alias, treatAsDeclarations ) ).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String oneToManyFilterFragment(String alias) throws MappingException {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String oneToManyFilterFragment(String alias, Set<String> treatAsDeclarations) {
|
||||
return oneToManyFilterFragment( alias );
|
||||
}
|
||||
|
||||
protected boolean isInsertCallable() {
|
||||
return insertCallable;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.io.Serializable;
|
|||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
|
@ -343,14 +344,26 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
|
|||
.createBatchingCollectionInitializer( this, batchSize, getFactory(), loadQueryInfluencers );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses, Set<String> treatAsDeclarations) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String whereJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String whereJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses, Set<String> treatAsDeclarations) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CollectionInitializer createSubselectInitializer(SubselectFetch subselect, SessionImplementor session) {
|
||||
return new SubselectCollectionLoader(
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.io.Serializable;
|
|||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
|
@ -478,21 +479,37 @@ public class OneToManyPersister extends AbstractCollectionPersister {
|
|||
.createBatchingOneToManyInitializer( this, batchSize, getFactory(), loadQueryInfluencers );
|
||||
}
|
||||
|
||||
public String fromJoinFragment(String alias,
|
||||
boolean innerJoin,
|
||||
boolean includeSubclasses) {
|
||||
return ( ( Joinable ) getElementPersister() ).fromJoinFragment( alias, innerJoin, includeSubclasses );
|
||||
@Override
|
||||
public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
|
||||
return ( (Joinable) getElementPersister() ).fromJoinFragment( alias, innerJoin, includeSubclasses );
|
||||
}
|
||||
|
||||
public String whereJoinFragment(String alias,
|
||||
@Override
|
||||
public String fromJoinFragment(
|
||||
String alias,
|
||||
boolean innerJoin,
|
||||
boolean includeSubclasses) {
|
||||
return ( ( Joinable ) getElementPersister() ).whereJoinFragment( alias, innerJoin, includeSubclasses );
|
||||
boolean includeSubclasses,
|
||||
Set<String> treatAsDeclarations) {
|
||||
return ( (Joinable) getElementPersister() ).fromJoinFragment( alias, innerJoin, includeSubclasses, treatAsDeclarations );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String whereJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
|
||||
return ( (Joinable) getElementPersister() ).whereJoinFragment( alias, innerJoin, includeSubclasses );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String whereJoinFragment(
|
||||
String alias,
|
||||
boolean innerJoin,
|
||||
boolean includeSubclasses,
|
||||
Set<String> treatAsDeclarations) {
|
||||
return ( (Joinable) getElementPersister() ).whereJoinFragment( alias, innerJoin, includeSubclasses, treatAsDeclarations );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTableName() {
|
||||
return ( ( Joinable ) getElementPersister() ).getTableName();
|
||||
return ( (Joinable) getElementPersister() ).getTableName();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -505,6 +522,15 @@ public class OneToManyPersister extends AbstractCollectionPersister {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String filterFragment(String alias, Set<String> treatAsDeclarations) throws MappingException {
|
||||
String result = super.filterFragment( alias );
|
||||
if ( getElementPersister() instanceof Joinable ) {
|
||||
result += ( ( Joinable ) getElementPersister() ).oneToManyFilterFragment( alias, treatAsDeclarations );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CollectionInitializer createSubselectInitializer(SubselectFetch subselect, SessionImplementor session) {
|
||||
return new SubselectOneToManyLoader(
|
||||
|
|
|
@ -318,6 +318,8 @@ public abstract class AbstractEntityPersister
|
|||
|
||||
protected abstract String filterFragment(String alias) throws MappingException;
|
||||
|
||||
protected abstract String filterFragment(String alias, Set<String> treatAsDeclarations);
|
||||
|
||||
private static final String DISCRIMINATOR_ALIAS = "clazz_";
|
||||
|
||||
public String getDiscriminatorColumnName() {
|
||||
|
@ -3697,12 +3699,20 @@ public abstract class AbstractEntityPersister
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filterFragment(String alias, Map enabledFilters) throws MappingException {
|
||||
final StringBuilder sessionFilterFragment = new StringBuilder();
|
||||
filterHelper.render( sessionFilterFragment, getFilterAliasGenerator(alias), enabledFilters );
|
||||
return sessionFilterFragment.append( filterFragment( alias ) ).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filterFragment(String alias, Map enabledFilters, Set<String> treatAsDeclarations) {
|
||||
final StringBuilder sessionFilterFragment = new StringBuilder();
|
||||
filterHelper.render( sessionFilterFragment, getFilterAliasGenerator(alias), enabledFilters );
|
||||
return sessionFilterFragment.append( filterFragment( alias, treatAsDeclarations ) ).toString();
|
||||
}
|
||||
|
||||
public String generateFilterConditionAlias(String rootAlias) {
|
||||
return rootAlias;
|
||||
}
|
||||
|
@ -3711,55 +3721,137 @@ public abstract class AbstractEntityPersister
|
|||
return "";
|
||||
}
|
||||
|
||||
public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
|
||||
return getSubclassTableSpan() == 1 ?
|
||||
"" : //just a performance opt!
|
||||
createJoin( alias, innerJoin, includeSubclasses ).toFromFragmentString();
|
||||
@Override
|
||||
public String oneToManyFilterFragment(String alias, Set<String> treatAsDeclarations) {
|
||||
return oneToManyFilterFragment( alias );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
|
||||
// NOTE : Not calling createJoin here is just a performance optimization
|
||||
return getSubclassTableSpan() == 1
|
||||
? ""
|
||||
: createJoin( alias, innerJoin, includeSubclasses, Collections.<String>emptySet() ).toFromFragmentString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String fromJoinFragment(
|
||||
String alias,
|
||||
boolean innerJoin,
|
||||
boolean includeSubclasses,
|
||||
Set<String> treatAsDeclarations) {
|
||||
// NOTE : Not calling createJoin here is just a performance optimization
|
||||
return getSubclassTableSpan() == 1
|
||||
? ""
|
||||
: createJoin( alias, innerJoin, includeSubclasses, treatAsDeclarations ).toFromFragmentString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String whereJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses) {
|
||||
return getSubclassTableSpan() == 1 ?
|
||||
"" : //just a performance opt!
|
||||
createJoin( alias, innerJoin, includeSubclasses ).toWhereFragmentString();
|
||||
// NOTE : Not calling createJoin here is just a performance optimization
|
||||
return getSubclassTableSpan() == 1
|
||||
? ""
|
||||
: createJoin( alias, innerJoin, includeSubclasses, Collections.<String>emptySet() ).toWhereFragmentString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String whereJoinFragment(
|
||||
String alias,
|
||||
boolean innerJoin,
|
||||
boolean includeSubclasses,
|
||||
Set<String> treatAsDeclarations) {
|
||||
// NOTE : Not calling createJoin here is just a performance optimization
|
||||
return getSubclassTableSpan() == 1
|
||||
? ""
|
||||
: createJoin( alias, innerJoin, includeSubclasses, treatAsDeclarations ).toWhereFragmentString();
|
||||
}
|
||||
|
||||
protected boolean isSubclassTableLazy(int j) {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected JoinFragment createJoin(String name, boolean innerJoin, boolean includeSubclasses) {
|
||||
final String[] idCols = StringHelper.qualify( name, getIdentifierColumnNames() ); //all joins join to the pk of the driving table
|
||||
protected JoinFragment createJoin(String name, boolean innerJoin, boolean includeSubclasses, Set<String> treatAsDeclarations) {
|
||||
// IMPL NOTE : all joins join to the pk of the driving table
|
||||
final String[] idCols = StringHelper.qualify( name, getIdentifierColumnNames() );
|
||||
final JoinFragment join = getFactory().getDialect().createOuterJoinFragment();
|
||||
final int tableSpan = getSubclassTableSpan();
|
||||
for ( int j = 1; j < tableSpan; j++ ) { //notice that we skip the first table; it is the driving table!
|
||||
final boolean joinIsIncluded = isClassOrSuperclassTable( j ) ||
|
||||
( includeSubclasses && !isSubclassTableSequentialSelect( j ) && !isSubclassTableLazy( j ) );
|
||||
if ( joinIsIncluded ) {
|
||||
join.addJoin( getSubclassTableName( j ),
|
||||
// IMPL NOTE : notice that we skip the first table; it is the driving table!
|
||||
for ( int j = 1; j < tableSpan; j++ ) {
|
||||
final JoinType joinType = determineSubclassTableJoinType(
|
||||
j,
|
||||
innerJoin,
|
||||
includeSubclasses,
|
||||
treatAsDeclarations
|
||||
);
|
||||
|
||||
if ( joinType != null && joinType != JoinType.NONE ) {
|
||||
join.addJoin(
|
||||
getSubclassTableName( j ),
|
||||
generateTableAlias( name, j ),
|
||||
idCols,
|
||||
getSubclassTableKeyColumns( j ),
|
||||
innerJoin && isClassOrSuperclassTable( j ) && !isInverseTable( j ) && !isNullableTable( j ) ?
|
||||
JoinType.INNER_JOIN : //we can inner join to superclass tables (the row MUST be there)
|
||||
JoinType.LEFT_OUTER_JOIN //we can never inner join to subclass tables
|
||||
joinType
|
||||
);
|
||||
}
|
||||
}
|
||||
return join;
|
||||
}
|
||||
|
||||
protected JoinType determineSubclassTableJoinType(
|
||||
int subclassTableNumber,
|
||||
boolean canInnerJoin,
|
||||
boolean includeSubclasses,
|
||||
Set<String> treatAsDeclarations) {
|
||||
|
||||
if ( isClassOrSuperclassTable( subclassTableNumber ) ) {
|
||||
final boolean shouldInnerJoin = canInnerJoin
|
||||
&& !isInverseTable( subclassTableNumber )
|
||||
&& !isNullableTable( subclassTableNumber );
|
||||
// the table is either this persister's driving table or (one of) its super class persister's driving
|
||||
// tables which can be inner joined as long as the `shouldInnerJoin` condition resolves to true
|
||||
return shouldInnerJoin ? JoinType.INNER_JOIN : JoinType.LEFT_OUTER_JOIN;
|
||||
}
|
||||
|
||||
// otherwise we have a subclass table and need to look a little deeper...
|
||||
|
||||
// IMPL NOTE : By default includeSubclasses indicates that all subclasses should be joined and that each
|
||||
// subclass ought to be joined by outer-join. However, TREAT-AS always requires that an inner-join be used
|
||||
// so we give TREAT-AS higher precedence...
|
||||
|
||||
if ( isSubclassTableIndicatedByTreatAsDeclarations( subclassTableNumber, treatAsDeclarations ) ) {
|
||||
return JoinType.INNER_JOIN;
|
||||
}
|
||||
|
||||
if ( includeSubclasses
|
||||
&& !isSubclassTableSequentialSelect( subclassTableNumber )
|
||||
&& !isSubclassTableLazy( subclassTableNumber ) ) {
|
||||
return JoinType.LEFT_OUTER_JOIN;
|
||||
}
|
||||
|
||||
return JoinType.NONE;
|
||||
}
|
||||
|
||||
protected boolean isSubclassTableIndicatedByTreatAsDeclarations(
|
||||
int subclassTableNumber,
|
||||
Set<String> treatAsDeclarations) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
protected JoinFragment createJoin(int[] tableNumbers, String drivingAlias) {
|
||||
final String[] keyCols = StringHelper.qualify( drivingAlias, getSubclassTableKeyColumns( tableNumbers[0] ) );
|
||||
final JoinFragment jf = getFactory().getDialect().createOuterJoinFragment();
|
||||
for ( int i = 1; i < tableNumbers.length; i++ ) { //skip the driving table
|
||||
// IMPL NOTE : notice that we skip the first table; it is the driving table!
|
||||
for ( int i = 1; i < tableNumbers.length; i++ ) {
|
||||
final int j = tableNumbers[i];
|
||||
jf.addJoin( getSubclassTableName( j ),
|
||||
generateTableAlias( getRootAlias(), j ),
|
||||
keyCols,
|
||||
getSubclassTableKeyColumns( j ),
|
||||
isInverseSubclassTable( j ) || isNullableSubclassTable( j ) ?
|
||||
JoinType.LEFT_OUTER_JOIN :
|
||||
JoinType.INNER_JOIN );
|
||||
isInverseSubclassTable( j ) || isNullableSubclassTable( j )
|
||||
? JoinType.LEFT_OUTER_JOIN
|
||||
: JoinType.INNER_JOIN
|
||||
);
|
||||
}
|
||||
return jf;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
package org.hibernate.persister.entity;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
|
||||
|
@ -55,21 +56,44 @@ public interface Joinable {
|
|||
* (optional operation)
|
||||
*/
|
||||
public String whereJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses);
|
||||
|
||||
/**
|
||||
* Get the where clause part of any joins
|
||||
* (optional operation)
|
||||
*/
|
||||
public String whereJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses, Set<String> treatAsDeclarations);
|
||||
|
||||
/**
|
||||
* Get the from clause part of any joins
|
||||
* (optional operation)
|
||||
*/
|
||||
public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses);
|
||||
|
||||
/**
|
||||
* Get the from clause part of any joins
|
||||
* (optional operation)
|
||||
*/
|
||||
public String fromJoinFragment(String alias, boolean innerJoin, boolean includeSubclasses, Set<String> treatAsDeclarations);
|
||||
|
||||
/**
|
||||
* The columns to join on
|
||||
*/
|
||||
public String[] getKeyColumnNames();
|
||||
|
||||
/**
|
||||
* Get the where clause filter, given a query alias and considering enabled session filters
|
||||
*/
|
||||
public String filterFragment(String alias, Map enabledFilters) throws MappingException;
|
||||
|
||||
/**
|
||||
* Get the where clause filter, given a query alias and considering enabled session filters
|
||||
*/
|
||||
public String filterFragment(String alias, Map enabledFilters, Set<String> treatAsDeclarations) throws MappingException;
|
||||
|
||||
public String oneToManyFilterFragment(String alias) throws MappingException;
|
||||
|
||||
public String oneToManyFilterFragment(String alias, Set<String> treatAsDeclarations);
|
||||
|
||||
/**
|
||||
* Is this instance actually a CollectionPersister?
|
||||
*/
|
||||
|
|
|
@ -24,10 +24,14 @@
|
|||
package org.hibernate.persister.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -45,6 +49,7 @@ import org.hibernate.internal.util.collections.ArrayHelper;
|
|||
import org.hibernate.mapping.Column;
|
||||
import org.hibernate.mapping.Join;
|
||||
import org.hibernate.mapping.KeyValue;
|
||||
import org.hibernate.mapping.MappedSuperclass;
|
||||
import org.hibernate.mapping.PersistentClass;
|
||||
import org.hibernate.mapping.Property;
|
||||
import org.hibernate.mapping.Selectable;
|
||||
|
@ -498,6 +503,8 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
|
|||
k++;
|
||||
}
|
||||
|
||||
subclassNamesBySubclassTable = buildSubclassNamesBySubclassTableMapping( persistentClass, factory );
|
||||
|
||||
initLockers();
|
||||
|
||||
initSubclassPropertyAliasesMap( persistentClass );
|
||||
|
@ -506,6 +513,160 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
|
|||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Used to hold the name of subclasses that each "subclass table" is part of. For example, given a hierarchy like:
|
||||
* {@code JoinedEntity <- JoinedEntitySubclass <- JoinedEntitySubSubclass}..
|
||||
* <p/>
|
||||
* For the persister for JoinedEntity, we'd have:
|
||||
* <pre>
|
||||
* subclassClosure[0] = "JoinedEntitySubSubclass"
|
||||
* subclassClosure[1] = "JoinedEntitySubclass"
|
||||
* subclassClosure[2] = "JoinedEntity"
|
||||
*
|
||||
* subclassTableNameClosure[0] = "T_JoinedEntity"
|
||||
* subclassTableNameClosure[1] = "T_JoinedEntitySubclass"
|
||||
* subclassTableNameClosure[2] = "T_JoinedEntitySubSubclass"
|
||||
*
|
||||
* subclassNameClosureBySubclassTable[0] = ["JoinedEntitySubSubclass", "JoinedEntitySubclass"]
|
||||
* subclassNameClosureBySubclassTable[1] = ["JoinedEntitySubSubclass"]
|
||||
* </pre>
|
||||
* Note that there are only 2 entries in subclassNameClosureBySubclassTable. That is because there are really only
|
||||
* 2 tables here that make up the subclass mapping, the others make up the class/superclass table mappings. We
|
||||
* do not need to account for those here. The "offset" is defined by the value of {@link #getTableSpan()}.
|
||||
* Therefore the corresponding row in subclassNameClosureBySubclassTable for a given row in subclassTableNameClosure
|
||||
* is calculated as {@code subclassTableNameClosureIndex - getTableSpan()}.
|
||||
* <p/>
|
||||
* As we consider each subclass table we can look into this array based on the subclass table's index and see
|
||||
* which subclasses would require it to be included. E.g., given {@code TREAT( x AS JoinedEntitySubSubclass )},
|
||||
* when trying to decide whether to include join to "T_JoinedEntitySubclass" (subclassTableNameClosureIndex = 1),
|
||||
* we'd look at {@code subclassNameClosureBySubclassTable[0]} and see if the TREAT-AS subclass name is included in
|
||||
* its values. Since {@code subclassNameClosureBySubclassTable[1]} includes "JoinedEntitySubSubclass", we'd
|
||||
* consider it included.
|
||||
* <p/>
|
||||
* {@link #subclassTableNameClosure} also accounts for secondary tables and we properly handle those as we
|
||||
* build the subclassNamesBySubclassTable array and they are therefore properly handled when we use it
|
||||
*/
|
||||
private final String[][] subclassNamesBySubclassTable;
|
||||
|
||||
/**
|
||||
* Essentially we are building a mapping that we can later use to determine whether a given "subclass table"
|
||||
* should be included in joins when JPA TREAT-AS is used.
|
||||
*
|
||||
* @param persistentClass
|
||||
* @param factory
|
||||
* @return
|
||||
*/
|
||||
private String[][] buildSubclassNamesBySubclassTableMapping(PersistentClass persistentClass, SessionFactoryImplementor factory) {
|
||||
// this value represents the number of subclasses (and not the class itself)
|
||||
final int numberOfSubclassTables = subclassTableNameClosure.length - coreTableSpan;
|
||||
if ( numberOfSubclassTables == 0 ) {
|
||||
return new String[0][];
|
||||
}
|
||||
|
||||
final String[][] mapping = new String[numberOfSubclassTables][];
|
||||
processPersistentClassHierarchy( persistentClass, true, factory, mapping );
|
||||
return mapping;
|
||||
}
|
||||
|
||||
private Set<String> processPersistentClassHierarchy(
|
||||
PersistentClass persistentClass,
|
||||
boolean isBase,
|
||||
SessionFactoryImplementor factory,
|
||||
String[][] mapping) {
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
// collect all the class names that indicate that the "main table" of the given PersistentClass should be
|
||||
// included when one of the collected class names is used in TREAT
|
||||
final Set<String> classNames = new HashSet<String>();
|
||||
|
||||
final Iterator itr = persistentClass.getDirectSubclasses();
|
||||
while ( itr.hasNext() ) {
|
||||
final Subclass subclass = (Subclass) itr.next();
|
||||
final Set<String> subclassSubclassNames = processPersistentClassHierarchy(
|
||||
subclass,
|
||||
false,
|
||||
factory,
|
||||
mapping
|
||||
);
|
||||
classNames.addAll( subclassSubclassNames );
|
||||
}
|
||||
|
||||
classNames.add( persistentClass.getEntityName() );
|
||||
|
||||
if ( ! isBase ) {
|
||||
MappedSuperclass msc = persistentClass.getSuperMappedSuperclass();
|
||||
while ( msc != null ) {
|
||||
classNames.add( msc.getMappedClass().getName() );
|
||||
msc = msc.getSuperMappedSuperclass();
|
||||
}
|
||||
|
||||
associateSubclassNamesToSubclassTableIndexes( persistentClass, classNames, mapping, factory );
|
||||
}
|
||||
|
||||
return classNames;
|
||||
}
|
||||
|
||||
private void associateSubclassNamesToSubclassTableIndexes(
|
||||
PersistentClass persistentClass,
|
||||
Set<String> classNames,
|
||||
String[][] mapping,
|
||||
SessionFactoryImplementor factory) {
|
||||
|
||||
final String tableName = persistentClass.getTable().getQualifiedName(
|
||||
factory.getDialect(),
|
||||
factory.getSettings().getDefaultCatalogName(),
|
||||
factory.getSettings().getDefaultSchemaName()
|
||||
);
|
||||
|
||||
associateSubclassNamesToSubclassTableIndex( tableName, classNames, mapping );
|
||||
|
||||
Iterator itr = persistentClass.getJoinIterator();
|
||||
while ( itr.hasNext() ) {
|
||||
final Join join = (Join) itr.next();
|
||||
final String secondaryTableName = join.getTable().getQualifiedName(
|
||||
factory.getDialect(),
|
||||
factory.getSettings().getDefaultCatalogName(),
|
||||
factory.getSettings().getDefaultSchemaName()
|
||||
);
|
||||
associateSubclassNamesToSubclassTableIndex( secondaryTableName, classNames, mapping );
|
||||
}
|
||||
}
|
||||
|
||||
private void associateSubclassNamesToSubclassTableIndex(
|
||||
String tableName,
|
||||
Set<String> classNames,
|
||||
String[][] mapping) {
|
||||
// find the table's entry in the subclassTableNameClosure array
|
||||
boolean found = false;
|
||||
for ( int i = 0; i < subclassTableNameClosure.length; i++ ) {
|
||||
if ( subclassTableNameClosure[i].equals( tableName ) ) {
|
||||
found = true;
|
||||
final int index = i - coreTableSpan;
|
||||
if ( index < 0 || index >= mapping.length ) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
"Encountered 'subclass table index' [%s] was outside expected range ( [%s] < i < [%s] )",
|
||||
index,
|
||||
0,
|
||||
mapping.length
|
||||
)
|
||||
);
|
||||
}
|
||||
mapping[index] = classNames.toArray( new String[ classNames.size() ] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !found ) {
|
||||
throw new IllegalStateException(
|
||||
String.format(
|
||||
"Was unable to locate subclass table [%s] in 'subclassTableNameClosure'",
|
||||
tableName
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public JoinedSubclassEntityPersister(
|
||||
final EntityBinding entityBinding,
|
||||
final EntityRegionAccessStrategy cacheAccessStrategy,
|
||||
|
@ -545,6 +706,7 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
|
|||
discriminatorSQLString = null;
|
||||
coreTableSpan = -1;
|
||||
isNullableTable = null;
|
||||
subclassNamesBySubclassTable = null;
|
||||
}
|
||||
|
||||
protected boolean isNullableTable(int j) {
|
||||
|
@ -716,10 +878,16 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
|
|||
return cases;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filterFragment(String alias) {
|
||||
return hasWhere() ?
|
||||
" and " + getSQLWhereString( generateFilterConditionAlias( alias ) ) :
|
||||
"";
|
||||
return hasWhere()
|
||||
? " and " + getSQLWhereString( generateFilterConditionAlias( alias ) )
|
||||
: "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filterFragment(String alias, Set<String> treatAsDeclarations) {
|
||||
return filterFragment( alias );
|
||||
}
|
||||
|
||||
public String generateFilterConditionAlias(String rootAlias) {
|
||||
|
@ -802,6 +970,41 @@ public class JoinedSubclassEntityPersister extends AbstractEntityPersister {
|
|||
return isClassOrSuperclassTable[j];
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSubclassTableIndicatedByTreatAsDeclarations(
|
||||
int subclassTableNumber,
|
||||
Set<String> treatAsDeclarations) {
|
||||
if ( treatAsDeclarations == null || treatAsDeclarations.isEmpty() ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final String[] inclusionSubclassNameClosure = getSubclassNameClosureBySubclassTable( subclassTableNumber );
|
||||
|
||||
// NOTE : we assume the entire hierarchy is joined-subclass here
|
||||
for ( String subclassName : treatAsDeclarations ) {
|
||||
for ( String inclusionSubclassName : inclusionSubclassNameClosure ) {
|
||||
if ( inclusionSubclassName.equals( subclassName ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private String[] getSubclassNameClosureBySubclassTable(int subclassTableNumber) {
|
||||
final int index = subclassTableNumber - getTableSpan();
|
||||
|
||||
if ( index > subclassNamesBySubclassTable.length ) {
|
||||
throw new IllegalArgumentException(
|
||||
"Given subclass table number is outside expected range [" + subclassNamesBySubclassTable.length
|
||||
+ "] as defined by subclassTableNameClosure/subclassClosure"
|
||||
);
|
||||
}
|
||||
|
||||
return subclassNamesBySubclassTable[index];
|
||||
}
|
||||
|
||||
public String getPropertyTableName(String propertyName) {
|
||||
Integer index = getEntityMetamodel().getPropertyIndexOrNull( propertyName );
|
||||
if ( index == null ) {
|
||||
|
|
|
@ -28,7 +28,9 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
|
@ -835,22 +837,47 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
|||
return getTableName() + ' ' + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filterFragment(String alias) throws MappingException {
|
||||
String result = discriminatorFilterFragment(alias);
|
||||
if ( hasWhere() ) result += " and " + getSQLWhereString(alias);
|
||||
return result;
|
||||
}
|
||||
|
||||
public String oneToManyFilterFragment(String alias) throws MappingException {
|
||||
return forceDiscriminator ?
|
||||
discriminatorFilterFragment(alias) :
|
||||
"";
|
||||
private String discriminatorFilterFragment(String alias) throws MappingException {
|
||||
return discriminatorFilterFragment( alias, null );
|
||||
}
|
||||
|
||||
private String discriminatorFilterFragment(String alias) throws MappingException {
|
||||
if ( needsDiscriminator() ) {
|
||||
InFragment frag = new InFragment();
|
||||
public String oneToManyFilterFragment(String alias) throws MappingException {
|
||||
return forceDiscriminator
|
||||
? discriminatorFilterFragment( alias, null )
|
||||
: "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String oneToManyFilterFragment(String alias, Set<String> treatAsDeclarations) {
|
||||
return needsDiscriminator()
|
||||
? discriminatorFilterFragment( alias, treatAsDeclarations )
|
||||
: "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filterFragment(String alias, Set<String> treatAsDeclarations) {
|
||||
String result = discriminatorFilterFragment( alias, treatAsDeclarations );
|
||||
if ( hasWhere() ) {
|
||||
result += " and " + getSQLWhereString( alias );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private String discriminatorFilterFragment(String alias, Set<String> treatAsDeclarations) {
|
||||
final boolean hasTreatAs = treatAsDeclarations != null && !treatAsDeclarations.isEmpty();
|
||||
|
||||
if ( !needsDiscriminator() && !hasTreatAs) {
|
||||
return "";
|
||||
}
|
||||
|
||||
final InFragment frag = new InFragment();
|
||||
if ( isDiscriminatorFormula() ) {
|
||||
frag.setFormula( alias, getDiscriminatorFormulaTemplate() );
|
||||
}
|
||||
|
@ -858,27 +885,49 @@ public class SingleTableEntityPersister extends AbstractEntityPersister {
|
|||
frag.setColumn( alias, getDiscriminatorColumnName() );
|
||||
}
|
||||
|
||||
String[] subclasses = getSubclassClosure();
|
||||
for ( int i=0; i<subclasses.length; i++ ) {
|
||||
final Queryable queryable = (Queryable) getFactory().getEntityPersister( subclasses[i] );
|
||||
if ( !queryable.isAbstract() ) frag.addValue( queryable.getDiscriminatorSQLValue() );
|
||||
}
|
||||
|
||||
StringBuilder buf = new StringBuilder(50)
|
||||
.append(" and ")
|
||||
.append( frag.toFragmentString() );
|
||||
|
||||
return buf.toString();
|
||||
if ( hasTreatAs ) {
|
||||
frag.addValues( decodeTreatAsRequests( treatAsDeclarations ) );
|
||||
}
|
||||
else {
|
||||
return "";
|
||||
frag.addValues( fullDiscriminatorValues() );
|
||||
}
|
||||
|
||||
return " and " + frag.toFragmentString();
|
||||
}
|
||||
|
||||
private boolean needsDiscriminator() {
|
||||
return forceDiscriminator || isInherited();
|
||||
}
|
||||
|
||||
private String[] decodeTreatAsRequests(Set<String> treatAsDeclarations) {
|
||||
final List<String> values = new ArrayList<String>();
|
||||
for ( String subclass : treatAsDeclarations ) {
|
||||
final Queryable queryable = (Queryable) getFactory().getEntityPersister( subclass );
|
||||
if ( !queryable.isAbstract() ) {
|
||||
values.add( queryable.getDiscriminatorSQLValue() );
|
||||
}
|
||||
}
|
||||
return values.toArray( new String[ values.size() ] );
|
||||
}
|
||||
|
||||
private String[] fullDiscriminatorValues;
|
||||
|
||||
private String[] fullDiscriminatorValues() {
|
||||
if ( fullDiscriminatorValues == null ) {
|
||||
// first access; build it
|
||||
final List<String> values = new ArrayList<String>();
|
||||
for ( String subclass : getSubclassClosure() ) {
|
||||
final Queryable queryable = (Queryable) getFactory().getEntityPersister( subclass );
|
||||
if ( !queryable.isAbstract() ) {
|
||||
values.add( queryable.getDiscriminatorSQLValue() );
|
||||
}
|
||||
}
|
||||
fullDiscriminatorValues = values.toArray( new String[values.size() ] );
|
||||
}
|
||||
|
||||
return fullDiscriminatorValues;
|
||||
}
|
||||
|
||||
public String getSubclassPropertyTableName(int i) {
|
||||
return subclassTableNameClosure[ subclassPropertyTableNumberClosure[i] ];
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.util.HashSet;
|
|||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -356,10 +357,16 @@ public class UnionSubclassEntityPersister extends AbstractEntityPersister {
|
|||
return getTableName() + ' ' + name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String filterFragment(String name) {
|
||||
return hasWhere() ?
|
||||
" and " + getSQLWhereString(name) :
|
||||
"";
|
||||
return hasWhere()
|
||||
? " and " + getSQLWhereString( name )
|
||||
: "";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String filterFragment(String alias, Set<String> treatAsDeclarations) {
|
||||
return filterFragment( alias );
|
||||
}
|
||||
|
||||
public String getSubclassPropertyTableName(int i) {
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
package org.hibernate.sql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
|
@ -34,6 +35,7 @@ import org.hibernate.internal.util.StringHelper;
|
|||
* <br>
|
||||
* <code>... in(...)</code>
|
||||
* <br>
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class InFragment {
|
||||
|
@ -50,7 +52,12 @@ public class InFragment {
|
|||
* @return {@code this}, for method chaining
|
||||
*/
|
||||
public InFragment addValue(Object value) {
|
||||
values.add(value);
|
||||
values.add( value );
|
||||
return this;
|
||||
}
|
||||
|
||||
public InFragment addValues(Object[] values) {
|
||||
Collections.addAll( this.values, values );
|
||||
return this;
|
||||
}
|
||||
|
||||
|
@ -61,33 +68,34 @@ public class InFragment {
|
|||
|
||||
public InFragment setColumn(String alias, String columnName) {
|
||||
this.columnName = StringHelper.qualify( alias, columnName );
|
||||
return setColumn(this.columnName);
|
||||
return setColumn( this.columnName );
|
||||
}
|
||||
|
||||
public InFragment setFormula(String alias, String formulaTemplate) {
|
||||
this.columnName = StringHelper.replace(formulaTemplate, Template.TEMPLATE, alias);
|
||||
return setColumn(this.columnName);
|
||||
this.columnName = StringHelper.replace( formulaTemplate, Template.TEMPLATE, alias );
|
||||
return setColumn( this.columnName );
|
||||
}
|
||||
|
||||
public String toFragmentString() {
|
||||
|
||||
if (values.size() == 0) {
|
||||
if ( values.size() == 0 ) {
|
||||
return "1=2";
|
||||
}
|
||||
|
||||
StringBuilder buf = new StringBuilder(values.size() * 5);
|
||||
StringBuilder buf = new StringBuilder( values.size() * 5 );
|
||||
|
||||
if (values.size() == 1) {
|
||||
Object value = values.get(0);
|
||||
buf.append(columnName);
|
||||
if ( values.size() == 1 ) {
|
||||
Object value = values.get( 0 );
|
||||
buf.append( columnName );
|
||||
|
||||
if (NULL.equals(value)) {
|
||||
buf.append(" is null");
|
||||
} else {
|
||||
if (NOT_NULL.equals(value)) {
|
||||
buf.append(" is not null");
|
||||
} else {
|
||||
buf.append('=').append(value);
|
||||
if ( NULL.equals( value ) ) {
|
||||
buf.append( " is null" );
|
||||
}
|
||||
else {
|
||||
if ( NOT_NULL.equals( value ) ) {
|
||||
buf.append( " is not null" );
|
||||
}
|
||||
else {
|
||||
buf.append( '=' ).append( value );
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
|
@ -95,35 +103,38 @@ public class InFragment {
|
|||
|
||||
boolean allowNull = false;
|
||||
|
||||
for (Object value : values) {
|
||||
if (NULL.equals(value)) {
|
||||
for ( Object value : values ) {
|
||||
if ( NULL.equals( value ) ) {
|
||||
allowNull = true;
|
||||
} else {
|
||||
if (NOT_NULL.equals(value)) {
|
||||
throw new IllegalArgumentException("not null makes no sense for in expression");
|
||||
}
|
||||
else {
|
||||
if ( NOT_NULL.equals( value ) ) {
|
||||
throw new IllegalArgumentException( "not null makes no sense for in expression" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allowNull) {
|
||||
buf.append('(').append(columnName).append(" is null or ").append(columnName).append(" in (");
|
||||
} else {
|
||||
buf.append(columnName).append(" in (");
|
||||
if ( allowNull ) {
|
||||
buf.append( '(' ).append( columnName ).append( " is null or " ).append( columnName ).append( " in (" );
|
||||
}
|
||||
else {
|
||||
buf.append( columnName ).append( " in (" );
|
||||
}
|
||||
|
||||
for (Object value : values) {
|
||||
if ( ! NULL.equals(value) ) {
|
||||
buf.append(value);
|
||||
buf.append(", ");
|
||||
for ( Object value : values ) {
|
||||
if ( !NULL.equals( value ) ) {
|
||||
buf.append( value );
|
||||
buf.append( ", " );
|
||||
}
|
||||
}
|
||||
|
||||
buf.setLength(buf.length() - 2);
|
||||
buf.setLength( buf.length() - 2 );
|
||||
|
||||
if (allowNull) {
|
||||
buf.append("))");
|
||||
} else {
|
||||
buf.append(')');
|
||||
if ( allowNull ) {
|
||||
buf.append( "))" );
|
||||
}
|
||||
else {
|
||||
buf.append( ')' );
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
|
|
|
@ -30,6 +30,7 @@ import java.sql.ResultSet;
|
|||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.dom4j.Node;
|
||||
|
||||
|
@ -510,6 +511,14 @@ public class AnyType extends AbstractType implements CompositeType, AssociationT
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOnCondition(
|
||||
String alias,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters,
|
||||
Set<String> treatAsDeclarations) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to externalize discrimination per a given identifier. For example, when writing to
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
package org.hibernate.type;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
@ -80,6 +81,12 @@ public interface AssociationType extends Type {
|
|||
public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters)
|
||||
throws MappingException;
|
||||
|
||||
/**
|
||||
* Get the "filtering" SQL fragment that is applied in the
|
||||
* SQL on clause, in addition to the usual join condition
|
||||
*/
|
||||
public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters, Set<String> treatAsDeclarations);
|
||||
|
||||
/**
|
||||
* Do we dirty check this association, even when there are
|
||||
* no columns to be updated?
|
||||
|
|
|
@ -33,6 +33,7 @@ import java.util.HashMap;
|
|||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
|
@ -694,6 +695,15 @@ public abstract class CollectionType extends AbstractType implements Association
|
|||
return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOnCondition(
|
||||
String alias,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters,
|
||||
Set<String> treatAsDeclarations) {
|
||||
return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters, treatAsDeclarations );
|
||||
}
|
||||
|
||||
/**
|
||||
* instantiate a collection wrapper (called when loading an object)
|
||||
*
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.io.Serializable;
|
|||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.dom4j.Element;
|
||||
import org.dom4j.Node;
|
||||
|
@ -466,13 +467,22 @@ public abstract class EntityType extends AbstractType implements AssociationType
|
|||
}
|
||||
}
|
||||
|
||||
public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters)
|
||||
throws MappingException {
|
||||
if ( isReferenceToPrimaryKey() ) { //TODO: this is a bit arbitrary, expose a switch to the user?
|
||||
@Override
|
||||
public String getOnCondition(String alias, SessionFactoryImplementor factory, Map enabledFilters) {
|
||||
return getOnCondition( alias, factory, enabledFilters, null );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getOnCondition(
|
||||
String alias,
|
||||
SessionFactoryImplementor factory,
|
||||
Map enabledFilters,
|
||||
Set<String> treatAsDeclarations) {
|
||||
if ( isReferenceToPrimaryKey() && ( treatAsDeclarations == null || treatAsDeclarations.isEmpty() ) ) {
|
||||
return "";
|
||||
}
|
||||
else {
|
||||
return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters );
|
||||
return getAssociatedJoinable( factory ).filterFragment( alias, enabledFilters, treatAsDeclarations );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,21 +23,288 @@
|
|||
*/
|
||||
package org.hibernate.test.jpa.ql;
|
||||
|
||||
import javax.persistence.DiscriminatorColumn;
|
||||
import javax.persistence.DiscriminatorType;
|
||||
import javax.persistence.DiscriminatorValue;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.Session;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.test.jpa.AbstractJPATest;
|
||||
import org.hibernate.testing.FailureExpected;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class TreatKeywordTest extends AbstractJPATest {
|
||||
public class TreatKeywordTest extends BaseCoreFunctionalTestCase {
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
JoinedEntity.class, JoinedEntitySubclass.class, JoinedEntitySubSubclass.class,
|
||||
JoinedEntitySubclass2.class, JoinedEntitySubSubclass2.class,
|
||||
DiscriminatorEntity.class, DiscriminatorEntitySubclass.class, DiscriminatorEntitySubSubclass.class
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsageInSelect() {
|
||||
public void testBasicUsageInJoin() {
|
||||
// todo : assert invalid naming of non-subclasses in TREAT statement
|
||||
Session s = openSession();
|
||||
s.createQuery( "from MyEntity e join treat(e.other as MySubclassEntity) o" ).list();
|
||||
s.createQuery( "from MyEntity e join TREAT(e.other as MySubclassEntity) o" ).list();
|
||||
|
||||
s.createQuery( "from DiscriminatorEntity e join treat(e.other as DiscriminatorEntitySubclass) o" ).list();
|
||||
s.createQuery( "from DiscriminatorEntity e join treat(e.other as DiscriminatorEntitySubSubclass) o" ).list();
|
||||
s.createQuery( "from DiscriminatorEntitySubclass e join treat(e.other as DiscriminatorEntitySubSubclass) o" ).list();
|
||||
|
||||
s.createQuery( "from JoinedEntity e join treat(e.other as JoinedEntitySubclass) o" ).list();
|
||||
s.createQuery( "from JoinedEntity e join treat(e.other as JoinedEntitySubSubclass) o" ).list();
|
||||
s.createQuery( "from JoinedEntitySubclass e join treat(e.other as JoinedEntitySubSubclass) o" ).list();
|
||||
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-8637" )
|
||||
public void testFilteringDiscriminatorSubclasses() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
DiscriminatorEntity root = new DiscriminatorEntity( 1, "root" );
|
||||
s.save( root );
|
||||
DiscriminatorEntitySubclass child = new DiscriminatorEntitySubclass( 2, "child", root );
|
||||
s.save( child );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
|
||||
// in select clause
|
||||
List result = s.createQuery( "select e from DiscriminatorEntity e" ).list();
|
||||
assertEquals( 2, result.size() );
|
||||
result = s.createQuery( "select treat (e as DiscriminatorEntitySubclass) from DiscriminatorEntity e" ).list();
|
||||
assertEquals( 1, result.size() );
|
||||
result = s.createQuery( "select treat (e as DiscriminatorEntitySubSubclass) from DiscriminatorEntity e" ).list();
|
||||
assertEquals( 0, result.size() );
|
||||
|
||||
// in join
|
||||
result = s.createQuery( "from DiscriminatorEntity e inner join e.other" ).list();
|
||||
assertEquals( 1, result.size() );
|
||||
result = s.createQuery( "from DiscriminatorEntity e inner join treat (e.other as DiscriminatorEntitySubclass)" ).list();
|
||||
assertEquals( 0, result.size() );
|
||||
result = s.createQuery( "from DiscriminatorEntity e inner join treat (e.other as DiscriminatorEntitySubSubclass)" ).list();
|
||||
assertEquals( 0, result.size() );
|
||||
|
||||
s.close();
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
s.delete( root );
|
||||
s.delete( child );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue( jiraKey = "HHH-8637" )
|
||||
public void testFilteringJoinedSubclasses() {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
JoinedEntity root = new JoinedEntity( 1, "root" );
|
||||
s.save( root );
|
||||
JoinedEntitySubclass child = new JoinedEntitySubclass( 2, "child", root );
|
||||
s.save( child );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
|
||||
// in the select clause which causes an implicit inclusion of subclass joins, the test here makes sure that
|
||||
// the TREAT-AS effects the join-type used.
|
||||
List result = s.createQuery( "select e from JoinedEntity e" ).list();
|
||||
assertEquals( 2, result.size() );
|
||||
result = s.createQuery( "select treat (e as JoinedEntitySubclass) from JoinedEntity e" ).list();
|
||||
assertEquals( 1, result.size() );
|
||||
result = s.createQuery( "select treat (e as JoinedEntitySubSubclass) from JoinedEntity e" ).list();
|
||||
assertEquals( 0, result.size() );
|
||||
|
||||
// in join
|
||||
result = s.createQuery( "from JoinedEntity e inner join e.other" ).list();
|
||||
assertEquals( 1, result.size() );
|
||||
result = s.createQuery( "from JoinedEntity e inner join treat (e.other as JoinedEntitySubclass)" ).list();
|
||||
assertEquals( 0, result.size() );
|
||||
result = s.createQuery( "from JoinedEntity e inner join treat (e.other as JoinedEntitySubSubclass)" ).list();
|
||||
assertEquals( 0, result.size() );
|
||||
|
||||
s.close();
|
||||
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
s.delete( child );
|
||||
s.delete( root );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
}
|
||||
|
||||
@Entity( name = "JoinedEntity" )
|
||||
@Table( name = "JoinedEntity" )
|
||||
@Inheritance( strategy = InheritanceType.JOINED )
|
||||
public static class JoinedEntity {
|
||||
@Id
|
||||
public Integer id;
|
||||
public String name;
|
||||
@ManyToOne( fetch = FetchType.LAZY )
|
||||
public JoinedEntity other;
|
||||
|
||||
public JoinedEntity() {
|
||||
}
|
||||
|
||||
public JoinedEntity(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public JoinedEntity(Integer id, String name, JoinedEntity other) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.other = other;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "JoinedEntitySubclass" )
|
||||
@Table( name = "JoinedEntitySubclass" )
|
||||
public static class JoinedEntitySubclass extends JoinedEntity {
|
||||
public JoinedEntitySubclass() {
|
||||
}
|
||||
|
||||
public JoinedEntitySubclass(Integer id, String name) {
|
||||
super( id, name );
|
||||
}
|
||||
|
||||
public JoinedEntitySubclass(Integer id, String name, JoinedEntity other) {
|
||||
super( id, name, other );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "JoinedEntitySubSubclass" )
|
||||
@Table( name = "JoinedEntitySubSubclass" )
|
||||
public static class JoinedEntitySubSubclass extends JoinedEntitySubclass {
|
||||
public JoinedEntitySubSubclass() {
|
||||
}
|
||||
|
||||
public JoinedEntitySubSubclass(Integer id, String name) {
|
||||
super( id, name );
|
||||
}
|
||||
|
||||
public JoinedEntitySubSubclass(Integer id, String name, JoinedEntity other) {
|
||||
super( id, name, other );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "JoinedEntitySubclass2" )
|
||||
@Table( name = "JoinedEntitySubclass2" )
|
||||
public static class JoinedEntitySubclass2 extends JoinedEntity {
|
||||
public JoinedEntitySubclass2() {
|
||||
}
|
||||
|
||||
public JoinedEntitySubclass2(Integer id, String name) {
|
||||
super( id, name );
|
||||
}
|
||||
|
||||
public JoinedEntitySubclass2(Integer id, String name, JoinedEntity other) {
|
||||
super( id, name, other );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "JoinedEntitySubSubclass2" )
|
||||
@Table( name = "JoinedEntitySubSubclass2" )
|
||||
public static class JoinedEntitySubSubclass2 extends JoinedEntitySubclass2 {
|
||||
public JoinedEntitySubSubclass2() {
|
||||
}
|
||||
|
||||
public JoinedEntitySubSubclass2(Integer id, String name) {
|
||||
super( id, name );
|
||||
}
|
||||
|
||||
public JoinedEntitySubSubclass2(Integer id, String name, JoinedEntity other) {
|
||||
super( id, name, other );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "DiscriminatorEntity" )
|
||||
@Table( name = "DiscriminatorEntity" )
|
||||
@Inheritance( strategy = InheritanceType.SINGLE_TABLE )
|
||||
@DiscriminatorColumn( name = "e_type", discriminatorType = DiscriminatorType.STRING )
|
||||
@DiscriminatorValue( "B" )
|
||||
public static class DiscriminatorEntity {
|
||||
@Id
|
||||
public Integer id;
|
||||
public String name;
|
||||
@ManyToOne( fetch = FetchType.LAZY )
|
||||
public DiscriminatorEntity other;
|
||||
|
||||
public DiscriminatorEntity() {
|
||||
}
|
||||
|
||||
public DiscriminatorEntity(Integer id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public DiscriminatorEntity(
|
||||
Integer id,
|
||||
String name,
|
||||
DiscriminatorEntity other) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.other = other;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "DiscriminatorEntitySubclass" )
|
||||
@DiscriminatorValue( "S" )
|
||||
public static class DiscriminatorEntitySubclass extends DiscriminatorEntity {
|
||||
public DiscriminatorEntitySubclass() {
|
||||
}
|
||||
|
||||
public DiscriminatorEntitySubclass(Integer id, String name) {
|
||||
super( id, name );
|
||||
}
|
||||
|
||||
public DiscriminatorEntitySubclass(
|
||||
Integer id,
|
||||
String name,
|
||||
DiscriminatorEntity other) {
|
||||
super( id, name, other );
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "DiscriminatorEntitySubSubclass" )
|
||||
@DiscriminatorValue( "SS" )
|
||||
public static class DiscriminatorEntitySubSubclass extends DiscriminatorEntitySubclass {
|
||||
public DiscriminatorEntitySubSubclass() {
|
||||
}
|
||||
|
||||
public DiscriminatorEntitySubSubclass(Integer id, String name) {
|
||||
super( id, name );
|
||||
}
|
||||
|
||||
public DiscriminatorEntitySubSubclass(
|
||||
Integer id,
|
||||
String name,
|
||||
DiscriminatorEntity other) {
|
||||
super( id, name, other );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue