HHH-7913 - Schema replacement in <subselect> / @Subselect

This commit is contained in:
Steve Ebersole 2024-11-20 16:31:42 -06:00
parent c8a66789d1
commit 21095565bc
8 changed files with 451 additions and 53 deletions

View File

@ -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: If we add a new `AccountTransaction` entity and refresh the `AccountSummary` entity, the balance is updated accordingly:
[[mapping-Subselect-refresh-find-example]] [[mapping-Subselect-refresh-find-example]]

View File

@ -40,10 +40,14 @@ public interface SqlStringGenerationContext {
Identifier getDefaultCatalog(); Identifier getDefaultCatalog();
/** /**
* @param explicitCatalogOrNull An explicitly configured catalog, or {@code null}. * Interpret the incoming catalog, returning the incoming value if it is non-null.
* @return The given identifier if non-{@code null}, or the default catalog otherwise. * 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. * @return The default schema, used for table/sequence names that do not explicitly mention a schema.
@ -54,10 +58,14 @@ public interface SqlStringGenerationContext {
Identifier getDefaultSchema(); Identifier getDefaultSchema();
/** /**
* @param explicitSchemaOrNull An explicitly configured schema, or {@code null}. * Interpret the incoming schema, returning the incoming value if it is non-null.
* @return The given identifier if non-{@code null}, or the default schema otherwise. * 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 * Render a formatted a table name
@ -101,4 +109,49 @@ public interface SqlStringGenerationContext {
* @return {@code true} if and only if this is a migration * @return {@code true} if and only if this is a migration
*/ */
boolean isMigration(); 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;
}
} }

View File

@ -146,48 +146,11 @@ public class SqlStringGenerationContextImpl
return defaultCatalog; return defaultCatalog;
} }
@Override
public Identifier catalogWithDefault(Identifier explicitCatalogOrNull) {
return explicitCatalogOrNull != null ? explicitCatalogOrNull : defaultCatalog;
}
@Override @Override
public Identifier getDefaultSchema() { public Identifier getDefaultSchema() {
return defaultSchema; 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 @Override
public String format(QualifiedTableName qualifiedName) { public String format(QualifiedTableName qualifiedName) {
return qualifiedObjectNameFormatter.format( withDefaults( qualifiedName ), dialect ); return qualifiedObjectNameFormatter.format( withDefaults( qualifiedName ), dialect );

View File

@ -4620,9 +4620,21 @@ public abstract class AbstractEntityPersister
} }
protected String determineTableName(Table table) { 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 @Override
public EntityEntryFactory getEntityEntryFactory() { public EntityEntryFactory getEntityEntryFactory() {
return this.entityEntryFactory; return this.entityEntryFactory;

View File

@ -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;
}
}

View File

@ -4,8 +4,6 @@
*/ */
package org.hibernate.query.sql.internal; package org.hibernate.query.sql.internal;
import java.util.Map;
import org.hibernate.QueryException; import org.hibernate.QueryException;
import org.hibernate.boot.model.naming.Identifier; import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.relational.SqlStringGenerationContext; import org.hibernate.boot.model.relational.SqlStringGenerationContext;
@ -14,6 +12,8 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.collection.CollectionPersister; import org.hibernate.persister.collection.CollectionPersister;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import java.util.Map;
/** /**
* Substitutes escape sequences of form {@code {alias}}, * Substitutes escape sequences of form {@code {alias}},
* {@code {alias.field}}, and {@code {alias.*}} in a * {@code {alias.field}}, and {@code {alias.*}} in a
@ -25,11 +25,11 @@ import org.hibernate.persister.entity.EntityPersister;
* @author Paul Benedict * @author Paul Benedict
*/ */
public class SQLQueryParser { public class SQLQueryParser {
private final SessionFactoryImplementor factory;
private final String originalQueryString; private final String originalQueryString;
private final ParserContext context; private final ParserContext context;
private final SqlStringGenerationContext sqlStringGenerationContext;
private long aliasesFound; private long aliasesFound;
public interface ParserContext { public interface ParserContext {
@ -43,9 +43,16 @@ public class SQLQueryParser {
} }
public SQLQueryParser(String queryString, ParserContext context, SessionFactoryImplementor factory) { 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.originalQueryString = queryString;
this.context = context; this.context = context;
this.factory = factory; this.sqlStringGenerationContext = sqlStringGenerationContext;
} }
public boolean queryHasAliases() { public boolean queryHasAliases() {
@ -169,10 +176,9 @@ public class SQLQueryParser {
} }
private void handlePlaceholder(String token, StringBuilder result) { private void handlePlaceholder(String token, StringBuilder result) {
final SqlStringGenerationContext context = factory.getSqlStringGenerationContext(); final Identifier defaultCatalog = sqlStringGenerationContext.getDefaultCatalog();
final Identifier defaultCatalog = context.getDefaultCatalog(); final Identifier defaultSchema = sqlStringGenerationContext.getDefaultSchema();
final Identifier defaultSchema = context.getDefaultSchema(); final Dialect dialect = sqlStringGenerationContext.getDialect();
final Dialect dialect = context.getDialect();
switch (token) { switch (token) {
case "h-domain": case "h-domain":
if ( defaultCatalog != null ) { if ( defaultCatalog != null ) {

View File

@ -0,0 +1,101 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.sql;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.entity.ExplicitSqlStringGenerationContext;
import org.hibernate.query.sql.internal.SQLQueryParser;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.RequiresDialect;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SQLQueryParser}
*
* @author Steve Ebersole
*/
@SuppressWarnings("JUnitMalformedDeclaration")
public class SQLQueryParserUnitTests {
@Test
@DomainModel
@SessionFactory
@RequiresDialect(H2Dialect.class)
void testDomainParsing(SessionFactoryScope scope) {
final SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
final String sqlQuery = "select id, name from {h-domain}the_table";
final String full = processSqlString( sqlQuery, "my_catalog", "my_schema", sessionFactory );
assertThat( full ).endsWith( " my_catalog.my_schema.the_table" );
final String catalogOnly = processSqlString( sqlQuery, "my_catalog", null, sessionFactory );
assertThat( catalogOnly ).endsWith( " my_catalog.the_table" );
final String schemaOnly = processSqlString( sqlQuery, null, "my_schema", sessionFactory );
assertThat( schemaOnly ).endsWith( " my_schema.the_table" );
final String none = processSqlString( sqlQuery, null, null, sessionFactory );
assertThat( none ).endsWith( " the_table" );
}
@Test
@DomainModel
@SessionFactory
@RequiresDialect(H2Dialect.class)
void testCatalogParsing(SessionFactoryScope scope) {
final SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
final String sqlQuery = "select id, name from {h-catalog}the_table";
final String full = processSqlString( sqlQuery, "my_catalog", "my_schema", sessionFactory );
assertThat( full ).endsWith( " my_catalog.the_table" );
final String catalogOnly = processSqlString( sqlQuery, "my_catalog", null, sessionFactory );
assertThat( catalogOnly ).endsWith( " my_catalog.the_table" );
final String schemaOnly = processSqlString( sqlQuery, null, "my_schema", sessionFactory );
assertThat( schemaOnly ).endsWith( " the_table" );
final String none = processSqlString( sqlQuery, null, null, sessionFactory );
assertThat( none ).endsWith( " the_table" );
}
@Test
@DomainModel
@SessionFactory
@RequiresDialect(H2Dialect.class)
void testSchemaParsing(SessionFactoryScope scope) {
final SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
final String sqlQuery = "select id, name from {h-schema}the_table";
final String full = processSqlString( sqlQuery, "my_catalog", "my_schema", sessionFactory );
assertThat( full ).endsWith( " my_schema.the_table" );
final String catalogOnly = processSqlString( sqlQuery, "my_catalog", null, sessionFactory );
assertThat( catalogOnly ).endsWith( " the_table" );
final String schemaOnly = processSqlString( sqlQuery, null, "my_schema", sessionFactory );
assertThat( schemaOnly ).endsWith( " my_schema.the_table" );
final String none = processSqlString( sqlQuery, null, null, sessionFactory );
assertThat( none ).endsWith( " the_table" );
}
private static String processSqlString(
String sqlQuery,
String catalogName,
String schemaName,
SessionFactoryImplementor sessionFactory) {
// Use a custom SqlStringGenerationContext to integrate the catalog and schema
final ExplicitSqlStringGenerationContext stringGenerationContext
= new ExplicitSqlStringGenerationContext( catalogName, schemaName, sessionFactory );
return new SQLQueryParser( sqlQuery, null, stringGenerationContext ).process();
}
}

View File

@ -0,0 +1,157 @@
/*
* SPDX-License-Identifier: LGPL-2.1-or-later
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.subselect;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import org.hibernate.annotations.Subselect;
import org.hibernate.cfg.MappingSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.metamodel.spi.MappingMetamodelImplementor;
import org.hibernate.metamodel.spi.RuntimeMetamodelsImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Simple tests that catalog/schema placeholders in {@link org.hibernate.annotations.Subselect}
* mappings are handled correctly.
*
* @author Steve Ebersole
*/
@SuppressWarnings("JUnitMalformedDeclaration")
public class SubselectWithPlaceholdersUnitTests {
@Test
@DomainModel(annotatedClasses = {EntityInCatalog.class, EntityInCatalogAndSchema.class, EntityInSchema.class, EntityInLocalCatalog.class})
@SessionFactory(exportSchema = false)
void testPlaceholdersWithNone(SessionFactoryScope scope) {
final RuntimeMetamodelsImplementor runtimeMetamodels = scope.getSessionFactory().getRuntimeMetamodels();
final MappingMetamodelImplementor mappingMetamodel = runtimeMetamodels.getMappingMetamodel();
final EntityPersister entityInCatalogDescriptor = mappingMetamodel.getEntityDescriptor( EntityInCatalog.class );
assertThat( entityInCatalogDescriptor.getTableName() ).endsWith( " from table_in_catalog )" );
final EntityPersister entityInSchemaDescriptor = mappingMetamodel.getEntityDescriptor( EntityInSchema.class );
assertThat( entityInSchemaDescriptor.getTableName() ).endsWith( " from table_in_schema )" );
final EntityPersister entityInCatalogAndSchemaDescriptor = mappingMetamodel.getEntityDescriptor( EntityInCatalogAndSchema.class );
assertThat( entityInCatalogAndSchemaDescriptor.getTableName() ).endsWith( " from table_in_both )" );
}
@Test
@ServiceRegistry(settings = {
@Setting(name= MappingSettings.DEFAULT_CATALOG, value = "the_catalog")
})
@DomainModel(annotatedClasses = {EntityInCatalog.class, EntityInCatalogAndSchema.class, EntityInSchema.class, EntityInLocalCatalog.class})
@SessionFactory(exportSchema = false)
void testPlaceholdersWithCatalog(SessionFactoryScope scope) {
final SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
final RuntimeMetamodelsImplementor runtimeMetamodels = sessionFactory.getRuntimeMetamodels();
final MappingMetamodelImplementor mappingMetamodel = runtimeMetamodels.getMappingMetamodel();
final EntityPersister entityInCatalogDescriptor = mappingMetamodel.getEntityDescriptor( EntityInCatalog.class );
assertThat( entityInCatalogDescriptor.getTableName() ).endsWith( " from the_catalog.table_in_catalog )" );
final EntityPersister entityInSchemaDescriptor = mappingMetamodel.getEntityDescriptor( EntityInSchema.class );
assertThat( entityInSchemaDescriptor.getTableName() ).endsWith( " from table_in_schema )" );
final EntityPersister entityInCatalogAndSchemaDescriptor = mappingMetamodel.getEntityDescriptor( EntityInCatalogAndSchema.class );
assertThat( entityInCatalogAndSchemaDescriptor.getTableName() ).endsWith( " from the_catalog.table_in_both )" );
final EntityPersister entityInLocalCatalogDescriptor = mappingMetamodel.getEntityDescriptor( EntityInLocalCatalog.class );
assertThat( entityInLocalCatalogDescriptor.getTableName() ).endsWith( " from local_catalog.table_in_local_catalog )" );
}
@Test
@ServiceRegistry(settings = {
@Setting(name= MappingSettings.DEFAULT_SCHEMA, value = "the_schema")
})
@DomainModel(annotatedClasses = {EntityInCatalog.class, EntityInCatalogAndSchema.class, EntityInSchema.class, EntityInLocalCatalog.class})
@SessionFactory(exportSchema = false)
void testPlaceholdersWithSchema(SessionFactoryScope scope) {
final SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
final RuntimeMetamodelsImplementor runtimeMetamodels = sessionFactory.getRuntimeMetamodels();
final MappingMetamodelImplementor mappingMetamodel = runtimeMetamodels.getMappingMetamodel();
final EntityPersister entityInCatalogDescriptor = mappingMetamodel.getEntityDescriptor( EntityInCatalog.class );
assertThat( entityInCatalogDescriptor.getTableName() ).endsWith( " from table_in_catalog )" );
final EntityPersister entityInSchemaDescriptor = mappingMetamodel.getEntityDescriptor( EntityInSchema.class );
assertThat( entityInSchemaDescriptor.getTableName() ).endsWith( " from the_schema.table_in_schema )" );
final EntityPersister entityInCatalogAndSchemaDescriptor = mappingMetamodel.getEntityDescriptor( EntityInCatalogAndSchema.class );
assertThat( entityInCatalogAndSchemaDescriptor.getTableName() ).endsWith( " from the_schema.table_in_both )" );
final EntityPersister entityInLocalCatalogDescriptor = mappingMetamodel.getEntityDescriptor( EntityInLocalCatalog.class );
assertThat( entityInLocalCatalogDescriptor.getTableName() ).endsWith( " from local_catalog.table_in_local_catalog )" );
}
@Test
@ServiceRegistry(settings = {
@Setting(name= MappingSettings.DEFAULT_CATALOG, value = "the_catalog"),
@Setting(name= MappingSettings.DEFAULT_SCHEMA, value = "the_schema")
})
@DomainModel(annotatedClasses = {EntityInCatalog.class, EntityInCatalogAndSchema.class, EntityInSchema.class, EntityInLocalCatalog.class})
@SessionFactory(exportSchema = false)
void testPlaceholdersWithCatalogAndSchema(SessionFactoryScope scope) {
final SessionFactoryImplementor sessionFactory = scope.getSessionFactory();
final RuntimeMetamodelsImplementor runtimeMetamodels = sessionFactory.getRuntimeMetamodels();
final MappingMetamodelImplementor mappingMetamodel = runtimeMetamodels.getMappingMetamodel();
final EntityPersister entityInCatalogDescriptor = mappingMetamodel.getEntityDescriptor( EntityInCatalog.class );
assertThat( entityInCatalogDescriptor.getTableName() ).endsWith( " from the_catalog.table_in_catalog )" );
final EntityPersister entityInSchemaDescriptor = mappingMetamodel.getEntityDescriptor( EntityInSchema.class );
assertThat( entityInSchemaDescriptor.getTableName() ).endsWith( " from the_schema.table_in_schema )" );
final EntityPersister entityInCatalogAndSchemaDescriptor = mappingMetamodel.getEntityDescriptor( EntityInCatalogAndSchema.class );
assertThat( entityInCatalogAndSchemaDescriptor.getTableName() ).endsWith( " from the_catalog.the_schema.table_in_both )" );
final EntityPersister entityInLocalCatalogDescriptor = mappingMetamodel.getEntityDescriptor( EntityInLocalCatalog.class );
assertThat( entityInLocalCatalogDescriptor.getTableName() ).endsWith( " from local_catalog.table_in_local_catalog )" );
}
@Entity
@Subselect("select id, name from {h-catalog}table_in_catalog")
public static class EntityInCatalog {
@Id
private Integer id;
private String name;
}
@Entity
@Subselect("select id, name from {h-catalog}table_in_local_catalog")
@Table(catalog = "local_catalog")
public static class EntityInLocalCatalog {
@Id
private Integer id;
private String name;
}
@Entity(name = "EntityInCatalogAndSchema")
@Subselect("select id, name from {h-domain}table_in_both")
public static class EntityInCatalogAndSchema {
@Id
private Integer id;
private String name;
}
@Entity(name = "EntityInSchema")
@Subselect("select id, name from {h-schema}table_in_schema")
public static class EntityInSchema {
@Id
private Integer id;
private String name;
}
}