HHH-13763 : Update all load-by-key handling to use SQL AST
SingleUniqueKeyEntityLoader
This commit is contained in:
parent
0a41ac8466
commit
e112d9631e
|
@ -6,17 +6,38 @@
|
|||
*/
|
||||
package org.hibernate.loader.ast.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.NotYetImplementedFor6Exception;
|
||||
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.loader.ast.spi.SingleUniqueKeyEntityLoader;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.SingularAttributeMapping;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.query.spi.QueryParameterBindings;
|
||||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstTranslatorFactory;
|
||||
import org.hibernate.sql.ast.tree.expression.JdbcParameter;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
|
||||
import org.hibernate.sql.exec.spi.Callback;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBinding;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.exec.spi.JdbcSelect;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SingleUniqueKeyEntityLoaderStandard implements SingleUniqueKeyEntityLoader {
|
||||
public class SingleUniqueKeyEntityLoaderStandard<T> implements SingleUniqueKeyEntityLoader<T> {
|
||||
private final EntityMappingType entityDescriptor;
|
||||
private final SingularAttributeMapping uniqueKeyAttribute;
|
||||
|
||||
|
@ -33,10 +54,172 @@ public class SingleUniqueKeyEntityLoaderStandard implements SingleUniqueKeyEntit
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object load(
|
||||
public T load(
|
||||
Object ukValue,
|
||||
LockOptions lockOptions,
|
||||
SharedSessionContractImplementor session) {
|
||||
throw new NotYetImplementedFor6Exception( getClass() );
|
||||
final SessionFactoryImplementor sessionFactory = session.getFactory();
|
||||
|
||||
// todo (6.0) : cache the SQL AST and JdbcParameters
|
||||
final List<JdbcParameter> jdbcParameters = new ArrayList<>();
|
||||
final SelectStatement sqlAst = LoaderSelectBuilder.createSelect(
|
||||
entityDescriptor,
|
||||
Collections.emptyList(),
|
||||
uniqueKeyAttribute,
|
||||
null,
|
||||
1,
|
||||
LoadQueryInfluencers.NONE,
|
||||
LockOptions.READ,
|
||||
jdbcParameters::add,
|
||||
sessionFactory
|
||||
);
|
||||
|
||||
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
||||
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
|
||||
|
||||
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAst );
|
||||
|
||||
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( jdbcParameters.size() );
|
||||
final Iterator<JdbcParameter> jdbcParamItr = jdbcParameters.iterator();
|
||||
uniqueKeyAttribute.visitJdbcValues(
|
||||
ukValue,
|
||||
Clause.WHERE,
|
||||
(jdbcValue, jdbcMapping) -> {
|
||||
assert jdbcParamItr.hasNext();
|
||||
final JdbcParameter jdbcParameter = jdbcParamItr.next();
|
||||
jdbcParameterBindings.addBinding(
|
||||
jdbcParameter,
|
||||
new JdbcParameterBinding() {
|
||||
@Override
|
||||
public JdbcMapping getBindType() {
|
||||
return jdbcMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBindValue() {
|
||||
return jdbcValue;
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
session
|
||||
);
|
||||
|
||||
final List<Object> list = sessionFactory.getJdbcServices().getJdbcSelectExecutor().list(
|
||||
jdbcSelect,
|
||||
jdbcParameterBindings,
|
||||
new ExecutionContext() {
|
||||
@Override
|
||||
public SharedSessionContractImplementor getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryOptions getQueryOptions() {
|
||||
return QueryOptions.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryParameterBindings getQueryParameterBindings() {
|
||||
return QueryParameterBindings.NO_PARAM_BINDINGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Callback getCallback() {
|
||||
return afterLoadAction -> {
|
||||
};
|
||||
}
|
||||
},
|
||||
row -> row[0]
|
||||
);
|
||||
|
||||
assert list.size() == 1;
|
||||
|
||||
//noinspection unchecked
|
||||
return (T) list.get( 0 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object resolveId(Object ukValue, SharedSessionContractImplementor session) {
|
||||
final SessionFactoryImplementor sessionFactory = session.getFactory();
|
||||
|
||||
// todo (6.0) : cache the SQL AST and JdbcParameters
|
||||
final List<JdbcParameter> jdbcParameters = new ArrayList<>();
|
||||
final SelectStatement sqlAst = LoaderSelectBuilder.createSelect(
|
||||
entityDescriptor,
|
||||
Collections.singletonList( entityDescriptor.getIdentifierMapping() ),
|
||||
uniqueKeyAttribute,
|
||||
null,
|
||||
1,
|
||||
LoadQueryInfluencers.NONE,
|
||||
LockOptions.READ,
|
||||
jdbcParameters::add,
|
||||
sessionFactory
|
||||
);
|
||||
|
||||
final JdbcServices jdbcServices = sessionFactory.getJdbcServices();
|
||||
final JdbcEnvironment jdbcEnvironment = jdbcServices.getJdbcEnvironment();
|
||||
final SqlAstTranslatorFactory sqlAstTranslatorFactory = jdbcEnvironment.getSqlAstTranslatorFactory();
|
||||
|
||||
final JdbcSelect jdbcSelect = sqlAstTranslatorFactory.buildSelectTranslator( sessionFactory ).translate( sqlAst );
|
||||
|
||||
final JdbcParameterBindings jdbcParameterBindings = new JdbcParameterBindingsImpl( jdbcParameters.size() );
|
||||
final Iterator<JdbcParameter> jdbcParamItr = jdbcParameters.iterator();
|
||||
uniqueKeyAttribute.visitJdbcValues(
|
||||
ukValue,
|
||||
Clause.WHERE,
|
||||
(jdbcValue, jdbcMapping) -> {
|
||||
assert jdbcParamItr.hasNext();
|
||||
final JdbcParameter jdbcParameter = jdbcParamItr.next();
|
||||
jdbcParameterBindings.addBinding(
|
||||
jdbcParameter,
|
||||
new JdbcParameterBinding() {
|
||||
@Override
|
||||
public JdbcMapping getBindType() {
|
||||
return jdbcMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getBindValue() {
|
||||
return jdbcValue;
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
session
|
||||
);
|
||||
|
||||
final List<Object> list = sessionFactory.getJdbcServices().getJdbcSelectExecutor().list(
|
||||
jdbcSelect,
|
||||
jdbcParameterBindings,
|
||||
new ExecutionContext() {
|
||||
@Override
|
||||
public SharedSessionContractImplementor getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryOptions getQueryOptions() {
|
||||
return QueryOptions.NONE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public QueryParameterBindings getQueryParameterBindings() {
|
||||
return QueryParameterBindings.NO_PARAM_BINDINGS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Callback getCallback() {
|
||||
return afterLoadAction -> {
|
||||
};
|
||||
}
|
||||
},
|
||||
row -> row[0]
|
||||
);
|
||||
|
||||
assert list.size() == 1;
|
||||
|
||||
return list.get( 0 );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,4 +20,9 @@ public interface SingleUniqueKeyEntityLoader<T> extends SingleEntityLoader {
|
|||
*/
|
||||
@Override
|
||||
T load(Object ukValue, LockOptions lockOptions, SharedSessionContractImplementor session);
|
||||
|
||||
/**
|
||||
* Resolve the matching id
|
||||
*/
|
||||
Object resolveId(Object key, SharedSessionContractImplementor session);
|
||||
}
|
||||
|
|
|
@ -1255,19 +1255,6 @@ public abstract class AbstractEntityPersister
|
|||
);
|
||||
}
|
||||
|
||||
private void resolveTableReferenceJoins(
|
||||
TableReference rootTableReference,
|
||||
SqlAliasBase sqlAliasBase,
|
||||
org.hibernate.sql.ast.JoinType joinType,
|
||||
Consumer<TableReferenceJoin> collector,
|
||||
SqlExpressionResolver sqlExpressionResolver) {
|
||||
for ( int i = 1; i < getSubclassTableSpan(); i++ ) {
|
||||
collector.accept(
|
||||
createTableReferenceJoin( i, rootTableReference, joinType, sqlAliasBase, sqlExpressionResolver )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyTableReferences(
|
||||
SqlAliasBase sqlAliasBase,
|
||||
|
@ -1817,8 +1804,7 @@ public abstract class AbstractEntityPersister
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object getIdByUniqueKey(Object key, String uniquePropertyName, SharedSessionContractImplementor session)
|
||||
throws HibernateException {
|
||||
public Object getIdByUniqueKey(Object key, String uniquePropertyName, SharedSessionContractImplementor session) {
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.tracef(
|
||||
"resolving unique key [%s] to identifier for entity [%s]",
|
||||
|
@ -1827,103 +1813,7 @@ public abstract class AbstractEntityPersister
|
|||
);
|
||||
}
|
||||
|
||||
int propertyIndex = getSubclassPropertyIndex( uniquePropertyName );
|
||||
if ( propertyIndex < 0 ) {
|
||||
throw new HibernateException(
|
||||
"Could not determine Type for property [" + uniquePropertyName + "] on entity [" + getEntityName() + "]"
|
||||
);
|
||||
}
|
||||
Type propertyType = getSubclassPropertyType( propertyIndex );
|
||||
|
||||
try {
|
||||
PreparedStatement ps = session
|
||||
.getJdbcCoordinator()
|
||||
.getStatementPreparer()
|
||||
.prepareStatement( generateIdByUniqueKeySelectString( uniquePropertyName ) );
|
||||
try {
|
||||
propertyType.nullSafeSet( ps, key, 1, session );
|
||||
ResultSet rs = session.getJdbcCoordinator().getResultSetReturn().extract( ps );
|
||||
try {
|
||||
//if there is no resulting row, return null
|
||||
if ( !rs.next() ) {
|
||||
return null;
|
||||
}
|
||||
return (Serializable) getIdentifierType().nullSafeGet( rs, getIdentifierAliases(), session, null );
|
||||
}
|
||||
finally {
|
||||
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( rs, ps );
|
||||
}
|
||||
}
|
||||
finally {
|
||||
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( ps );
|
||||
session.getJdbcCoordinator().afterStatementExecution();
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
||||
e,
|
||||
String.format(
|
||||
"could not resolve unique property [%s] to identifier for entity [%s]",
|
||||
uniquePropertyName,
|
||||
getEntityName()
|
||||
),
|
||||
getSQLSnapshotSelectString()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected String generateIdByUniqueKeySelectString(String uniquePropertyName) {
|
||||
Select select = new Select( getFactory().getDialect() );
|
||||
|
||||
if ( getFactory().getSessionFactoryOptions().isCommentsEnabled() ) {
|
||||
select.setComment( "resolve id by unique property [" + getEntityName() + "." + uniquePropertyName + "]" );
|
||||
}
|
||||
|
||||
final String rooAlias = getRootAlias();
|
||||
|
||||
select.setFromClause( fromTableFragment( rooAlias ) + fromJoinFragment( rooAlias, true, false ) );
|
||||
|
||||
SelectFragment selectFragment = new SelectFragment();
|
||||
selectFragment.addColumns( rooAlias, getIdentifierColumnNames(), getIdentifierAliases() );
|
||||
select.setSelectClause( selectFragment );
|
||||
|
||||
StringBuilder whereClauseBuffer = new StringBuilder();
|
||||
final int uniquePropertyIndex = getSubclassPropertyIndex( uniquePropertyName );
|
||||
final String uniquePropertyTableAlias = generateTableAlias(
|
||||
rooAlias,
|
||||
getSubclassPropertyTableNumber( uniquePropertyIndex )
|
||||
);
|
||||
String sep = "";
|
||||
for ( String columnTemplate : getSubclassPropertyColumnReaderTemplateClosure()[uniquePropertyIndex] ) {
|
||||
if ( columnTemplate == null ) {
|
||||
continue;
|
||||
}
|
||||
final String columnReference = StringHelper.replace(
|
||||
columnTemplate,
|
||||
Template.TEMPLATE,
|
||||
uniquePropertyTableAlias
|
||||
);
|
||||
whereClauseBuffer.append( sep ).append( columnReference ).append( "=?" );
|
||||
sep = " and ";
|
||||
}
|
||||
for ( String formulaTemplate : getSubclassPropertyFormulaTemplateClosure()[uniquePropertyIndex] ) {
|
||||
if ( formulaTemplate == null ) {
|
||||
continue;
|
||||
}
|
||||
final String formulaReference = StringHelper.replace(
|
||||
formulaTemplate,
|
||||
Template.TEMPLATE,
|
||||
uniquePropertyTableAlias
|
||||
);
|
||||
whereClauseBuffer.append( sep ).append( formulaReference ).append( "=?" );
|
||||
sep = " and ";
|
||||
}
|
||||
whereClauseBuffer.append( whereJoinFragment( rooAlias, true, false ) );
|
||||
|
||||
select.setWhereClause( whereClauseBuffer.toString() );
|
||||
|
||||
return select.setOuterJoins( "", "" ).toStatementString();
|
||||
return getUniqueKeyLoader( uniquePropertyName ).resolveId( key, session );
|
||||
}
|
||||
|
||||
|
||||
|
@ -2644,13 +2534,18 @@ public abstract class AbstractEntityPersister
|
|||
|
||||
}
|
||||
|
||||
private Map<SingularAttributeMapping, SingleUniqueKeyEntityLoader<?>> uniqueKeyLoadersNew;
|
||||
|
||||
public Object loadByUniqueKey(
|
||||
String propertyName,
|
||||
Object uniqueKey,
|
||||
SharedSessionContractImplementor session) throws HibernateException {
|
||||
final SingularAttributeMapping attribute = (SingularAttributeMapping) findSubPart( propertyName );
|
||||
return getUniqueKeyLoader( propertyName ).load( uniqueKey, LockOptions.READ, session );
|
||||
}
|
||||
|
||||
private Map<SingularAttributeMapping, SingleUniqueKeyEntityLoader<?>> uniqueKeyLoadersNew;
|
||||
|
||||
protected SingleUniqueKeyEntityLoader getUniqueKeyLoader(String attributeName) {
|
||||
final SingularAttributeMapping attribute = (SingularAttributeMapping) findSubPart( attributeName );
|
||||
final SingleUniqueKeyEntityLoader<?> existing;
|
||||
if ( uniqueKeyLoadersNew == null ) {
|
||||
uniqueKeyLoadersNew = new IdentityHashMap<>();
|
||||
|
@ -2666,6 +2561,7 @@ public abstract class AbstractEntityPersister
|
|||
|
||||
final SingleUniqueKeyEntityLoader loader = new SingleUniqueKeyEntityLoaderStandard( this, attribute );
|
||||
uniqueKeyLoadersNew.put( attribute, loader );
|
||||
|
||||
return loader;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,10 @@ import org.hibernate.dialect.Dialect;
|
|||
* Implementation of InsertSelect.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @deprecated (since 6.0) Use {@link org.hibernate.sql.ast.tree.insert.InsertSelectStatement} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public class InsertSelect {
|
||||
private String tableName;
|
||||
private String comment;
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
/*
|
||||
* 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.sql;
|
||||
|
||||
|
||||
/**
|
||||
* TODO : javadoc
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface SelectExpression {
|
||||
public String getExpression();
|
||||
public String getAlias();
|
||||
}
|
|
@ -1,103 +0,0 @@
|
|||
/*
|
||||
* 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.sql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Models a SELECT values lists. Eventually, rather than Strings, pass in the Column/Formula representations (something
|
||||
* like {@link org.hibernate.sql.ordering.antlr.ColumnReference}/{@link org.hibernate.sql.ordering.antlr.FormulaReference}
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class SelectValues {
|
||||
private static final Logger log = Logger.getLogger( SelectValues.class );
|
||||
|
||||
private static class SelectValue {
|
||||
private final String qualifier;
|
||||
private final String value;
|
||||
private final String alias;
|
||||
|
||||
private SelectValue(String qualifier, String value, String alias) {
|
||||
this.qualifier = qualifier;
|
||||
this.value = value;
|
||||
this.alias = alias;
|
||||
}
|
||||
}
|
||||
|
||||
private final Dialect dialect;
|
||||
private final ArrayList<SelectValue> selectValueList = new ArrayList<SelectValue>();
|
||||
|
||||
public SelectValues(Dialect dialect) {
|
||||
this.dialect = dialect;
|
||||
}
|
||||
|
||||
public SelectValues addColumns(String qualifier, String[] columnNames, String[] columnAliases) {
|
||||
for ( int i = 0; i < columnNames.length; i++ ) {
|
||||
if ( columnNames[i] != null ) {
|
||||
addColumn( qualifier, columnNames[i], columnAliases[i] );
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public SelectValues addColumn(String qualifier, String columnName, String columnAlias) {
|
||||
selectValueList.add( new SelectValue( qualifier, columnName, columnAlias ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
public SelectValues addParameter(int jdbcTypeCode, int length) {
|
||||
final String selectExpression = dialect.requiresCastingOfParametersInSelectClause()
|
||||
? dialect.cast( "?", jdbcTypeCode, length )
|
||||
: "?";
|
||||
selectValueList.add( new SelectValue( null, selectExpression, null ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
public SelectValues addParameter(int jdbcTypeCode, int precision, int scale) {
|
||||
final String selectExpression = dialect.requiresCastingOfParametersInSelectClause()
|
||||
? dialect.cast( "?", jdbcTypeCode, precision, scale )
|
||||
: "?";
|
||||
selectValueList.add( new SelectValue( null, selectExpression, null ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
public String render() {
|
||||
final StringBuilder buf = new StringBuilder( selectValueList.size() * 10 );
|
||||
final HashSet<String> uniqueAliases = new HashSet<String>();
|
||||
boolean firstExpression = true;
|
||||
for ( SelectValue selectValue : selectValueList ) {
|
||||
if ( selectValue.alias != null ) {
|
||||
if ( ! uniqueAliases.add( selectValue.alias ) ) {
|
||||
log.debug( "Skipping select-value with non-unique alias" );
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( firstExpression ) {
|
||||
firstExpression = false;
|
||||
}
|
||||
else {
|
||||
buf.append( ", " );
|
||||
}
|
||||
|
||||
if ( selectValue.qualifier != null ) {
|
||||
buf.append( selectValue.qualifier ).append( '.' );
|
||||
}
|
||||
buf.append( selectValue.value );
|
||||
if ( selectValue.alias != null ) {
|
||||
buf.append( " as " ).append( selectValue.alias );
|
||||
}
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue