Skip to content
Closed
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 @@ -339,6 +339,12 @@ include::{sourcedir-mapping}/basic/SubselectTest.java[tag=mapping-Subselect-enti
----
====

[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 @@ -32,7 +32,10 @@ public class SQLQueryParser {
private static final String CATALOG_PLACEHOLDER = "h-catalog";
private static final String SCHEMA_PLACEHOLDER = "h-schema";

private final SessionFactoryImplementor factory;
private final String schemaName;
private final String catalogName;
private final boolean jdbcStyleParamsZeroBased;

private final String originalQueryString;
private final ParserContext context;

Expand All @@ -51,9 +54,26 @@ interface ParserContext {
}

public SQLQueryParser(String queryString, ParserContext context, SessionFactoryImplementor factory) {
this(
queryString,
context,
factory.getSettings().getDefaultSchemaName(),
factory.getSettings().getDefaultCatalogName(),
factory.getSessionFactoryOptions().jdbcStyleParamsZeroBased()
);
}

protected SQLQueryParser(
String queryString,
ParserContext context,
String schemaName,
String catalogName,
boolean jdbcStyleParamsZeroBased) {
this.originalQueryString = queryString;
this.context = context;
this.factory = factory;
this.schemaName = schemaName;
this.catalogName = catalogName;
this.jdbcStyleParamsZeroBased = jdbcStyleParamsZeroBased;
}

public List<ParameterBinder> getParameterValueBinders() {
Expand Down Expand Up @@ -107,28 +127,24 @@ protected String substituteBrackets(String sqlQuery) throws QueryException {
if ( isPlaceholder ) {
// Domain replacement
if ( DOMAIN_PLACEHOLDER.equals( aliasPath ) ) {
final String catalogName = factory.getSettings().getDefaultCatalogName();
if ( catalogName != null ) {
result.append( catalogName );
result.append( "." );
}
final String schemaName = factory.getSettings().getDefaultSchemaName();
if ( schemaName != null ) {
result.append( schemaName );
result.append( "." );
}
}
// Schema replacement
else if ( SCHEMA_PLACEHOLDER.equals( aliasPath ) ) {
final String schemaName = factory.getSettings().getDefaultSchemaName();
if ( schemaName != null ) {
result.append(schemaName);
result.append(".");
}
}
// Catalog replacement
else if ( CATALOG_PLACEHOLDER.equals( aliasPath ) ) {
final String catalogName = factory.getSettings().getDefaultCatalogName();
if ( catalogName != null ) {
result.append( catalogName );
result.append( "." );
Expand Down Expand Up @@ -281,7 +297,7 @@ private String resolveProperties(String aliasName, String propertyName) {
* @return The SQL query with parameter substitution complete.
*/
private String substituteParams(String sqlString) {
final ParameterSubstitutionRecognizer recognizer = new ParameterSubstitutionRecognizer( factory );
final ParameterSubstitutionRecognizer recognizer = new ParameterSubstitutionRecognizer( jdbcStyleParamsZeroBased );
ParameterParser.parse( sqlString, recognizer );

paramValueBinders = recognizer.getParameterValueBinders();
Expand All @@ -295,10 +311,8 @@ public static class ParameterSubstitutionRecognizer implements ParameterParser.R
int jdbcPositionalParamCount;
private List<ParameterBinder> paramValueBinders;

public ParameterSubstitutionRecognizer(SessionFactoryImplementor factory) {
this.jdbcPositionalParamCount = factory.getSessionFactoryOptions().jdbcStyleParamsZeroBased()
? 0
: 1;
public ParameterSubstitutionRecognizer(boolean jdbcStyleParamsZeroBased) {
this.jdbcPositionalParamCount = jdbcStyleParamsZeroBased ? 0 : 1;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4147,6 +4147,10 @@ private String substituteBrackets(String sql) {
return new SubstituteBracketSQLQueryParser( sql, getFactory() ).process();
}

private String substituteBrackets(String sql, String schemaName, String catalogName) {
return new SubstituteBracketSQLQueryParser( sql, schemaName, catalogName ).process();
}

public final void postInstantiate() throws MappingException {
doLateInit();

Expand Down Expand Up @@ -5465,7 +5469,7 @@ public int determineTableNumberForColumn(String columnName) {

protected String determineTableName(Table table, JdbcEnvironment jdbcEnvironment) {
if ( table.getSubselect() != null ) {
return "( " + table.getSubselect() + " )";
return "( " + substituteBrackets( table.getSubselect(), table.getSchema(), table.getCatalog() ) + " )";
}

return jdbcEnvironment.getQualifiedObjectNameFormatter().format(
Expand Down Expand Up @@ -5792,6 +5796,10 @@ private static class SubstituteBracketSQLQueryParser extends SQLQueryParser {
super( queryString, null, factory );
}

SubstituteBracketSQLQueryParser(String queryString, String schemaName, String catalogName) {
super( queryString, null, schemaName, catalogName, true /*ignored*/ );
}

@Override
public String process() {
return substituteBrackets( getOriginalQueryString() );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?xml version="1.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>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping
package="org.hibernate.persister.entity"
default-access="field">

<class name="org.hibernate.test.subselect.SubselectSubstituteBracketsNonDefaultSchemaTest$XmlViewFoo"
mutable="false" schema="SubselectSubstituteBracketsNonDefaultSchemaTest">

<subselect>
SELECT ID,NAME FROM {h-schema}FOO
</subselect>
<synchronize table="FOO"/>

<id name="id" column="ID"/>
<property name="name" column="NAME"/>
</class>

<class name="org.hibernate.test.subselect.SubselectSubstituteBracketsNonDefaultSchemaTest$CatalogXmlViewFoo"
mutable="false" catalog="SubselectSubstituteBracketsNonDefaultSchemaTest">

<subselect>
SELECT ID,NAME FROM {h-catalog}FOO
</subselect>
<synchronize table="FOO"/>

<id name="id" column="ID"/>
<property name="name" column="NAME"/>
</class>
</hibernate-mapping>
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package org.hibernate.test.subselect;

import java.util.Properties;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.Subselect;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.H2Dialect;

import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;

/**
* @author Oleksii Chumak
*/
@RequiresDialect(H2Dialect.class)
public class SubselectSubstituteBracketsNonDefaultSchemaTest extends BaseCoreFunctionalTestCase {

private static final String SCHEMA_NAME = "SubselectSubstituteBracketsNonDefaultSchemaTest";

@Before
public void prepareTestData() {
doInHibernate( this::sessionFactory, session -> {
session.doWork( connection -> {
connection.createStatement()
.executeUpdate( "CREATE TABLE " + SCHEMA_NAME + ".FOO (ID int not null, NAME varchar(255), primary key (ID))" );
connection.createStatement()
.executeUpdate( "INSERT INTO " + SCHEMA_NAME + ".FOO (ID,NAME) VALUES(1,'name1')" );
connection.createStatement()
.executeUpdate( "INSERT INTO " + SCHEMA_NAME + ".FOO (ID,NAME) VALUES(2,'name2')" );
connection.createStatement()
.executeUpdate( "INSERT INTO " + SCHEMA_NAME + ".FOO (ID,NAME) VALUES(3,'name3')" );
} );
} );
}

@After
public void deleteTestData() {
doInHibernate( this::sessionFactory, session -> {
session.doWork( connection -> {
connection.createStatement().executeUpdate( "DROP TABLE " + SCHEMA_NAME + ".FOO" );
} );
} );
}

@Test
public void testAnnotationConfiguredSubselect() {
doInHibernate( this::sessionFactory, session -> {
assertEquals( "name1", session.get( ViewFoo.class, 1 ).name );
assertEquals( "name2", session.get( ViewFoo.class, 2 ).name );
assertEquals( "name3", session.get( ViewFoo.class, 3 ).name );

long count = (long) session.createQuery(
"SELECT COUNT(*) FROM SubselectSubstituteBracketsNonDefaultSchemaTest$ViewFoo" )
.uniqueResult();
assertEquals( 3L, count );
} );
}

@Test
public void testAnnotationConfiguredSubselectCatalog() {
doInHibernate( this::sessionFactory, session -> {
assertEquals( "name1", session.get( CatalogViewFoo.class, 1 ).name );
assertEquals( "name2", session.get( CatalogViewFoo.class, 2 ).name );
assertEquals( "name3", session.get( CatalogViewFoo.class, 3 ).name );

long count = (long) session.createQuery(
"SELECT COUNT(*) FROM SubselectSubstituteBracketsNonDefaultSchemaTest$CatalogViewFoo" )
.uniqueResult();
assertEquals( 3L, count );
} );
}

@Test
public void testXmlConfiguredSubselect() {
doInHibernate( this::sessionFactory, session -> {
assertEquals( "name1", session.get( XmlViewFoo.class, 1 ).name );
assertEquals( "name2", session.get( XmlViewFoo.class, 2 ).name );
assertEquals( "name3", session.get( XmlViewFoo.class, 3 ).name );

long count = (long) session.createQuery(
"SELECT COUNT(*) FROM SubselectSubstituteBracketsNonDefaultSchemaTest$XmlViewFoo" )
.uniqueResult();
assertEquals( 3L, count );
} );
}

@Test
public void testXmlConfiguredSubselectCatalog() {
doInHibernate( this::sessionFactory, session -> {
assertEquals( "name1", session.get( CatalogXmlViewFoo.class, 1 ).name );
assertEquals( "name2", session.get( CatalogXmlViewFoo.class, 2 ).name );
assertEquals( "name3", session.get( CatalogXmlViewFoo.class, 3 ).name );

long count = (long) session.createQuery(
"SELECT COUNT(*) FROM SubselectSubstituteBracketsNonDefaultSchemaTest$CatalogXmlViewFoo" )
.uniqueResult();
assertEquals( 3L, count );
} );
}

@Override
public String[] getMappings() {
return new String[] { "subselect/SubselectSubstituteBracketsNonDefaultSchemaTest.hbm.xml" };
}

@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
ViewFoo.class,
CatalogViewFoo.class
};
}


@Override
protected void configure(Configuration configuration) {
final Properties properties = new Properties();
properties.put( AvailableSettings.DEFAULT_SCHEMA, "WRONG_VALUE" );
properties.put( AvailableSettings.DEFAULT_CATALOG, "WRONG_VALUE" );
configuration.addProperties( properties );
}

@Override
protected String createSecondSchema() {
return SCHEMA_NAME;
}


@Entity
@Table(schema = SCHEMA_NAME)
@Subselect("SELECT ID,NAME FROM {h-schema}FOO")
public static class ViewFoo {

@Id
@Column(name = "ID")
public Integer id;

@Column(name = "NAME")
public String name;
}


@Entity
@Table(catalog = SCHEMA_NAME)
@Subselect("SELECT ID,NAME FROM {h-catalog}FOO")
public static class CatalogViewFoo {

@Id
@Column(name = "ID")
public Integer id;

@Column(name = "NAME")
public String name;
}

public static class XmlViewFoo {
public Integer id;
public String name;
}

public static class CatalogXmlViewFoo {
public Integer id;
public String name;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.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>.
-->
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping
package="org.hibernate.persister.entity"
default-access="field">

<class name="org.hibernate.test.subselect.SubselectSubstituteBracketsTest$XmlViewFoo" mutable="false">

<subselect>
SELECT ID,NAME FROM {h-schema}FOO
</subselect>
<synchronize table="FOO"/>

<id name="id" column="ID"/>
<property name="name" column="NAME"/>
</class>

<class name="org.hibernate.test.subselect.SubselectSubstituteBracketsTest$CatalogXmlViewFoo" mutable="false">

<subselect>
SELECT ID,NAME FROM {h-catalog}FOO
</subselect>
<synchronize table="FOO"/>

<id name="id" column="ID"/>
<property name="name" column="NAME"/>
</class>
</hibernate-mapping>
Loading