Merge remote-tracking branch 'upstream/main' into wip/6.0

This commit is contained in:
Andrea Boriero 2021-06-10 09:52:24 +02:00
commit 6d59fec436
28 changed files with 599 additions and 171 deletions

View File

@ -926,6 +926,7 @@ Valid options are defined by the `strategy` value of the https://docs.jboss.org/
`*hibernate.hbm2ddl.delimiter*` (e.g. `;`)::
Identifies the delimiter to use to separate schema management statements in script outputs.
The default value is `;`.
`*hibernate.schema_management_tool*` (e.g. A schema name)::
Used to specify the `SchemaManagementTool` to use for performing schema management. The default is to use `HibernateSchemaManagementTool`.

View File

@ -1967,7 +1967,8 @@ public interface AvailableSettings extends org.hibernate.jpa.AvailableSettings {
String HBM2DDL_JDBC_METADATA_EXTRACTOR_STRATEGY = "hibernate.hbm2ddl.jdbc_metadata_extraction_strategy";
/**
* Identifies the delimiter to use to separate schema management statements in script outputs
* Identifies the delimiter to use to separate schema management statements in script outputs.
* The default value is <code>;</code>.
*/
String HBM2DDL_DELIMITER = "hibernate.hbm2ddl.delimiter";

View File

@ -3298,13 +3298,15 @@ public abstract class AbstractEntityPersister
* without resolving associations or collections. Question: should
* this really be here, or should it be sent back to Loader?
*/
@Override
public Object[] hydrate(
final ResultSet rs,
final Object id,
final Object object,
final Loadable rootLoadable,
final String[][] suffixedPropertyColumns,
final boolean allProperties,
final boolean forceEager,
final boolean[] propertiesForceEager,
final SharedSessionContractImplementor session) throws SQLException, HibernateException {
if ( LOG.isTraceEnabled() ) {
@ -3366,7 +3368,7 @@ public abstract class AbstractEntityPersister
if ( !propertySelectable[i] ) {
values[i] = PropertyAccessStrategyBackRefImpl.UNKNOWN;
}
else if ( allProperties || !laziness[i] ) {
else if ( forceEager || !laziness[i] || propertiesForceEager != null && propertiesForceEager[i] ) {
//decide which ResultSet to get the property value from:
final boolean propertyIsDeferred = hasDeferred &&
rootPersister.isSubclassPropertyDeferred( propNames[i], propSubclassNames[i] );

View File

@ -79,6 +79,20 @@ public interface Loadable extends EntityPersister {
*/
boolean hasRowId();
/**
* Retrieve property values from one row of a result set
*/
default Object[] hydrate(
ResultSet rs,
Object id,
Object object,
Loadable rootLoadable,
String[][] suffixedPropertyColumns,
boolean forceEager,
SharedSessionContractImplementor session) throws SQLException, HibernateException {
return hydrate( rs, id, object, rootLoadable, suffixedPropertyColumns, forceEager, null, session );
}
/**
* Retrieve property values from one row of a result set
*/
@ -88,7 +102,8 @@ public interface Loadable extends EntityPersister {
Object object,
Loadable rootLoadable,
String[][] suffixedPropertyColumns,
boolean allProperties,
boolean forceEager,
boolean[] propertiesForceEager,
SharedSessionContractImplementor session) throws SQLException, HibernateException;
boolean isAbstract();

View File

@ -224,7 +224,7 @@ public class StatisticsImpl implements StatisticsImplementor, Service, Manageabl
public EntityStatisticsImpl getEntityStatistics(String entityName) {
return entityStatsMap.getOrCompute(
entityName,
s -> new EntityStatisticsImpl( metamodel.entityPersister( s ) )
this::instantiateEntityStatistics
);
}
@ -328,7 +328,7 @@ public class StatisticsImpl implements StatisticsImplementor, Service, Manageabl
public CollectionStatisticsImpl getCollectionStatistics(String role) {
return collectionStatsMap.getOrCompute(
role,
s -> new CollectionStatisticsImpl( metamodel.collectionPersister( s ) )
this::instantiateCollectionStatistics
);
}
@ -416,13 +416,7 @@ public class StatisticsImpl implements StatisticsImplementor, Service, Manageabl
public NaturalIdStatisticsImpl getNaturalIdStatistics(String rootEntityName) {
return naturalIdQueryStatsMap.getOrCompute(
rootEntityName,
s -> {
final EntityPersister entityDescriptor = metamodel.entityPersister( s );
if ( !entityDescriptor.hasNaturalIdentifier() ) {
throw new IllegalArgumentException( "Given entity [" + s + "] does not define natural-id" );
}
return new NaturalIdStatisticsImpl( entityDescriptor );
}
this::instantiateNaturalStatistics
);
}
@ -431,10 +425,7 @@ public class StatisticsImpl implements StatisticsImplementor, Service, Manageabl
final String key = cache.unqualifyRegionName( regionName );
return deprecatedNaturalIdStatsMap.getOrCompute(
key,
unqualifiedRegionName -> new DeprecatedNaturalIdCacheStatisticsImpl(
unqualifiedRegionName,
cache.getNaturalIdAccessesInRegion( unqualifiedRegionName )
)
this::instantiateDeprecatedNaturalIdCacheStatistics
);
}
@ -563,21 +554,7 @@ public class StatisticsImpl implements StatisticsImplementor, Service, Manageabl
public CacheRegionStatisticsImpl getDomainDataRegionStatistics(String regionName) {
return l2CacheStatsMap.getOrCompute(
regionName,
s -> {
final Region region = cache.getRegion( s );
if ( region == null ) {
throw new IllegalArgumentException( "Unknown cache region : " + s );
}
if ( region instanceof QueryResultsRegion ) {
throw new IllegalArgumentException(
"Region name [" + s + "] referred to a query result region, not a domain data region"
);
}
return new CacheRegionStatisticsImpl( region );
}
this::instantiateCacheRegionStatistics
);
}
@ -608,22 +585,7 @@ public class StatisticsImpl implements StatisticsImplementor, Service, Manageabl
return l2CacheStatsMap.getOrCompute(
regionName,
s -> {
Region region = cache.getRegion( s );
if ( region == null ) {
if ( ! queryCacheEnabled ) {
return null;
}
// this is the pre-5.3 behavior. and since this is a pre-5.3 method it should behave consistently
// NOTE that this method is deprecated
region = cache.getQueryResultsCache( s ).getRegion();
}
return new CacheRegionStatisticsImpl( region );
}
this::createCacheRegionStatistics
);
}
@ -816,7 +778,7 @@ public class StatisticsImpl implements StatisticsImplementor, Service, Manageabl
private CacheRegionStatisticsImpl getQueryRegionStats(String regionName) {
return l2CacheStatsMap.getOrCompute(
regionName,
s -> new CacheRegionStatisticsImpl( cache.getQueryResultsCache( regionName ).getRegion() )
this::instantiateCacheRegionStatsForQueryResults
);
}
@ -989,4 +951,64 @@ public class StatisticsImpl implements StatisticsImplementor, Service, Manageabl
.append( ']' )
.toString();
}
private EntityStatisticsImpl instantiateEntityStatistics(final String entityName) {
return new EntityStatisticsImpl( metamodel.entityPersister( entityName ) );
}
private CollectionStatisticsImpl instantiateCollectionStatistics(final String role) {
return new CollectionStatisticsImpl( metamodel.collectionPersister( role ) );
}
private NaturalIdStatisticsImpl instantiateNaturalStatistics(final String entityName) {
final EntityPersister entityDescriptor = metamodel.entityPersister( entityName );
if ( !entityDescriptor.hasNaturalIdentifier() ) {
throw new IllegalArgumentException( "Given entity [" + entityName + "] does not define natural-id" );
}
return new NaturalIdStatisticsImpl( entityDescriptor );
}
private DeprecatedNaturalIdCacheStatisticsImpl instantiateDeprecatedNaturalIdCacheStatistics(final String unqualifiedRegionName) {
return new DeprecatedNaturalIdCacheStatisticsImpl(
unqualifiedRegionName,
cache.getNaturalIdAccessesInRegion( unqualifiedRegionName )
);
}
private CacheRegionStatisticsImpl instantiateCacheRegionStatistics(final String regionName) {
final Region region = cache.getRegion( regionName );
if ( region == null ) {
throw new IllegalArgumentException( "Unknown cache region : " + regionName );
}
if ( region instanceof QueryResultsRegion ) {
throw new IllegalArgumentException(
"Region name [" + regionName + "] referred to a query result region, not a domain data region"
);
}
return new CacheRegionStatisticsImpl( region );
}
private CacheRegionStatisticsImpl instantiateCacheRegionStatsForQueryResults(final String regionName) {
return new CacheRegionStatisticsImpl( cache.getQueryResultsCache( regionName ).getRegion() );
}
private CacheRegionStatisticsImpl createCacheRegionStatistics(final String regionName) {
Region region = cache.getRegion( regionName );
if ( region == null ) {
if ( !queryCacheEnabled ) {
return null;
}
// this is the pre-5.3 behavior. and since this is a pre-5.3 method it should behave consistently
// NOTE that this method is deprecated
region = cache.getQueryResultsCache( regionName ).getRegion();
}
return new CacheRegionStatisticsImpl( region );
}
}

View File

@ -123,7 +123,7 @@ public class HibernateSchemaManagementTool implements SchemaManagementTool, Serv
JdbcContext jdbcContext,
Map options,
boolean needsAutoCommit) {
final String scriptDelimiter = ConfigurationHelper.getString( HBM2DDL_DELIMITER, options );
final String scriptDelimiter = ConfigurationHelper.getString( HBM2DDL_DELIMITER, options, ";" );
final GenerationTarget[] targets = new GenerationTarget[ targetDescriptor.getTargetTypes().size() ];
@ -156,7 +156,7 @@ public class HibernateSchemaManagementTool implements SchemaManagementTool, Serv
TargetDescriptor targetDescriptor,
DdlTransactionIsolator ddlTransactionIsolator,
Map options) {
final String scriptDelimiter = ConfigurationHelper.getString( HBM2DDL_DELIMITER, options );
final String scriptDelimiter = ConfigurationHelper.getString( HBM2DDL_DELIMITER, options, ";" );
final GenerationTarget[] targets = new GenerationTarget[ targetDescriptor.getTargetTypes().size() ];

View File

@ -85,7 +85,7 @@ public class PostgreSQLMultipleSchemaSequenceTest extends BaseUnitTestCase {
);
try(Statement statement = ddlTransactionIsolator1.getIsolatedConnection().createStatement()) {
statement.execute( String.format( "DROP SCHEMA IF EXISTS %s CASCADE", extraSchemaName ) );
statement.execute( String.format( "CREATE SCHEMA %s", extraSchemaName ) );
statement.execute( String.format( "CREATE SCHEMA %s;", extraSchemaName ) );
try(ResultSet resultSet = statement.executeQuery( "SELECT NEXTVAL('SEQ_TEST')" )) {
while ( resultSet.next() ) {
@ -156,7 +156,7 @@ public class PostgreSQLMultipleSchemaSequenceTest extends BaseUnitTestCase {
assertEquals( 2 ,
sqlLines
.stream()
.filter( s -> s.equalsIgnoreCase( "create sequence SEQ_TEST start with 1 increment by 1" ) )
.filter( s -> s.equalsIgnoreCase( "create sequence SEQ_TEST start with 1 increment by 1;" ) )
.count()
);
}

View File

@ -61,7 +61,7 @@ public class TableCommentTest extends BaseNonConfigCoreFunctionalTestCase {
final String tableName = getTableName();
for ( String sqlStatement : sqlLines ) {
if ( sqlStatement.toLowerCase()
.equals( "comment on table " + tableName.toLowerCase() + " is 'comment snippet'" ) ) {
.equals( "comment on table " + tableName.toLowerCase() + " is 'comment snippet';" ) ) {
if ( getDialect().supportsCommentOn() ) {
found = true;
}

View File

@ -68,12 +68,12 @@ public class SequenceGenerationTest extends BaseUnitTestCase {
List<String> commands = Files.readAllLines( output.toPath() );
assertThat(
isCommandGenerated( commands, "create table test_entity \\(id .*, primary key \\(id\\)\\)" ),
isCommandGenerated( commands, "create table test_entity \\(id .*, primary key \\(id\\)\\);" ),
is( true )
);
assertThat(
isCommandGenerated( commands, "create sequence sequence_generator start with 5 increment by 3" ),
isCommandGenerated( commands, "create sequence sequence_generator start with 5 increment by 3;" ),
is( true )
);
}

View File

@ -70,12 +70,12 @@ public class SequenceGeneratorsTest extends BaseUnitTestCase {
List<String> commands = Files.readAllLines( output.toPath() );
assertThat(
isCommandGenerated( commands, "CREATE TABLE TEST_ENTITY \\(ID .*, PRIMARY KEY \\(ID\\)\\)" ),
isCommandGenerated( commands, "CREATE TABLE TEST_ENTITY \\(ID .*, PRIMARY KEY \\(ID\\)\\);" ),
is( true )
);
assertThat(
isCommandGenerated( commands, "CREATE SEQUENCE SEQUENCE_GENERATOR START WITH 5 INCREMENT BY 3" ),
isCommandGenerated( commands, "CREATE SEQUENCE SEQUENCE_GENERATOR START WITH 5 INCREMENT BY 3;" ),
is( true )
);
}

View File

@ -71,13 +71,13 @@ public class TableGeneratorTest extends BaseUnitTestCase {
final List<String> commands = Files.readAllLines( output.toPath() );
final String expectedTestEntityTableCreationCommand = "CREATE TABLE TEST_ENTITY \\(ID .*, PRIMARY KEY \\(ID\\)\\)";
final String expectedTestEntityTableCreationCommand = "CREATE TABLE TEST_ENTITY \\(ID .*, PRIMARY KEY \\(ID\\)\\);";
assertTrue(
"The command '" + expectedTestEntityTableCreationCommand + "' has not been correctly generated",
isCommandGenerated( commands, expectedTestEntityTableCreationCommand )
);
final String expectedIdTableGeneratorCreationCommand = "CREATE TABLE ID_TABLE_GENERATOR \\(PK .*, VALUE .*, PRIMARY KEY \\(PK\\)\\)";
final String expectedIdTableGeneratorCreationCommand = "CREATE TABLE ID_TABLE_GENERATOR \\(PK .*, VALUE .*, PRIMARY KEY \\(PK\\)\\);";
assertTrue(
"The command '" + expectedIdTableGeneratorCreationCommand + "' has not been correctly generated",
@ -88,7 +88,7 @@ public class TableGeneratorTest extends BaseUnitTestCase {
)
);
final String expectedInsertIntoTableGeneratorCommand = "INSERT INTO ID_TABLE_GENERATOR\\(PK, VALUE\\) VALUES \\('TEST_ENTITY_ID'," + EXPECTED_DB_INSERTED_VALUE + "\\)";
final String expectedInsertIntoTableGeneratorCommand = "INSERT INTO ID_TABLE_GENERATOR\\(PK, VALUE\\) VALUES \\('TEST_ENTITY_ID'," + EXPECTED_DB_INSERTED_VALUE + "\\);";
assertTrue(
"The command '" + expectedInsertIntoTableGeneratorCommand + "' has not been correctly generated",

View File

@ -72,13 +72,13 @@ public class TableGeneratorsTest extends BaseUnitTestCase {
final List<String> commands = Files.readAllLines( output.toPath() );
final String expectedTestEntityTableCreationCommand = "CREATE TABLE TEST_ENTITY \\(ID .*, PRIMARY KEY \\(ID\\)\\)";
final String expectedTestEntityTableCreationCommand = "CREATE TABLE TEST_ENTITY \\(ID .*, PRIMARY KEY \\(ID\\)\\);";
assertTrue(
"The command '" + expectedTestEntityTableCreationCommand + "' has not been correctly generated",
isCommandGenerated( commands, expectedTestEntityTableCreationCommand )
);
final String expectedIdTableGeneratorCreationCommand = "CREATE TABLE ID_TABLE_GENERATOR \\(PK .*, VALUE .*, PRIMARY KEY \\(PK\\)\\)";
final String expectedIdTableGeneratorCreationCommand = "CREATE TABLE ID_TABLE_GENERATOR \\(PK .*, VALUE .*, PRIMARY KEY \\(PK\\)\\);";
assertTrue(
"The command '" + expectedIdTableGeneratorCreationCommand + "' has not been correctly generated",
@ -89,7 +89,7 @@ public class TableGeneratorsTest extends BaseUnitTestCase {
)
);
final String expectedInsertIntoTableGeneratorCommand = "INSERT INTO ID_TABLE_GENERATOR\\(PK, VALUE\\) VALUES \\('TEST_ENTITY_ID'," + EXPECTED_DB_INSERTED_VALUE + "\\)";
final String expectedInsertIntoTableGeneratorCommand = "INSERT INTO ID_TABLE_GENERATOR\\(PK, VALUE\\) VALUES \\('TEST_ENTITY_ID'," + EXPECTED_DB_INSERTED_VALUE + "\\);";
assertTrue(
"The command '" + expectedInsertIntoTableGeneratorCommand + "' has not been correctly generated",

View File

@ -93,7 +93,7 @@ public class UniqueConstraintGenerationTest {
private boolean isUniqueConstraintGenerated(String tableName, String columnName) throws IOException {
boolean matches = false;
final String regex = getDialect().getAlterTableString( tableName ) + " add constraint uk_(.)* unique \\(" + columnName + "\\)";
final String regex = getDialect().getAlterTableString( tableName ) + " add constraint uk_(.)* unique \\(" + columnName + "\\);";
final String fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase();
final String[] split = fileContent.split( System.lineSeparator() );
@ -109,7 +109,7 @@ public class UniqueConstraintGenerationTest {
private boolean isCreateUniqueIndexGenerated(String tableName, String columnName) throws IOException {
boolean matches = false;
String regex = "create unique index uk_(.)* on " + tableName + " \\(" + columnName + "\\)";
String regex = "create unique index uk_(.)* on " + tableName + " \\(" + columnName + "\\);";
final String fileContent = new String( Files.readAllBytes( output.toPath() ) ).toLowerCase();
final String[] split = fileContent.split( System.lineSeparator() );

View File

@ -26,6 +26,7 @@ import javax.persistence.criteria.JoinType;
import java.util.List;
import static org.hibernate.Hibernate.isInitialized;
import static org.hibernate.Hibernate.isPropertyInitialized;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@ -142,6 +143,11 @@ public class HHH3949Test extends BaseCoreFunctionalTestCase {
for ( Person person : persons ) {
assertTrue( isInitialized( person ) );
if ( shouldHaveVehicle( person ) ) {
// We used a "join fetch", so the vehicle must be initialized
// before we even call the getter
// (which could trigger lazy initialization if the join fetch didn't work).
assertTrue( isPropertyInitialized( person, "vehicle" ) );
assertNotNull( person.getVehicle() );
assertTrue( isInitialized( person.getVehicle() ) );
assertNotNull( person.getVehicle().getDriver() );
@ -157,6 +163,11 @@ public class HHH3949Test extends BaseCoreFunctionalTestCase {
}
for ( Vehicle vehicle : vehicles ) {
if ( shouldHaveDriver( vehicle ) ) {
// We used a "join fetch", so the drover must be initialized
// before we even call the getter
// (which could trigger lazy initialization if the join fetch didn't work).
assertTrue( isPropertyInitialized( vehicle, "driver" ) );
assertNotNull( vehicle.getDriver() );
assertNotNull( vehicle.getDriver().getVehicle() );
}

View File

@ -20,12 +20,17 @@ import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInter
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.BeforeClassOnce;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -36,6 +41,7 @@ import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.annotations.FetchMode.JOIN;
import static org.junit.Assert.assertTrue;
/**
* Test for lazy uni-directional to-one (with JOIN fetching) when enhanced proxies are allowed
@ -61,17 +67,6 @@ public class JoinFetchedManyToOneAllowProxyTests extends BaseNonConfigCoreFuncti
@Test
public void testOwnerIsProxy() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final Order order = new Order( 1, customer, BigDecimal.ONE );
session.persist( order );
}
);
sqlStatementInterceptor.clear();
final EntityPersister orderDescriptor = sessionFactory().getMetamodel().entityPersister( Order.class );
final BytecodeEnhancementMetadata orderEnhancementMetadata = orderDescriptor.getBytecodeEnhancementMetadata();
assertThat( orderEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
@ -116,6 +111,50 @@ public class JoinFetchedManyToOneAllowProxyTests extends BaseNonConfigCoreFuncti
);
}
@Test
@TestForIssue(jiraKey = "HHH-14659")
public void testQueryJoinFetch() {
Order order = fromTransaction( (session) -> {
final Order result = session.createQuery(
"select o from Order o join fetch o.customer",
Order.class )
.uniqueResult();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
return result;
} );
// The "join fetch" should have already initialized the property,
// so that the getter can safely be called outside of a session.
assertTrue( Hibernate.isPropertyInitialized( order, "customer" ) );
// The "join fetch" should have already initialized the associated entity.
Customer customer = order.getCustomer();
assertTrue( Hibernate.isInitialized( customer ) );
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
}
@Before
public void createTestData() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final Order order = new Order( 1, customer, BigDecimal.ONE );
session.persist( order );
}
);
sqlStatementInterceptor.clear();
}
@After
public void dropTestData() {
inTransaction(
(session) -> {
session.createQuery( "delete Order" ).executeUpdate();
session.createQuery( "delete Customer" ).executeUpdate();
}
);
}
@Entity( name = "Customer" )
@Table( name = "customer" )
public static class Customer {

View File

@ -12,6 +12,7 @@ import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.Hibernate;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
@ -19,8 +20,10 @@ import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLaziness
import org.hibernate.bytecode.enhance.spi.interceptor.LazyAttributeLoadingInterceptor;
import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
@ -36,6 +39,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* Test for lazy uni-directional to-one (with SELECT fetching) when enhanced proxies are allowed
@ -61,8 +65,6 @@ public class ManyToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTestCas
@Test
public void testOwnerIsProxy() {
sqlStatementInterceptor.clear();
final EntityPersister orderDescriptor = sessionFactory().getMetamodel().entityPersister( Order.class );
final BytecodeEnhancementMetadata orderEnhancementMetadata = orderDescriptor.getBytecodeEnhancementMetadata();
assertThat( orderEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
@ -115,6 +117,27 @@ public class ManyToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTestCas
);
}
@Test
@TestForIssue(jiraKey = "HHH-14659")
public void testQueryJoinFetch() {
Order order = fromTransaction( (session) -> {
final Order result = session.createQuery(
"select o from Order o join fetch o.customer",
Order.class )
.uniqueResult();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
return result;
} );
// The "join fetch" should have already initialized the property,
// so that the getter can safely be called outside of a session.
assertTrue( Hibernate.isPropertyInitialized( order, "customer" ) );
// The "join fetch" should have already initialized the associated entity.
Customer customer = order.getCustomer();
assertTrue( Hibernate.isInitialized( customer ) );
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
}
@Before
public void createTestData() {
inTransaction(
@ -125,6 +148,7 @@ public class ManyToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTestCas
session.persist( order );
}
);
sqlStatementInterceptor.clear();
}
@After

View File

@ -12,6 +12,7 @@ import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
import org.hibernate.Hibernate;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
@ -22,11 +23,13 @@ import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -37,6 +40,7 @@ import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.annotations.LazyToOneOption.NO_PROXY;
import static org.junit.Assert.assertTrue;
/**
* Baseline test for uni-directional to-one, using an explicit @LazyToOne(NO_PROXY)
@ -64,17 +68,6 @@ public class ManyToOneExplicitOptionTests extends BaseNonConfigCoreFunctionalTes
@Test
public void testOwnerIsProxy() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final Order order = new Order( 1, customer, BigDecimal.ONE );
session.persist( order );
}
);
sqlStatementInterceptor.clear();
final EntityPersister orderDescriptor = sessionFactory().getMetamodel().entityPersister( Order.class );
final BytecodeEnhancementMetadata orderEnhancementMetadata = orderDescriptor.getBytecodeEnhancementMetadata();
assertThat( orderEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
@ -127,6 +120,40 @@ public class ManyToOneExplicitOptionTests extends BaseNonConfigCoreFunctionalTes
);
}
@Test
@TestForIssue(jiraKey = "HHH-14659")
public void testQueryJoinFetch() {
Order order = fromTransaction( (session) -> {
final Order result = session.createQuery(
"select o from Order o join fetch o.customer",
Order.class )
.uniqueResult();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
return result;
} );
// The "join fetch" should have already initialized the property,
// so that the getter can safely be called outside of a session.
assertTrue( Hibernate.isPropertyInitialized( order, "customer" ) );
// The "join fetch" should have already initialized the associated entity.
Customer customer = order.getCustomer();
assertTrue( Hibernate.isInitialized( customer ) );
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
}
@Before
public void createTestData() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final Order order = new Order( 1, customer, BigDecimal.ONE );
session.persist( order );
}
);
sqlStatementInterceptor.clear();
}
@After
public void dropTestData() {
inTransaction(

View File

@ -11,6 +11,7 @@ import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.Hibernate;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
@ -20,6 +21,7 @@ import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
@ -35,6 +37,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* @author Steve Ebersole
@ -60,8 +63,6 @@ public class InverseToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTest
@Test
public void testOwnerIsProxy() {
sqlStatementInterceptor.clear();
final EntityPersister supplementalInfoDescriptor = sessionFactory().getMetamodel().entityPersister( SupplementalInfo.class );
final BytecodeEnhancementMetadata supplementalInfoEnhancementMetadata = supplementalInfoDescriptor.getBytecodeEnhancementMetadata();
assertThat( supplementalInfoEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
@ -113,6 +114,27 @@ public class InverseToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTest
);
}
@Test
@TestForIssue(jiraKey = "HHH-14659")
public void testQueryJoinFetch() {
SupplementalInfo info = fromTransaction( (session) -> {
final SupplementalInfo result = session.createQuery(
"select s from SupplementalInfo s join fetch s.customer",
SupplementalInfo.class )
.uniqueResult();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
return result;
} );
// The "join fetch" should have already initialized the property,
// so that the getter can safely be called outside of a session.
assertTrue( Hibernate.isPropertyInitialized( info, "customer" ) );
// The "join fetch" should have already initialized the associated entity.
Customer customer = info.getCustomer();
assertTrue( Hibernate.isInitialized( customer ) );
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
}
@Before
public void createTestData() {
inTransaction(
@ -123,6 +145,7 @@ public class InverseToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTest
session.persist( supplementalInfo );
}
);
sqlStatementInterceptor.clear();
}
@After

View File

@ -11,6 +11,7 @@ import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.Hibernate;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
@ -20,11 +21,13 @@ import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -33,6 +36,7 @@ import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.annotations.LazyToOneOption.NO_PROXY;
import static org.junit.Assert.assertTrue;
/**
* Baseline test for inverse (mappedBy) to-one, using an explicit @LazyToOne(NO_PROXY)
@ -58,17 +62,6 @@ public class InverseToOneExplicitOptionTests extends BaseNonConfigCoreFunctional
@Test
public void testOwnerIsProxy() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final SupplementalInfo supplementalInfo = new SupplementalInfo( 1, customer, "extra details" );
session.persist( supplementalInfo );
}
);
sqlStatementInterceptor.clear();
final EntityPersister supplementalInfoDescriptor = sessionFactory().getMetamodel().entityPersister( SupplementalInfo.class );
final BytecodeEnhancementMetadata supplementalInfoEnhancementMetadata = supplementalInfoDescriptor.getBytecodeEnhancementMetadata();
assertThat( supplementalInfoEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
@ -140,6 +133,40 @@ public class InverseToOneExplicitOptionTests extends BaseNonConfigCoreFunctional
);
}
@Test
@TestForIssue(jiraKey = "HHH-14659")
public void testQueryJoinFetch() {
SupplementalInfo info = fromTransaction( (session) -> {
final SupplementalInfo result = session.createQuery(
"select s from SupplementalInfo s join fetch s.customer",
SupplementalInfo.class )
.uniqueResult();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
return result;
} );
// The "join fetch" should have already initialized the property,
// so that the getter can safely be called outside of a session.
assertTrue( Hibernate.isPropertyInitialized( info, "customer" ) );
// The "join fetch" should have already initialized the associated entity.
Customer customer = info.getCustomer();
assertTrue( Hibernate.isInitialized( customer ) );
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
}
@Before
public void createTestData() {
inTransaction(
(session) -> {
final Customer customer = new Customer( 1, "Acme Brick" );
session.persist( customer );
final SupplementalInfo supplementalInfo = new SupplementalInfo( 1, customer, "extra details" );
session.persist( supplementalInfo );
}
);
sqlStatementInterceptor.clear();
}
@After
public void dropTestData() {
inTransaction(

View File

@ -11,6 +11,7 @@ import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Fetch;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
@ -21,6 +22,7 @@ import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
@ -37,6 +39,7 @@ import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.annotations.FetchMode.JOIN;
import static org.junit.Assert.assertTrue;
/**
* @author Steve Ebersole
@ -62,8 +65,6 @@ public class JoinFetchedInverseToOneAllowProxyTests extends BaseNonConfigCoreFun
@Test
public void testOwnerIsProxy() {
sqlStatementInterceptor.clear();
final EntityPersister supplementalInfoDescriptor = sessionFactory().getMetamodel().entityPersister( SupplementalInfo.class );
final BytecodeEnhancementMetadata supplementalInfoEnhancementMetadata = supplementalInfoDescriptor.getBytecodeEnhancementMetadata();
assertThat( supplementalInfoEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
@ -110,6 +111,27 @@ public class JoinFetchedInverseToOneAllowProxyTests extends BaseNonConfigCoreFun
);
}
@Test
@TestForIssue(jiraKey = "HHH-14659")
public void testQueryJoinFetch() {
SupplementalInfo info = fromTransaction( (session) -> {
final SupplementalInfo result = session.createQuery(
"select s from SupplementalInfo s join fetch s.customer",
SupplementalInfo.class )
.uniqueResult();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
return result;
} );
// The "join fetch" should have already initialized the property,
// so that the getter can safely be called outside of a session.
assertTrue( Hibernate.isPropertyInitialized( info, "customer" ) );
// The "join fetch" should have already initialized the associated entity.
Customer customer = info.getCustomer();
assertTrue( Hibernate.isInitialized( customer ) );
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
}
@Before
public void createTestData() {
inTransaction(
@ -120,6 +142,7 @@ public class JoinFetchedInverseToOneAllowProxyTests extends BaseNonConfigCoreFun
session.persist( supplementalInfo );
}
);
sqlStatementInterceptor.clear();
}
@After

View File

@ -11,6 +11,7 @@ import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Fetch;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
@ -18,6 +19,7 @@ import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
@ -31,6 +33,7 @@ import static javax.persistence.FetchType.LAZY;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.annotations.FetchMode.JOIN;
import static org.junit.Assert.assertTrue;
/**
* @author Steve Ebersole
@ -56,8 +59,6 @@ public class JoinFetchedOneToOneAllowProxyTests extends BaseNonConfigCoreFunctio
@Test
public void testOwnerIsProxy() {
sqlStatementInterceptor.clear();
final EntityPersister supplementalInfoDescriptor = sessionFactory().getMetamodel().entityPersister( SupplementalInfo.class );
final BytecodeEnhancementMetadata supplementalInfoEnhancementMetadata = supplementalInfoDescriptor.getBytecodeEnhancementMetadata();
assertThat( supplementalInfoEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
@ -94,6 +95,27 @@ public class JoinFetchedOneToOneAllowProxyTests extends BaseNonConfigCoreFunctio
);
}
@Test
@TestForIssue(jiraKey = "HHH-14659")
public void testQueryJoinFetch() {
SupplementalInfo info = fromTransaction( (session) -> {
final SupplementalInfo result = session.createQuery(
"select s from SupplementalInfo s join fetch s.customer",
SupplementalInfo.class )
.uniqueResult();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
return result;
} );
// The "join fetch" should have already initialized the property,
// so that the getter can safely be called outside of a session.
assertTrue( Hibernate.isPropertyInitialized( info, "customer" ) );
// The "join fetch" should have already initialized the associated entity.
Customer customer = info.getCustomer();
assertTrue( Hibernate.isInitialized( customer ) );
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
}
@Before
public void createTestData() {
inTransaction(
@ -104,6 +126,7 @@ public class JoinFetchedOneToOneAllowProxyTests extends BaseNonConfigCoreFunctio
session.persist( supplementalInfo );
}
);
sqlStatementInterceptor.clear();
}
@After

View File

@ -11,6 +11,7 @@ import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.Hibernate;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
@ -20,6 +21,7 @@ import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
@ -35,6 +37,7 @@ import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* @author Steve Ebersole
@ -60,8 +63,6 @@ public class OneToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTestCase
@Test
public void testOwnerIsProxy() {
sqlStatementInterceptor.clear();
final EntityPersister supplementalInfoDescriptor = sessionFactory().getMetamodel().entityPersister( SupplementalInfo.class );
final BytecodeEnhancementMetadata supplementalInfoEnhancementMetadata = supplementalInfoDescriptor.getBytecodeEnhancementMetadata();
assertThat( supplementalInfoEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
@ -114,6 +115,27 @@ public class OneToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTestCase
);
}
@Test
@TestForIssue(jiraKey = "HHH-14659")
public void testQueryJoinFetch() {
SupplementalInfo info = fromTransaction( (session) -> {
final SupplementalInfo result = session.createQuery(
"select s from SupplementalInfo s join fetch s.customer",
SupplementalInfo.class )
.uniqueResult();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
return result;
} );
// The "join fetch" should have already initialized the property,
// so that the getter can safely be called outside of a session.
assertTrue( Hibernate.isPropertyInitialized( info, "customer" ) );
// The "join fetch" should have already initialized the associated entity.
Customer customer = info.getCustomer();
assertTrue( Hibernate.isInitialized( customer ) );
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
}
@Before
public void createTestData() {
inTransaction(
@ -124,6 +146,7 @@ public class OneToOneAllowProxyTests extends BaseNonConfigCoreFunctionalTestCase
session.persist( supplementalInfo );
}
);
sqlStatementInterceptor.clear();
}
@After

View File

@ -11,6 +11,7 @@ import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.Hibernate;
import org.hibernate.annotations.LazyToOne;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
@ -21,6 +22,7 @@ import org.hibernate.bytecode.spi.BytecodeEnhancementMetadata;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
@ -37,6 +39,7 @@ import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.sameInstance;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hibernate.annotations.LazyToOneOption.NO_PROXY;
import static org.junit.Assert.assertTrue;
/**
* Baseline test for uni-directional one-to-one, using an explicit @LazyToOne(NO_PROXY) and allowing enhanced proxies
@ -62,8 +65,6 @@ public class OneToOneExplicitOptionTests extends BaseNonConfigCoreFunctionalTest
@Test
public void testOwnerIsProxy() {
sqlStatementInterceptor.clear();
final EntityPersister supplementalInfoDescriptor = sessionFactory().getMetamodel().entityPersister( SupplementalInfo.class );
final BytecodeEnhancementMetadata supplementalInfoEnhancementMetadata = supplementalInfoDescriptor.getBytecodeEnhancementMetadata();
assertThat( supplementalInfoEnhancementMetadata.isEnhancedForLazyLoading(), is( true ) );
@ -116,6 +117,27 @@ public class OneToOneExplicitOptionTests extends BaseNonConfigCoreFunctionalTest
);
}
@Test
@TestForIssue(jiraKey = "HHH-14659")
public void testQueryJoinFetch() {
SupplementalInfo info = fromTransaction( (session) -> {
final SupplementalInfo result = session.createQuery(
"select s from SupplementalInfo s join fetch s.customer",
SupplementalInfo.class )
.uniqueResult();
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
return result;
} );
// The "join fetch" should have already initialized the property,
// so that the getter can safely be called outside of a session.
assertTrue( Hibernate.isPropertyInitialized( info, "customer" ) );
// The "join fetch" should have already initialized the associated entity.
Customer customer = info.getCustomer();
assertTrue( Hibernate.isInitialized( customer ) );
assertThat( sqlStatementInterceptor.getSqlQueries().size(), is( 1 ) );
}
@Before
public void createTestData() {
inTransaction(
@ -126,6 +148,7 @@ public class OneToOneExplicitOptionTests extends BaseNonConfigCoreFunctionalTest
session.persist( supplementalInfo );
}
);
sqlStatementInterceptor.clear();
}
@After

View File

@ -20,6 +20,7 @@ import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
@ -72,6 +73,21 @@ public class JoinFetchedPolymorphicToOneTests extends BaseNonConfigCoreFunctiona
);
}
@Test
@TestForIssue(jiraKey = "HHH-14659")
public void testQueryJoinFetch() {
inTransaction(
(session) -> {
final Order order = session.createQuery( "select o from Order o join fetch o.customer", Order.class )
.uniqueResult();
assertTrue( Hibernate.isPropertyInitialized( order, "customer" ) );
Customer customer = order.getCustomer();
assertTrue( Hibernate.isInitialized( customer ) );
}
);
}
@Before
public void createTestData() {
inTransaction(

View File

@ -22,6 +22,7 @@ import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
@ -36,6 +37,7 @@ import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Steve Ebersole
@ -100,6 +102,21 @@ public class PolymorphicToOneExplicitOptionTests extends BaseNonConfigCoreFuncti
);
}
@Test
@TestForIssue(jiraKey = "HHH-14659")
public void testQueryJoinFetch() {
inTransaction(
(session) -> {
final Order order = session.createQuery( "select o from Order o join fetch o.customer", Order.class )
.uniqueResult();
assertTrue( Hibernate.isPropertyInitialized( order, "customer" ) );
Customer customer = order.getCustomer();
assertTrue( Hibernate.isInitialized( customer ) );
}
);
}
@Before
public void createTestData() {
inTransaction(

View File

@ -20,6 +20,7 @@ import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.bytecode.enhancement.EnhancementOptions;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
@ -34,6 +35,7 @@ import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Steve Ebersole
@ -72,6 +74,21 @@ public class PolymorphicToOneImplicitOptionTests extends BaseNonConfigCoreFuncti
);
}
@Test
@TestForIssue(jiraKey = "HHH-14659")
public void testQueryJoinFetch() {
inTransaction(
(session) -> {
final Order order = session.createQuery( "select o from Order o join fetch o.customer", Order.class )
.uniqueResult();
assertTrue( Hibernate.isPropertyInitialized( order, "customer" ) );
Customer customer = order.getCustomer();
assertTrue( Hibernate.isInitialized( customer ) );
}
);
}
@Before
public void createTestData() {
inTransaction(

View File

@ -0,0 +1,154 @@
/*
* 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.graalvm.internal;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicBoolean;
import org.hibernate.internal.build.AllowSysOut;
import org.hibernate.internal.util.ReflectHelper;
import com.oracle.svm.core.annotate.AutomaticFeature;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.RuntimeReflection;
/**
* This registers all ANTLR parser nodes for reflection, something that is necessary
* as the HQL parser's inner workings are based on reflection.
* This is different than the "static" registrations of {@link GraalVMStaticAutofeature}
* as we only register these if the HQL parser is actually reachable: some particularly
* simple applications might not need dynamic queries being expressed in string form,
* and for such cases the reflective registrations can be skipped.
*
* At time of writing, this is particularly unlikely to be effective as Hibernate ORM
* requires the parsers during bootstrap, but there is reasonable hope that this might
* be improved on, and can already be used by framework integrations which are able
* to bypass the traditional boot sequence.
*
* @author Sanne Grinovero
*/
@AutomaticFeature
public final class QueryParsingSupport implements Feature {
private final AtomicBoolean triggered = new AtomicBoolean( false);
/**
* To set this, add `-J-Dorg.hibernate.graalvm.diagnostics=true` to the native-image parameters
*/
private static final boolean log = Boolean.getBoolean( "org.hibernate.graalvm.diagnostics" );
@Override
public void beforeAnalysis(BeforeAnalysisAccess access) {
Class<?> lexerClazz = access.findClassByName("org.hibernate.grammars.hql.HqlLexer");
Class<?> parserClazz = access.findClassByName("org.hibernate.grammars.hql.HqlParser");
access.registerReachabilityHandler(this::enableHQLSupport, lexerClazz);
access.registerReachabilityHandler(this::enableHQLSupport, parserClazz);
}
@AllowSysOut
private void enableHQLSupport(DuringAnalysisAccess duringAnalysisAccess) {
final boolean needsEnablingYet = triggered.compareAndSet( false, true );
if ( needsEnablingYet ) {
if ( log ) {
System.out.println( "Hibernate ORM 's automatic feature for GraalVM native images: enabling support for HQL query parsing" );
}
enableAntlrParsersSupport();
}
}
private void enableAntlrParsersSupport() {
final Class<?>[] needsHavingSimpleConstructors = typesNeedingDefaultConstructorAccessible();
final Class[] neddingAllConstructorsAccessible = typesNeedingAllConstructorsAccessible();
//Size formula is just a reasonable guess:
ArrayList<Executable> executables = new ArrayList<>( needsHavingSimpleConstructors.length + neddingAllConstructorsAccessible.length * 3 );
for ( Class c : needsHavingSimpleConstructors ) {
executables.add( ReflectHelper.getDefaultConstructor( c ) );
}
for ( Class c : neddingAllConstructorsAccessible ) {
for ( Constructor declaredConstructor : c.getDeclaredConstructors() ) {
executables.add( declaredConstructor );
}
}
RuntimeReflection.register( needsHavingSimpleConstructors );
RuntimeReflection.register( neddingAllConstructorsAccessible );
RuntimeReflection.register( executables.toArray(new Executable[0]) );
}
public static Class[] typesNeedingAllConstructorsAccessible() {
return new Class[] {
//ANTLR special ones:
// org.hibernate.hql.internal.ast.tree.EntityJoinFromElement.class,
// org.hibernate.hql.internal.ast.tree.MapKeyEntityFromElement.class,
// org.hibernate.hql.internal.ast.tree.ComponentJoin.class,
};
}
public static Class[] typesNeedingDefaultConstructorAccessible() {
return new Class[] {
//Support for @OrderBy
// org.hibernate.sql.ordering.antlr.NodeSupport.class,
// org.hibernate.sql.ordering.antlr.OrderByFragment.class,
// org.hibernate.sql.ordering.antlr.SortSpecification.class,
// org.hibernate.sql.ordering.antlr.OrderingSpecification.class,
// org.hibernate.sql.ordering.antlr.CollationSpecification.class,
// org.hibernate.sql.ordering.antlr.SortKey.class,
//ANTLR tokens:
// antlr.CommonToken.class,
// org.hibernate.hql.internal.ast.tree.SelectClause.class,
// org.hibernate.hql.internal.ast.tree.HqlSqlWalkerNode.class,
// org.hibernate.hql.internal.ast.tree.MethodNode.class,
// org.hibernate.hql.internal.ast.tree.UnaryLogicOperatorNode.class,
// org.hibernate.hql.internal.ast.tree.NullNode.class,
// org.hibernate.hql.internal.ast.tree.IntoClause.class,
// org.hibernate.hql.internal.ast.tree.UpdateStatement.class,
// org.hibernate.hql.internal.ast.tree.SelectExpressionImpl.class,
// org.hibernate.hql.internal.ast.tree.CastFunctionNode.class,
// org.hibernate.hql.internal.ast.tree.DeleteStatement.class,
// org.hibernate.hql.internal.ast.tree.SqlNode.class,
// org.hibernate.hql.internal.ast.tree.SearchedCaseNode.class,
// org.hibernate.hql.internal.ast.tree.FromElement.class,
// org.hibernate.hql.internal.ast.tree.JavaConstantNode.class,
// org.hibernate.hql.internal.ast.tree.SqlFragment.class,
// org.hibernate.hql.internal.ast.tree.MapKeyNode.class,
// org.hibernate.hql.internal.ast.tree.ImpliedFromElement.class,
// org.hibernate.hql.internal.ast.tree.IsNotNullLogicOperatorNode.class,
// org.hibernate.hql.internal.ast.tree.InsertStatement.class,
// org.hibernate.hql.internal.ast.tree.UnaryArithmeticNode.class,
// org.hibernate.hql.internal.ast.tree.CollectionFunction.class,
// org.hibernate.hql.internal.ast.tree.BinaryLogicOperatorNode.class,
// org.hibernate.hql.internal.ast.tree.CountNode.class,
// org.hibernate.hql.internal.ast.tree.IsNullLogicOperatorNode.class,
// org.hibernate.hql.internal.ast.tree.IdentNode.class,
// org.hibernate.hql.internal.ast.tree.ParameterNode.class,
// org.hibernate.hql.internal.ast.tree.MapEntryNode.class,
// org.hibernate.hql.internal.ast.tree.MapValueNode.class,
// org.hibernate.hql.internal.ast.tree.InLogicOperatorNode.class,
// org.hibernate.hql.internal.ast.tree.IndexNode.class,
// org.hibernate.hql.internal.ast.tree.DotNode.class,
// org.hibernate.hql.internal.ast.tree.ResultVariableRefNode.class,
// org.hibernate.hql.internal.ast.tree.BetweenOperatorNode.class,
// org.hibernate.hql.internal.ast.tree.AggregateNode.class,
// org.hibernate.hql.internal.ast.tree.QueryNode.class,
// org.hibernate.hql.internal.ast.tree.BooleanLiteralNode.class,
// org.hibernate.hql.internal.ast.tree.SimpleCaseNode.class,
// org.hibernate.hql.internal.ast.tree.OrderByClause.class,
// org.hibernate.hql.internal.ast.tree.FromClause.class,
// org.hibernate.hql.internal.ast.tree.ConstructorNode.class,
// org.hibernate.hql.internal.ast.tree.LiteralNode.class,
// org.hibernate.hql.internal.ast.tree.BinaryArithmeticOperatorNode.class,
//Special tokens:
// org.hibernate.hql.internal.ast.HqlToken.class,
// org.hibernate.hql.internal.ast.tree.Node.class,
};
}
}

View File

@ -31,78 +31,18 @@ final class StaticClassLists {
org.hibernate.persister.entity.UnionSubclassEntityPersister.class,
org.hibernate.persister.entity.SingleTableEntityPersister.class,
org.hibernate.tuple.entity.PojoEntityTuplizer.class,
//ANTLR special ones:
// org.hibernate.hql.internal.ast.tree.EntityJoinFromElement.class,
// org.hibernate.hql.internal.ast.tree.MapKeyEntityFromElement.class,
// org.hibernate.hql.internal.ast.tree.ComponentJoin.class,
};
}
public static Class[] typesNeedingDefaultConstructorAccessible() {
return new Class[] {
//Support for @OrderBy
// org.hibernate.sql.ordering.antlr.NodeSupport.class,
// org.hibernate.sql.ordering.antlr.OrderByFragment.class,
// org.hibernate.sql.ordering.antlr.SortSpecification.class,
// org.hibernate.sql.ordering.antlr.OrderingSpecification.class,
// org.hibernate.sql.ordering.antlr.CollationSpecification.class,
// org.hibernate.sql.ordering.antlr.SortKey.class,
//ANTLR tokens:
// antlr.CommonToken.class,
// org.hibernate.hql.internal.ast.tree.SelectClause.class,
// org.hibernate.hql.internal.ast.tree.HqlSqlWalkerNode.class,
// org.hibernate.hql.internal.ast.tree.MethodNode.class,
// org.hibernate.hql.internal.ast.tree.UnaryLogicOperatorNode.class,
// org.hibernate.hql.internal.ast.tree.NullNode.class,
// org.hibernate.hql.internal.ast.tree.IntoClause.class,
// org.hibernate.hql.internal.ast.tree.UpdateStatement.class,
// org.hibernate.hql.internal.ast.tree.SelectExpressionImpl.class,
// org.hibernate.hql.internal.ast.tree.CastFunctionNode.class,
// org.hibernate.hql.internal.ast.tree.DeleteStatement.class,
// org.hibernate.hql.internal.ast.tree.SqlNode.class,
// org.hibernate.hql.internal.ast.tree.SearchedCaseNode.class,
// org.hibernate.hql.internal.ast.tree.FromElement.class,
// org.hibernate.hql.internal.ast.tree.JavaConstantNode.class,
// org.hibernate.hql.internal.ast.tree.SqlFragment.class,
// org.hibernate.hql.internal.ast.tree.MapKeyNode.class,
// org.hibernate.hql.internal.ast.tree.ImpliedFromElement.class,
// org.hibernate.hql.internal.ast.tree.IsNotNullLogicOperatorNode.class,
// org.hibernate.hql.internal.ast.tree.InsertStatement.class,
// org.hibernate.hql.internal.ast.tree.UnaryArithmeticNode.class,
// org.hibernate.hql.internal.ast.tree.CollectionFunction.class,
// org.hibernate.hql.internal.ast.tree.BinaryLogicOperatorNode.class,
// org.hibernate.hql.internal.ast.tree.CountNode.class,
// org.hibernate.hql.internal.ast.tree.IsNullLogicOperatorNode.class,
// org.hibernate.hql.internal.ast.tree.IdentNode.class,
// org.hibernate.hql.internal.ast.tree.ParameterNode.class,
// org.hibernate.hql.internal.ast.tree.MapEntryNode.class,
// org.hibernate.hql.internal.ast.tree.MapValueNode.class,
// org.hibernate.hql.internal.ast.tree.InLogicOperatorNode.class,
// org.hibernate.hql.internal.ast.tree.IndexNode.class,
// org.hibernate.hql.internal.ast.tree.DotNode.class,
// org.hibernate.hql.internal.ast.tree.ResultVariableRefNode.class,
// org.hibernate.hql.internal.ast.tree.BetweenOperatorNode.class,
// org.hibernate.hql.internal.ast.tree.AggregateNode.class,
// org.hibernate.hql.internal.ast.tree.QueryNode.class,
// org.hibernate.hql.internal.ast.tree.BooleanLiteralNode.class,
// org.hibernate.hql.internal.ast.tree.SimpleCaseNode.class,
// org.hibernate.hql.internal.ast.tree.OrderByClause.class,
// org.hibernate.hql.internal.ast.tree.FromClause.class,
// org.hibernate.hql.internal.ast.tree.ConstructorNode.class,
// org.hibernate.hql.internal.ast.tree.LiteralNode.class,
// org.hibernate.hql.internal.ast.tree.BinaryArithmeticOperatorNode.class,
//Various well known needs:
org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl.class,
org.hibernate.id.enhanced.SequenceStyleGenerator.class,
org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl.class,
org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl.class,
EnumType.class,
MultipleLinesSqlCommandExtractor.class,
// org.hibernate.hql.internal.ast.HqlToken.class,
// org.hibernate.hql.internal.ast.tree.Node.class,
};
}