Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,12 @@ include::{example-dir-mapping}/basic/SubselectTest.java[tag=mapping-Subselect-en
----
====

[NOTE]
====
The underlying `@Subselect` SQL query supports standard Hibernate placeholders ( see <<chapters/query/native/Native.adoc#sql-global-catalog-schema,Catalog and schema in SQL queries>> ).
Global settings can be overridden using the `schema` or `catalog` attributes of the `@Table` annotation.
====

If we add a new `AccountTransaction` entity and refresh the `AccountSummary` entity, the balance is updated accordingly:

[[mapping-Subselect-refresh-find-example]]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,14 @@ public interface SqlStringGenerationContext {
Identifier getDefaultCatalog();

/**
* @param explicitCatalogOrNull An explicitly configured catalog, or {@code null}.
* @return The given identifier if non-{@code null}, or the default catalog otherwise.
* Interpret the incoming catalog, returning the incoming value if it is non-null.
* Otherwise, returns the current {@linkplain #getDefaultCatalog() default catalog}.
*
* @apiNote May return {@code null} if {@linkplain #getDefaultCatalog() default catalog} is {@code null}.
*/
Identifier catalogWithDefault(Identifier explicitCatalogOrNull);
default Identifier catalogWithDefault(Identifier explicitCatalogOrNull) {
return explicitCatalogOrNull != null ? explicitCatalogOrNull : getDefaultCatalog();
}

/**
* @return The default schema, used for table/sequence names that do not explicitly mention a schema.
Expand All @@ -54,10 +58,14 @@ public interface SqlStringGenerationContext {
Identifier getDefaultSchema();

/**
* @param explicitSchemaOrNull An explicitly configured schema, or {@code null}.
* @return The given identifier if non-{@code null}, or the default schema otherwise.
* Interpret the incoming schema, returning the incoming value if it is non-null.
* Otherwise, returns the current {@linkplain #getDefaultSchema() default schema}.
*
* @apiNote May return {@code null} if {@linkplain #getDefaultSchema() default schema} is {@code null}.
*/
Identifier schemaWithDefault(Identifier explicitSchemaOrNull);
default Identifier schemaWithDefault(Identifier explicitSchemaOrNull) {
return explicitSchemaOrNull != null ? explicitSchemaOrNull : getDefaultSchema();
}

/**
* Render a formatted a table name
Expand Down Expand Up @@ -101,4 +109,49 @@ public interface SqlStringGenerationContext {
* @return {@code true} if and only if this is a migration
*/
boolean isMigration();

/**
* Apply default catalog and schema, if necessary, to the given name. May return a new reference.
*/
default QualifiedTableName withDefaults(QualifiedTableName name) {
if ( name.getCatalogName() == null && getDefaultCatalog() != null
|| name.getSchemaName() == null && getDefaultSchema() != null ) {
return new QualifiedTableName(
catalogWithDefault( name.getCatalogName() ),
schemaWithDefault( name.getSchemaName() ),
name.getTableName()
);
}
return name;
}

/**
* Apply default catalog and schema, if necessary, to the given name. May return a new reference.
*/
default QualifiedSequenceName withDefaults(QualifiedSequenceName name) {
if ( name.getCatalogName() == null && getDefaultCatalog() != null
|| name.getSchemaName() == null && getDefaultSchema() != null ) {
return new QualifiedSequenceName(
catalogWithDefault( name.getCatalogName() ),
schemaWithDefault( name.getSchemaName() ),
name.getSequenceName()
);
}
return name;
}

/**
* Apply default catalog and schema, if necessary, to the given name. May return a new reference.
*/
default QualifiedName withDefaults(QualifiedName name) {
if ( name.getCatalogName() == null && getDefaultCatalog() != null
|| name.getSchemaName() == null && getDefaultSchema() != null ) {
return new QualifiedNameImpl(
catalogWithDefault( name.getCatalogName() ),
schemaWithDefault( name.getSchemaName() ),
name.getObjectName()
);
}
return name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,48 +146,11 @@ public Identifier getDefaultCatalog() {
return defaultCatalog;
}

@Override
public Identifier catalogWithDefault(Identifier explicitCatalogOrNull) {
return explicitCatalogOrNull != null ? explicitCatalogOrNull : defaultCatalog;
}

@Override
public Identifier getDefaultSchema() {
return defaultSchema;
}

@Override
public Identifier schemaWithDefault(Identifier explicitSchemaOrNull) {
return explicitSchemaOrNull != null ? explicitSchemaOrNull : defaultSchema;
}

private QualifiedTableName withDefaults(QualifiedTableName name) {
if ( name.getCatalogName() == null && defaultCatalog != null
|| name.getSchemaName() == null && defaultSchema != null ) {
return new QualifiedTableName( catalogWithDefault( name.getCatalogName() ),
schemaWithDefault( name.getSchemaName() ), name.getTableName() );
}
return name;
}

private QualifiedSequenceName withDefaults(QualifiedSequenceName name) {
if ( name.getCatalogName() == null && defaultCatalog != null
|| name.getSchemaName() == null && defaultSchema != null ) {
return new QualifiedSequenceName( catalogWithDefault( name.getCatalogName() ),
schemaWithDefault( name.getSchemaName() ), name.getSequenceName() );
}
return name;
}

private QualifiedName withDefaults(QualifiedName name) {
if ( name.getCatalogName() == null && defaultCatalog != null
|| name.getSchemaName() == null && defaultSchema != null ) {
return new QualifiedSequenceName( catalogWithDefault( name.getCatalogName() ),
schemaWithDefault( name.getSchemaName() ), name.getObjectName() );
}
return name;
}

@Override
public String format(QualifiedTableName qualifiedName) {
return qualifiedObjectNameFormatter.format( withDefaults( qualifiedName ), dialect );
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4620,9 +4620,21 @@ protected int determineTableNumberForColumn(String columnName) {
}

protected String determineTableName(Table table) {
return MappingModelCreationHelper.getTableIdentifierExpression( table, factory );
if ( table.getSubselect() != null ) {
final SQLQueryParser sqlQueryParser = new SQLQueryParser(
table.getSubselect(),
null,
// NOTE : this allows finer control over catalog and schema used for placeholder
// handling (`{h-catalog}`, `{h-schema}`, `{h-domain}`)
new ExplicitSqlStringGenerationContext( table.getCatalog(), table.getSchema(), factory )
);
return "( " + sqlQueryParser.process() + " )";
}

return factory.getSqlStringGenerationContext().format( table.getQualifiedTableName() );
}


@Override
public EntityEntryFactory getEntityEntryFactory() {
return this.entityEntryFactory;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.persister.entity;

import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.QualifiedName;
import org.hibernate.boot.model.relational.QualifiedSequenceName;
import org.hibernate.boot.model.relational.QualifiedTableName;
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.engine.jdbc.env.spi.QualifiedObjectNameFormatter;
import org.hibernate.engine.spi.SessionFactoryImplementor;

/**
* SqlStringGenerationContext implementation with support for overriding the
* default catalog and schema
*
* @author Steve Ebersole
*/
public class ExplicitSqlStringGenerationContext implements SqlStringGenerationContext {
private final SessionFactoryImplementor factory;
private final Identifier defaultCatalog;
private final Identifier defaultSchema;

public ExplicitSqlStringGenerationContext(
String defaultCatalog,
String defaultSchema,
SessionFactoryImplementor factory) {
this.factory = factory;
this.defaultCatalog = defaultCatalog != null
? toIdentifier( defaultCatalog )
: toIdentifier( factory.getSessionFactoryOptions().getDefaultCatalog() );
this.defaultSchema = defaultSchema != null
? toIdentifier( defaultSchema )
: toIdentifier( factory.getSessionFactoryOptions().getDefaultSchema() );
}

@Override
public Dialect getDialect() {
return factory.getJdbcServices().getDialect();
}

@Override
public Identifier toIdentifier(String text) {
return factory.getJdbcServices().getJdbcEnvironment().getIdentifierHelper().toIdentifier( text );
}

@Override
public Identifier getDefaultCatalog() {
return defaultCatalog;
}

@Override
public Identifier getDefaultSchema() {
return defaultSchema;
}

@Override
public String format(QualifiedTableName qualifiedName) {
return nameFormater().format( withDefaults( qualifiedName ), getDialect() );
}

private QualifiedObjectNameFormatter nameFormater() {
final JdbcEnvironment jdbcEnvironment = factory.getJdbcServices().getJdbcEnvironment();
//noinspection deprecation
return jdbcEnvironment.getQualifiedObjectNameFormatter();
}

@Override
public String format(QualifiedSequenceName qualifiedName) {
return nameFormater().format( withDefaults( qualifiedName ), getDialect() );
}

@Override
public String format(QualifiedName qualifiedName) {
return nameFormater().format( withDefaults( qualifiedName ), getDialect() );
}

@Override
public String formatWithoutCatalog(QualifiedSequenceName qualifiedName) {
QualifiedSequenceName nameToFormat;
if ( qualifiedName.getCatalogName() != null
|| qualifiedName.getSchemaName() == null && defaultSchema != null ) {
nameToFormat = new QualifiedSequenceName( null,
schemaWithDefault( qualifiedName.getSchemaName() ), qualifiedName.getSequenceName() );
}
else {
nameToFormat = qualifiedName;
}
return nameFormater().format( nameToFormat, getDialect() );
}

@Override
public boolean isMigration() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
*/
package org.hibernate.query.sql.internal;

import java.util.Map;

import org.hibernate.QueryException;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
Expand All @@ -14,6 +12,8 @@
import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister;

import java.util.Map;

/**
* Substitutes escape sequences of form {@code {alias}},
* {@code {alias.field}}, and {@code {alias.*}} in a
Expand All @@ -25,11 +25,11 @@
* @author Paul Benedict
*/
public class SQLQueryParser {

private final SessionFactoryImplementor factory;
private final String originalQueryString;
private final ParserContext context;

private final SqlStringGenerationContext sqlStringGenerationContext;

private long aliasesFound;

public interface ParserContext {
Expand All @@ -43,9 +43,16 @@ public interface ParserContext {
}

public SQLQueryParser(String queryString, ParserContext context, SessionFactoryImplementor factory) {
this( queryString, context, factory.getSqlStringGenerationContext() );
}

public SQLQueryParser(
String queryString,
ParserContext context,
SqlStringGenerationContext sqlStringGenerationContext) {
this.originalQueryString = queryString;
this.context = context;
this.factory = factory;
this.sqlStringGenerationContext = sqlStringGenerationContext;
}

public boolean queryHasAliases() {
Expand Down Expand Up @@ -169,10 +176,9 @@ else if ( context.isEntityAlias( aliasName ) ) {
}

private void handlePlaceholder(String token, StringBuilder result) {
final SqlStringGenerationContext context = factory.getSqlStringGenerationContext();
final Identifier defaultCatalog = context.getDefaultCatalog();
final Identifier defaultSchema = context.getDefaultSchema();
final Dialect dialect = context.getDialect();
final Identifier defaultCatalog = sqlStringGenerationContext.getDefaultCatalog();
final Identifier defaultSchema = sqlStringGenerationContext.getDefaultSchema();
final Dialect dialect = sqlStringGenerationContext.getDialect();
switch (token) {
case "h-domain":
if ( defaultCatalog != null ) {
Expand Down
Loading
Loading