Merge branch 'main' into wip/6.0

This commit is contained in:
Andrea Boriero 2021-07-16 11:24:53 +02:00
commit 98e64579fa
40 changed files with 827 additions and 93 deletions

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

BIN
gradlew.bat vendored

Binary file not shown.

View File

@ -0,0 +1,92 @@
/*
* 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.boot.model.naming;
import java.util.Locale;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
/**
* Originally copied from Spring Boot as this strategy is popular there
* (original name is SpringPhysicalNamingStrategy).
*
* @author Phillip Webb
* @author Madhura Bhave
*/
public class CamelCaseToUnderscoresNamingStrategy implements PhysicalNamingStrategy {
@Override
public Identifier toPhysicalCatalogName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return apply( name, jdbcEnvironment );
}
@Override
public Identifier toPhysicalSchemaName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return apply( name, jdbcEnvironment );
}
@Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return apply( name, jdbcEnvironment );
}
@Override
public Identifier toPhysicalSequenceName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return apply( name, jdbcEnvironment );
}
@Override
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment jdbcEnvironment) {
return apply( name, jdbcEnvironment );
}
private Identifier apply(final Identifier name, final JdbcEnvironment jdbcEnvironment) {
if ( name == null ) {
return null;
}
StringBuilder builder = new StringBuilder( name.getText().replace( '.', '_' ) );
for ( int i = 1; i < builder.length() - 1; i++ ) {
if ( isUnderscoreRequired( builder.charAt( i - 1 ), builder.charAt( i ), builder.charAt( i + 1 ) ) ) {
builder.insert( i++, '_' );
}
}
return getIdentifier( builder.toString(), name.isQuoted(), jdbcEnvironment );
}
/**
* Get an identifier for the specified details. By default this method will return an identifier
* with the name adapted based on the result of {@link #isCaseInsensitive(JdbcEnvironment)}
*
* @param name the name of the identifier
* @param quoted if the identifier is quoted
* @param jdbcEnvironment the JDBC environment
*
* @return an identifier instance
*/
protected Identifier getIdentifier(String name, final boolean quoted, final JdbcEnvironment jdbcEnvironment) {
if ( isCaseInsensitive( jdbcEnvironment ) ) {
name = name.toLowerCase( Locale.ROOT );
}
return new Identifier( name, quoted );
}
/**
* Specify whether the database is case sensitive.
*
* @param jdbcEnvironment the JDBC environment which can be used to determine case
*
* @return true if the database is case insensitive sensitivity
*/
protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
return true;
}
private boolean isUnderscoreRequired(final char before, final char current, final char after) {
return Character.isLowerCase( before ) && Character.isUpperCase( current ) && Character.isLowerCase( after );
}
}

View File

@ -630,7 +630,7 @@ public void reassociateProxy(Object value, Object id) throws MappingException {
private void reassociateProxy(LazyInitializer li, HibernateProxy proxy) { private void reassociateProxy(LazyInitializer li, HibernateProxy proxy) {
if ( li.getSession() != this.getSession() ) { if ( li.getSession() != this.getSession() ) {
final EntityPersister persister = session.getFactory().getMetamodel().entityPersister( li.getEntityName() ); final EntityPersister persister = session.getFactory().getMetamodel().entityPersister( li.getEntityName() );
final EntityKey key = session.generateEntityKey( li.getIdentifier(), persister ); final EntityKey key = session.generateEntityKey( li.getInternalIdentifier(), persister );
// any earlier proxy takes precedence // any earlier proxy takes precedence
getOrInitializeProxiesByKey().putIfAbsent( key, proxy ); getOrInitializeProxiesByKey().putIfAbsent( key, proxy );
proxy.getHibernateLazyInitializer().setSession( session ); proxy.getHibernateLazyInitializer().setSession( session );
@ -1338,7 +1338,7 @@ && isFoundInParent( propertyName, childEntity, persister, collectionPersister, p
); );
} }
if ( found ) { if ( found ) {
return proxy.getHibernateLazyInitializer().getIdentifier(); return proxy.getHibernateLazyInitializer().getInternalIdentifier();
} }
} }
} }

View File

@ -52,7 +52,7 @@ public void onEvict(EvictEvent event) throws HibernateException {
if ( object instanceof HibernateProxy ) { if ( object instanceof HibernateProxy ) {
final LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer(); final LazyInitializer li = ( (HibernateProxy) object ).getHibernateLazyInitializer();
final Object id = li.getIdentifier(); final Object id = li.getInternalIdentifier();
if ( id == null ) { if ( id == null ) {
throw new IllegalArgumentException( "Could not determine identifier of proxy passed to evict()" ); throw new IllegalArgumentException( "Could not determine identifier of proxy passed to evict()" );
} }

View File

@ -103,7 +103,7 @@ public void onMerge(MergeEvent event, Map copiedAlready) throws HibernateExcepti
LazyInitializer li = ( (HibernateProxy) original ).getHibernateLazyInitializer(); LazyInitializer li = ( (HibernateProxy) original ).getHibernateLazyInitializer();
if ( li.isUninitialized() ) { if ( li.isUninitialized() ) {
LOG.trace( "Ignoring uninitialized proxy" ); LOG.trace( "Ignoring uninitialized proxy" );
event.setResult( source.load( li.getEntityName(), li.getIdentifier() ) ); event.setResult( source.load( li.getEntityName(), li.getInternalIdentifier() ) );
//EARLY EXIT! //EARLY EXIT!
return; return;
} }

View File

@ -1471,7 +1471,7 @@ public Object getIdentifier(Object object) throws HibernateException {
if ( li.getSession() != this ) { if ( li.getSession() != this ) {
throw new TransientObjectException( "The proxy was not associated with this session" ); throw new TransientObjectException( "The proxy was not associated with this session" );
} }
return li.getIdentifier(); return li.getInternalIdentifier();
} }
else { else {
EntityEntry entry = persistenceContext.getEntry( object ); EntityEntry entry = persistenceContext.getEntry( object );
@ -1499,7 +1499,7 @@ public Object getContextEntityIdentifier(Object object) {
} }
private Object getProxyIdentifier(Object proxy) { private Object getProxyIdentifier(Object proxy) {
return ( (HibernateProxy) proxy ).getHibernateLazyInitializer().getIdentifier(); return ( (HibernateProxy) proxy ).getHibernateLazyInitializer().getInternalIdentifier();
} }
@Override @Override

View File

@ -68,7 +68,7 @@ public Object getIdentifier(Object entity) {
} }
if ( entity instanceof HibernateProxy ) { if ( entity instanceof HibernateProxy ) {
return ((HibernateProxy) entity).getHibernateLazyInitializer().getIdentifier(); return ((HibernateProxy) entity).getHibernateLazyInitializer().getInternalIdentifier();
} }
else if ( entity instanceof ManagedEntity ) { else if ( entity instanceof ManagedEntity ) {
EntityEntry entityEntry = ((ManagedEntity) entity).$$_hibernate_getEntityEntry(); EntityEntry entityEntry = ((ManagedEntity) entity).$$_hibernate_getEntityEntry();

View File

@ -122,8 +122,8 @@ public String getAlias(Dialect dialect) {
if ( lastLetter == -1 ) { if ( lastLetter == -1 ) {
alias = "column"; alias = "column";
} }
else if ( name.length() > lastLetter + 1 ) { else if ( alias.length() > lastLetter + 1 ) {
alias = name.substring( 0, lastLetter + 1 ); alias = alias.substring( 0, lastLetter + 1 );
} }
boolean useRawName = name.length() + suffix.length() <= dialect.getMaxAliasLength() boolean useRawName = name.length() + suffix.length() <= dialect.getMaxAliasLength()

View File

@ -81,6 +81,11 @@ public final String getEntityName() {
return entityName; return entityName;
} }
@Override
public final Object getInternalIdentifier() {
return id;
}
@Override @Override
public final Object getIdentifier() { public final Object getIdentifier() {
if ( isUninitialized() && isInitializeProxyWhenAccessingIdentifier() ) { if ( isUninitialized() && isInitializeProxyWhenAccessingIdentifier() ) {
@ -90,7 +95,7 @@ public final Object getIdentifier() {
} }
private boolean isInitializeProxyWhenAccessingIdentifier() { private boolean isInitializeProxyWhenAccessingIdentifier() {
return session != null && session.getFactory() return getSession() != null && getSession().getFactory()
.getSessionFactoryOptions() .getSessionFactoryOptions()
.getJpaCompliance().isJpaProxyComplianceEnabled(); .getJpaCompliance().isJpaProxyComplianceEnabled();
} }
@ -256,7 +261,7 @@ else if ( session.isOpenOrWaitingForAutoClose() && session.isConnected() ) {
public final void initializeWithoutLoadIfPossible() { public final void initializeWithoutLoadIfPossible() {
if ( !initialized && session != null && session.isOpenOrWaitingForAutoClose() ) { if ( !initialized && session != null && session.isOpenOrWaitingForAutoClose() ) {
final EntityKey key = session.generateEntityKey( final EntityKey key = session.generateEntityKey(
getIdentifier(), getInternalIdentifier(),
session.getFactory().getMetamodel().entityPersister( getEntityName() ) session.getFactory().getMetamodel().entityPersister( getEntityName() )
); );
final Object entity = session.getPersistenceContextInternal().getEntity( key ); final Object entity = session.getPersistenceContextInternal().getEntity( key );
@ -301,7 +306,7 @@ protected final boolean isConnectedToSession() {
} }
private Object getProxyOrNull() { private Object getProxyOrNull() {
final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), session, getEntityName() ); final EntityKey entityKey = generateEntityKeyOrNull( getInternalIdentifier(), session, getEntityName() );
if ( entityKey != null && session != null && session.isOpenOrWaitingForAutoClose() ) { if ( entityKey != null && session != null && session.isOpenOrWaitingForAutoClose() ) {
return session.getPersistenceContextInternal().getProxy( entityKey ); return session.getPersistenceContextInternal().getProxy( entityKey );
} }
@ -322,7 +327,7 @@ public final void setImplementation(Object target) {
@Override @Override
public final Object getImplementation(SharedSessionContractImplementor s) throws HibernateException { public final Object getImplementation(SharedSessionContractImplementor s) throws HibernateException {
final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), s, getEntityName() ); final EntityKey entityKey = generateEntityKeyOrNull( getInternalIdentifier(), s, getEntityName() );
return ( entityKey == null ? null : s.getPersistenceContext().getEntity( entityKey ) ); return ( entityKey == null ? null : s.getPersistenceContext().getEntity( entityKey ) );
} }
@ -372,7 +377,7 @@ public final void setReadOnly(boolean readOnly) {
} }
this.readOnly = readOnly; this.readOnly = readOnly;
if ( initialized ) { if ( initialized ) {
EntityKey key = generateEntityKeyOrNull( getIdentifier(), session, getEntityName() ); EntityKey key = generateEntityKeyOrNull( getInternalIdentifier(), session, getEntityName() );
final PersistenceContext persistenceContext = session.getPersistenceContext(); final PersistenceContext persistenceContext = session.getPersistenceContext();
if ( key != null && persistenceContext.containsEntity( key ) ) { if ( key != null && persistenceContext.containsEntity( key ) ) {
persistenceContext.setReadOnly( target, readOnly ); persistenceContext.setReadOnly( target, readOnly );

View File

@ -28,6 +28,17 @@ public interface LazyInitializer {
* *
* @return The identifier value. * @return The identifier value.
*/ */
default Object getInternalIdentifier() {
return getIdentifier();
}
/**
* Retrieve the identifier value for the entity our owning proxy represents.
*
* When JPA proxy compliance is enabled the proxy is initialized.
*
* @return The identifier value.
*/
Object getIdentifier(); Object getIdentifier();
/** /**

View File

@ -116,7 +116,7 @@ public Object writeReplace() {
private Object serializableProxy() { private Object serializableProxy() {
return new SerializableMapProxy( return new SerializableMapProxy(
li.getEntityName(), li.getEntityName(),
li.getIdentifier(), li.getInternalIdentifier(),
( li.isReadOnlySettingAvailable() ? Boolean.valueOf( li.isReadOnly() ) : li.isReadOnlyBeforeAttachedToSession() ), ( li.isReadOnlySettingAvailable() ? Boolean.valueOf( li.isReadOnly() ) : li.isReadOnlyBeforeAttachedToSession() ),
li.getSessionFactoryUuid(), li.getSessionFactoryUuid(),
li.isAllowLoadOutsideTransaction() li.isAllowLoadOutsideTransaction()

View File

@ -84,7 +84,7 @@ protected Object serializableProxy() {
getEntityName(), getEntityName(),
persistentClass, persistentClass,
interfaces, interfaces,
getIdentifier(), getInternalIdentifier(),
( isReadOnlySettingAvailable() ? Boolean.valueOf( isReadOnly() ) : isReadOnlyBeforeAttachedToSession() ), ( isReadOnlySettingAvailable() ? Boolean.valueOf( isReadOnly() ) : isReadOnlyBeforeAttachedToSession() ),
getSessionFactoryUuid(), getSessionFactoryUuid(),
isAllowLoadOutsideTransaction(), isAllowLoadOutsideTransaction(),

View File

@ -214,7 +214,7 @@ public Object getIdentifier(Object entity, SharedSessionContractImplementor sess
id = entity; id = entity;
} }
else if ( HibernateProxy.class.isInstance( entity ) ) { else if ( HibernateProxy.class.isInstance( entity ) ) {
id = ( (HibernateProxy) entity ).getHibernateLazyInitializer().getIdentifier(); id = ( (HibernateProxy) entity ).getHibernateLazyInitializer().getInternalIdentifier();
} }
else { else {
if ( idGetter == null ) { if ( idGetter == null ) {
@ -451,7 +451,7 @@ private static Object determineEntityId(
if ( entity instanceof HibernateProxy ) { if ( entity instanceof HibernateProxy ) {
// entity is a proxy, so we know it is not transient; just return ID from proxy // entity is a proxy, so we know it is not transient; just return ID from proxy
return ( (HibernateProxy) entity ).getHibernateLazyInitializer().getIdentifier(); return ( (HibernateProxy) entity ).getHibernateLazyInitializer().getInternalIdentifier();
} }
if ( session != null ) { if ( session != null ) {

View File

@ -335,7 +335,7 @@ public int getHashCode(Object x, SessionFactoryImplementor factory) {
final Object id; final Object id;
if ( x instanceof HibernateProxy ) { if ( x instanceof HibernateProxy ) {
id = ( (HibernateProxy) x ).getHibernateLazyInitializer().getIdentifier(); id = ( (HibernateProxy) x ).getHibernateLazyInitializer().getInternalIdentifier();
} }
else { else {
final Class mappedClass = persister.getMappedClass(); final Class mappedClass = persister.getMappedClass();
@ -365,7 +365,7 @@ public boolean isEqual(Object x, Object y, SessionFactoryImplementor factory) {
Object xid; Object xid;
if ( x instanceof HibernateProxy ) { if ( x instanceof HibernateProxy ) {
xid = ( (HibernateProxy) x ).getHibernateLazyInitializer() xid = ( (HibernateProxy) x ).getHibernateLazyInitializer()
.getIdentifier(); .getInternalIdentifier();
} }
else { else {
if ( mappedClass.isAssignableFrom( x.getClass() ) ) { if ( mappedClass.isAssignableFrom( x.getClass() ) ) {
@ -379,7 +379,8 @@ public boolean isEqual(Object x, Object y, SessionFactoryImplementor factory) {
Object yid; Object yid;
if ( y instanceof HibernateProxy ) { if ( y instanceof HibernateProxy ) {
yid = ( (HibernateProxy) y ).getHibernateLazyInitializer().getIdentifier(); yid = ( (HibernateProxy) y ).getHibernateLazyInitializer()
.getInternalIdentifier();
} }
else { else {
if ( mappedClass.isAssignableFrom( y.getClass() ) ) { if ( mappedClass.isAssignableFrom( y.getClass() ) ) {
@ -523,7 +524,7 @@ public String toLoggableString(Object value, SessionFactoryImplementor factory)
final Object id; final Object id;
if ( value instanceof HibernateProxy ) { if ( value instanceof HibernateProxy ) {
HibernateProxy proxy = (HibernateProxy) value; HibernateProxy proxy = (HibernateProxy) value;
id = proxy.getHibernateLazyInitializer().getIdentifier(); id = proxy.getHibernateLazyInitializer().getInternalIdentifier();
} }
else { else {
id = persister.getIdentifier( value ); id = persister.getIdentifier( value );

View File

@ -0,0 +1,211 @@
package org.hibernate.jpa.test.ops;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javax.persistence.CascadeType;
import javax.persistence.Embeddable;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil2.fromTransaction;
import static org.hibernate.testing.transaction.TransactionUtil2.inTransaction;
@TestForIssue( jiraKey = "HHH-14608")
public class MergeJpaComplianceTest extends BaseEntityManagerFunctionalTestCase {
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] {
Person.class, Occupation.class, PersonOccupation.class
};
}
@Override
protected void addConfigOptions(Map config) {
config.put( org.hibernate.cfg.AvailableSettings.JPA_PROXY_COMPLIANCE, true );
}
@Test
public void testMerge() {
Person person = fromTransaction(
entityManagerFactory(),
entityManager -> {
Person p;
p = new Person( "1", "Fab" );
Occupation t = new Occupation( 1l, "Some work" );
entityManager.persist( p );
entityManager.persist( t );
entityManager.flush();
PersonOccupation participant = new PersonOccupation( p, t );
entityManager.persist( participant );
return p;
}
);
inTransaction(
entityManagerFactory(),
entityManager -> {
person.setName( "Fabiana" );
entityManager.merge( person );
}
);
}
@Entity(name = "Person")
public static class Person {
@Id
private String id;
private String name;
@OneToMany(mappedBy = "pk.person", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PersonOccupation> occupations;
public Person() {
}
public Person(String id, String name) {
this.id = id;
this.name = name;
}
protected void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public List<PersonOccupation> getOccupations() {
return occupations;
}
protected void addOccupationPeoplet(PersonOccupation personOccupation) {
if ( this.occupations == null ) {
occupations = new ArrayList<>();
}
this.occupations.add( personOccupation );
personOccupation.getPk().setPerson( this );
}
protected void setOccupations(List<PersonOccupation> occupations) {
this.occupations = occupations;
}
}
@Entity(name = "Occupation")
public static class Occupation {
@Id
private long id;
private String name;
@OneToMany(mappedBy = "pk.occupation", cascade = CascadeType.ALL)
private List<PersonOccupation> personOccupations;
protected Occupation() {
}
public Occupation(long id, String name) {
this.id = id;
this.name = name;
}
public long getId() {
return id;
}
protected void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
protected void setName(String name) {
this.name = name;
}
public List<PersonOccupation> getPersonOccupations() {
return personOccupations;
}
protected void addPersonOccupation(PersonOccupation participant) {
if ( personOccupations == null ) {
personOccupations = new ArrayList<>();
}
personOccupations.add( participant );
participant.getPk().setOccupation( this );
}
protected void setPersonOccupations(List<PersonOccupation> personOccupations) {
this.personOccupations = personOccupations;
}
}
@Entity(name = "PersonOccupation")
public static class PersonOccupation {
@EmbeddedId
private PersonOccupationPK pk = new PersonOccupationPK();
protected PersonOccupation() {
}
public PersonOccupation(Person person, Occupation occupation) {
person.addOccupationPeoplet( this );
occupation.addPersonOccupation( this );
}
public PersonOccupationPK getPk() {
return pk;
}
public void setPk(PersonOccupationPK pk) {
this.pk = pk;
}
}
@Embeddable
public static class PersonOccupationPK implements Serializable {
@ManyToOne(fetch = FetchType.LAZY)
private Person person;
@ManyToOne(fetch = FetchType.LAZY)
private Occupation occupation;
public Person getPerson() {
return person;
}
public void setPerson(Person person) {
this.person = person;
}
public Occupation getOccupation() {
return occupation;
}
public void setOccupation(Occupation occupation) {
this.occupation = occupation;
}
}
}

View File

@ -0,0 +1,25 @@
package org.hibernate.orm.test.mapping.lazytoone.collectioninitializer;
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class Company {
@Id
private Long id;
@Override
public String toString() {
return "Company{" +
"id=" + id +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}

View File

@ -0,0 +1,39 @@
package org.hibernate.orm.test.mapping.lazytoone.collectioninitializer;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class CostCenter {
@Id
private Long id;
@ManyToOne(optional = false)
private Company company;
@Override
public String toString() {
return "CostCenter{" +
"id=" + id +
", company=" + company +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
}

View File

@ -0,0 +1,191 @@
/*
* 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.orm.test.mapping.lazytoone.collectioninitializer;
import org.hibernate.Hibernate;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hibernate.loader.BatchFetchStyle.PADDED;
/**
* Test lazy-to-one initialization within a collection initialization,
* with the PADDED batch-fetch-style.
* <p>
* In particular, with Offer having a lazy to-one association to CostCenter,
* and User having a lazy to-many association to UserAuthorization1 and UserAuthorization2,
* and UserAuthorization1 and UserAuthorization2 having an EAGER association to CostCenter,
* test:
* <ul>
* <li>Get a reference to Offer (which will create an uninitialized proxy for CostCenter)</li>
* <li>Get a reference to User</li>
* <li>Initialize User's collection containing UserAuthorization1 and UserAuthorization2,
* which will initialize CostCenter DURING the loading,
* which used to fail because we tried to initialize CostCenter twice
* (once for UserAuthorization1, and once for UserAuthorization2)</li>
* </ul>
*/
@RunWith(BytecodeEnhancerRunner.class)
@TestForIssue(jiraKey = "HHH-14730")
public class InitLazyToOneWithinPaddedCollectionInitializationAllowProxyTest
extends BaseNonConfigCoreFunctionalTestCase {
@Override
protected void applyMetadataSources(MetadataSources sources) {
super.applyMetadataSources( sources );
sources.addAnnotatedClass( User.class );
sources.addAnnotatedClass( UserAuthorization.class );
sources.addAnnotatedClass( Company.class );
sources.addAnnotatedClass( CostCenter.class );
sources.addAnnotatedClass( Offer.class );
}
@Override
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
super.configureStandardServiceRegistryBuilder( ssrb );
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, true );
ssrb.applySetting( AvailableSettings.BATCH_FETCH_STYLE, PADDED );
ssrb.applySetting( AvailableSettings.DEFAULT_BATCH_FETCH_SIZE, 10 );
}
@Override
protected void afterSessionFactoryBuilt(SessionFactoryImplementor sessionFactory) {
inTransaction( session -> {
User user0 = new User();
user0.setId( 0L );
session.persist( user0 );
User user1 = new User();
user1.setId( 1L );
session.persist( user1 );
User user2 = new User();
user2.setId( 2L );
session.persist( user2 );
Company company = new Company();
company.setId( 2L );
session.persist( company );
CostCenter costCenter = new CostCenter();
costCenter.setId( 3L );
costCenter.setCompany( company );
session.persist( costCenter );
UserAuthorization user0Authorization1 = new UserAuthorization();
user0Authorization1.setId( 1L );
user0Authorization1.setUser( user0 );
user0Authorization1.setCostCenter( costCenter );
session.persist( user0Authorization1 );
UserAuthorization user1Authorization1 = new UserAuthorization();
user1Authorization1.setId( 11L );
user1Authorization1.setUser( user1 );
user1Authorization1.setCostCenter( costCenter );
session.persist( user1Authorization1 );
UserAuthorization user1Authorization2 = new UserAuthorization();
user1Authorization2.setId( 12L );
user1Authorization2.setUser( user1 );
user1Authorization2.setCostCenter( costCenter );
session.persist( user1Authorization2 );
UserAuthorization user2Authorization1 = new UserAuthorization();
user2Authorization1.setId( 21L );
user2Authorization1.setUser( user2 );
user2Authorization1.setCostCenter( costCenter );
session.persist( user2Authorization1 );
UserAuthorization user2Authorization2 = new UserAuthorization();
user2Authorization2.setId( 22L );
user2Authorization2.setUser( user2 );
user2Authorization2.setCostCenter( costCenter );
session.persist( user2Authorization2 );
UserAuthorization user2Authorization3 = new UserAuthorization();
user2Authorization3.setId( 23L );
user2Authorization3.setUser( user2 );
user2Authorization3.setCostCenter( costCenter );
session.persist( user2Authorization3 );
Offer offer = new Offer();
offer.setId( 6L );
offer.setCostCenter( costCenter );
session.persist( offer );
} );
}
@Test
public void testOneReference() {
inTransaction( (session) -> {
// Add a lazy proxy of the cost center to the persistence context
// through the lazy to-one association from the offer.
Offer offer = session.find( Offer.class, 6L );
User user = session.find( User.class, 0L );
assertThat( Hibernate.isInitialized( offer.getCostCenter() ) ).isFalse();
// Trigger lazy-loading of the cost center
// through the loading of the authorization,
// which contains an eager reference to the cost center.
assertThat( user.getAuthorizations().size() ).isEqualTo( 1 );
assertThat( Hibernate.isInitialized( offer.getCostCenter() ) ).isTrue();
} );
}
@Test
public void testTwoReferences() {
inTransaction( (session) -> {
// Add a lazy proxy of the cost center to the persistence context
// through the lazy to-one association from the offer.
Offer offer = session.find( Offer.class, 6L );
User user = session.find( User.class, 1L );
assertThat( Hibernate.isInitialized( offer.getCostCenter() ) ).isFalse();
// Trigger lazy-loading of the cost center
// through the loading of the 2 authorizations,
// which both contain an eager reference to the cost center.
assertThat( user.getAuthorizations().size() ).isEqualTo( 2 );
assertThat( Hibernate.isInitialized( offer.getCostCenter() ) ).isTrue();
} );
}
@Test
public void testThreeReferences() {
inTransaction( (session) -> {
// Add a lazy proxy of the cost center to the persistence context
// through the lazy to-one association from the offer.
Offer offer = session.find( Offer.class, 6L );
User user = session.find( User.class, 2L );
assertThat( Hibernate.isInitialized( offer.getCostCenter() ) ).isFalse();
// Trigger lazy-loading of the cost center
// through the loading of the 3 authorizations,
// which all contain an eager reference to the cost center.
assertThat( user.getAuthorizations().size() ).isEqualTo( 3 );
assertThat( Hibernate.isInitialized( offer.getCostCenter() ) ).isTrue();
} );
}
}

View File

@ -0,0 +1,39 @@
package org.hibernate.orm.test.mapping.lazytoone.collectioninitializer;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import static javax.persistence.FetchType.LAZY;
@Entity
public class Offer {
@Id
private Long id;
@ManyToOne(fetch = LAZY, optional = false)
private CostCenter costCenter;
@Override
public String toString() {
return "Offer{" +
"id=" + getId() +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public CostCenter getCostCenter() {
return costCenter;
}
public void setCostCenter(CostCenter costCenter) {
this.costCenter = costCenter;
}
}

View File

@ -0,0 +1,43 @@
package org.hibernate.orm.test.mapping.lazytoone.collectioninitializer;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import static javax.persistence.CascadeType.ALL;
@Entity
@Table(name = "users")
public class User {
@Id
private Long id;
@OneToMany(mappedBy = "user", cascade = ALL, orphanRemoval = true)
private List<UserAuthorization> authorizations = new ArrayList<>();
@Override
public String toString() {
return "User{" +
"id='" + getId() + '\'' +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public List<UserAuthorization> getAuthorizations() {
return authorizations;
}
public void setAuthorizations(List<UserAuthorization> authorizations) {
this.authorizations = authorizations;
}
}

View File

@ -0,0 +1,49 @@
package org.hibernate.orm.test.mapping.lazytoone.collectioninitializer;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
@Entity
public class UserAuthorization {
@Id
private Long id;
@ManyToOne(optional = false)
private User user;
@ManyToOne(optional = false)
private CostCenter costCenter;
@Override
public String toString() {
return "UserAuthorization{" +
"id='" + getId() + '\'' +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public CostCenter getCostCenter() {
return costCenter;
}
public void setCostCenter(CostCenter costCenter) {
this.costCenter = costCenter;
}
}

View File

@ -0,0 +1,45 @@
/*
* 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.mapping;
import java.util.Locale;
import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.PostgreSQL94Dialect;
import org.hibernate.mapping.Column;
import org.hibernate.testing.TestForIssue;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Aliases should always be lower-case. This tests that an alias for
* a column name that ends in a character that is not a letter gets
* generated to be all in lower-case.
*
* @author Gail Badner
*/
public class ColumnLastIndexNotLetterAliasTest {
// Arbitrarily choose PostgreSQL
private static final Dialect DIALECT = new PostgreSQL94Dialect();;
@Test
@TestForIssue(jiraKey = "HHH-14720")
public void testColumnNameEndinWithNonCharacter() {
test( "aColumn1" );
test( "aColumn_" );
test( "aVeryVeryVeryLongColumnName1" );
test( "aVeryVeryVeryLongColumnName_" );
}
private void test(String columnName) {
final Column column = new Column( columnName );
final String alias = column.getAlias( DIALECT );
assertEquals( alias.toLowerCase( Locale.ROOT ), alias );
}
}

View File

@ -94,7 +94,7 @@ private void addCollectionChangeWorkUnit(
if ( value instanceof HibernateProxy ) { if ( value instanceof HibernateProxy ) {
final HibernateProxy hibernateProxy = (HibernateProxy) value; final HibernateProxy hibernateProxy = (HibernateProxy) value;
id = hibernateProxy.getHibernateLazyInitializer().getIdentifier(); id = hibernateProxy.getHibernateLazyInitializer().getInternalIdentifier();
// We've got to initialize the object from the proxy to later read its state. // We've got to initialize the object from the proxy to later read its state.
value = EntityTools.getTargetFromProxy( session.getFactory(), hibernateProxy ); value = EntityTools.getTargetFromProxy( session.getFactory(), hibernateProxy );
// HHH-7249 // HHH-7249

View File

@ -111,7 +111,7 @@ private void replaceNonAuditIdProxies(Map versionsEntity, Number revision) {
final HibernateProxy hibernateProxy = (HibernateProxy) value; final HibernateProxy hibernateProxy = (HibernateProxy) value;
final LazyInitializer initializer = hibernateProxy.getHibernateLazyInitializer(); final LazyInitializer initializer = hibernateProxy.getHibernateLazyInitializer();
final String entityName = initializer.getEntityName(); final String entityName = initializer.getEntityName();
final Object entityId = initializer.getIdentifier(); final Object entityId = initializer.getInternalIdentifier();
if ( enversService.getEntitiesConfigurations().isVersioned( entityName ) ) { if ( enversService.getEntitiesConfigurations().isVersioned( entityName ) ) {
final String entityClassName = enversService.getEntitiesConfigurations().get( entityName ).getEntityClassName(); final String entityClassName = enversService.getEntitiesConfigurations().get( entityName ).getEntityClassName();
final Class entityClass = ReflectionTools.loadClass( final Class entityClass = ReflectionTools.loadClass(

View File

@ -88,7 +88,7 @@ public Object mapToIdFromEntity(final Object data) {
if ( data instanceof HibernateProxy ) { if ( data instanceof HibernateProxy ) {
final HibernateProxy hibernateProxy = (HibernateProxy) data; final HibernateProxy hibernateProxy = (HibernateProxy) data;
return hibernateProxy.getHibernateLazyInitializer().getIdentifier(); return hibernateProxy.getHibernateLazyInitializer().getInternalIdentifier();
} }
else { else {
return AccessController.doPrivileged( return AccessController.doPrivileged(
@ -122,7 +122,7 @@ public void mapToMapFromEntity(Map<String, Object> data, final Object obj) {
else { else {
if ( obj instanceof HibernateProxy ) { if ( obj instanceof HibernateProxy ) {
final HibernateProxy hibernateProxy = (HibernateProxy) obj; final HibernateProxy hibernateProxy = (HibernateProxy) obj;
data.put( propertyData.getName(), hibernateProxy.getHibernateLazyInitializer().getIdentifier() ); data.put( propertyData.getName(), hibernateProxy.getHibernateLazyInitializer().getInternalIdentifier() );
} }
else { else {
final Object value = AccessController.doPrivileged( final Object value = AccessController.doPrivileged(

View File

@ -155,7 +155,7 @@ public void mapToMapFromEntity(Map<String, Object> data, Object obj) {
else { else {
if ( obj instanceof HibernateProxy ) { if ( obj instanceof HibernateProxy ) {
final HibernateProxy proxy = (HibernateProxy) obj; final HibernateProxy proxy = (HibernateProxy) obj;
data.put( propertyData.getName(), proxy.getHibernateLazyInitializer().getIdentifier() ); data.put( propertyData.getName(), proxy.getHibernateLazyInitializer().getInternalIdentifier() );
} }
else { else {
final Object value = AccessController.doPrivileged( final Object value = AccessController.doPrivileged(

View File

@ -33,7 +33,7 @@ public static Object getIdentifier(SessionImplementor session, String entityName
if ( obj instanceof HibernateProxy ) { if ( obj instanceof HibernateProxy ) {
final HibernateProxy hibernateProxy = (HibernateProxy) obj; final HibernateProxy hibernateProxy = (HibernateProxy) obj;
return hibernateProxy.getHibernateLazyInitializer().getIdentifier(); return hibernateProxy.getHibernateLazyInitializer().getInternalIdentifier();
} }
return session.getEntityPersister( entityName, obj ).getIdentifier( obj, session ); return session.getEntityPersister( entityName, obj ).getIdentifier( obj, session );
@ -51,7 +51,7 @@ public static Object getTargetFromProxy(SessionFactoryImplementor sessionFactory
try { try {
return tempSession.get( return tempSession.get(
proxy.getHibernateLazyInitializer().getEntityName(), proxy.getHibernateLazyInitializer().getEntityName(),
proxy.getHibernateLazyInitializer().getIdentifier() proxy.getHibernateLazyInitializer().getInternalIdentifier()
); );
} }
finally { finally {

View File

@ -80,7 +80,7 @@ private Number getRevisionNumber(Map versionsEntity) {
Object revisionInfoObject = ( (Map) versionsEntity.get( originalId ) ).get( revisionPropertyName ); Object revisionInfoObject = ( (Map) versionsEntity.get( originalId ) ).get( revisionPropertyName );
if ( revisionInfoObject instanceof HibernateProxy ) { if ( revisionInfoObject instanceof HibernateProxy ) {
return (Number) ( (HibernateProxy) revisionInfoObject ).getHibernateLazyInitializer().getIdentifier(); return (Number) ( (HibernateProxy) revisionInfoObject ).getHibernateLazyInitializer().getInternalIdentifier();
} }
else { else {
// Not a proxy - must be read from cache or with a join // Not a proxy - must be read from cache or with a join

View File

@ -114,8 +114,8 @@ public void testProxyIdentifier() {
LazyInitializer lazyInitializer = proxyCreateByEnvers.getHibernateLazyInitializer(); LazyInitializer lazyInitializer = proxyCreateByEnvers.getHibernateLazyInitializer();
Assert.assertTrue( lazyInitializer.isUninitialized() ); Assert.assertTrue( lazyInitializer.isUninitialized() );
Assert.assertNotNull( lazyInitializer.getIdentifier() ); Assert.assertNotNull( lazyInitializer.getInternalIdentifier() );
Assert.assertEquals( tnae1.getId(), lazyInitializer.getIdentifier() ); Assert.assertEquals( tnae1.getId(), lazyInitializer.getInternalIdentifier() );
Assert.assertTrue( lazyInitializer.isUninitialized() ); Assert.assertTrue( lazyInitializer.isUninitialized() );
Assert.assertEquals( uste1.getId(), rev1.getReference().getId() ); Assert.assertEquals( uste1.getId(), rev1.getReference().getId() );

View File

@ -9,34 +9,10 @@ description = 'Integration tests for running Hibernate ORM in the Java module pa
apply from: rootProject.file( 'gradle/java-module.gradle' ) apply from: rootProject.file( 'gradle/java-module.gradle' )
// See https://docs.gradle.org/6.7.1/userguide/java_testing.html#blackbox_integration_testing
// See https://docs.gradle.org/6.7.1/samples/sample_java_modules_multi_project_with_integration_tests.html
java.modularity.inferModulePath = true java.modularity.inferModulePath = true
// In this module, the "main" code is actually just test code that happens
// to be built independently so as to generate a Java module.
// So, let's override settings for compilation of the main code, just for this particular case.
def testJavaVersions = gradle.ext.javaVersions.test
tasks.compileJava {
if ( !gradle.ext.javaToolchainEnabled ) {
sourceCompatibility = JavaVersion.toVersion( testJavaVersions.release )
targetCompatibility = JavaVersion.toVersion( testJavaVersions.release )
}
else {
javaCompiler = javaToolchains.compilerFor {
languageVersion = testJavaVersions.compiler
}
// Remove JDK8-only options (if any) that are incompatible with options.release
for ( it = options.compilerArgs.listIterator(); it.hasNext(); ) {
if ( it.next() in ['-source', '-target'] ) {
it.remove()
it.next()
it.remove()
}
}
options.release = testJavaVersions.release.asInt()
}
}
// Checkstyle fails for module-info // Checkstyle fails for module-info
checkstyleMain.exclude '**/module-info.java' checkstyleMain.exclude '**/module-info.java'
@ -44,6 +20,7 @@ checkstyleMain.exclude '**/module-info.java'
dependencies { dependencies {
api project( ':hibernate-core' ) api project( ':hibernate-core' )
api project( ':hibernate-envers' ) api project( ':hibernate-envers' )
api libraries.jpa
implementation libraries.jpa implementation libraries.jpa
} }

View File

@ -1,27 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
module org.hibernate.orm.integrationtest.java.module {
exports org.hibernate.orm.integrationtest.java.module.service;
opens org.hibernate.orm.integrationtest.java.module.entity to
org.hibernate.orm.core;
requires java.persistence;
/*
* IDEA will not find the modules below because it apparently doesn't support automatic module names
* for modules in the current project.
* Everything should work fine when building from the command line, though.
*/
requires org.hibernate.orm.core;
requires org.hibernate.orm.envers;
/*
* This is necessary in order to use SessionFactory,
* which extends "javax.naming.Referenceable".
* Without this, compilation as a Java module fails.
*/
requires java.naming;
}

View File

@ -0,0 +1,33 @@
/*
* 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>.
*/
module org.hibernate.orm.integrationtest.java.module.test {
/*
* Main configuration, necessary for real client applications.
*/
opens org.hibernate.orm.integrationtest.java.module.test.entity to
org.hibernate.orm.core;
requires java.persistence;
// IDEA will not find the modules below because it apparently doesn't support automatic module names
// for modules in the current project.
// Everything should work fine when building from the command line, though.
requires org.hibernate.orm.core;
requires org.hibernate.orm.envers;
// Transitive dependencies that leak through the Hibernate ORM API
requires java.sql;
requires java.naming; // SessionFactory extends "javax.naming.Referenceable"
/*
* Test-only configuration.
*/
opens org.hibernate.orm.integrationtest.java.module.test to junit;
requires junit;
}

View File

@ -11,7 +11,7 @@
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.envers.boot.internal.EnversIntegrator; import org.hibernate.envers.boot.internal.EnversIntegrator;
import org.hibernate.orm.integrationtest.java.module.service.AuthorService; import org.hibernate.orm.integrationtest.java.module.test.service.AuthorService;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;

View File

@ -15,7 +15,7 @@
import org.hibernate.boot.archive.scan.internal.StandardScanner; import org.hibernate.boot.archive.scan.internal.StandardScanner;
import org.hibernate.boot.archive.scan.spi.ClassDescriptor; import org.hibernate.boot.archive.scan.spi.ClassDescriptor;
import org.hibernate.boot.archive.scan.spi.ScanResult; import org.hibernate.boot.archive.scan.spi.ScanResult;
import org.hibernate.orm.integrationtest.java.module.entity.Author; import org.hibernate.orm.integrationtest.java.module.test.entity.Author;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.orm.integrationtest.java.module.entity; package org.hibernate.orm.integrationtest.java.module.test.entity;
import javax.persistence.Basic; import javax.persistence.Basic;
import javax.persistence.Entity; import javax.persistence.Entity;

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * 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>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.orm.integrationtest.java.module.service; package org.hibernate.orm.integrationtest.java.module.test.service;
import java.util.List; import java.util.List;
@ -14,7 +14,7 @@
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.envers.AuditReader; import org.hibernate.envers.AuditReader;
import org.hibernate.envers.AuditReaderFactory; import org.hibernate.envers.AuditReaderFactory;
import org.hibernate.orm.integrationtest.java.module.entity.Author; import org.hibernate.orm.integrationtest.java.module.test.entity.Author;
public class AuthorService implements AutoCloseable { public class AuthorService implements AutoCloseable {

View File

@ -10,7 +10,7 @@
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
version="1.0"> version="1.0">
<persistence-unit name="primaryPU"> <persistence-unit name="primaryPU">
<class>org.hibernate.orm.integrationtest.java.module.entity.Author</class> <class>org.hibernate.orm.integrationtest.java.module.test.entity.Author</class>
<properties> <properties>
<property name="hibernate.connection.driver_class" value="org.h2.Driver"/> <property name="hibernate.connection.driver_class" value="org.h2.Driver"/>
<property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/> <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>