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

This commit is contained in:
Andrea Boriero 2021-03-05 11:18:14 +01:00
commit 6250942e7f
15 changed files with 981 additions and 33 deletions

View File

@ -434,7 +434,7 @@ Enable lazy loading feature in runtime bytecode enhancement. This way, even basi
Enable association management feature in runtime bytecode enhancement which automatically synchronizes a bidirectional association when only one side is changed.
`*hibernate.bytecode.provider*` (e.g. `bytebuddy` (default value))::
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/bytecode/spi/BytecodeProvider.html[`BytecodeProvider`] built-in implementation flavor. Currently, only `bytebuddy` and `javassist` are valid values.
The https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/bytecode/spi/BytecodeProvider.html[`BytecodeProvider`] built-in implementation flavor. Currently, only `bytebuddy` and `javassist` are valid values; `bytebuddy` is the default and recommended choice; `javassist` will be removed soon.
`*hibernate.bytecode.use_reflection_optimizer*` (e.g. `true` or `false` (default value))::
Should we use reflection optimization? The reflection optimizer implements the https://docs.jboss.org/hibernate/orm/{majorMinorVersion}/javadocs/org/hibernate/bytecode/spi/ReflectionOptimizer.html[`ReflectionOptimizer`] interface and improves entity instantiation and property getter/setter calls.

View File

@ -30,6 +30,7 @@ import org.hibernate.boot.jaxb.Origin;
import org.hibernate.boot.jaxb.SourceType;
import org.hibernate.boot.model.convert.internal.ClassBasedConverterDescriptor;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.ClassLoaderAccess;
import org.hibernate.boot.spi.XmlMappingBinderAccess;
@ -269,6 +270,16 @@ public class ScanningCoordinator {
continue;
}
// Last, try it by loading the class
try {
Class<?> clazz = classLoaderService.classForName( unresolvedListedClassName );
managedResources.addAnnotatedClassReference( clazz );
continue;
}
catch (ClassLoadingException ignore) {
// ignore this error
}
if ( log.isDebugEnabled() ) {
log.debugf(
"Unable to resolve class [%s] named in persistence unit [%s]",

View File

@ -238,18 +238,6 @@ public class OneToOneSecondPass implements SecondPass {
boolean referenceToPrimaryKey = referencesDerivedId || mappedBy == null;
value.setReferenceToPrimaryKey( referenceToPrimaryKey );
// If the other side is an entity with an ID that is derived from
// this side's owner entity, and both sides of the association are eager,
// then this side must be set to FetchMode.SELECT; otherwise,
// there will be an infinite loop attempting to load the derived ID on
// the opposite side.
if ( referencesDerivedId &&
!value.isLazy() &&
value.getFetchMode() == FetchMode.JOIN &&
!otherSideProperty.isLazy() ) {
value.setFetchMode( FetchMode.SELECT );
}
String propertyRef = value.getReferencedPropertyName();
if ( propertyRef != null ) {
buildingContext.getMetadataCollector().addUniquePropertyReference(

View File

@ -642,6 +642,10 @@ public abstract class AbstractPersistentCollection<E> implements Serializable, P
LOG.queuedOperationWhenDetachFromSession( collectionInfoString );
}
}
if ( allowLoadOutsideTransaction && !initialized && session.getLoadQueryInfluencers().hasEnabledFilters() ) {
final String collectionInfoString = MessageHelper.collectionInfoString( getRole(), getKey() );
LOG.enabledFiltersWhenDetachFromSession( collectionInfoString );
}
this.session = null;
}
return true;

View File

@ -185,7 +185,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
this.transactionCoordinator = sharedOptions.getTransactionCoordinator();
this.jdbcCoordinator = sharedOptions.getJdbcCoordinator();
// todo : "wrap" the transaction to no-op comit/rollback attempts?
// todo : "wrap" the transaction to no-op commit/rollback attempts?
this.currentHibernateTransaction = sharedOptions.getTransaction();
if ( sharedOptions.shouldAutoJoinTransactions() ) {
@ -292,7 +292,7 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
@Override
public UUID getSessionIdentifier() {
if ( this.sessionIdentifier == null ) {
//Lazily initialized: otherwise all the UUID generations will cause of significant amount of contention.
//Lazily initialized: otherwise all the UUID generations will cause significant amount of contention.
this.sessionIdentifier = StandardRandomStrategy.INSTANCE.generateUUID( null );
}
return sessionIdentifier;

View File

@ -1828,4 +1828,13 @@ public interface CoreMessageLogger extends BasicLogger {
@Message(value = "Ignoring ServiceConfigurationError caught while trying to instantiate service '%s'.", id = 505)
void ignoringServiceConfigurationError(Class<?> serviceContract, @Cause ServiceConfigurationError error);
@LogMessage(level = WARN)
@Message(value = "Detaching an uninitialized collection with enabled filters from a session: %s", id = 506)
void enabledFiltersWhenDetachFromSession(String collectionInfoString);
@LogMessage(level = WARN)
@Message(value = "The Javassist based BytecodeProvider is deprecated. Please switch to using the ByteBuddy based BytecodeProvider, " +
"which is the default since Hibernate ORM 5.3. The Javassist one will be removed soon.", id = 507)
void warnUsingJavassistBytecodeProviderIsDeprecated();
}

View File

@ -1332,30 +1332,48 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
// entityGraphHintedQueryPlan = null;
// }
// else {
// final SharedSessionContractImplementor producer = getProducer();
// entityGraphHintedQueryPlan = new HQLQueryPlan(
// hql,
// false,
// getSession().getLoadQueryInfluencers().getEnabledFilters(),
// getSession().getFactory(),
// producer.getLoadQueryInfluencers().getEnabledFilters(),
// producer.getFactory(),
// entityGraphQueryHint
// );
// }
//
// final QueryParameters queryParameters = new QueryParameters(
// QueryParameters queryParameters = new QueryParameters(
// getQueryParameterBindings(),
// getLockOptions(),
// queryOptions,
// true,
// isReadOnly(),
// cacheable,
// cacheRegion,
// comment,
// dbHints,
// null,
// optionalObject,
// optionalEntityName,
// optionalId,
// resultTransformer
// );
// queryParameters.setQueryPlan( entityGraphHintedQueryPlan );
//
// appendQueryPlanToQueryParameters( hql, queryParameters, entityGraphHintedQueryPlan );
//
// if ( passDistinctThrough != null ) {
// queryParameters.setPassDistinctThrough( passDistinctThrough );
// }
// return queryParameters;
// }
// protected void appendQueryPlanToQueryParameters(
// String hql,
// QueryParameters queryParameters,
// HQLQueryPlan queryPlan) {
// if ( queryPlan != null ) {
// queryParameters.setQueryPlan( queryPlan );
// }
// }
private FlushMode sessionFlushMode;
@ -1551,7 +1569,7 @@ public abstract class AbstractProducedQuery<R> implements QueryImplementor<R> {
this.optionalObject = optionalObject;
}
private boolean isSelect() {
protected boolean isSelect() {
throw new NotYetImplementedFor6Exception( getClass() );
// return getSession().getFactory().getQueryInterpretationCache()

View File

@ -13,6 +13,7 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.hibernate.EntityMode;
@ -517,5 +518,18 @@ public class AnyType extends AbstractType implements CompositeType, AssociationT
this.entityName = entityName;
this.id = id;
}
public int hashCode() {
return Objects.hash( entityName, id );
}
public boolean equals(Object object) {
if (object instanceof ObjectTypeCacheEntry) {
ObjectTypeCacheEntry objectTypeCacheEntry = (ObjectTypeCacheEntry)object;
return Objects.equals( objectTypeCacheEntry.entityName, entityName ) && Objects.equals( objectTypeCacheEntry.id, id );
}
return false;
}
}
}

View File

@ -24,10 +24,10 @@ import org.hibernate.boot.archive.spi.InputStreamAccess;
import org.hibernate.boot.model.convert.spi.ConverterDescriptor;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.boot.registry.classloading.spi.ClassLoadingException;
import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.XmlMappingBinderAccess;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.hibernate.testing.logger.LoggerInspectionRule;
@ -41,6 +41,11 @@ import org.jboss.logging.Logger;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
@ -67,9 +72,11 @@ public class ScanningCoordinatorTest extends BaseUnitTestCase {
@Before
public void init() {
Mockito.reset( managedResources );
Mockito.reset( scanResult );
Mockito.reset( bootstrapContext );
Mockito.reset( scanEnvironment );
Mockito.reset( classLoaderService );
when( bootstrapContext.getScanEnvironment() ).thenReturn( scanEnvironment );
when( bootstrapContext.getServiceRegistry() ).thenReturn( serviceRegistry );
@ -78,7 +85,9 @@ public class ScanningCoordinatorTest extends BaseUnitTestCase {
when( scanEnvironment.getExplicitlyListedClassNames() ).thenReturn(
Arrays.asList( "a.b.C" ) );
when( classLoaderService.classForName( "a.b.C" ) ).thenReturn( Object.class );
when( classLoaderService.classForName( eq( "a.b.C" ) ) ).thenThrow( ClassLoadingException.class );
when( classLoaderService.locateResource( eq( "a/b/c.class" ) ) ).thenReturn( null );
when( classLoaderService.locateResource( eq( "a/b/c/package-info.class" ) ) ).thenReturn( null );
triggerable = logInspection.watchForLogMessages( "Unable" );
triggerable.reset();
@ -110,6 +119,24 @@ public class ScanningCoordinatorTest extends BaseUnitTestCase {
assertEquals( "Unable to resolve class [a.b.C] named in persistence unit [http://http://hibernate.org/]", triggerable.triggerMessage() );
}
@Test
@TestForIssue(jiraKey = "HHH-14473")
public void testApplyScanResultsToManagedResultsWhileExplicitClassNameLoadable() {
Class<Object> expectedClass = Object.class;
when( classLoaderService.classForName( eq( "a.b.C" ) ) ).thenReturn( expectedClass );
ScanningCoordinator.INSTANCE.applyScanResultsToManagedResources(
managedResources,
scanResult,
bootstrapContext,
xmlMappingBinderAccess
);
verify( managedResources, times( 0 ) ).addAnnotatedClassName( any() );
verify( managedResources, times( 1 ) ).addAnnotatedClassReference( same( expectedClass ) );
verify( classLoaderService, times( 1 ) ).classForName( eq( "a.b.C" ) );
}
@Test
@TestForIssue(jiraKey = "HHH-12505")
public void testManagedResourcesAfterCoordinateScanWithDisabledScanner() {

View File

@ -6,22 +6,28 @@
*/
package org.hibernate.stat.internal;
import java.util.List;
import java.util.Map;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.LockModeType;
import javax.persistence.NamedQuery;
import javax.persistence.Tuple;
import javax.persistence.TypedQuery;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Environment;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.stat.QueryStatistics;
import org.hibernate.stat.Statistics;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
@ -39,6 +45,7 @@ public class QueryPlanCacheStatisticsTest extends BaseEntityManagerFunctionalTes
};
}
@Override
protected void addConfigOptions(Map options) {
options.put( Environment.GENERATE_STATISTICS, "true" );
}
@ -63,6 +70,7 @@ public class QueryPlanCacheStatisticsTest extends BaseEntityManagerFunctionalTes
@Test
public void test() {
statistics.clear();
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
assertEquals( 0, statistics.getQueryPlanCacheMissCount() );
@ -115,6 +123,165 @@ public class QueryPlanCacheStatisticsTest extends BaseEntityManagerFunctionalTes
} );
}
@Test
@TestForIssue( jiraKey = "HHH-13077" )
public void testCreateQueryHitCount() {
statistics.clear();
doInJPA( this::entityManagerFactory, entityManager -> {
List<Employee> employees = entityManager.createQuery(
"select e from Employee e", Employee.class )
.getResultList();
assertEquals( 5, employees.size() );
//First time, we get a cache miss, so the query is compiled
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
//The hit count should be 0 as we don't need to go to the cache after we already compiled the query
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
List<Employee> employees = entityManager.createQuery(
"select e from Employee e", Employee.class )
.getResultList();
assertEquals( 5, employees.size() );
//The miss count is still 1, as no we got the query plan from the cache
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
//And the cache hit count increases.
assertEquals( 1, statistics.getQueryPlanCacheHitCount() );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
List<Employee> employees = entityManager.createQuery(
"select e from Employee e", Employee.class )
.getResultList();
assertEquals( 5, employees.size() );
//The miss count is still 1, as no we got the query plan from the cache
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
//And the cache hit count increases.
assertEquals( 2, statistics.getQueryPlanCacheHitCount() );
} );
}
@Test
@TestForIssue( jiraKey = "HHH-13077" )
public void testCreateNamedQueryHitCount() {
//This is for the NamedQuery that gets compiled
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
statistics.clear();
doInJPA( this::entityManagerFactory, entityManager -> {
Employee employees = entityManager.createNamedQuery(
"find_employee_by_name", Employee.class )
.setParameter( "name", "Employee: 1" )
.getSingleResult();
//The miss count is 0 because the plan was compiled when the EMF was built, and we cleared the Statistics
assertEquals( 0, statistics.getQueryPlanCacheMissCount() );
//The hit count is 1 since we got the plan from the cache
assertEquals( 1, statistics.getQueryPlanCacheHitCount() );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
Employee employees = entityManager.createNamedQuery(
"find_employee_by_name", Employee.class )
.setParameter( "name", "Employee: 1" )
.getSingleResult();
//The miss count is still 0 because the plan was compiled when the EMF was built, and we cleared the Statistics
assertEquals( 0, statistics.getQueryPlanCacheMissCount() );
//The hit count is 2 since we got the plan from the cache twice
assertEquals( 2, statistics.getQueryPlanCacheHitCount() );
} );
}
@Test
@TestForIssue( jiraKey = "HHH-13077" )
public void testCreateQueryTupleHitCount() {
statistics.clear();
doInJPA( this::entityManagerFactory, entityManager -> {
List<Tuple> employees = entityManager.createQuery(
"select e.id, e.name from Employee e", Tuple.class )
.getResultList();
assertEquals( 5, employees.size() );
//First time, we get a cache miss, so the query is compiled
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
//The hit count should be 0 as we don't need to go to the cache after we already compiled the query
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
List<Tuple> employees = entityManager.createQuery(
"select e.id, e.name from Employee e", Tuple.class )
.getResultList();
assertEquals( 5, employees.size() );
//The miss count is still 1, as no we got the query plan from the cache
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
//And the cache hit count increases.
assertEquals( 1, statistics.getQueryPlanCacheHitCount() );
} );
doInJPA( this::entityManagerFactory, entityManager -> {
List<Tuple> employees = entityManager.createQuery(
"select e.id, e.name from Employee e", Tuple.class )
.getResultList();
assertEquals( 5, employees.size() );
//The miss count is still 1, as no we got the query plan from the cache
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
//And the cache hit count increases.
assertEquals( 2, statistics.getQueryPlanCacheHitCount() );
} );
}
@Test
@TestForIssue(jiraKey = "HHH-13077")
public void testLockModeHitCount() {
statistics.clear();
doInJPA( this::entityManagerFactory, entityManager -> {
TypedQuery<Employee> typedQuery = entityManager.createQuery( "select e from Employee e", Employee.class );
List<Employee> employees = typedQuery.getResultList();
assertEquals( 5, employees.size() );
//First time, we get a cache miss, so the query is compiled
assertEquals( 1, statistics.getQueryPlanCacheMissCount() );
//The hit count should be 0 as we don't need to go to the cache after we already compiled the query
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
typedQuery.setLockMode( LockModeType.READ );
//The hit count should still be 0 as setLockMode() shouldn't trigger a cache hit
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
assertNotNull( typedQuery.getLockMode() );
//The hit count should still be 0 as getLockMode() shouldn't trigger a cache hit
assertEquals( 0, statistics.getQueryPlanCacheHitCount() );
} );
}
private void assertQueryStatistics(String hql, int hitCount) {
QueryStatistics queryStatistics = statistics.getQueryStatistics( hql );
@ -125,6 +292,10 @@ public class QueryPlanCacheStatisticsTest extends BaseEntityManagerFunctionalTes
}
@Entity(name = "Employee")
@NamedQuery(
name = "find_employee_by_name",
query = "select e from Employee e where e.name = :name"
)
public static class Employee {
@Id

View File

@ -0,0 +1,236 @@
/*
* 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.test.annotations.derivedidentities.bidirectional;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.Hibernate;
import org.hibernate.testing.TestForIssue;
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;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
public class ManyToOneEagerDerivedIdFetchModeJoinTest extends BaseCoreFunctionalTestCase {
private Foo foo;
@Test
@TestForIssue(jiraKey = "HHH-14466")
public void testQuery() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = (Bar) session.createQuery( "SELECT b FROM Bar b WHERE b.foo.id = :id" )
.setParameter( "id", foo.getId() )
.uniqueResult();
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertTrue( Hibernate.isInitialized( newBar.getFoo() ) );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertTrue( Hibernate.isInitialized( newBar.getFoo().getBars() ) );
assertEquals( 1, newBar.getFoo().getBars().size() );
assertSame( newBar, newBar.getFoo().getBars().iterator().next() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@TestForIssue(jiraKey = "HHH-14466")
public void testQueryById() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = (Bar) session.createQuery( "SELECT b FROM Bar b WHERE b.foo = :foo" )
.setParameter( "foo", foo )
.uniqueResult();
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertTrue( Hibernate.isInitialized( newBar.getFoo() ) );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertTrue( Hibernate.isInitialized( newBar.getFoo().getBars() ) );
assertEquals( 1, newBar.getFoo().getBars().size() );
assertSame( newBar, newBar.getFoo().getBars().iterator().next() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@TestForIssue(jiraKey = "HHH-14466")
public void testFindByPrimaryKey() {
doInHibernate( this::sessionFactory, session -> {
Bar newBar = session.find( Bar.class, foo.getId() );
assertNotNull( newBar );
assertNotNull( newBar.getFoo() );
assertTrue( Hibernate.isInitialized( newBar.getFoo() ) );
assertEquals( foo.getId(), newBar.getFoo().getId() );
assertTrue( Hibernate.isInitialized( newBar.getFoo().getBars() ) );
assertEquals( 1, newBar.getFoo().getBars().size() );
assertSame( newBar, newBar.getFoo().getBars().iterator().next() );
assertEquals( "Some details", newBar.getDetails() );
});
}
@Test
@TestForIssue(jiraKey = "HHH-14466")
public void testFindByInversePrimaryKey() {
doInHibernate( this::sessionFactory, session -> {
Foo newFoo = session.find( Foo.class, foo.getId() );
assertNotNull( newFoo );
assertNotNull( newFoo.getBars() );
assertTrue( Hibernate.isInitialized( newFoo.getBars() ) );
assertEquals( 1, newFoo.getBars().size() );
assertSame( newFoo, newFoo.getBars().iterator().next().getFoo() );
assertEquals( "Some details", newFoo.getBars().iterator().next().getDetails() );
});
}
@Before
public void setupData() {
this.foo = doInHibernate( this::sessionFactory, session -> {
Foo foo = new Foo();
foo.id = 1L;
session.persist( foo );
Bar bar = new Bar();
bar.setFoo( foo );
bar.setDetails( "Some details" );
foo.getBars().add( bar );
session.persist( bar );
session.flush();
assertNotNull( foo.getId() );
assertEquals( foo.getId(), bar.getFoo().getId() );
return foo;
});
}
@After
public void cleanupData() {
doInHibernate( this::sessionFactory, session -> {
session.delete( session.find( Foo.class, foo.id ) );
});
this.foo = null;
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Foo.class,
Bar.class,
};
}
@Entity(name = "Foo")
public static class Foo {
@Id
private Long id;
private String name;
@OneToMany(mappedBy = "foo", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<Bar> bars = new HashSet<>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Set<Bar> getBars() {
return bars;
}
public void setBars(Set<Bar> bars) {
this.bars = bars;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
Foo foo = (Foo) o;
return id.equals( foo.id );
}
@Override
public int hashCode() {
return Objects.hash( id );
}
}
@Entity(name = "Bar")
public static class Bar implements Serializable {
@Id
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "BAR_ID")
private Foo foo;
private String details;
public Foo getFoo() {
return foo;
}
public void setFoo(Foo foo) {
this.foo = foo;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
Bar bar = (Bar) o;
return foo.equals( bar.foo );
}
@Override
public int hashCode() {
return Objects.hash( foo );
}
}
}

View File

@ -0,0 +1,215 @@
/*
* 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.test.annotations.query;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.TypedQuery;
import org.hibernate.Hibernate;
import org.hibernate.annotations.Fetch;
import org.hibernate.annotations.FetchMode;
import org.hibernate.cfg.Configuration;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests the handling and expansion of list parameters,
* particularly when using {@code @Fetch(FetchMode.SUBSELECT)}
* (because this fetch mode involves building a map of parameters).
*/
public class QueryListParametersWithFetchSubSelectTest extends BaseCoreFunctionalTestCase {
private SQLStatementInterceptor sqlStatementInterceptor;
@Override
protected void configure(Configuration configuration) {
sqlStatementInterceptor = new SQLStatementInterceptor( configuration );
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { Parent.class, Child.class };
}
@Override
protected void afterSessionFactoryBuilt() {
inTransaction( s -> {
for ( int i = 0; i < 10; i++ ) {
Parent parent = new Parent( i );
s.persist( parent );
for ( int j = 0; j < 10; j++ ) {
Child child = new Child( i * 100 + j, parent );
parent.children.add( child );
s.persist( child );
}
}
} );
}
@Test
public void simple() {
sqlStatementInterceptor.clear();
inTransaction( s -> {
TypedQuery<Parent> query = s.createQuery( "select p from Parent p where id in :ids", Parent.class );
query.setParameter( "ids", Arrays.asList( 0, 1, 2 ) );
List<Parent> results = query.getResultList();
assertThat( results )
.allSatisfy( parent -> assertThat( Hibernate.isInitialized( parent.getChildren() ) ).isTrue() )
.extracting( Parent::getId ).containsExactly( 0, 1, 2 );
} );
// If we get here, children were initialized eagerly.
// Did ORM actually use subselects?
assertThat( sqlStatementInterceptor.getSqlQueries() ).hasSize( 2 );
}
@Test
@TestForIssue(jiraKey = "HHH-14439")
public void reusingQueryWithFewerNamedParameters() {
sqlStatementInterceptor.clear();
inTransaction( s -> {
TypedQuery<Parent> query = s.createQuery( "select p from Parent p where id in :ids", Parent.class );
query.setParameter( "ids", Arrays.asList( 0, 1, 2, 3 ) );
List<Parent> results = query.getResultList();
assertThat( results )
.allSatisfy( parent -> assertThat( Hibernate.isInitialized( parent.getChildren() ) ).isTrue() )
.extracting( Parent::getId ).containsExactly( 0, 1, 2, 3 );
query.setParameter( "ids", Arrays.asList( 4, 5, 6 ) );
results = query.getResultList();
assertThat( results )
.allSatisfy( parent -> assertThat( Hibernate.isInitialized( parent.getChildren() ) ).isTrue() )
.extracting( Parent::getId ).containsExactly( 4, 5, 6 );
query.setParameter( "ids", Arrays.asList( 7, 8 ) );
results = query.getResultList();
assertThat( results )
.allSatisfy( parent -> assertThat( Hibernate.isInitialized( parent.getChildren() ) ).isTrue() )
.extracting( Parent::getId ).containsExactly( 7, 8 );
} );
// If we get here, children were initialized eagerly.
// Did ORM actually use subselects?
assertThat( sqlStatementInterceptor.getSqlQueries() ).hasSize( 3 * 2 );
}
@Test
@TestForIssue(jiraKey = "HHH-14439")
public void reusingQueryWithFewerOrdinalParameters() {
sqlStatementInterceptor.clear();
inTransaction( s -> {
TypedQuery<Parent> query = s.createQuery( "select p from Parent p where id in ?0", Parent.class );
query.setParameter( 0, Arrays.asList( 0, 1, 2, 3 ) );
List<Parent> results = query.getResultList();
assertThat( results )
.allSatisfy( parent -> assertThat( Hibernate.isInitialized( parent.getChildren() ) ).isTrue() )
.extracting( Parent::getId ).containsExactly( 0, 1, 2, 3 );
query.setParameter( 0, Arrays.asList( 4, 5, 6 ) );
results = query.getResultList();
assertThat( results )
.allSatisfy( parent -> assertThat( Hibernate.isInitialized( parent.getChildren() ) ).isTrue() )
.extracting( Parent::getId ).containsExactly( 4, 5, 6 );
query.setParameter( 0, Arrays.asList( 7, 8 ) );
results = query.getResultList();
assertThat( results )
.allSatisfy( parent -> assertThat( Hibernate.isInitialized( parent.getChildren() ) ).isTrue() )
.extracting( Parent::getId ).containsExactly( 7, 8 );
} );
// If we get here, children were initialized eagerly.
// Did ORM actually use subselects?
assertThat( sqlStatementInterceptor.getSqlQueries() ).hasSize( 3 * 2 );
}
@Entity(name = "Parent")
public static class Parent {
@Id
private Integer id;
@OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
@Fetch(FetchMode.SUBSELECT)
private List<Child> children = new ArrayList<>();
public Parent() {
}
public Parent(Integer id) {
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public List<Child> getChildren() {
return children;
}
public void setChildren(List<Child> children) {
this.children = children;
}
}
@Entity(name = "Child")
public static class Child {
@Id
private Integer id;
@ManyToOne
private Parent parent;
public Child() {
}
public Child(Integer id, Parent parent) {
this.id = id;
this.parent = parent;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Parent getParent() {
return parent;
}
public void setParent(Parent parent) {
this.parent = parent;
}
}
}

View File

@ -0,0 +1,149 @@
/*
* 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.test.hql;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.persistence.CollectionTable;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MapKeyColumn;
import javax.persistence.OneToMany;
import org.hibernate.dialect.DerbyDialect;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.SkipForDialect;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
/**
* @author Christian Beikov
*/
public class CountExpressionTest extends BaseCoreFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
Document.class,
Person.class
};
}
@Override
protected void prepareTest() throws Exception {
doInHibernate( this::sessionFactory, session -> {
Document document = new Document();
document.setId( 1 );
Person p1 = new Person();
Person p2 = new Person();
p1.getLocalized().put(1, "p1.1");
p1.getLocalized().put(2, "p1.2");
p2.getLocalized().put(1, "p2.1");
p2.getLocalized().put(2, "p2.2");
document.getContacts().put(1, p1);
document.getContacts().put(2, p2);
session.persist(p1);
session.persist(p2);
session.persist(document);
} );
}
@Override
protected boolean isCleanupTestDataRequired() {
return true;
}
@Test
@TestForIssue(jiraKey = "HHH-9182")
@SkipForDialect(value = DerbyDialect.class, comment = "Derby can't cast from integer to varchar i.e. it requires an intermediary step")
public void testCountDistinctExpression() {
doInHibernate( this::sessionFactory, session -> {
List results = session.createQuery(
"SELECT " +
" d.id, " +
" COUNT(DISTINCT CONCAT(CAST(KEY(l) AS java.lang.String), 'test')) " +
"FROM Document d " +
"LEFT JOIN d.contacts c " +
"LEFT JOIN c.localized l " +
"GROUP BY d.id")
.getResultList();
assertEquals(1, results.size());
Object[] tuple = (Object[]) results.get( 0 );
assertEquals(1, tuple[0]);
} );
}
@Entity(name = "Document")
public static class Document {
private Integer id;
private Map<Integer, Person> contacts = new HashMap<>();
@Id
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@OneToMany
@CollectionTable
@MapKeyColumn(name = "position")
public Map<Integer, Person> getContacts() {
return contacts;
}
public void setContacts(Map<Integer, Person> contacts) {
this.contacts = contacts;
}
}
@Entity(name = "Person")
public static class Person {
private Integer id;
private Map<Integer, String> localized = new HashMap<>();
@Id
@GeneratedValue
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
@ElementCollection
public Map<Integer, String> getLocalized() {
return localized;
}
public void setLocalized(Map<Integer, String> localized) {
this.localized = localized;
}
}
}

View File

@ -138,6 +138,23 @@ public class MetaAttributeGenerationVisitor extends SimpleTypeVisitor6<Annotatio
accessTypeInfo.setDefaultAccessType( entity.getEntityAccessTypeInfo().getAccessType() );
}
}
if ( TypeUtils.containsAnnotation(
element,
Constants.BASIC,
Constants.CONVERT,
Constants.HIBERNATE_TYPE
) && !TypeUtils.containsAnnotation(
element,
Constants.ONE_TO_MANY,
Constants.MANY_TO_MANY,
Constants.ELEMENT_COLLECTION
) ) {
return new AnnotationMetaSingleAttribute(
entity,
element,
TypeUtils.toTypeString( declaredType )
);
}
if ( collection.equals( Constants.MAP_ATTRIBUTE ) ) {
return createAnnotationMetaAttributeForMap( declaredType, element, collection, targetEntity );
}

View File

@ -0,0 +1,89 @@
/*
* 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.jpamodelgen.test.collectionbasictype;
import org.hibernate.jpamodelgen.test.util.CompilationTest;
import org.hibernate.jpamodelgen.test.util.TestForIssue;
import org.hibernate.jpamodelgen.test.util.WithClasses;
import org.junit.Test;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertAttributeTypeInMetaModelFor;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertListAttributeTypeInMetaModelFor;
import static org.hibernate.jpamodelgen.test.util.TestUtil.assertMetamodelClassGeneratedFor;
/**
* @author helloztt
*/
public class CollectionAsBasicTypeTest extends CompilationTest {
@Test
@TestForIssue(jiraKey = "HHH-12338")
@WithClasses({Goods.class, Product.class})
public void testConvert() throws ClassNotFoundException, NoSuchFieldException {
assertMetamodelClassGeneratedFor(Product.class);
assertMetamodelClassGeneratedFor(Goods.class);
assertListAttributeTypeInMetaModelFor(
Goods.class,
"productList",
Product.class,
"ListAttribute generic type should be Product"
);
assertAttributeTypeInMetaModelFor(
Goods.class,
"tags",
Goods.class.getDeclaredField("tags").getGenericType(),
"Wrong meta model type"
);
}
@Test
@TestForIssue(jiraKey = "HHH-12338")
@WithClasses({Person.class})
public void testListType() throws ClassNotFoundException, NoSuchFieldException {
assertMetamodelClassGeneratedFor(Person.class);
assertAttributeTypeInMetaModelFor(
Person.class,
"phones",
Person.class.getDeclaredField("phones").getGenericType(),
"Wrong meta model type"
);
}
@Test
@TestForIssue(jiraKey = "HHH-12338")
@WithClasses({PersonPhone.class})
public void testListTypeWithImport() throws ClassNotFoundException, NoSuchFieldException {
assertMetamodelClassGeneratedFor(PersonPhone.class);
assertAttributeTypeInMetaModelFor(
PersonPhone.class,
"phones",
PersonPhone.class.getDeclaredField("phones").getGenericType(),
"Wrong meta model type"
);
}
@Test
@TestForIssue(jiraKey = "HHH-12338")
@WithClasses({PhoneBook.class})
public void testMapType() throws ClassNotFoundException, NoSuchFieldException {
assertMetamodelClassGeneratedFor(PhoneBook.class);
assertAttributeTypeInMetaModelFor(
PhoneBook.class,
"phones",
PhoneBook.class.getDeclaredField("phones").getGenericType(),
"Wrong meta model type"
);
}
}