/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.loader;
import java.util.
Collections;
import java.util.
List;
import java.util.
Map;
import org.hibernate.
MappingException;
import org.hibernate.engine.internal.
JoinHelper;
import org.hibernate.engine.spi.
SessionFactoryImplementor;
import org.hibernate.persister.collection.
QueryableCollection;
import org.hibernate.persister.entity.
Joinable;
import org.hibernate.sql.
JoinFragment;
import org.hibernate.sql.
JoinType;
import org.hibernate.type.
AssociationType;
import org.hibernate.type.
EntityType;
/**
* Part of the Hibernate SQL rendering internals. This class represents
* a joinable association.
*
* @author Gavin King
*/
public final class
OuterJoinableAssociation {
private final
PropertyPath propertyPath;
private final
AssociationType joinableType;
private final
Joinable joinable;
private final
String lhsAlias; // belong to other persister
private final
String[]
lhsColumns; // belong to other persister
private final
String rhsAlias;
private final
String[]
rhsColumns;
private final
JoinType joinType;
private final
String on;
private final
Map enabledFilters;
private final boolean
hasRestriction;
public static
OuterJoinableAssociation createRoot(
AssociationType joinableType,
String alias,
SessionFactoryImplementor factory) {
return new
OuterJoinableAssociation(
new
PropertyPath(),
joinableType,
null,
null,
alias,
JoinType.
LEFT_OUTER_JOIN,
null,
false,
factory,
Collections.
EMPTY_MAP
);
}
public
OuterJoinableAssociation(
PropertyPath propertyPath,
AssociationType joinableType,
String lhsAlias,
String[]
lhsColumns,
String rhsAlias,
JoinType joinType,
String withClause,
boolean
hasRestriction,
SessionFactoryImplementor factory,
Map enabledFilters) throws
MappingException {
this.
propertyPath =
propertyPath;
this.
joinableType =
joinableType;
this.
lhsAlias =
lhsAlias;
this.
lhsColumns =
lhsColumns;
this.
rhsAlias =
rhsAlias;
this.
joinType =
joinType;
this.
joinable =
joinableType.
getAssociatedJoinable(
factory );
this.
rhsColumns =
JoinHelper.
getRHSColumnNames(
joinableType,
factory );
this.
on =
joinableType.
getOnCondition(
rhsAlias,
factory,
enabledFilters )
+ (
withClause == null ||
withClause.
trim().
length() == 0 ? "" : " and ( " +
withClause + " )" );
this.
hasRestriction =
hasRestriction;
this.
enabledFilters =
enabledFilters; // needed later for many-to-many/filter application
}
public
PropertyPath getPropertyPath() {
return
propertyPath;
}
public
JoinType getJoinType() {
return
joinType;
}
public
String getLhsAlias() {
return
lhsAlias;
}
public
String getRHSAlias() {
return
rhsAlias;
}
public
String getRhsAlias() {
return
rhsAlias;
}
private boolean
isOneToOne() {
if (
joinableType.
isEntityType() ) {
EntityType etype = (
EntityType)
joinableType;
return
etype.
isOneToOne() /*&& etype.isReferenceToPrimaryKey()*/;
}
else {
return false;
}
}
public
AssociationType getJoinableType() {
return
joinableType;
}
public
String getRHSUniqueKeyName() {
return
joinableType.
getRHSUniqueKeyPropertyName();
}
public boolean
isCollection() {
return
joinableType.
isCollectionType();
}
public
Joinable getJoinable() {
return
joinable;
}
public boolean
hasRestriction() {
return
hasRestriction;
}
public int
getOwner(final
List associations) {
if (
isOneToOne() ||
isCollection() ) {
return
getPosition(
lhsAlias,
associations );
}
else {
return -1;
}
}
/**
* Get the position of the join with the given alias in the
* list of joins
*/
private static int
getPosition(
String lhsAlias,
List associations) {
int
result = 0;
for (
Object association :
associations ) {
final
OuterJoinableAssociation oj = (
OuterJoinableAssociation)
association;
if (
oj.
getJoinable().
consumesEntityAlias() /*|| oj.getJoinable().consumesCollectionAlias() */ ) {
if (
oj.
rhsAlias.
equals(
lhsAlias ) ) {
return
result;
}
result++;
}
}
return -1;
}
public void
addJoins(
JoinFragment outerjoin) throws
MappingException {
outerjoin.
addJoin(
joinable.
getTableName(),
rhsAlias,
lhsColumns,
rhsColumns,
joinType,
on
);
outerjoin.
addJoins(
joinable.
fromJoinFragment(
rhsAlias, false, true ),
joinable.
whereJoinFragment(
rhsAlias, false, true )
);
}
public void
validateJoin(
String path) throws
MappingException {
if (
rhsColumns == null ||
lhsColumns == null
||
lhsColumns.length !=
rhsColumns.length ||
lhsColumns.length == 0 ) {
throw new
MappingException( "invalid join columns for association: " +
path );
}
}
public boolean
isManyToManyWith(
OuterJoinableAssociation other) {
if (
joinable.
isCollection() ) {
QueryableCollection persister = (
QueryableCollection)
joinable;
if (
persister.
isManyToMany() ) {
return
persister.
getElementType() ==
other.
getJoinableType();
}
}
return false;
}
public void
addManyToManyJoin(
JoinFragment outerjoin,
QueryableCollection collection) throws
MappingException {
String manyToManyFilter =
collection.
getManyToManyFilterFragment(
rhsAlias,
enabledFilters );
String condition = "".
equals(
manyToManyFilter )
?
on
: "".
equals(
on )
?
manyToManyFilter
:
on + " and " +
manyToManyFilter;
outerjoin.
addJoin(
joinable.
getTableName(),
rhsAlias,
lhsColumns,
rhsColumns,
joinType,
condition
);
outerjoin.
addJoins(
joinable.
fromJoinFragment(
rhsAlias, false, true ),
joinable.
whereJoinFragment(
rhsAlias, false, true )
);
}
}