Merge remote-tracking branch 'upstream5/master' into wip/6.0_merged_11
This commit is contained in:
commit
4b0da2444b
|
@ -3,6 +3,55 @@ Hibernate 5 Changelog
|
|||
|
||||
Note: Please refer to JIRA to learn more about each issue.
|
||||
|
||||
Changes in 5.4.8.Final (October 28, 2019)
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
https://hibernate.atlassian.net/projects/HHH/versions/31804/tab/release-report-done
|
||||
|
||||
** Bug
|
||||
* [HHH-12965] - Hibernate Envers Audit tables are created with foreign key with the entity. Because of this I am not able to delete any entries from the entity tables.
|
||||
* [HHH-13446] - java.lang.VerifyError from compile-time enhanced @Entity
|
||||
* [HHH-13651] - NPE on flushing when ElementCollection field contains null element
|
||||
* [HHH-13695] - DDL export forgets to close a Statement
|
||||
* [HHH-13696] - Multiple OSGi bundles initializing concurrently would overlap classloaders
|
||||
|
||||
** Improvement
|
||||
* [HHH-13686] - Upgrade to Agroal 1.6
|
||||
|
||||
|
||||
Changes in 5.4.7.Final (October 21, 2019)
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
https://hibernate.atlassian.net/projects/HHH/versions/31799/tab/release-report-done
|
||||
|
||||
** Bug
|
||||
* [HHH-4235] - MapBinder.createFormulatedValue() does not honor DB schema name when creating query
|
||||
* [HHH-13633] - Bugs join-fetching a collection when scrolling with a stateless session using enhancement as proxy
|
||||
* [HHH-13634] - PersistenceContext can get cleared before load completes using StatelessSessionImpl
|
||||
* [HHH-13640] - Uninitialized HibernateProxy mapped as NO_PROXY gets initialized when reloaded with enhancement-as-proxy enabled
|
||||
* [HHH-13653] - Uninitialized entity does not get initialized when a setter is called with enhancement-as-proxy enabled
|
||||
* [HHH-13655] - Envers Map<Enum, Integer> causes NullPointerException when mapped with @MapKeyEnumerated since Hibernate 5.4.6
|
||||
* [HHH-13663] - Session#setHibernateFlushMode() method not callable without an active transaction
|
||||
* [HHH-13665] - Selecting an entity annotated with @Immutable but not with @Cachable causes a NPE when use_reference_entries is enabled
|
||||
* [HHH-13672] - The temporary PersistenceContext of a StatelessSession is not cleared after a refresh operation
|
||||
* [HHH-13675] - Optimize PersistentBag.groupByEqualityHash()
|
||||
|
||||
** New Feature
|
||||
* [HHH-10398] - _MOD columns not named correctly when using custom column names
|
||||
|
||||
** Task
|
||||
* [HHH-13680] - Upgrade to Byte Buddy 1.10.2
|
||||
* [HHH-13681] - Upgrade to Byteman 4.0.8
|
||||
|
||||
** Improvement
|
||||
* [HHH-12858] - integration overrides during JPA bootstrap ought to override all logically related settings
|
||||
* [HHH-13432] - Have EntityManagerFactory expose persistence.xml `jta-data-source` element as a `javax.persistence.nonJtaDataSource` property
|
||||
* [HHH-13660] - Reduce allocation costs of IdentityMaps used by ResultSetProcessingContextImpl
|
||||
* [HHH-13662] - Avoid initializing XmlMappingBinderAccess when no XML mappings are defined
|
||||
* [HHH-13666] - AssertionFailure: Exception releasing cache locks upon After/BeforeTransactionCompletionProcess failure
|
||||
* [HHH-13673] - Cryptic error when providing import.sql file without a terminal char at the end of each line
|
||||
|
||||
|
||||
Changes in 5.4.6.Final (September 30, 2019)
|
||||
------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
== Preface
|
||||
|
||||
Working with both Object-Oriented software and Relational Databases can be cumbersome and time-consuming.
|
||||
Development costs are significantly higher due to a paradigm mismatch between how data is represented in objects
|
||||
Development costs are significantly higher due to a number of "paradigm mismatches" between how data is represented in objects
|
||||
versus relational databases. Hibernate is an Object/Relational Mapping (ORM) solution for Java environments. The
|
||||
term Object/Relational Mapping refers to the technique of mapping data between an object model representation to
|
||||
a relational data model representation. See http://en.wikipedia.org/wiki/Object-relational_mapping for a good
|
||||
|
@ -14,7 +14,10 @@ takes a look at many of the mismatch problems.
|
|||
Although having a strong background in SQL is not required to use Hibernate, having a basic understanding of the
|
||||
concepts can help you understand Hibernate more quickly and fully. An understanding of data modeling principles
|
||||
is especially important. Both http://www.agiledata.org/essays/dataModeling101.html and
|
||||
http://en.wikipedia.org/wiki/Data_modeling are good starting points for understanding these data modeling principles.
|
||||
http://en.wikipedia.org/wiki/Data_modeling are good starting points for understanding these data modeling
|
||||
principles. If you are completely new to database access in Java,
|
||||
https://www.marcobehler.com/guides/a-guide-to-accessing-databases-in-java contains a good overview of the various parts,
|
||||
pieces and options.
|
||||
|
||||
Hibernate takes care of the mapping from Java classes to database tables, and from Java data types to SQL data
|
||||
types. In addition, it provides data query and retrieval facilities. It can significantly reduce development
|
||||
|
@ -32,4 +35,4 @@ representation to a graph of objects.
|
|||
|
||||
See http://hibernate.org/orm/contribute/ for information on getting involved.
|
||||
|
||||
IMPORTANT: The projects and code for the tutorials referenced in this guide are available as link:hibernate-tutorials.zip[]
|
||||
IMPORTANT: The projects and code for the tutorials referenced in this guide are available as link:hibernate-tutorials.zip[]
|
||||
|
|
|
@ -14,7 +14,7 @@ ext {
|
|||
junit5Version = '5.3.1'
|
||||
|
||||
h2Version = '1.4.196'
|
||||
bytemanVersion = '4.0.3' //Compatible with JDK10
|
||||
bytemanVersion = '4.0.8' //Compatible with JDK14
|
||||
jnpVersion = '5.0.6.CR1'
|
||||
|
||||
hibernateCommonsVersion = '5.1.0.Final'
|
||||
|
@ -26,7 +26,9 @@ ext {
|
|||
weldVersion = '3.0.0.Final'
|
||||
|
||||
javassistVersion = '3.24.0-GA'
|
||||
byteBuddyVersion = '1.9.11'
|
||||
byteBuddyVersion = '1.10.2'
|
||||
|
||||
agroalVersion = '1.6'
|
||||
|
||||
geolatteVersion = '1.4.0'
|
||||
|
||||
|
@ -146,8 +148,8 @@ ext {
|
|||
proxool: "proxool:proxool:0.8.3",
|
||||
hikaricp: "com.zaxxer:HikariCP:3.2.0",
|
||||
vibur: "org.vibur:vibur-dbcp:22.2",
|
||||
agroal_api: "io.agroal:agroal-api:1.4",
|
||||
agroal_pool: "io.agroal:agroal-pool:1.4",
|
||||
agroal_api: "io.agroal:agroal-api:${agroalVersion}",
|
||||
agroal_pool: "io.agroal:agroal-pool:${agroalVersion}",
|
||||
|
||||
atomikos: "com.atomikos:transactions:4.0.6",
|
||||
atomikos_jta: "com.atomikos:transactions-jta:4.0.6",
|
||||
|
|
|
@ -58,9 +58,10 @@ final class BiDirectionalAssociationHandler implements Implementation {
|
|||
String mappedBy = getMappedBy( persistentField, targetEntity, enhancementContext );
|
||||
if ( mappedBy == null || mappedBy.isEmpty() ) {
|
||||
log.infof(
|
||||
"Could not find bi-directional association for field [%s#%s]",
|
||||
"Bi-directional association not managed for field [%s#%s]: Could not find target field in [%s]",
|
||||
managedCtClass.getName(),
|
||||
persistentField.getName()
|
||||
persistentField.getName(),
|
||||
targetEntity.getCanonicalName()
|
||||
);
|
||||
return implementation;
|
||||
}
|
||||
|
@ -101,7 +102,7 @@ final class BiDirectionalAssociationHandler implements Implementation {
|
|||
|
||||
if ( persistentField.getType().asErasure().isAssignableTo( Map.class ) || targetType.isAssignableTo( Map.class ) ) {
|
||||
log.infof(
|
||||
"Bi-directional association for field [%s#%s] not managed: @ManyToMany in java.util.Map attribute not supported ",
|
||||
"Bi-directional association not managed for field [%s#%s]: @ManyToMany in java.util.Map attribute not supported ",
|
||||
managedCtClass.getName(),
|
||||
persistentField.getName()
|
||||
);
|
||||
|
@ -145,7 +146,7 @@ final class BiDirectionalAssociationHandler implements Implementation {
|
|||
|
||||
if ( targetClass == null ) {
|
||||
log.infof(
|
||||
"Could not find type of bi-directional association for field [%s#%s]",
|
||||
"Bi-directional association not managed for field [%s#%s]: Could not find target type",
|
||||
managedCtClass.getName(),
|
||||
persistentField.getName()
|
||||
);
|
||||
|
@ -163,7 +164,7 @@ final class BiDirectionalAssociationHandler implements Implementation {
|
|||
|
||||
private static TypeDescription.Generic target(AnnotatedFieldDescription persistentField) {
|
||||
AnnotationDescription.Loadable<Access> access = persistentField.getDeclaringType().asErasure().getDeclaredAnnotations().ofType( Access.class );
|
||||
if ( access != null && access.loadSilent().value() == AccessType.FIELD ) {
|
||||
if ( access != null && access.load().value() == AccessType.FIELD ) {
|
||||
return persistentField.getType();
|
||||
}
|
||||
else {
|
||||
|
@ -183,7 +184,20 @@ final class BiDirectionalAssociationHandler implements Implementation {
|
|||
return getMappedByManyToMany( target, targetEntity, context );
|
||||
}
|
||||
else {
|
||||
return mappedBy;
|
||||
// HHH-13446 - mappedBy from annotation may not be a valid bi-directional association, verify by calling isValidMappedBy()
|
||||
return isValidMappedBy( target, targetEntity, mappedBy, context ) ? mappedBy : "";
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isValidMappedBy(AnnotatedFieldDescription persistentField, TypeDescription targetEntity, String mappedBy, ByteBuddyEnhancementContext context) {
|
||||
try {
|
||||
FieldDescription f = FieldLocator.ForClassHierarchy.Factory.INSTANCE.make( targetEntity ).locate( mappedBy ).getField();
|
||||
AnnotatedFieldDescription annotatedF = new AnnotatedFieldDescription( context, f );
|
||||
|
||||
return context.isPersistentField( annotatedF ) && persistentField.getDeclaringType().asErasure().isAssignableTo( entityType( f.getType() ) );
|
||||
}
|
||||
catch ( IllegalStateException e ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -514,7 +514,7 @@ public class EnhancerImpl implements Enhancer {
|
|||
private AnnotationList doGetAnnotations() {
|
||||
AnnotationDescription.Loadable<Access> access = fieldDescription.getDeclaringType().asErasure()
|
||||
.getDeclaredAnnotations().ofType( Access.class );
|
||||
if ( access != null && access.loadSilent().value() == AccessType.PROPERTY ) {
|
||||
if ( access != null && access.load().value() == AccessType.PROPERTY ) {
|
||||
Optional<MethodDescription> getter = getGetter();
|
||||
if ( getter.isPresent() ) {
|
||||
return getter.get().getDeclaredAnnotations();
|
||||
|
@ -523,7 +523,7 @@ public class EnhancerImpl implements Enhancer {
|
|||
return fieldDescription.getDeclaredAnnotations();
|
||||
}
|
||||
}
|
||||
else if ( access != null && access.loadSilent().value() == AccessType.FIELD ) {
|
||||
else if ( access != null && access.load().value() == AccessType.FIELD ) {
|
||||
return fieldDescription.getDeclaredAnnotations();
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -325,7 +325,7 @@ public class PersistentAttributesEnhancer extends EnhancerImpl {
|
|||
final CtClass targetEntity = PersistentAttributesHelper.getTargetEntityClass( managedCtClass, persistentField );
|
||||
if ( targetEntity == null ) {
|
||||
log.infof(
|
||||
"Could not find type of bi-directional association for field [%s#%s]",
|
||||
"Bi-directional association not managed for field [%s#%s]: Could not find target type",
|
||||
managedCtClass.getName(),
|
||||
persistentField.getName()
|
||||
);
|
||||
|
@ -334,9 +334,10 @@ public class PersistentAttributesEnhancer extends EnhancerImpl {
|
|||
final String mappedBy = PersistentAttributesHelper.getMappedBy( persistentField, targetEntity, enhancementContext );
|
||||
if ( mappedBy == null || mappedBy.isEmpty() ) {
|
||||
log.infof(
|
||||
"Could not find bi-directional association for field [%s#%s]",
|
||||
"Bi-directional association not managed for field [%s#%s]: Could not find target field in [%s]",
|
||||
managedCtClass.getName(),
|
||||
persistentField.getName()
|
||||
persistentField.getName(),
|
||||
targetEntity.getName()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -459,7 +460,7 @@ public class PersistentAttributesEnhancer extends EnhancerImpl {
|
|||
if ( PersistentAttributesHelper.isAssignable( persistentField.getType(), Map.class.getName() ) ||
|
||||
PersistentAttributesHelper.isAssignable( targetEntity.getField( mappedBy ).getType(), Map.class.getName() ) ) {
|
||||
log.infof(
|
||||
"Bi-directional association for field [%s#%s] not managed: @ManyToMany in java.util.Map attribute not supported ",
|
||||
"Bi-directional association not managed for field [%s#%s]: @ManyToMany in java.util.Map attribute not supported ",
|
||||
managedCtClass.getName(),
|
||||
persistentField.getName()
|
||||
);
|
||||
|
|
|
@ -209,7 +209,23 @@ public class PersistentAttributesHelper {
|
|||
|
||||
public static String getMappedBy(CtField persistentField, CtClass targetEntity, JavassistEnhancementContext context) throws NotFoundException {
|
||||
final String local = getMappedByFromAnnotation( persistentField );
|
||||
return local.isEmpty() ? getMappedByFromTargetEntity( persistentField, targetEntity, context ) : local;
|
||||
if ( local == null || local.isEmpty() ) {
|
||||
return getMappedByFromTargetEntity( persistentField, targetEntity, context );
|
||||
}
|
||||
else {
|
||||
// HHH-13446 - mappedBy from annotation may not be a valid bi-directional association, verify by calling isValidMappedBy()
|
||||
return isValidMappedBy( persistentField, targetEntity, local, context ) ? local : "";
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isValidMappedBy(CtField persistentField, CtClass targetEntity, String mappedBy, JavassistEnhancementContext context) {
|
||||
try {
|
||||
CtField f = targetEntity.getField( mappedBy );
|
||||
return context.isPersistentField( f ) && isAssignable( persistentField.getDeclaringClass(), inferFieldTypeName( f ) );
|
||||
}
|
||||
catch ( NotFoundException e ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static String getMappedByFromAnnotation(CtField persistentField) {
|
||||
|
|
|
@ -11,11 +11,12 @@ import java.sql.ResultSet;
|
|||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
|
@ -68,6 +69,7 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
public PersistentBag(SessionImplementor session) {
|
||||
this( (SharedSessionContractImplementor) session );
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a PersistentBag
|
||||
*
|
||||
|
@ -95,7 +97,7 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
* @param coll The base elements.
|
||||
*
|
||||
* @deprecated {@link #PersistentBag(SharedSessionContractImplementor, Collection)}
|
||||
* should be used instead.
|
||||
* should be used instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public PersistentBag(SessionImplementor session, Collection coll) {
|
||||
|
@ -128,7 +130,7 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
throws HibernateException, SQLException {
|
||||
// note that if we load this collection from a cartesian product
|
||||
// the multiplicity would be broken ... so use an idbag instead
|
||||
final Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() ) ;
|
||||
final Object element = persister.readElement( rs, owner, descriptor.getSuffixedElementAliases(), getSession() );
|
||||
if ( element != null ) {
|
||||
bag.add( element );
|
||||
}
|
||||
|
@ -159,7 +161,7 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings( "unchecked" )
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean equalsSnapshot(CollectionPersister persister) throws HibernateException {
|
||||
final Type elementType = persister.getElementType();
|
||||
final List<Object> sn = (List<Object>) getSnapshot();
|
||||
|
@ -199,7 +201,8 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
instance,
|
||||
instancesBag,
|
||||
elementType,
|
||||
countOccurrences( instance, instancesSn, elementType ) ) ) {
|
||||
countOccurrences( instance, instancesSn, elementType )
|
||||
) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -213,7 +216,28 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
* @return Map of "equality" hashCode to List of objects
|
||||
*/
|
||||
private Map<Integer, List<Object>> groupByEqualityHash(List<Object> searchedBag, Type elementType) {
|
||||
return searchedBag.stream().collect( Collectors.groupingBy( elementType::getHashCode ) );
|
||||
if ( searchedBag.isEmpty() ) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<Integer, List<Object>> map = new HashMap<>();
|
||||
for ( Object o : searchedBag ) {
|
||||
map.computeIfAbsent( nullableHashCode( o, elementType ), k -> new ArrayList<>() ).add( o );
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param o
|
||||
* @param elementType
|
||||
* @return the default elementType hashcode of the object o, or null if the object is null
|
||||
*/
|
||||
private Integer nullableHashCode(Object o, Type elementType) {
|
||||
if ( o == null ) {
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
return elementType.getHashCode( o );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -264,7 +288,7 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
public Object disassemble(CollectionPersister persister) {
|
||||
final int length = bag.size();
|
||||
final Object[] result = new Object[length];
|
||||
for ( int i=0; i<length; i++ ) {
|
||||
for ( int i = 0; i < length; i++ ) {
|
||||
result[i] = persister.getElementType().disassemble( bag.get( i ), getSession(), null );
|
||||
}
|
||||
return result;
|
||||
|
@ -306,13 +330,13 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
final ArrayList deletes = new ArrayList();
|
||||
final List sn = (List) getSnapshot();
|
||||
final Iterator olditer = sn.iterator();
|
||||
int i=0;
|
||||
int i = 0;
|
||||
while ( olditer.hasNext() ) {
|
||||
final Object old = olditer.next();
|
||||
final Iterator newiter = bag.iterator();
|
||||
boolean found = false;
|
||||
if ( bag.size()>i && elementType.isSame( old, bag.get( i++ ) ) ) {
|
||||
//a shortcut if its location didn't change!
|
||||
if ( bag.size() > i && elementType.isSame( old, bag.get( i++ ) ) ) {
|
||||
//a shortcut if its location didn't change!
|
||||
found = true;
|
||||
}
|
||||
else {
|
||||
|
@ -368,7 +392,7 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return readSize() ? getCachedSize()==0 : bag.isEmpty();
|
||||
return readSize() ? getCachedSize() == 0 : bag.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -431,7 +455,7 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean addAll(Collection values) {
|
||||
if ( values.size()==0 ) {
|
||||
if ( values.size() == 0 ) {
|
||||
return false;
|
||||
}
|
||||
if ( !isOperationQueueEnabled() ) {
|
||||
|
@ -442,14 +466,14 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
for ( Object value : values ) {
|
||||
queueOperation( new SimpleAdd( value ) );
|
||||
}
|
||||
return values.size()>0;
|
||||
return values.size() > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public boolean removeAll(Collection c) {
|
||||
if ( c.size()>0 ) {
|
||||
if ( c.size() > 0 ) {
|
||||
initialize( true );
|
||||
if ( bag.removeAll( c ) ) {
|
||||
elementRemoved = true;
|
||||
|
@ -486,7 +510,7 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
}
|
||||
else {
|
||||
initialize( true );
|
||||
if ( ! bag.isEmpty() ) {
|
||||
if ( !bag.isEmpty() ) {
|
||||
bag.clear();
|
||||
dirty();
|
||||
}
|
||||
|
@ -495,7 +519,7 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
|
||||
@Override
|
||||
public Object getIndex(Object entry, int i, CollectionPersister persister) {
|
||||
throw new UnsupportedOperationException("Bags don't have indexes");
|
||||
throw new UnsupportedOperationException( "Bags don't have indexes" );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -608,7 +632,7 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
|
||||
@Override
|
||||
public boolean entryExists(Object entry, int i) {
|
||||
return entry!=null;
|
||||
return entry != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -622,8 +646,9 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
* JVM instance comparison to do the equals.
|
||||
* The semantic is broken not to have to initialize a
|
||||
* collection for a simple equals() operation.
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
*
|
||||
* @see java.lang.Object#equals(java.lang.Object)
|
||||
* <p>
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
|
@ -649,7 +674,7 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
|
||||
@Override
|
||||
public Object getOrphan() {
|
||||
throw new UnsupportedOperationException("queued clear cannot be used with orphan delete");
|
||||
throw new UnsupportedOperationException( "queued clear cannot be used with orphan delete" );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -532,7 +532,8 @@ public class MySQLDialect extends Dialect {
|
|||
@Override
|
||||
public JDBCException convert(SQLException sqlException, String message, String sql) {
|
||||
switch ( sqlException.getErrorCode() ) {
|
||||
case 1205: {
|
||||
case 1205:
|
||||
case 3572: {
|
||||
return new PessimisticLockException( message, sqlException, sql );
|
||||
}
|
||||
case 1207:
|
||||
|
|
|
@ -91,31 +91,42 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
|
||||
private static final int INIT_COLL_SIZE = 8;
|
||||
|
||||
/*
|
||||
Eagerly Initialized Fields
|
||||
the following fields are used in all circumstances, and are not worth (or not suited) to being converted into lazy
|
||||
*/
|
||||
private SharedSessionContractImplementor session;
|
||||
|
||||
// Loaded entity instances, by EntityKey
|
||||
private Map<EntityKey, Object> entitiesByKey;
|
||||
|
||||
// Loaded entity instances, by EntityUniqueKey
|
||||
private Map<EntityUniqueKey, Object> entitiesByUniqueKey;
|
||||
|
||||
private EntityEntryContext entityEntryContext;
|
||||
|
||||
/*
|
||||
Everything else below should be carefully initialized only on first need;
|
||||
this optimisation is very effective as null checks are free, while allocation costs
|
||||
are very often the dominating cost of an application using ORM.
|
||||
This is not general advice, but it's worth the added maintenance burden in this case
|
||||
as this is a very central component of our library.
|
||||
*/
|
||||
|
||||
// Loaded entity instances, by EntityKey
|
||||
private HashMap<EntityKey, Object> entitiesByKey;
|
||||
|
||||
// Loaded entity instances, by EntityUniqueKey
|
||||
private HashMap<EntityUniqueKey, Object> entitiesByUniqueKey;
|
||||
|
||||
// Entity proxies, by EntityKey
|
||||
private ConcurrentMap<EntityKey, Object> proxiesByKey;
|
||||
private ConcurrentReferenceHashMap<EntityKey, Object> proxiesByKey;
|
||||
|
||||
// Snapshots of current database state for entities
|
||||
// that have *not* been loaded
|
||||
private Map<EntityKey, Object> entitySnapshotsByKey;
|
||||
private HashMap<EntityKey, Object> entitySnapshotsByKey;
|
||||
|
||||
// Identity map of array holder ArrayHolder instances, by the array instance
|
||||
private Map<Object, PersistentCollection> arrayHolders;
|
||||
private IdentityHashMap<Object, PersistentCollection> arrayHolders;
|
||||
|
||||
// Identity map of CollectionEntry instances, by the collection wrapper
|
||||
private IdentityMap<PersistentCollection, CollectionEntry> collectionEntries;
|
||||
|
||||
// Collection wrappers, by the CollectionKey
|
||||
private Map<CollectionKey, PersistentCollection> collectionsByKey;
|
||||
private HashMap<CollectionKey, PersistentCollection> collectionsByKey;
|
||||
|
||||
// Set of EntityKeys of deleted objects
|
||||
private HashSet<EntityKey> nullifiableEntityKeys;
|
||||
|
@ -125,15 +136,15 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
|
||||
// A list of collection wrappers that were instantiating during result set
|
||||
// processing, that we will need to initialize at the end of the query
|
||||
private List<PersistentCollection> nonlazyCollections;
|
||||
private ArrayList<PersistentCollection> nonlazyCollections;
|
||||
|
||||
// A container for collections we load up when the owning entity is not
|
||||
// yet loaded ... for now, this is purely transient!
|
||||
private Map<CollectionKey,PersistentCollection> unownedCollections;
|
||||
private HashMap<CollectionKey,PersistentCollection> unownedCollections;
|
||||
|
||||
// Parent entities cache by their child for cascading
|
||||
// May be empty or not contains all relation
|
||||
private Map<Object,Object> parentsByChild;
|
||||
private IdentityHashMap<Object,Object> parentsByChild;
|
||||
|
||||
private int cascading;
|
||||
private int loadCounter;
|
||||
|
@ -146,7 +157,6 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
private LoadContexts loadContexts;
|
||||
private BatchFetchQueue batchFetchQueue;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a PersistentContext, bound to the given session.
|
||||
*
|
||||
|
@ -154,12 +164,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
*/
|
||||
public StatefulPersistenceContext(SharedSessionContractImplementor session) {
|
||||
this.session = session;
|
||||
|
||||
entitiesByKey = new HashMap<>( INIT_COLL_SIZE );
|
||||
entitySnapshotsByKey = new HashMap<>( INIT_COLL_SIZE );
|
||||
|
||||
entityEntryContext = new EntityEntryContext( this );
|
||||
collectionsByKey = new HashMap<>( INIT_COLL_SIZE );
|
||||
this.entityEntryContext = new EntityEntryContext( this );
|
||||
}
|
||||
|
||||
private ConcurrentMap<EntityKey, Object> getOrInitializeProxiesByKey() {
|
||||
|
@ -241,12 +246,12 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
}
|
||||
|
||||
arrayHolders = null;
|
||||
entitiesByKey.clear();
|
||||
entitiesByKey = null;
|
||||
entitiesByUniqueKey = null;
|
||||
entityEntryContext.clear();
|
||||
parentsByChild = null;
|
||||
entitySnapshotsByKey.clear();
|
||||
collectionsByKey.clear();
|
||||
entitySnapshotsByKey = null;
|
||||
collectionsByKey = null;
|
||||
nonlazyCollections = null;
|
||||
collectionEntries = null;
|
||||
unownedCollections = null;
|
||||
|
@ -306,12 +311,15 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
@Override
|
||||
public Object[] getDatabaseSnapshot(Object id, EntityPersister persister) throws HibernateException {
|
||||
final EntityKey key = session.generateEntityKey( id, persister );
|
||||
final Object cached = entitySnapshotsByKey.get( key );
|
||||
final Object cached = entitySnapshotsByKey == null ? null : entitySnapshotsByKey.get( key );
|
||||
if ( cached != null ) {
|
||||
return cached == NO_ROW ? null : (Object[]) cached;
|
||||
}
|
||||
else {
|
||||
final Object[] snapshot = persister.getDatabaseSnapshot( id, session );
|
||||
if ( entitySnapshotsByKey == null ) {
|
||||
entitySnapshotsByKey = new HashMap<>( INIT_COLL_SIZE );
|
||||
}
|
||||
entitySnapshotsByKey.put( key, snapshot == null ? NO_ROW : snapshot );
|
||||
return snapshot;
|
||||
}
|
||||
|
@ -370,7 +378,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
|
||||
@Override
|
||||
public Object[] getCachedDatabaseSnapshot(EntityKey key) {
|
||||
final Object snapshot = entitySnapshotsByKey.get( key );
|
||||
final Object snapshot = entitySnapshotsByKey == null ? null : entitySnapshotsByKey.get( key );
|
||||
if ( snapshot == NO_ROW ) {
|
||||
throw new IllegalStateException(
|
||||
"persistence context reported no row snapshot for "
|
||||
|
@ -382,43 +390,56 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
|
||||
@Override
|
||||
public void addEntity(EntityKey key, Object entity) {
|
||||
if ( entitiesByKey == null ) {
|
||||
entitiesByKey = new HashMap<>( INIT_COLL_SIZE );
|
||||
}
|
||||
entitiesByKey.put( key, entity );
|
||||
if( batchFetchQueue != null ) {
|
||||
getBatchFetchQueue().removeBatchLoadableEntityKey(key);
|
||||
final BatchFetchQueue fetchQueue = this.batchFetchQueue;
|
||||
if ( fetchQueue != null ) {
|
||||
fetchQueue.removeBatchLoadableEntityKey( key );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEntity(EntityKey key) {
|
||||
return entitiesByKey.get( key );
|
||||
return entitiesByKey == null ? null : entitiesByKey.get( key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsEntity(EntityKey key) {
|
||||
return entitiesByKey.containsKey( key );
|
||||
return entitiesByKey == null ? false : entitiesByKey.containsKey( key );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object removeEntity(EntityKey key) {
|
||||
final Object entity = entitiesByKey.remove( key );
|
||||
if ( entitiesByUniqueKey != null ) {
|
||||
final Iterator itr = entitiesByUniqueKey.values().iterator();
|
||||
while ( itr.hasNext() ) {
|
||||
if ( itr.next() == entity ) {
|
||||
itr.remove();
|
||||
final Object entity;
|
||||
if ( entitiesByKey != null ) {
|
||||
entity = entitiesByKey.remove( key );
|
||||
if ( entitiesByUniqueKey != null ) {
|
||||
final Iterator itr = entitiesByUniqueKey.values().iterator();
|
||||
while ( itr.hasNext() ) {
|
||||
if ( itr.next() == entity ) {
|
||||
itr.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
entity = null;
|
||||
}
|
||||
|
||||
// Clear all parent cache
|
||||
parentsByChild = null;
|
||||
entitySnapshotsByKey.remove( key );
|
||||
if ( entitySnapshotsByKey != null ) {
|
||||
entitySnapshotsByKey.remove( key );
|
||||
}
|
||||
if ( nullifiableEntityKeys != null ) {
|
||||
nullifiableEntityKeys.remove( key );
|
||||
}
|
||||
if( batchFetchQueue != null ) {
|
||||
getBatchFetchQueue().removeBatchLoadableEntityKey( key );
|
||||
getBatchFetchQueue().removeSubselect( key );
|
||||
final BatchFetchQueue fetchQueue = this.batchFetchQueue;
|
||||
if ( fetchQueue != null ) {
|
||||
fetchQueue.removeBatchLoadableEntityKey( key );
|
||||
fetchQueue.removeSubselect( key );
|
||||
}
|
||||
return entity;
|
||||
}
|
||||
|
@ -752,6 +773,9 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
|
||||
@Override
|
||||
public void addEnhancedProxy(EntityKey key, PersistentAttributeInterceptable entity) {
|
||||
if ( entitiesByKey == null ) {
|
||||
entitiesByKey = new HashMap<>( INIT_COLL_SIZE );
|
||||
}
|
||||
entitiesByKey.put( key, entity );
|
||||
}
|
||||
|
||||
|
@ -885,7 +909,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
private void addCollection(PersistentCollection coll, CollectionEntry entry, Object key) {
|
||||
getOrInitializeCollectionEntries().put( coll, entry );
|
||||
final CollectionKey collectionKey = new CollectionKey( entry.getLoadedPersister(), key );
|
||||
final PersistentCollection old = collectionsByKey.put( collectionKey, coll );
|
||||
final PersistentCollection old = addCollectionByKey( collectionKey, coll );
|
||||
if ( old != null ) {
|
||||
if ( old == coll ) {
|
||||
throw new AssertionFailure( "bug adding collection twice" );
|
||||
|
@ -942,7 +966,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
|
||||
@Override
|
||||
public PersistentCollection getCollection(CollectionKey collectionKey) {
|
||||
return collectionsByKey.get( collectionKey );
|
||||
return collectionsByKey == null ? null : collectionsByKey.get( collectionKey );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1037,9 +1061,10 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
|
||||
@Override
|
||||
public Object removeProxy(EntityKey key) {
|
||||
if ( batchFetchQueue != null ) {
|
||||
batchFetchQueue.removeBatchLoadableEntityKey( key );
|
||||
batchFetchQueue.removeSubselect( key );
|
||||
final BatchFetchQueue fetchQueue = this.batchFetchQueue;
|
||||
if ( fetchQueue != null ) {
|
||||
fetchQueue.removeBatchLoadableEntityKey( key );
|
||||
fetchQueue.removeSubselect( key );
|
||||
}
|
||||
return removeProxyByKey( key );
|
||||
}
|
||||
|
@ -1052,9 +1077,25 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
return nullifiableEntityKeys;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated this will be removed: it provides too wide access, making it hard to optimise the internals
|
||||
* for specific access needs. Consider using #iterateEntities instead.
|
||||
* @return
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public Map getEntitiesByKey() {
|
||||
return entitiesByKey;
|
||||
return entitiesByKey == null ? Collections.emptyMap() : entitiesByKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator managedEntitiesIterator() {
|
||||
if ( entitiesByKey == null ) {
|
||||
return Collections.emptyIterator();
|
||||
}
|
||||
else {
|
||||
return entitiesByKey.values().iterator();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1094,7 +1135,12 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
|
||||
@Override
|
||||
public Map getCollectionsByKey() {
|
||||
return collectionsByKey;
|
||||
if ( collectionsByKey == null ) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
else {
|
||||
return collectionsByKey;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1185,8 +1231,9 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PersistenceContext[entityKeys=" + entitiesByKey.keySet()
|
||||
+ ",collectionKeys=" + collectionsByKey.keySet() + "]";
|
||||
final String entityKeySet = entitiesByKey == null ? "[]" : entitiesByKey.keySet().toString();
|
||||
final String collectionsKeySet = collectionsByKey == null ? "[]" : collectionsByKey.keySet().toString();
|
||||
return "PersistenceContext[entityKeys=" + entityKeySet + ", collectionKeys=" + collectionsKeySet + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1483,7 +1530,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
|
||||
@Override
|
||||
public void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Object generatedId) {
|
||||
final Object entity = entitiesByKey.remove( oldKey );
|
||||
final Object entity = entitiesByKey == null ? null : entitiesByKey.remove( oldKey );
|
||||
final EntityEntry oldEntry = entityEntryContext.removeEntityEntry( entity );
|
||||
this.parentsByChild = null;
|
||||
|
||||
|
@ -1516,13 +1563,18 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
oos.writeBoolean( defaultReadOnly );
|
||||
oos.writeBoolean( hasNonReadOnlyEntities );
|
||||
|
||||
oos.writeInt( entitiesByKey.size() );
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.trace( "Starting serialization of [" + entitiesByKey.size() + "] entitiesByKey entries" );
|
||||
if ( entitiesByKey == null ) {
|
||||
oos.writeInt( 0 );
|
||||
}
|
||||
for ( Map.Entry<EntityKey,Object> entry : entitiesByKey.entrySet() ) {
|
||||
entry.getKey().serialize( oos );
|
||||
oos.writeObject( entry.getValue() );
|
||||
else {
|
||||
oos.writeInt( entitiesByKey.size() );
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.trace( "Starting serialization of [" + entitiesByKey.size() + "] entitiesByKey entries" );
|
||||
}
|
||||
for ( Map.Entry<EntityKey,Object> entry : entitiesByKey.entrySet() ) {
|
||||
entry.getKey().serialize( oos );
|
||||
oos.writeObject( entry.getValue() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( entitiesByUniqueKey == null ) {
|
||||
|
@ -1553,24 +1605,34 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
}
|
||||
}
|
||||
|
||||
oos.writeInt( entitySnapshotsByKey.size() );
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.trace( "Starting serialization of [" + entitySnapshotsByKey.size() + "] entitySnapshotsByKey entries" );
|
||||
if ( entitySnapshotsByKey == null ) {
|
||||
oos.writeInt( 0 );
|
||||
}
|
||||
for ( Map.Entry<EntityKey,Object> entry : entitySnapshotsByKey.entrySet() ) {
|
||||
entry.getKey().serialize( oos );
|
||||
oos.writeObject( entry.getValue() );
|
||||
else {
|
||||
oos.writeInt( entitySnapshotsByKey.size() );
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.trace( "Starting serialization of [" + entitySnapshotsByKey.size() + "] entitySnapshotsByKey entries" );
|
||||
}
|
||||
for ( Map.Entry<EntityKey,Object> entry : entitySnapshotsByKey.entrySet() ) {
|
||||
entry.getKey().serialize( oos );
|
||||
oos.writeObject( entry.getValue() );
|
||||
}
|
||||
}
|
||||
|
||||
entityEntryContext.serialize( oos );
|
||||
|
||||
oos.writeInt( collectionsByKey.size() );
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.trace( "Starting serialization of [" + collectionsByKey.size() + "] collectionsByKey entries" );
|
||||
if ( collectionsByKey == null ) {
|
||||
oos.writeInt( 0 );
|
||||
}
|
||||
for ( Map.Entry<CollectionKey,PersistentCollection> entry : collectionsByKey.entrySet() ) {
|
||||
entry.getKey().serialize( oos );
|
||||
oos.writeObject( entry.getValue() );
|
||||
else {
|
||||
oos.writeInt( collectionsByKey.size() );
|
||||
if ( LOG.isTraceEnabled() ) {
|
||||
LOG.trace( "Starting serialization of [" + collectionsByKey.size() + "] collectionsByKey entries" );
|
||||
}
|
||||
for ( Map.Entry<CollectionKey, PersistentCollection> entry : collectionsByKey.entrySet() ) {
|
||||
entry.getKey().serialize( oos );
|
||||
oos.writeObject( entry.getValue() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( collectionEntries == null ) {
|
||||
|
@ -1831,6 +1893,32 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearCollectionsByKey() {
|
||||
if ( collectionsByKey != null ) {
|
||||
//A valid alternative would be to set this to null, like we do on close.
|
||||
//The difference being that in this case we expect the collection will be used again, so we bet that clear()
|
||||
//might allow us to skip having to re-allocate the collection.
|
||||
collectionsByKey.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PersistentCollection addCollectionByKey(CollectionKey collectionKey, PersistentCollection persistentCollection) {
|
||||
if ( collectionsByKey == null ) {
|
||||
collectionsByKey = new HashMap<>( INIT_COLL_SIZE );
|
||||
}
|
||||
final PersistentCollection old = collectionsByKey.put( collectionKey, persistentCollection );
|
||||
return old;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeCollectionByKey(CollectionKey collectionKey) {
|
||||
if ( collectionsByKey != null ) {
|
||||
collectionsByKey.remove( collectionKey );
|
||||
}
|
||||
}
|
||||
|
||||
private void cleanUpInsertedKeysAfterTransaction() {
|
||||
if ( insertedKeysMap != null ) {
|
||||
insertedKeysMap.clear();
|
||||
|
|
|
@ -159,8 +159,12 @@ public abstract class AbstractBatchImpl implements Batch {
|
|||
clearBatch( statement );
|
||||
resourceRegistry.release( statement );
|
||||
}
|
||||
jdbcCoordinator.afterStatementExecution();
|
||||
// IMPL NOTE: If the statements are not cleared and JTA is being used, then
|
||||
// jdbcCoordinator.afterStatementExecution() will abort the batch and a
|
||||
// warning will be logged. To avoid the warning, clear statements first,
|
||||
// before calling jdbcCoordinator.afterStatementExecution().
|
||||
statements.clear();
|
||||
jdbcCoordinator.afterStatementExecution();
|
||||
}
|
||||
|
||||
protected void clearBatch(PreparedStatement statement) {
|
||||
|
|
|
@ -124,4 +124,13 @@ public abstract class BasicConnectionCreator implements ConnectionCreator {
|
|||
}
|
||||
|
||||
protected abstract Connection makeConnection(String url, Properties connectionProps);
|
||||
|
||||
/**
|
||||
* Exposed for testing purposes only.
|
||||
* @return
|
||||
*/
|
||||
public Properties getConnectionProperties() {
|
||||
return new Properties( connectionProps );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -417,6 +417,7 @@ public class ConnectionProviderInitiator implements StandardServiceInitiator<Con
|
|||
SPECIAL_PROPERTIES.add( AvailableSettings.ISOLATION );
|
||||
SPECIAL_PROPERTIES.add( AvailableSettings.DRIVER );
|
||||
SPECIAL_PROPERTIES.add( AvailableSettings.USER );
|
||||
SPECIAL_PROPERTIES.add( AvailableSettings.CONNECTION_PROVIDER_DISABLES_AUTOCOMMIT );
|
||||
|
||||
ISOLATION_VALUE_MAP = new ConcurrentHashMap<String, Integer>();
|
||||
ISOLATION_VALUE_MAP.put( "TRANSACTION_NONE", Connection.TRANSACTION_NONE );
|
||||
|
|
|
@ -215,6 +215,16 @@ public class DriverManagerConnectionProviderImpl
|
|||
}
|
||||
//CHECKSTYLE:END_ALLOW_FINALIZER
|
||||
|
||||
/**
|
||||
* Exposed to facilitate testing only.
|
||||
* @return
|
||||
*/
|
||||
public Properties getConnectionProperties() {
|
||||
BasicConnectionCreator connectionCreator = (BasicConnectionCreator) this.state.pool.connectionCreator;
|
||||
return connectionCreator.getConnectionProperties();
|
||||
}
|
||||
|
||||
|
||||
public static class PooledConnections {
|
||||
|
||||
private final ConcurrentLinkedQueue<Connection> allConnections = new ConcurrentLinkedQueue<Connection>();
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.engine.spi;
|
|||
import java.io.Serializable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -487,7 +488,10 @@ public interface PersistenceContext {
|
|||
|
||||
/**
|
||||
* Get the mapping from key value to entity instance
|
||||
* @deprecated this will be removed: it provides too wide access, making it hard to optimise the internals
|
||||
* for specific access needs. Consider using #iterateEntities instead.
|
||||
*/
|
||||
@Deprecated
|
||||
Map getEntitiesByKey();
|
||||
|
||||
/**
|
||||
|
@ -526,7 +530,12 @@ public interface PersistenceContext {
|
|||
|
||||
/**
|
||||
* Get the mapping from collection key to collection instance
|
||||
* @deprecated this method should be removed; alternative methods are available that better express the intent, allowing
|
||||
* for better optimisations. Not aggressively removing this as it's an SPI, but also useful for testing and other
|
||||
* contexts which are not performance sensitive.
|
||||
* N.B. This might return an immutable map: do not use for mutations!
|
||||
*/
|
||||
@Deprecated
|
||||
Map getCollectionsByKey();
|
||||
|
||||
/**
|
||||
|
@ -769,6 +778,31 @@ public interface PersistenceContext {
|
|||
*/
|
||||
CollectionEntry removeCollectionEntry(PersistentCollection collection);
|
||||
|
||||
/**
|
||||
* Remove all state of the collections-by-key map.
|
||||
*/
|
||||
void clearCollectionsByKey();
|
||||
|
||||
/**
|
||||
* Adds a collection in the collections-by-key map.
|
||||
* @param collectionKey
|
||||
* @param persistentCollection
|
||||
* @return the previous collection, it the key was already mapped.
|
||||
*/
|
||||
PersistentCollection addCollectionByKey(CollectionKey collectionKey, PersistentCollection persistentCollection);
|
||||
|
||||
/**
|
||||
* Remove a collection-by-key mapping.
|
||||
* @param collectionKey the key to clear
|
||||
*/
|
||||
void removeCollectionByKey(CollectionKey collectionKey);
|
||||
|
||||
/**
|
||||
* A read-only iterator on all entities managed by this persistence context
|
||||
* @return
|
||||
*/
|
||||
Iterator managedEntitiesIterator();
|
||||
|
||||
/**
|
||||
* Provides centralized access to natural-id-related functionality.
|
||||
*/
|
||||
|
|
|
@ -37,7 +37,6 @@ import org.hibernate.event.spi.FlushEntityEventListener;
|
|||
import org.hibernate.event.spi.FlushEvent;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.EntityPrinter;
|
||||
import org.hibernate.internal.util.collections.LazyIterator;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
@ -77,7 +76,7 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi
|
|||
EventSource session = event.getSession();
|
||||
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
session.getInterceptor().preFlush( new LazyIterator( persistenceContext.getEntitiesByKey() ) );
|
||||
session.getInterceptor().preFlush( persistenceContext.managedEntitiesIterator() );
|
||||
|
||||
prepareEntityFlushes( session, persistenceContext );
|
||||
// we could move this inside if we wanted to
|
||||
|
@ -369,7 +368,7 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi
|
|||
LOG.trace( "Post flush" );
|
||||
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
persistenceContext.getCollectionsByKey().clear();
|
||||
persistenceContext.clearCollectionsByKey();
|
||||
|
||||
// the database has changed now, so the subselect results need to be invalidated
|
||||
// the batch fetching queues should also be cleared - especially the collection batch fetching one
|
||||
|
@ -390,13 +389,14 @@ public abstract class AbstractFlushingEventListener implements JpaBootstrapSensi
|
|||
collectionEntry.getLoadedPersister(),
|
||||
collectionEntry.getLoadedKey()
|
||||
);
|
||||
persistenceContext.getCollectionsByKey().put( collectionKey, persistentCollection );
|
||||
persistenceContext.addCollectionByKey( collectionKey, persistentCollection );
|
||||
}
|
||||
}, true
|
||||
);
|
||||
}
|
||||
|
||||
protected void postPostFlush(SessionImplementor session) {
|
||||
session.getInterceptor().postFlush( new LazyIterator( session.getPersistenceContextInternal().getEntitiesByKey() ) );
|
||||
session.getInterceptor().postFlush( session.getPersistenceContextInternal().managedEntitiesIterator() );
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -81,9 +81,7 @@ public class EvictVisitor extends AbstractVisitor {
|
|||
}
|
||||
if ( ce.getLoadedPersister() != null && ce.getLoadedKey() != null ) {
|
||||
//TODO: is this 100% correct?
|
||||
persistenceContext.getCollectionsByKey().remove(
|
||||
new CollectionKey( ce.getLoadedPersister(), ce.getLoadedKey() )
|
||||
);
|
||||
persistenceContext.removeCollectionByKey( new CollectionKey( ce.getLoadedPersister(), ce.getLoadedKey() ) );
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2850,7 +2850,7 @@ public final class SessionImpl
|
|||
throw getExceptionConverter().convert( new IllegalArgumentException( e.getMessage(), e ) );
|
||||
}
|
||||
catch ( JDBCException e ) {
|
||||
if ( accessTransaction().getRollbackOnly() ) {
|
||||
if ( accessTransaction().isActive() && accessTransaction().getRollbackOnly() ) {
|
||||
// assume this is the similar to the WildFly / IronJacamar "feature" described under HHH-12472
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -55,7 +55,6 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
@Override
|
||||
public void setInternalFetchProfile(String internalFetchProfile) {
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
private final PersistenceContext temporaryPersistenceContext = new StatefulPersistenceContext( this );
|
||||
|
@ -240,6 +239,9 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
this.getLoadQueryInfluencers().setInternalFetchProfile( previousFetchProfile );
|
||||
}
|
||||
UnresolvableObjectException.throwIfNull( result, id, persister.getEntityName() );
|
||||
if ( temporaryPersistenceContext.isLoadFinished() ) {
|
||||
temporaryPersistenceContext.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -274,11 +276,12 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
boolean nullable) throws HibernateException {
|
||||
checkOpen();
|
||||
|
||||
EntityPersister persister = getFactory().getMetamodel().entityPersister( entityName );
|
||||
final EntityPersister persister = getFactory().getMetamodel().entityPersister( entityName );
|
||||
final EntityKey entityKey = generateEntityKey( id, persister );
|
||||
|
||||
// first, try to load it from the temp PC associated to this SS
|
||||
Object loaded = temporaryPersistenceContext.getEntity( entityKey );
|
||||
final PersistenceContext persistenceContext = getPersistenceContext();
|
||||
Object loaded = persistenceContext.getEntity( entityKey );
|
||||
if ( loaded != null ) {
|
||||
// we found it in the temp PC. Should indicate we are in the midst of processing a result set
|
||||
// containing eager fetches via join fetch
|
||||
|
@ -298,7 +301,6 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
// if the entity defines a HibernateProxy factory, see if there is an
|
||||
// existing proxy associated with the PC - and if so, use it
|
||||
if ( persister.getRepresentationStrategy().getProxyFactory() != null ) {
|
||||
final PersistenceContext persistenceContext = getPersistenceContext();
|
||||
final Object proxy = persistenceContext.getProxy( entityKey );
|
||||
|
||||
if ( proxy != null ) {
|
||||
|
@ -329,7 +331,6 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
}
|
||||
else {
|
||||
if ( persister.hasProxy() ) {
|
||||
final PersistenceContext persistenceContext = getPersistenceContext();
|
||||
final Object existingProxy = persistenceContext.getProxy( entityKey );
|
||||
if ( existingProxy != null ) {
|
||||
return persistenceContext.narrowProxy( existingProxy, persister, entityKey, null );
|
||||
|
@ -342,7 +343,16 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
}
|
||||
|
||||
// otherwise immediately materialize it
|
||||
return get( entityName, id );
|
||||
|
||||
// IMPLEMENTATION NOTE: increment/decrement the load count before/after getting the value
|
||||
// to ensure that #get does not clear the PersistenceContext.
|
||||
persistenceContext.beforeLoad();
|
||||
try {
|
||||
return get( entityName, id );
|
||||
}
|
||||
finally {
|
||||
persistenceContext.afterLoad();
|
||||
}
|
||||
}
|
||||
|
||||
private Object createProxy(EntityKey entityKey) {
|
||||
|
|
|
@ -1,40 +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>.
|
||||
*/
|
||||
package org.hibernate.internal.util.collections;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
public final class LazyIterator implements Iterator {
|
||||
|
||||
private final Map map;
|
||||
private Iterator iterator;
|
||||
|
||||
private Iterator getIterator() {
|
||||
if (iterator==null) {
|
||||
iterator = map.values().iterator();
|
||||
}
|
||||
return iterator;
|
||||
}
|
||||
|
||||
public LazyIterator(Map map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return getIterator().hasNext();
|
||||
}
|
||||
|
||||
public Object next() {
|
||||
return getIterator().next();
|
||||
}
|
||||
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
}
|
|
@ -86,6 +86,15 @@ public class GenerationTargetToDatabase implements GenerationTarget {
|
|||
|
||||
@Override
|
||||
public void release() {
|
||||
if ( jdbcStatement != null ) {
|
||||
try {
|
||||
jdbcStatement.close();
|
||||
jdbcStatement = null;
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw ddlTransactionIsolator.getJdbcContext().getSqlExceptionHelper().convert( e, "Unable to close JDBC Statement after DDL execution" );
|
||||
}
|
||||
}
|
||||
if ( releaseAfterUse ) {
|
||||
ddlTransactionIsolator.release();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.jpa.test.connection;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.Id;
|
||||
|
@ -15,10 +16,13 @@ import javax.persistence.criteria.CriteriaBuilder;
|
|||
import javax.persistence.criteria.CriteriaQuery;
|
||||
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
|
||||
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
|
||||
import org.hibernate.exception.SQLGrammarException;
|
||||
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
|
@ -46,6 +50,19 @@ public class TestConnectionPool
|
|||
AvailableSettings.POOL_SIZE,
|
||||
Integer.valueOf( CONNECTION_POOL_SIZE )
|
||||
);
|
||||
options.put( "hibernate.connection.customProperty", "x" );
|
||||
options.put( AvailableSettings.CONNECTION_PROVIDER_DISABLES_AUTOCOMMIT, "true" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-13700")
|
||||
public void testConnectionPoolPropertyFiltering() {
|
||||
ConnectionProvider cp = serviceRegistry().getService( ConnectionProvider.class );
|
||||
DriverManagerConnectionProviderImpl dmcp = (DriverManagerConnectionProviderImpl) cp;
|
||||
Properties connectionProperties = dmcp.getConnectionProperties();
|
||||
Assert.assertEquals( "x", connectionProperties.getProperty( "customProperty" ) );
|
||||
Assert.assertNull( connectionProperties.getProperty( "pool_size" ) );
|
||||
Assert.assertNull( connectionProperties.getProperty( "provider_disables_autocommit" ) );
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -18,13 +18,19 @@ import javax.persistence.spi.PersistenceProvider;
|
|||
import javax.persistence.spi.PersistenceUnitInfo;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.hibernate.boot.spi.MetadataImplementor;
|
||||
import org.hibernate.boot.spi.SessionFactoryOptions;
|
||||
import org.hibernate.cache.spi.access.AccessType;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl;
|
||||
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
|
||||
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
|
||||
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.hql.internal.ast.HqlSqlWalker;
|
||||
import org.hibernate.hql.spi.id.MultiTableBulkIdStrategy;
|
||||
import org.hibernate.jpa.HibernatePersistenceProvider;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
|
@ -88,6 +94,7 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase {
|
|||
final DataSource integrationDataSource = new DataSourceStub( "integrationDataSource" );
|
||||
|
||||
final HibernatePersistenceProvider provider = new HibernatePersistenceProvider();
|
||||
puInfo.getProperties().setProperty( AvailableSettings.HQL_BULK_ID_STRATEGY, MultiTableBulkIdStrategyStub.class.getName() );
|
||||
|
||||
final EntityManagerFactory emf = provider.createContainerEntityManagerFactory(
|
||||
puInfo,
|
||||
|
@ -273,6 +280,7 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase {
|
|||
final Map integrationOverrides = new HashMap();
|
||||
//noinspection unchecked
|
||||
integrationOverrides.put( AvailableSettings.JPA_JTA_DATASOURCE, integrationDataSource );
|
||||
integrationOverrides.put( AvailableSettings.HQL_BULK_ID_STRATEGY, new MultiTableBulkIdStrategyStub() );
|
||||
|
||||
final EntityManagerFactory emf = provider.createContainerEntityManagerFactory(
|
||||
new PersistenceUnitInfoAdapter(),
|
||||
|
@ -318,6 +326,7 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase {
|
|||
final DataSource override = new DataSourceStub( "integrationDataSource" );
|
||||
final Map<String,Object> integrationSettings = new HashMap<>();
|
||||
integrationSettings.put( AvailableSettings.JPA_NON_JTA_DATASOURCE, override );
|
||||
integrationSettings.put( AvailableSettings.HQL_BULK_ID_STRATEGY, new MultiTableBulkIdStrategyStub() );
|
||||
|
||||
final PersistenceProvider provider = new HibernatePersistenceProvider();
|
||||
|
||||
|
@ -502,4 +511,34 @@ public class PersistenceUnitOverridesTests extends BaseUnitTestCase {
|
|||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
public static class MultiTableBulkIdStrategyStub implements MultiTableBulkIdStrategy {
|
||||
|
||||
@Override
|
||||
public void prepare(
|
||||
JdbcServices jdbcServices,
|
||||
JdbcConnectionAccess connectionAccess,
|
||||
MetadataImplementor metadata,
|
||||
SessionFactoryOptions sessionFactoryOptions) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release(
|
||||
JdbcServices jdbcServices, JdbcConnectionAccess connectionAccess) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public UpdateHandler buildUpdateHandler(
|
||||
SessionFactoryImplementor factory, HqlSqlWalker walker) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DeleteHandler buildDeleteHandler(
|
||||
SessionFactoryImplementor factory, HqlSqlWalker walker) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,6 +73,10 @@ public class OneToManyAssociationTest {
|
|||
|
||||
String name;
|
||||
|
||||
// HHH-13446 - Type not validated in bi-directional association mapping
|
||||
@OneToMany(cascade = CascadeType.ALL, mappedBy = "custId", fetch = FetchType.EAGER)
|
||||
List<CustomerInventory> inventoryIdList = new ArrayList<>();
|
||||
|
||||
@OneToMany( mappedBy = "customer", cascade = CascadeType.ALL, fetch = FetchType.EAGER )
|
||||
List<CustomerInventory> customerInventories = new ArrayList<>();
|
||||
|
||||
|
|
|
@ -0,0 +1,323 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.lazy;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.ScrollableResults;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.StatelessSession;
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.SessionFactoryBuilder;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.dialect.DB2Dialect;
|
||||
import org.hibernate.query.Query;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
public class QueryScrollingWithInheritanceEagerManyToOneTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "false" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
|
||||
super.configureSessionFactoryBuilder( sfb );
|
||||
sfb.applyStatisticsSupport( true );
|
||||
sfb.applySecondLevelCacheSupport( false );
|
||||
sfb.applyQueryCacheSupport( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyMetadataSources(MetadataSources sources) {
|
||||
super.applyMetadataSources( sources );
|
||||
sources.addAnnotatedClass( EmployeeParent.class );
|
||||
sources.addAnnotatedClass( Employee.class );
|
||||
sources.addAnnotatedClass( OtherEntity.class );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScrollableWithStatelessSession() {
|
||||
final StatisticsImplementor stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
ScrollableResults scrollableResults = null;
|
||||
final StatelessSession statelessSession = sessionFactory().openStatelessSession();
|
||||
|
||||
try {
|
||||
statelessSession.beginTransaction();
|
||||
Query<Employee> query = statelessSession.createQuery(
|
||||
"select distinct e from Employee e left join fetch e.otherEntities order by e.dept",
|
||||
Employee.class
|
||||
);
|
||||
if ( getDialect() instanceof DB2Dialect ) {
|
||||
/*
|
||||
FetchingScrollableResultsImp#next() in order to check if the ResultSet is empty calls ResultSet#isBeforeFirst()
|
||||
but the support for ResultSet#isBeforeFirst() is optional for ResultSets with a result
|
||||
set type of TYPE_FORWARD_ONLY and db2 does not support it.
|
||||
*/
|
||||
scrollableResults = query.scroll( ScrollMode.SCROLL_INSENSITIVE );
|
||||
}
|
||||
else {
|
||||
scrollableResults = query.scroll( ScrollMode.FORWARD_ONLY );
|
||||
}
|
||||
|
||||
while ( scrollableResults.next() ) {
|
||||
final Employee employee = (Employee) scrollableResults.get( 0 );
|
||||
assertThat( Hibernate.isPropertyInitialized( employee, "otherEntities" ), is( true ) );
|
||||
assertThat( Hibernate.isInitialized( employee.getOtherEntities() ), is( true ) );
|
||||
if ( "ENG1".equals( employee.getDept() ) ) {
|
||||
assertThat( employee.getOtherEntities().size(), is( 2 ) );
|
||||
for ( OtherEntity otherEntity : employee.getOtherEntities() ) {
|
||||
if ( "test1".equals( otherEntity.id ) ) {
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employee" ), is( false ) );
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employeeParent" ), is( true ) );
|
||||
assertThat( otherEntity.employeeParent, is( employee ) );
|
||||
}
|
||||
else {
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employee" ), is( false ) );
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employeeParent" ), is( true ) );
|
||||
assertThat( Hibernate.isInitialized( otherEntity.employeeParent ), is( true ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
assertThat( employee.getOtherEntities().size(), is( 0 ) );
|
||||
}
|
||||
}
|
||||
statelessSession.getTransaction().commit();
|
||||
assertThat( stats.getPrepareStatementCount(), is( 2L ) );
|
||||
}
|
||||
finally {
|
||||
if ( scrollableResults != null ) {
|
||||
scrollableResults.close();
|
||||
}
|
||||
if ( statelessSession.getTransaction().isActive() ) {
|
||||
statelessSession.getTransaction().rollback();
|
||||
}
|
||||
statelessSession.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScrollableWithSession() {
|
||||
final StatisticsImplementor stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
ScrollableResults scrollableResults = null;
|
||||
final Session session = sessionFactory().openSession();
|
||||
|
||||
try {
|
||||
session.beginTransaction();
|
||||
Query<Employee> query = session.createQuery(
|
||||
"select distinct e from Employee e left join fetch e.otherEntities order by e.dept",
|
||||
Employee.class
|
||||
);
|
||||
if ( getDialect() instanceof DB2Dialect ) {
|
||||
/*
|
||||
FetchingScrollableResultsImp#next() in order to check if the ResultSet is empty calls ResultSet#isBeforeFirst()
|
||||
but the support for ResultSet#isBeforeFirst() is optional for ResultSets with a result
|
||||
set type of TYPE_FORWARD_ONLY and db2 does not support it.
|
||||
*/
|
||||
scrollableResults = query.scroll( ScrollMode.SCROLL_INSENSITIVE );
|
||||
}
|
||||
else {
|
||||
scrollableResults = query.scroll( ScrollMode.FORWARD_ONLY );
|
||||
}
|
||||
|
||||
while ( scrollableResults.next() ) {
|
||||
final Employee employee = (Employee) scrollableResults.get( 0 );
|
||||
assertThat( Hibernate.isPropertyInitialized( employee, "otherEntities" ), is( true ) );
|
||||
assertThat( Hibernate.isInitialized( employee.getOtherEntities() ), is( true ) );
|
||||
if ( "ENG1".equals( employee.getDept() ) ) {
|
||||
assertThat( employee.getOtherEntities().size(), is( 2 ) );
|
||||
for ( OtherEntity otherEntity : employee.getOtherEntities() ) {
|
||||
if ( "test1".equals( otherEntity.id ) ) {
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employee" ), is( false ) );
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employeeParent" ), is( true ) );
|
||||
assertThat( otherEntity.employeeParent, is( employee ) );
|
||||
}
|
||||
else {
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employee" ), is( false ) );
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employeeParent" ), is( true ) );
|
||||
assertThat( Hibernate.isInitialized( otherEntity.employeeParent ), is( true ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
assertThat( employee.getOtherEntities().size(), is( 0 ) );
|
||||
}
|
||||
}
|
||||
session.getTransaction().commit();
|
||||
assertThat( stats.getPrepareStatementCount(), is( 2L ) );
|
||||
}
|
||||
finally {
|
||||
if ( scrollableResults != null ) {
|
||||
scrollableResults.close();
|
||||
}
|
||||
if ( session.getTransaction().isActive() ) {
|
||||
session.getTransaction().rollback();
|
||||
}
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void prepareTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
Employee e1 = new Employee( "ENG1" );
|
||||
Employee e2 = new Employee( "ENG2" );
|
||||
OtherEntity other1 = new OtherEntity( "test1" );
|
||||
OtherEntity other2 = new OtherEntity( "test2" );
|
||||
e1.getOtherEntities().add( other1 );
|
||||
e1.getOtherEntities().add( other2 );
|
||||
e1.getParentOtherEntities().add( other1 );
|
||||
e1.getParentOtherEntities().add( other2 );
|
||||
other1.employee = e1;
|
||||
other2.employee = e1;
|
||||
other1.employeeParent = e1;
|
||||
other2.employeeParent = e2;
|
||||
session.persist( other1 );
|
||||
session.persist( other2 );
|
||||
session.persist( e1 );
|
||||
session.persist( e2 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUpTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from OtherEntity" ).executeUpdate();
|
||||
session.createQuery( "delete from Employee" ).executeUpdate();
|
||||
session.createQuery( "delete from EmployeeParent" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "EmployeeParent")
|
||||
@Table(name = "EmployeeParent")
|
||||
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||
public static abstract class EmployeeParent {
|
||||
|
||||
@Id
|
||||
private String dept;
|
||||
|
||||
@OneToMany(targetEntity = OtherEntity.class, mappedBy = "employeeParent", fetch = FetchType.LAZY)
|
||||
protected Set<OtherEntity> parentOtherEntities = new HashSet<>();
|
||||
|
||||
public Set<OtherEntity> getParentOtherEntities() {
|
||||
if ( parentOtherEntities == null ) {
|
||||
parentOtherEntities = new LinkedHashSet();
|
||||
}
|
||||
return parentOtherEntities;
|
||||
}
|
||||
|
||||
public void setOtherEntities(Set<OtherEntity> pParentOtherEntites) {
|
||||
parentOtherEntities = pParentOtherEntites;
|
||||
}
|
||||
|
||||
public String getDept() {
|
||||
return dept;
|
||||
}
|
||||
|
||||
protected void setDept(String dept) {
|
||||
this.dept = dept;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "Employee")
|
||||
@Table(name = "Employee")
|
||||
public static class Employee extends EmployeeParent {
|
||||
|
||||
@OneToMany(targetEntity = OtherEntity.class, mappedBy = "employee", fetch = FetchType.LAZY)
|
||||
protected Set<OtherEntity> otherEntities = new HashSet<>();
|
||||
|
||||
public Employee(String dept) {
|
||||
this();
|
||||
setDept( dept );
|
||||
}
|
||||
|
||||
protected Employee() {
|
||||
// this form used by Hibernate
|
||||
}
|
||||
|
||||
public Set<OtherEntity> getOtherEntities() {
|
||||
if ( otherEntities == null ) {
|
||||
otherEntities = new LinkedHashSet();
|
||||
}
|
||||
return otherEntities;
|
||||
}
|
||||
|
||||
public void setOtherEntities(Set<OtherEntity> pOtherEntites) {
|
||||
otherEntities = pOtherEntites;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "OtherEntity")
|
||||
@Table(name = "OtherEntity")
|
||||
public static class OtherEntity {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@JoinColumn(name = "Employee_Id")
|
||||
protected Employee employee = null;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
//@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@JoinColumn(name = "EmployeeParent_Id")
|
||||
protected EmployeeParent employeeParent = null;
|
||||
|
||||
protected OtherEntity() {
|
||||
// this form used by Hibernate
|
||||
}
|
||||
|
||||
public OtherEntity(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* 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.bytecode.enhancement.lazy.proxy;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.FetchType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Inheritance;
|
||||
import javax.persistence.InheritanceType;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.Hibernate;
|
||||
import org.hibernate.ScrollMode;
|
||||
import org.hibernate.ScrollableResults;
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.StatelessSession;
|
||||
import org.hibernate.annotations.LazyToOne;
|
||||
import org.hibernate.annotations.LazyToOneOption;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.SessionFactoryBuilder;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.dialect.DB2Dialect;
|
||||
import org.hibernate.query.Query;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
||||
import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@RunWith(BytecodeEnhancerRunner.class)
|
||||
public class QueryScrollingWithInheritanceProxyEagerManyToOneTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
@Override
|
||||
protected void configureStandardServiceRegistryBuilder(StandardServiceRegistryBuilder ssrb) {
|
||||
super.configureStandardServiceRegistryBuilder( ssrb );
|
||||
ssrb.applySetting( AvailableSettings.ALLOW_ENHANCEMENT_AS_PROXY, "true" );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
|
||||
super.configureSessionFactoryBuilder( sfb );
|
||||
sfb.applyStatisticsSupport( true );
|
||||
sfb.applySecondLevelCacheSupport( false );
|
||||
sfb.applyQueryCacheSupport( false );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applyMetadataSources(MetadataSources sources) {
|
||||
super.applyMetadataSources( sources );
|
||||
sources.addAnnotatedClass( EmployeeParent.class );
|
||||
sources.addAnnotatedClass( Employee.class );
|
||||
sources.addAnnotatedClass( OtherEntity.class );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScrollableWithStatelessSession() {
|
||||
final StatisticsImplementor stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
ScrollableResults scrollableResults = null;
|
||||
final StatelessSession statelessSession = sessionFactory().openStatelessSession();
|
||||
|
||||
try {
|
||||
statelessSession.beginTransaction();
|
||||
Query<Employee> query = statelessSession.createQuery(
|
||||
"select distinct e from Employee e left join fetch e.otherEntities order by e.dept",
|
||||
Employee.class
|
||||
);
|
||||
if ( getDialect() instanceof DB2Dialect ) {
|
||||
/*
|
||||
FetchingScrollableResultsImp#next() in order to check if the ResultSet is empty calls ResultSet#isBeforeFirst()
|
||||
but the support for ResultSet#isBeforeFirst() is optional for ResultSets with a result
|
||||
set type of TYPE_FORWARD_ONLY and db2 does not support it.
|
||||
*/
|
||||
scrollableResults = query.scroll( ScrollMode.SCROLL_INSENSITIVE );
|
||||
}
|
||||
else {
|
||||
scrollableResults = query.scroll( ScrollMode.FORWARD_ONLY );
|
||||
}
|
||||
|
||||
while ( scrollableResults.next() ) {
|
||||
final Employee employee = (Employee) scrollableResults.get( 0 );
|
||||
assertThat( Hibernate.isPropertyInitialized( employee, "otherEntities" ), is( true ) );
|
||||
assertThat( Hibernate.isInitialized( employee.getOtherEntities() ), is( true ) );
|
||||
if ( "ENG1".equals( employee.getDept() ) ) {
|
||||
assertThat( employee.getOtherEntities().size(), is( 2 ) );
|
||||
for ( OtherEntity otherEntity : employee.getOtherEntities() ) {
|
||||
if ( "test1".equals( otherEntity.id ) ) {
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employee" ), is( true ) );
|
||||
assertThat( otherEntity.employee, is( employee ) );
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employeeParent" ), is( true ) );
|
||||
assertThat( otherEntity.employeeParent, is( employee ) );
|
||||
}
|
||||
else {
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employee" ), is( true ) );
|
||||
assertThat( otherEntity.employee, is( employee ) );
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employeeParent" ), is( true ) );
|
||||
assertThat( Hibernate.isInitialized( otherEntity.employeeParent ), is( true ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
assertThat( employee.getOtherEntities().size(), is( 0 ) );
|
||||
}
|
||||
}
|
||||
statelessSession.getTransaction().commit();
|
||||
assertThat( stats.getPrepareStatementCount(), is( 2L ) );
|
||||
}
|
||||
finally {
|
||||
if ( scrollableResults != null ) {
|
||||
scrollableResults.close();
|
||||
}
|
||||
if ( statelessSession.getTransaction().isActive() ) {
|
||||
statelessSession.getTransaction().rollback();
|
||||
}
|
||||
statelessSession.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScrollableWithSession() {
|
||||
final StatisticsImplementor stats = sessionFactory().getStatistics();
|
||||
stats.clear();
|
||||
ScrollableResults scrollableResults = null;
|
||||
final Session session = sessionFactory().openSession();
|
||||
|
||||
try {
|
||||
session.beginTransaction();
|
||||
Query<Employee> query = session.createQuery(
|
||||
"select distinct e from Employee e left join fetch e.otherEntities order by e.dept",
|
||||
Employee.class
|
||||
);
|
||||
if ( getDialect() instanceof DB2Dialect ) {
|
||||
/*
|
||||
FetchingScrollableResultsImp#next() in order to check if the ResultSet is empty calls ResultSet#isBeforeFirst()
|
||||
but the support for ResultSet#isBeforeFirst() is optional for ResultSets with a result
|
||||
set type of TYPE_FORWARD_ONLY and db2 does not support it.
|
||||
*/
|
||||
scrollableResults = query.scroll( ScrollMode.SCROLL_INSENSITIVE );
|
||||
}
|
||||
else {
|
||||
scrollableResults = query.scroll( ScrollMode.FORWARD_ONLY );
|
||||
}
|
||||
|
||||
while ( scrollableResults.next() ) {
|
||||
final Employee employee = (Employee) scrollableResults.get( 0 );
|
||||
assertThat( Hibernate.isPropertyInitialized( employee, "otherEntities" ), is( true ) );
|
||||
assertThat( Hibernate.isInitialized( employee.getOtherEntities() ), is( true ) );
|
||||
if ( "ENG1".equals( employee.getDept() ) ) {
|
||||
assertThat( employee.getOtherEntities().size(), is( 2 ) );
|
||||
for ( OtherEntity otherEntity : employee.getOtherEntities() ) {
|
||||
if ( "test1".equals( otherEntity.id ) ) {
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employee" ), is( true ) );
|
||||
assertThat( otherEntity.employee, is( employee ) );
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employeeParent" ), is( true ) );
|
||||
assertThat( otherEntity.employeeParent, is( employee ) );
|
||||
}
|
||||
else {
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employee" ), is( true ) );
|
||||
assertThat( otherEntity.employee, is( employee ) );
|
||||
assertThat( Hibernate.isPropertyInitialized( otherEntity, "employeeParent" ), is( true ) );
|
||||
assertThat( Hibernate.isInitialized( otherEntity.employeeParent ), is( true ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
assertThat( employee.getOtherEntities().size(), is( 0 ) );
|
||||
}
|
||||
}
|
||||
session.getTransaction().commit();
|
||||
assertThat( stats.getPrepareStatementCount(), is( 2L ) );
|
||||
}
|
||||
finally {
|
||||
if ( scrollableResults != null ) {
|
||||
scrollableResults.close();
|
||||
}
|
||||
if ( session.getTransaction().isActive() ) {
|
||||
session.getTransaction().rollback();
|
||||
}
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void prepareTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
Employee e1 = new Employee( "ENG1" );
|
||||
Employee e2 = new Employee( "ENG2" );
|
||||
OtherEntity other1 = new OtherEntity( "test1" );
|
||||
OtherEntity other2 = new OtherEntity( "test2" );
|
||||
e1.getOtherEntities().add( other1 );
|
||||
e1.getOtherEntities().add( other2 );
|
||||
e1.getParentOtherEntities().add( other1 );
|
||||
e1.getParentOtherEntities().add( other2 );
|
||||
other1.employee = e1;
|
||||
other2.employee = e1;
|
||||
other1.employeeParent = e1;
|
||||
other2.employeeParent = e2;
|
||||
session.persist( other1 );
|
||||
session.persist( other2 );
|
||||
session.persist( e1 );
|
||||
session.persist( e2 );
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanUpTestData() {
|
||||
inTransaction(
|
||||
session -> {
|
||||
session.createQuery( "delete from OtherEntity" ).executeUpdate();
|
||||
session.createQuery( "delete from Employee" ).executeUpdate();
|
||||
session.createQuery( "delete from EmployeeParent" ).executeUpdate();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "EmployeeParent")
|
||||
@Table(name = "EmployeeParent")
|
||||
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
|
||||
public static abstract class EmployeeParent {
|
||||
|
||||
@Id
|
||||
private String dept;
|
||||
|
||||
@OneToMany(targetEntity = OtherEntity.class, mappedBy = "employeeParent", fetch = FetchType.LAZY)
|
||||
protected Set<OtherEntity> parentOtherEntities = new HashSet<>();
|
||||
|
||||
public Set<OtherEntity> getParentOtherEntities() {
|
||||
if ( parentOtherEntities == null ) {
|
||||
parentOtherEntities = new LinkedHashSet();
|
||||
}
|
||||
return parentOtherEntities;
|
||||
}
|
||||
|
||||
public void setOtherEntities(Set<OtherEntity> pParentOtherEntites) {
|
||||
parentOtherEntities = pParentOtherEntites;
|
||||
}
|
||||
|
||||
public String getDept() {
|
||||
return dept;
|
||||
}
|
||||
|
||||
protected void setDept(String dept) {
|
||||
this.dept = dept;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Entity(name = "Employee")
|
||||
@Table(name = "Employee")
|
||||
public static class Employee extends EmployeeParent {
|
||||
|
||||
@OneToMany(targetEntity = OtherEntity.class, mappedBy = "employee", fetch = FetchType.LAZY)
|
||||
protected Set<OtherEntity> otherEntities = new HashSet<>();
|
||||
|
||||
public Employee(String dept) {
|
||||
this();
|
||||
setDept( dept );
|
||||
}
|
||||
|
||||
protected Employee() {
|
||||
// this form used by Hibernate
|
||||
}
|
||||
|
||||
public Set<OtherEntity> getOtherEntities() {
|
||||
if ( otherEntities == null ) {
|
||||
otherEntities = new LinkedHashSet();
|
||||
}
|
||||
return otherEntities;
|
||||
}
|
||||
|
||||
public void setOtherEntities(Set<OtherEntity> pOtherEntites) {
|
||||
otherEntities = pOtherEntites;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "OtherEntity")
|
||||
@Table(name = "OtherEntity")
|
||||
public static class OtherEntity {
|
||||
|
||||
@Id
|
||||
private String id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@LazyToOne(LazyToOneOption.NO_PROXY)
|
||||
@JoinColumn(name = "Employee_Id")
|
||||
protected Employee employee = null;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "EmployeeParent_Id")
|
||||
protected EmployeeParent employeeParent = null;
|
||||
|
||||
protected OtherEntity() {
|
||||
// this form used by Hibernate
|
||||
}
|
||||
|
||||
public OtherEntity(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -9,6 +9,7 @@ package org.hibernate.test.collection.bag;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.persistence.CollectionTable;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.ElementCollection;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
|
@ -17,6 +18,9 @@ import javax.persistence.JoinColumn;
|
|||
import javax.persistence.OrderBy;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -31,7 +35,8 @@ public class BagElementNullBasicTest extends BaseCoreFunctionalTestCase {
|
|||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] {
|
||||
AnEntity.class
|
||||
AnEntity.class,
|
||||
NullableElementsEntity.class
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -85,6 +90,19 @@ public class BagElementNullBasicTest extends BaseCoreFunctionalTestCase {
|
|||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-13651")
|
||||
public void addNullValueToNullableCollections() {
|
||||
try (final Session s = sessionFactory().openSession()) {
|
||||
final Transaction tx = s.beginTransaction();
|
||||
NullableElementsEntity e = new NullableElementsEntity();
|
||||
e.list.add( null );
|
||||
s.persist( e );
|
||||
s.flush();
|
||||
tx.commit();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateNonNullValueToNull() {
|
||||
int entityId = doInHibernate(
|
||||
|
@ -169,4 +187,17 @@ public class BagElementNullBasicTest extends BaseCoreFunctionalTestCase {
|
|||
@OrderBy
|
||||
private List<String> aCollection = new ArrayList<String>();
|
||||
}
|
||||
|
||||
@Entity
|
||||
@Table(name="NullableElementsEntity")
|
||||
public static class NullableElementsEntity {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private int id;
|
||||
|
||||
@ElementCollection
|
||||
@CollectionTable(name="e_2_string", joinColumns=@JoinColumn(name="e_id"))
|
||||
@Column(name="string_value", unique = false, nullable = true, insertable = true, updatable = true)
|
||||
private List<String> list = new ArrayList<String>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* 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.connections;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
|
||||
import org.hibernate.Session;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.engine.jdbc.batch.internal.AbstractBatchImpl;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode;
|
||||
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.jta.TestingJtaBootstrap;
|
||||
import org.hibernate.testing.jta.TestingJtaPlatformImpl;
|
||||
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||
import org.hibernate.testing.logger.LoggerInspectionRule;
|
||||
import org.hibernate.testing.logger.Triggerable;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
@TestForIssue( jiraKey = "HHH-13307" )
|
||||
@RequiresDialect(H2Dialect.class)
|
||||
public class JdbcBatchingAgressiveReleaseTest extends BaseNonConfigCoreFunctionalTestCase {
|
||||
|
||||
@Rule
|
||||
public LoggerInspectionRule logInspection = new LoggerInspectionRule(
|
||||
Logger.getMessageLogger( CoreMessageLogger.class, AbstractBatchImpl.class.getName() )
|
||||
);
|
||||
|
||||
private Triggerable triggerable = logInspection.watchForLogMessages( "HHH000010" );
|
||||
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void addSettings(Map settings) {
|
||||
super.addSettings( settings );
|
||||
|
||||
TestingJtaBootstrap.prepare( settings );
|
||||
settings.put( AvailableSettings.TRANSACTION_COORDINATOR_STRATEGY, JtaTransactionCoordinatorBuilderImpl.class.getName() );
|
||||
settings.put( Environment.CONNECTION_HANDLING, PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_STATEMENT.toString() );
|
||||
settings.put( Environment.GENERATE_STATISTICS, "true" );
|
||||
settings.put( Environment.STATEMENT_BATCH_SIZE, "500" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJdbcBatching() throws Throwable {
|
||||
triggerable.reset();
|
||||
|
||||
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().begin();
|
||||
Session session = openSession();
|
||||
// The following 2 entity inserts will be batched.
|
||||
session.persist( new Person( 1, "Jane" ) );
|
||||
session.persist( new Person( 2, "Sally" ) );
|
||||
// The following entity has an IDENTITY ID, which cannot be batched.
|
||||
// As a result the existing batch is forced to execute before the Thing can be
|
||||
// inserted.
|
||||
session.persist( new Thing( "it" ) );
|
||||
session.close();
|
||||
TestingJtaPlatformImpl.INSTANCE.getTransactionManager().commit();
|
||||
|
||||
assertFalse( triggerable.wasTriggered() );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class[] getAnnotatedClasses() {
|
||||
return new Class[] { Person.class, Thing.class };
|
||||
}
|
||||
|
||||
@Entity( name = "Person")
|
||||
public static class Person {
|
||||
|
||||
@Id
|
||||
private int id;
|
||||
private String name;
|
||||
|
||||
public Person() {
|
||||
}
|
||||
|
||||
public Person(int id, String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "Thing")
|
||||
public static class Thing {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private int id;
|
||||
private String name;
|
||||
|
||||
public Thing() {
|
||||
}
|
||||
|
||||
public Thing(String name) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* 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.fileimport;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.MetadataSources;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.dialect.H2Dialect;
|
||||
import org.hibernate.engine.config.spi.ConfigurationService;
|
||||
import org.hibernate.hql.internal.antlr.SqlStatementParser;
|
||||
import org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl;
|
||||
import org.hibernate.resource.transaction.spi.TransactionCoordinatorBuilder;
|
||||
import org.hibernate.tool.hbm2ddl.ImportScriptException;
|
||||
import org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor;
|
||||
import org.hibernate.tool.schema.SourceType;
|
||||
import org.hibernate.tool.schema.TargetType;
|
||||
import org.hibernate.tool.schema.internal.ExceptionHandlerLoggedImpl;
|
||||
import org.hibernate.tool.schema.internal.SchemaCreatorImpl;
|
||||
import org.hibernate.tool.schema.spi.ExceptionHandler;
|
||||
import org.hibernate.tool.schema.spi.ExecutionOptions;
|
||||
import org.hibernate.tool.schema.spi.SchemaCreator;
|
||||
import org.hibernate.tool.schema.spi.ScriptSourceInput;
|
||||
import org.hibernate.tool.schema.spi.ScriptTargetOutput;
|
||||
import org.hibernate.tool.schema.spi.SourceDescriptor;
|
||||
import org.hibernate.tool.schema.spi.TargetDescriptor;
|
||||
|
||||
import org.hibernate.testing.RequiresDialect;
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.jta.TestingJtaBootstrap;
|
||||
import org.hibernate.testing.junit4.BaseUnitTestCase;
|
||||
import org.hibernate.test.schemaupdate.CommentGenerationTest;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-13673")
|
||||
@RequiresDialect(value = H2Dialect.class,
|
||||
jiraKey = "HHH-6286",
|
||||
comment = "Only running the tests against H2, because the sql statements in the import file are not generic. " +
|
||||
"This test should actually not test directly against the db")
|
||||
public class StatementsWithoutTerminalCharsImportFileTest extends BaseUnitTestCase implements ExecutionOptions {
|
||||
|
||||
|
||||
private StandardServiceRegistry ssr;
|
||||
private static final String EXPECTED_ERROR_MESSAGE = "Import script Sql statements must terminate with a ';' char";
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ssr = new StandardServiceRegistryBuilder()
|
||||
.applySetting( Environment.HBM2DDL_AUTO, "none" )
|
||||
.applySetting( Environment.DIALECT, CommentGenerationTest.SupportCommentDialect.class.getName() )
|
||||
.applySetting(
|
||||
Environment.HBM2DDL_IMPORT_FILES,
|
||||
"/org/hibernate/test/fileimport/statements-without-terminal-chars.sql"
|
||||
).applySetting( AvailableSettings.HBM2DDL_HALT_ON_ERROR, "true" )
|
||||
.applySetting(
|
||||
Environment.HBM2DDL_IMPORT_FILES_SQL_EXTRACTOR,
|
||||
MultipleLinesSqlCommandExtractor.class.getName()
|
||||
)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testImportFile() {
|
||||
try {
|
||||
final SchemaCreator schemaCreator = new SchemaCreatorImpl( ssr );
|
||||
|
||||
schemaCreator.doCreation(
|
||||
buildMappings( ssr ),
|
||||
this,
|
||||
SourceDescriptorImpl.INSTANCE,
|
||||
TargetDescriptorImpl.INSTANCE
|
||||
);
|
||||
|
||||
fail( "ImportScriptException expected" );
|
||||
}
|
||||
catch (ImportScriptException e) {
|
||||
final Throwable cause = e.getCause();
|
||||
|
||||
assertThat( cause, instanceOf( SqlStatementParser.StatementParserException.class ) );
|
||||
|
||||
assertThat( cause.getMessage(), is( EXPECTED_ERROR_MESSAGE ) );
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
if ( ssr != null ) {
|
||||
StandardServiceRegistryBuilder.destroy( ssr );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map getConfigurationValues() {
|
||||
return ssr.getService( ConfigurationService.class ).getSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldManageNamespaces() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExceptionHandler getExceptionHandler() {
|
||||
return ExceptionHandlerLoggedImpl.INSTANCE;
|
||||
}
|
||||
|
||||
private static class SourceDescriptorImpl implements SourceDescriptor {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
public static final SourceDescriptorImpl INSTANCE = new SourceDescriptorImpl();
|
||||
|
||||
@Override
|
||||
public SourceType getSourceType() {
|
||||
return SourceType.METADATA;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptSourceInput getScriptSourceInput() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TargetDescriptorImpl implements TargetDescriptor {
|
||||
/**
|
||||
* Singleton access
|
||||
*/
|
||||
public static final TargetDescriptorImpl INSTANCE = new TargetDescriptorImpl();
|
||||
|
||||
@Override
|
||||
public EnumSet<TargetType> getTargetTypes() {
|
||||
return EnumSet.of( TargetType.DATABASE );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScriptTargetOutput getScriptTargetOutput() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Metadata buildMappings(StandardServiceRegistry registry) {
|
||||
return new MetadataSources( registry )
|
||||
.buildMetadata();
|
||||
}
|
||||
|
||||
protected StandardServiceRegistry buildJtaStandardServiceRegistry() {
|
||||
StandardServiceRegistry registry = TestingJtaBootstrap.prepare().build();
|
||||
assertThat(
|
||||
registry.getService( TransactionCoordinatorBuilder.class ),
|
||||
instanceOf( JtaTransactionCoordinatorBuilderImpl.class )
|
||||
);
|
||||
return registry;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,196 @@
|
|||
/*
|
||||
* 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.stateless;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.function.Consumer;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.Table;
|
||||
|
||||
import org.hibernate.StatelessSession;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class StatelessSessionPersistentContextTest extends BaseCoreFunctionalTestCase {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class[] { TestEntity.class, OtherEntity.class };
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestForIssue(jiraKey = "HHH-13672")
|
||||
public void testStatelessSessionPersistenceContextIsCleared() {
|
||||
TestEntity testEntity = new TestEntity();
|
||||
consumeAndCheckPersistenceContextIsClosed(
|
||||
statelessSession -> {
|
||||
testEntity.setName( "Fab" );
|
||||
OtherEntity otherEntity = new OtherEntity();
|
||||
otherEntity.setName( "other" );
|
||||
testEntity.setOtherEntity( otherEntity );
|
||||
statelessSession.insert( otherEntity );
|
||||
statelessSession.insert( testEntity );
|
||||
}
|
||||
);
|
||||
|
||||
consumeAndCheckPersistenceContextIsClosed(
|
||||
statelessSession -> {
|
||||
statelessSession.get( TestEntity.class, testEntity.getId() );
|
||||
}
|
||||
);
|
||||
|
||||
consumeAndCheckPersistenceContextIsClosed(
|
||||
statelessSession -> {
|
||||
TestEntity p2 = (TestEntity) statelessSession.get( TestEntity.class, testEntity.getId() );
|
||||
p2.setName( "Fabulous" );
|
||||
statelessSession.update( p2 );
|
||||
}
|
||||
);
|
||||
|
||||
consumeAndCheckPersistenceContextIsClosed(
|
||||
statelessSession -> {
|
||||
TestEntity testEntity1 = (TestEntity) statelessSession.createQuery(
|
||||
"select p from TestEntity p where id = :id" )
|
||||
.setParameter( "id", testEntity.getId() )
|
||||
.uniqueResult();
|
||||
testEntity1.getOtherEntity();
|
||||
}
|
||||
);
|
||||
|
||||
consumeAndCheckPersistenceContextIsClosed(
|
||||
statelessSession -> {
|
||||
statelessSession.refresh( testEntity );
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
consumeAndCheckPersistenceContextIsClosed(
|
||||
statelessSession -> {
|
||||
statelessSession.delete( testEntity );
|
||||
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private void consumeAndCheckPersistenceContextIsClosed(Consumer<StatelessSession> consumer) {
|
||||
Transaction transaction = null;
|
||||
StatelessSession statelessSession = sessionFactory().openStatelessSession();
|
||||
try {
|
||||
transaction = statelessSession.beginTransaction();
|
||||
consumer.accept( statelessSession );
|
||||
transaction.commit();
|
||||
}
|
||||
catch (Exception e) {
|
||||
if ( transaction != null && transaction.isActive() ) {
|
||||
transaction.rollback();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
finally {
|
||||
statelessSession.close();
|
||||
}
|
||||
assertThatPersistenContextIsCleared( statelessSession );
|
||||
}
|
||||
|
||||
private void assertThatPersistenContextIsCleared(StatelessSession ss) {
|
||||
PersistenceContext persistenceContextInternal = ( (SharedSessionContractImplementor) ss ).getPersistenceContextInternal();
|
||||
assertTrue(
|
||||
"StatelessSession: PersistenceContext has not been cleared",
|
||||
persistenceContextInternal.getEntitiesByKey().isEmpty()
|
||||
);
|
||||
assertTrue(
|
||||
"StatelessSession: PersistenceContext has not been cleared",
|
||||
persistenceContextInternal.managedEntitiesIterator() == Collections.emptyIterator()
|
||||
);
|
||||
assertTrue(
|
||||
"StatelessSession: PersistenceContext has not been cleared",
|
||||
persistenceContextInternal.getCollectionsByKey().isEmpty()
|
||||
);
|
||||
assertTrue(
|
||||
"StatelessSession: PersistenceContext has not been cleared",
|
||||
persistenceContextInternal.getCollectionsByKey() == Collections.emptyMap()
|
||||
);
|
||||
}
|
||||
|
||||
@Entity(name = "TestEntity")
|
||||
@Table(name = "TestEntity")
|
||||
public static class TestEntity {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@ManyToOne
|
||||
@JoinColumn
|
||||
private OtherEntity otherEntity;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public OtherEntity getOtherEntity() {
|
||||
return otherEntity;
|
||||
}
|
||||
|
||||
public void setOtherEntity(OtherEntity otherEntity) {
|
||||
this.otherEntity = otherEntity;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity(name = "OtherEntity")
|
||||
@Table(name = "OtherEntity")
|
||||
public static class OtherEntity {
|
||||
@Id
|
||||
@GeneratedValue
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -71,7 +71,7 @@ public class StatelessSessionTest extends BaseCoreFunctionalTestCase {
|
|||
assertEquals( "Blahs", doc2.getName() );
|
||||
assertEquals( doc.getText(), doc2.getText() );
|
||||
|
||||
doc2 = (Document) ss.createNativeQuery( "select * from Document" )
|
||||
doc2 = (Document) ss.createSQLQuery( "select * from Document" )
|
||||
.addEntity( Document.class )
|
||||
.uniqueResult();
|
||||
assertEquals( "Blahs", doc2.getName() );
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
CREATE TABLE test_data ( id NUMBER NOT NULL PRIMARY KEY, text VARCHAR2(100) )
|
||||
INSERT INTO test_data(id, text) VALUES (1 `sample`)
|
||||
DELETE FROM test_data
|
|
@ -311,9 +311,7 @@ public final class IdMetadataGenerator {
|
|||
// HHH-11107
|
||||
// Use FK hbm magic value 'none' to skip making foreign key constraints between the Envers
|
||||
// schema and the base table schema when a @ManyToOne is present in an identifier.
|
||||
if ( mapper == null ) {
|
||||
manyToOneElement.addAttribute( "foreign-key", "none" );
|
||||
}
|
||||
manyToOneElement.addAttribute( "foreign-key", "none" );
|
||||
|
||||
MetadataTools.addColumns( manyToOneElement, value.getColumnIterator() );
|
||||
|
||||
|
|
|
@ -66,6 +66,48 @@ public class VirtualEntitySingleIdMapper extends SingleIdMapper {
|
|||
data.put( propertyData.getName(), entity );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mapToEntityFromEntity(Object objTo, Object objFrom) {
|
||||
if ( objTo == null || objFrom == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
AccessController.doPrivileged(
|
||||
new PrivilegedAction<Object>() {
|
||||
@Override
|
||||
public Object run() {
|
||||
final Getter getter = ReflectionTools.getGetter(
|
||||
objFrom.getClass(),
|
||||
propertyData,
|
||||
getServiceRegistry()
|
||||
);
|
||||
|
||||
final Setter setter = ReflectionTools.getSetter(
|
||||
objTo.getClass(),
|
||||
propertyData,
|
||||
getServiceRegistry()
|
||||
);
|
||||
|
||||
// Get the value from the containing entity
|
||||
final Object value = getter.get( objFrom );
|
||||
if ( value == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( !value.getClass().equals( propertyData.getVirtualReturnClass() ) ) {
|
||||
setter.set( objTo, getAssociatedEntityIdMapper().mapToIdFromEntity( value ), null );
|
||||
}
|
||||
else {
|
||||
// This means we're setting the object
|
||||
setter.set( objTo, value, null );
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mapToEntityFromMap(Object obj, Map data) {
|
||||
if ( data == null || obj == null ) {
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.envers.test.integration.manytoone.foreignkey;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.hibernate.testing.TestForIssue;
|
||||
|
||||
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||
|
||||
/**
|
||||
* Tests that no foreign key should be generated from audit schema to main schema.
|
||||
*
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
@TestForIssue(jiraKey = "HHH-12965")
|
||||
public class ForeignKeyExclusionTest extends BaseEnversJPAFunctionalTestCase {
|
||||
|
||||
private RootLayer rootLayer;
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getAnnotatedClasses() {
|
||||
return new Class<?>[] { RootLayer.class, MiddleLayer.class, LeafLayer.class };
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemovingAuditedEntityWithIdClassAndManyToOneForeignKeyConstraint() {
|
||||
// Revision 1 - Add Root/Middle/Leaf layers
|
||||
this.rootLayer = doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
final RootLayer rootLayer = new RootLayer();
|
||||
rootLayer.setMiddleLayers( new ArrayList<>() );
|
||||
|
||||
MiddleLayer middleLayer = new MiddleLayer();
|
||||
rootLayer.getMiddleLayers().add( middleLayer );
|
||||
middleLayer.setRootLayer( rootLayer );
|
||||
middleLayer.setValidFrom( LocalDate.of( 2019, 3, 19 ) );
|
||||
middleLayer.setLeafLayers( new ArrayList<>() );
|
||||
|
||||
LeafLayer leafLayer = new LeafLayer();
|
||||
leafLayer.setMiddleLayer( middleLayer );
|
||||
middleLayer.getLeafLayers().add( leafLayer );
|
||||
|
||||
entityManager.persist( rootLayer );
|
||||
return rootLayer;
|
||||
} );
|
||||
|
||||
// Revision 2 - Delete Root/Middle/Leaf layers
|
||||
// This causes FK violation
|
||||
doInJPA( this::entityManagerFactory, entityManager -> {
|
||||
final RootLayer rootLayer = entityManager.find( RootLayer.class, this.rootLayer.getId() );
|
||||
entityManager.remove( rootLayer );
|
||||
} );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.envers.test.integration.manytoone.foreignkey;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.JoinColumns;
|
||||
import javax.persistence.ManyToOne;
|
||||
|
||||
import org.hibernate.envers.Audited;
|
||||
|
||||
/**
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
@Entity(name = "LeafLayer")
|
||||
@Audited
|
||||
public class LeafLayer {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
@ManyToOne(optional = false)
|
||||
@JoinColumns({
|
||||
@JoinColumn(name = "middle_layer_valid_from_fk", referencedColumnName = "valid_from"),
|
||||
@JoinColumn(name = "middle_layer_root_layer_fk", referencedColumnName = "root_layer_fk") })
|
||||
private MiddleLayer middleLayer;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public MiddleLayer getMiddleLayer() {
|
||||
return middleLayer;
|
||||
}
|
||||
|
||||
public void setMiddleLayer(MiddleLayer middleLayer) {
|
||||
this.middleLayer = middleLayer;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.envers.test.integration.manytoone.foreignkey;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.IdClass;
|
||||
import javax.persistence.JoinColumn;
|
||||
import javax.persistence.ManyToOne;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
import org.hibernate.envers.Audited;
|
||||
|
||||
/**
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
@Audited
|
||||
@Entity
|
||||
@IdClass(MiddleLayerPK.class)
|
||||
public class MiddleLayer {
|
||||
@Id
|
||||
@Column(name = "valid_from", nullable = false)
|
||||
private LocalDate validFrom;
|
||||
@Id
|
||||
@ManyToOne
|
||||
@JoinColumn(name = "root_layer_fk")
|
||||
private RootLayer rootLayer;
|
||||
@OneToMany(mappedBy = "middleLayer", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private List<LeafLayer> leafLayers;
|
||||
|
||||
public LocalDate getValidFrom() {
|
||||
return validFrom;
|
||||
}
|
||||
|
||||
public void setValidFrom(LocalDate validFrom) {
|
||||
this.validFrom = validFrom;
|
||||
}
|
||||
|
||||
public RootLayer getRootLayer() {
|
||||
return rootLayer;
|
||||
}
|
||||
|
||||
public void setRootLayer(RootLayer rootLayer) {
|
||||
this.rootLayer = rootLayer;
|
||||
}
|
||||
|
||||
public List<LeafLayer> getLeafLayers() {
|
||||
return leafLayers;
|
||||
}
|
||||
|
||||
public void setLeafLayers(List<LeafLayer> leafLayers) {
|
||||
this.leafLayers = leafLayers;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* 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.envers.test.integration.manytoone.foreignkey;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
public class MiddleLayerPK implements Serializable {
|
||||
private Long rootLayer;
|
||||
private LocalDate validFrom;
|
||||
|
||||
public Long getRootLayer() {
|
||||
return rootLayer;
|
||||
}
|
||||
|
||||
public void setRootLayer(Long rootLayer) {
|
||||
this.rootLayer = rootLayer;
|
||||
}
|
||||
|
||||
public LocalDate getValidFrom() {
|
||||
return validFrom;
|
||||
}
|
||||
|
||||
public void setValidFrom(LocalDate validFrom) {
|
||||
this.validFrom = validFrom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
MiddleLayerPK that = (MiddleLayerPK) o;
|
||||
return Objects.equals( rootLayer, that.rootLayer ) &&
|
||||
Objects.equals( validFrom, that.validFrom );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash( rootLayer, validFrom );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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.envers.test.integration.manytoone.foreignkey;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.persistence.CascadeType;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.OneToMany;
|
||||
|
||||
import org.hibernate.envers.Audited;
|
||||
|
||||
/**
|
||||
* @author Chris Cranford
|
||||
*/
|
||||
@Entity(name = "RootLayer")
|
||||
@Audited
|
||||
public class RootLayer {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private Long id;
|
||||
@OneToMany(mappedBy = "rootLayer", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private List<MiddleLayer> middleLayers;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public List<MiddleLayer> getMiddleLayers() {
|
||||
return middleLayers;
|
||||
}
|
||||
|
||||
public void setMiddleLayers(List<MiddleLayer> middleLayers) {
|
||||
this.middleLayers = middleLayers;
|
||||
}
|
||||
}
|
|
@ -85,15 +85,23 @@ public class OsgiPersistenceProvider extends HibernatePersistenceProvider {
|
|||
final Map settings = generateSettings( properties );
|
||||
|
||||
// OSGi ClassLoaders must implement BundleReference
|
||||
final ClassLoader classLoader = info.getClassLoader();
|
||||
settings.put(
|
||||
org.hibernate.cfg.AvailableSettings.SCANNER,
|
||||
new OsgiScanner( ( (BundleReference) info.getClassLoader() ).getBundle() )
|
||||
new OsgiScanner( ( (BundleReference) classLoader).getBundle() )
|
||||
);
|
||||
|
||||
osgiClassLoader.addClassLoader( info.getClassLoader() );
|
||||
|
||||
return Bootstrap.getEntityManagerFactoryBuilder( info, settings,
|
||||
new OSGiClassLoaderServiceImpl( osgiClassLoader, osgiServiceUtil ) ).build();
|
||||
osgiClassLoader.addClassLoader( classLoader );
|
||||
|
||||
final ClassLoader prevCL = Thread.currentThread().getContextClassLoader();
|
||||
try {
|
||||
Thread.currentThread().setContextClassLoader( classLoader );
|
||||
return Bootstrap.getEntityManagerFactoryBuilder( info, settings,
|
||||
new OSGiClassLoaderServiceImpl( osgiClassLoader, osgiServiceUtil ) ).build();
|
||||
}
|
||||
finally {
|
||||
Thread.currentThread().setContextClassLoader( prevCL );
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
Loading…
Reference in New Issue