merge master

This commit is contained in:
Strong Liu 2013-08-03 23:10:54 +08:00
commit 0e62ff4856
103 changed files with 2671 additions and 1001 deletions

View File

@ -326,6 +326,19 @@
Name of column used for storing ordinal of the change in sets of embeddable elements. Name of column used for storing ordinal of the change in sets of embeddable elements.
</entry> </entry>
</row> </row>
<row>
<entry>
<property>org.hibernate.envers.allow_identifier_reuse</property>
</entry>
<entry>
false
</entry>
<entry>
Guarantees proper validity audit strategy behavior when application reuses identifiers
of deleted entities. Exactly one row with <literal>null</literal> end date exists
for each identifier.
</entry>
</row>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>

View File

@ -17,7 +17,7 @@
<bundle start-level="30">mvn:org.apache.aries.transaction/org.apache.aries.transaction.manager/1.0.1</bundle> <bundle start-level="30">mvn:org.apache.aries.transaction/org.apache.aries.transaction.manager/1.0.1</bundle>
<!-- JPA --> <!-- JPA -->
<bundle start-level="30">mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0-SNAPSHOT</bundle> <bundle start-level="30">mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0.Final</bundle>
<!-- No container currently supports JPA 2.1. Clone and build Aries JPA from the following fork (upgrades to <!-- No container currently supports JPA 2.1. Clone and build Aries JPA from the following fork (upgrades to
JPA 2.1). Aries should be upgrading as soon as the spec is out. JPA 2.1). Aries should be upgrading as soon as the spec is out.
https://github.com/brmeyer/aries/tree/jpa21 --> https://github.com/brmeyer/aries/tree/jpa21 -->
@ -46,7 +46,6 @@
<!-- These do not natively support OSGi, so using 3rd party bundles. --> <!-- These do not natively support OSGi, so using 3rd party bundles. -->
<bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.antlr/2.7.7_5</bundle> <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.antlr/2.7.7_5</bundle>
<bundle>wrap:mvn:org.javassist/javassist/3.18.0-GA</bundle>
<bundle>mvn:org.apache.servicemix.specs/org.apache.servicemix.specs.jsr303-api-1.0.0/2.2.0</bundle> <bundle>mvn:org.apache.servicemix.specs/org.apache.servicemix.specs.jsr303-api-1.0.0/2.2.0</bundle>
<bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.ant/1.8.2_2</bundle> <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.ant/1.8.2_2</bundle>
<bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.dom4j/1.6.1_5</bundle> <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.dom4j/1.6.1_5</bundle>
@ -55,8 +54,9 @@
<bundle>wrap:mvn:org.jboss/jandex/1.1.0.Alpha1</bundle> <bundle>wrap:mvn:org.jboss/jandex/1.1.0.Alpha1</bundle>
<bundle>wrap:mvn:org.hibernate.common/hibernate-commons-annotations/4.0.2.Final</bundle> <bundle>wrap:mvn:org.hibernate.common/hibernate-commons-annotations/4.0.2.Final</bundle>
<bundle>mvn:com.fasterxml/classmate/0.8.0</bundle> <bundle>mvn:com.fasterxml/classmate/0.5.4</bundle>
<bundle>mvn:org.jboss.logging/jboss-logging/3.1.1.GA</bundle> <bundle>mvn:org.jboss.logging/jboss-logging/3.1.0.GA</bundle>
<bundle>mvn:org.javassist/javassist/3.18.0-GA</bundle>
<bundle>mvn:org.hibernate/hibernate-core/5.0.0-SNAPSHOT</bundle> <bundle>mvn:org.hibernate/hibernate-core/5.0.0-SNAPSHOT</bundle>
<bundle>mvn:org.hibernate/hibernate-entitymanager/5.0.0-SNAPSHOT</bundle> <bundle>mvn:org.hibernate/hibernate-entitymanager/5.0.0-SNAPSHOT</bundle>

View File

@ -10,7 +10,7 @@
<dependency> <dependency>
<groupId>org.hibernate.javax.persistence</groupId> <groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId> <artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.0-SNAPSHOT</version> <version>1.0.0.Final</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.osgi</groupId> <groupId>org.osgi</groupId>

View File

@ -8,7 +8,7 @@
<bundle start-level="30">mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/1.1.1</bundle> <bundle start-level="30">mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/1.1.1</bundle>
<!-- JPA --> <!-- JPA -->
<bundle start-level="30">mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0-SNAPSHOT</bundle> <bundle start-level="30">mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0.Final</bundle>
<!-- Taken from Karaf-Tutorial --> <!-- Taken from Karaf-Tutorial -->
<bundle>mvn:commons-collections/commons-collections/3.2.1</bundle> <bundle>mvn:commons-collections/commons-collections/3.2.1</bundle>
@ -21,7 +21,6 @@
<!-- These do not natively support OSGi, so using 3rd party bundles. --> <!-- These do not natively support OSGi, so using 3rd party bundles. -->
<bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.antlr/2.7.7_5</bundle> <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.antlr/2.7.7_5</bundle>
<bundle>wrap:mvn:org.javassist/javassist/3.18.0-GA</bundle>
<bundle>mvn:org.apache.servicemix.specs/org.apache.servicemix.specs.jsr303-api-1.0.0/2.2.0</bundle> <bundle>mvn:org.apache.servicemix.specs/org.apache.servicemix.specs.jsr303-api-1.0.0/2.2.0</bundle>
<bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.ant/1.8.2_2</bundle> <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.ant/1.8.2_2</bundle>
<bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.dom4j/1.6.1_5</bundle> <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.dom4j/1.6.1_5</bundle>
@ -30,8 +29,9 @@
<bundle>wrap:mvn:org.hibernate.common/hibernate-commons-annotations/4.0.2.Final</bundle> <bundle>wrap:mvn:org.hibernate.common/hibernate-commons-annotations/4.0.2.Final</bundle>
<bundle>wrap:mvn:org.jboss/jandex/1.1.0.Alpha1</bundle> <bundle>wrap:mvn:org.jboss/jandex/1.1.0.Alpha1</bundle>
<bundle>mvn:com.fasterxml/classmate/0.8.0</bundle> <bundle>mvn:com.fasterxml/classmate/0.5.4</bundle>
<bundle>mvn:org.jboss.logging/jboss-logging/3.1.1.GA</bundle> <bundle>mvn:org.jboss.logging/jboss-logging/3.1.0.GA</bundle>
<bundle>mvn:org.javassist/javassist/3.18.0-GA</bundle>
<bundle>mvn:org.hibernate/hibernate-core/5.0.0-SNAPSHOT</bundle> <bundle>mvn:org.hibernate/hibernate-core/5.0.0-SNAPSHOT</bundle>
<bundle>mvn:org.hibernate/hibernate-entitymanager/5.0.0-SNAPSHOT</bundle> <bundle>mvn:org.hibernate/hibernate-entitymanager/5.0.0-SNAPSHOT</bundle>

View File

@ -10,7 +10,7 @@
<dependency> <dependency>
<groupId>org.hibernate.javax.persistence</groupId> <groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId> <artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.0-SNAPSHOT</version> <version>1.0.0.Final</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.osgi</groupId> <groupId>org.osgi</groupId>

View File

@ -8,7 +8,7 @@
<bundle start-level="30">mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/1.1.1</bundle> <bundle start-level="30">mvn:org.apache.geronimo.specs/geronimo-jta_1.1_spec/1.1.1</bundle>
<!-- JPA --> <!-- JPA -->
<bundle start-level="30">mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0-SNAPSHOT</bundle> <bundle start-level="30">mvn:org.hibernate.javax.persistence/hibernate-jpa-2.1-api/1.0.0.Final</bundle>
<!-- Taken from Karaf-Tutorial --> <!-- Taken from Karaf-Tutorial -->
<bundle>mvn:commons-collections/commons-collections/3.2.1</bundle> <bundle>mvn:commons-collections/commons-collections/3.2.1</bundle>
@ -21,7 +21,6 @@
<!-- These do not natively support OSGi, so using 3rd party bundles. --> <!-- These do not natively support OSGi, so using 3rd party bundles. -->
<bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.antlr/2.7.7_5</bundle> <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.antlr/2.7.7_5</bundle>
<bundle>wrap:mvn:org.javassist/javassist/3.18.0-GA</bundle>
<bundle>mvn:org.apache.servicemix.specs/org.apache.servicemix.specs.jsr303-api-1.0.0/2.2.0</bundle> <bundle>mvn:org.apache.servicemix.specs/org.apache.servicemix.specs.jsr303-api-1.0.0/2.2.0</bundle>
<bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.ant/1.8.2_2</bundle> <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.ant/1.8.2_2</bundle>
<bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.dom4j/1.6.1_5</bundle> <bundle>mvn:org.apache.servicemix.bundles/org.apache.servicemix.bundles.dom4j/1.6.1_5</bundle>
@ -36,8 +35,9 @@
<!-- Optional. Needed to test ehcache 2lc. --> <!-- Optional. Needed to test ehcache 2lc. -->
<!-- <bundle>wrap:mvn:net.sf.ehcache/ehcache-core/2.4.3</bundle> --> <!-- <bundle>wrap:mvn:net.sf.ehcache/ehcache-core/2.4.3</bundle> -->
<bundle>mvn:com.fasterxml/classmate/0.8.0</bundle> <bundle>mvn:com.fasterxml/classmate/0.5.4</bundle>
<bundle>mvn:org.jboss.logging/jboss-logging/3.1.1.GA</bundle> <bundle>mvn:org.jboss.logging/jboss-logging/3.1.0.GA</bundle>
<bundle>mvn:org.javassist/javassist/3.18.0-GA</bundle>
<!-- JACC is optional. --> <!-- JACC is optional. -->
<!--<bundle>mvn:javax.servlet/javax.servlet-api/3.0.1</bundle> <!--<bundle>mvn:javax.servlet/javax.servlet-api/3.0.1</bundle>

View File

@ -10,7 +10,7 @@
<dependency> <dependency>
<groupId>org.hibernate.javax.persistence</groupId> <groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId> <artifactId>hibernate-jpa-2.1-api</artifactId>
<version>1.0.0-SNAPSHOT</version> <version>1.0.0.Final</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.osgi</groupId> <groupId>org.osgi</groupId>

View File

@ -29,6 +29,9 @@ dependencies {
// for test runtime // for test runtime
transitive = true transitive = true
} }
// for testing stored procedure support
testCompile( libraries.derby )
testRuntime( 'jaxen:jaxen:1.1' ) testRuntime( 'jaxen:jaxen:1.1' )
testRuntime( libraries.javassist ) testRuntime( libraries.javassist )

View File

@ -1092,9 +1092,17 @@ public class Configuration implements Serializable {
) )
); );
} }
}
}
// Foreign keys must be created *after* unique keys for numerous DBs. See HH-8390.
iter = getTableMappings();
while ( iter.hasNext() ) {
Table table = (Table) iter.next();
if ( table.isPhysicalTable() ) {
if ( dialect.hasAlterTable() ) { if ( dialect.hasAlterTable() ) {
subIter = table.getForeignKeyIterator(); Iterator subIter = table.getForeignKeyIterator();
while ( subIter.hasNext() ) { while ( subIter.hasNext() ) {
ForeignKey fk = (ForeignKey) subIter.next(); ForeignKey fk = (ForeignKey) subIter.next();
if ( fk.isPhysicalConstraint() ) { if ( fk.isPhysicalConstraint() ) {
@ -1232,6 +1240,33 @@ public class Configuration implements Serializable {
} }
} }
Iterator subIter = table.getIndexIterator();
while ( subIter.hasNext() ) {
final Index index = (Index) subIter.next();
// Skip if index already exists
if ( tableInfo != null && StringHelper.isNotEmpty( index.getName() ) ) {
final IndexMetadata meta = tableInfo.getIndexMetadata( index.getName() );
if ( meta != null ) {
continue;
}
}
scripts.add( new SchemaUpdateScript( index.sqlCreateString( dialect, mapping, tableCatalog,
tableSchema ), false ) );
}
}
}
// Foreign keys must be created *after* unique keys for numerous DBs. See HH-8390.
iter = getTableMappings();
while ( iter.hasNext() ) {
Table table = (Table) iter.next();
String tableSchema = ( table.getSchema() == null ) ? defaultSchema : table.getSchema();
String tableCatalog = ( table.getCatalog() == null ) ? defaultCatalog : table.getCatalog();
if ( table.isPhysicalTable() ) {
TableMetadata tableInfo = databaseMetadata.getTableMetadata( table.getName(), tableSchema,
tableCatalog, table.isQuoted() );
if ( dialect.hasAlterTable() ) { if ( dialect.hasAlterTable() ) {
Iterator subIter = table.getForeignKeyIterator(); Iterator subIter = table.getForeignKeyIterator();
while ( subIter.hasNext() ) { while ( subIter.hasNext() ) {
@ -1247,20 +1282,6 @@ public class Configuration implements Serializable {
} }
} }
} }
Iterator subIter = table.getIndexIterator();
while ( subIter.hasNext() ) {
final Index index = (Index) subIter.next();
// Skip if index already exists
if ( tableInfo != null && StringHelper.isNotEmpty( index.getName() ) ) {
final IndexMetadata meta = tableInfo.getIndexMetadata( index.getName() );
if ( meta != null ) {
continue;
}
}
scripts.add( new SchemaUpdateScript( index.sqlCreateString( dialect, mapping, tableCatalog,
tableSchema ), false ) );
}
} }
} }

View File

@ -173,7 +173,8 @@ public class NamedProcedureCallDefinition {
: ParameterStrategy.POSITIONAL; : ParameterStrategy.POSITIONAL;
parameterDefinitions = new ParameterDefinition[ parameters.length ]; parameterDefinitions = new ParameterDefinition[ parameters.length ];
for ( int i = 0; i < parameters.length; i++ ) { for ( int i = 0; i < parameters.length; i++ ) {
parameterDefinitions[i] = new ParameterDefinition( i, parameters[i] ); // i+1 for the position because the apis say the numbers are 1-based, not zero
parameterDefinitions[i] = new ParameterDefinition( i+1, parameters[i] );
} }
} }
} }

View File

@ -28,6 +28,8 @@ import java.sql.Types;
import org.hibernate.MappingException; import org.hibernate.MappingException;
import org.hibernate.dialect.function.VarArgsSQLFunction; import org.hibernate.dialect.function.VarArgsSQLFunction;
import org.hibernate.dialect.unique.InformixUniqueDelegate;
import org.hibernate.dialect.unique.UniqueDelegate;
import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter; import org.hibernate.exception.spi.TemplatedViolatedConstraintNameExtracter;
import org.hibernate.exception.spi.ViolatedConstraintNameExtracter; import org.hibernate.exception.spi.ViolatedConstraintNameExtracter;
import org.hibernate.internal.util.JdbcExceptionHelper; import org.hibernate.internal.util.JdbcExceptionHelper;
@ -43,6 +45,8 @@ import org.hibernate.type.StandardBasicTypes;
*/ */
public class InformixDialect extends Dialect { public class InformixDialect extends Dialect {
private final UniqueDelegate uniqueDelegate;
/** /**
* Creates new <code>InformixDialect</code> instance. Sets up the JDBC / * Creates new <code>InformixDialect</code> instance. Sets up the JDBC /
* Informix type mappings. * Informix type mappings.
@ -77,6 +81,8 @@ public class InformixDialect extends Dialect {
registerColumnType( Types.VARCHAR, 32739, "lvarchar($l)" ); registerColumnType( Types.VARCHAR, 32739, "lvarchar($l)" );
registerFunction( "concat", new VarArgsSQLFunction( StandardBasicTypes.STRING, "(", "||", ")" ) ); registerFunction( "concat", new VarArgsSQLFunction( StandardBasicTypes.STRING, "(", "||", ")" ) );
uniqueDelegate = new InformixUniqueDelegate( this );
} }
@Override @Override
@ -286,4 +292,9 @@ public class InformixDialect extends Dialect {
public String getCreateTemporaryTablePostfix() { public String getCreateTemporaryTablePostfix() {
return "with no log"; return "with no log";
} }
@Override
public UniqueDelegate getUniqueDelegate() {
return uniqueDelegate;
}
} }

View File

@ -0,0 +1,62 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* JBoss, Home of Professional Open Source
* Copyright 2012 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
* You should have received a copy of the GNU Lesser General Public License,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.hibernate.dialect.unique;
import org.hibernate.dialect.Dialect;
import org.hibernate.metamodel.relational.UniqueKey;
/**
* Informix requires the constraint name to come last on the alter table.
*
* @author Brett Meyer
*/
public class InformixUniqueDelegate extends DefaultUniqueDelegate {
public InformixUniqueDelegate( Dialect dialect ) {
super( dialect );
}
// legacy model ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public String getAlterTableToAddUniqueKeyCommand(
org.hibernate.mapping.UniqueKey uniqueKey,
String defaultCatalog,
String defaultSchema) {
// Do this here, rather than allowing UniqueKey/Constraint to do it.
// We need full, simplified control over whether or not it happens.
final String tableName = uniqueKey.getTable().getQualifiedName( dialect, defaultCatalog, defaultSchema );
final String constraintName = dialect.quote( uniqueKey.getName() );
return "alter table " + tableName + " add constraint " + uniqueConstraintSql( uniqueKey ) + " constraint " + constraintName;
}
// new model ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@Override
public String getAlterTableToAddUniqueKeyCommand(UniqueKey uniqueKey) {
// Do this here, rather than allowing UniqueKey/Constraint to do it.
// We need full, simplified control over whether or not it happens.
final String tableName = uniqueKey.getTable().getQualifiedName( dialect );
final String constraintName = dialect.quote( uniqueKey.getName() );
return "alter table " + tableName + " add constraint " + uniqueConstraintSql( uniqueKey ) + " constraint " + constraintName;
}
}

View File

@ -55,7 +55,7 @@ public class NaturalIdXrefDelegate {
private static final Logger LOG = Logger.getLogger( NaturalIdXrefDelegate.class ); private static final Logger LOG = Logger.getLogger( NaturalIdXrefDelegate.class );
private final StatefulPersistenceContext persistenceContext; private final StatefulPersistenceContext persistenceContext;
private final Map<EntityPersister, NaturalIdResolutionCache> naturalIdResolutionCacheMap = new ConcurrentHashMap<EntityPersister, NaturalIdResolutionCache>(); private final ConcurrentHashMap<EntityPersister, NaturalIdResolutionCache> naturalIdResolutionCacheMap = new ConcurrentHashMap<EntityPersister, NaturalIdResolutionCache>();
/** /**
* Constructs a NaturalIdXrefDelegate * Constructs a NaturalIdXrefDelegate
@ -92,7 +92,10 @@ public class NaturalIdXrefDelegate {
NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister ); NaturalIdResolutionCache entityNaturalIdResolutionCache = naturalIdResolutionCacheMap.get( persister );
if ( entityNaturalIdResolutionCache == null ) { if ( entityNaturalIdResolutionCache == null ) {
entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister ); entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister );
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache ); NaturalIdResolutionCache previousInstance = naturalIdResolutionCacheMap.putIfAbsent( persister, entityNaturalIdResolutionCache );
if ( previousInstance != null ) {
entityNaturalIdResolutionCache = previousInstance;
}
} }
return entityNaturalIdResolutionCache.cache( pk, naturalIdValues ); return entityNaturalIdResolutionCache.cache( pk, naturalIdValues );
} }
@ -279,7 +282,10 @@ public class NaturalIdXrefDelegate {
if ( entityNaturalIdResolutionCache == null ) { if ( entityNaturalIdResolutionCache == null ) {
entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister ); entityNaturalIdResolutionCache = new NaturalIdResolutionCache( persister );
naturalIdResolutionCacheMap.put( persister, entityNaturalIdResolutionCache ); NaturalIdResolutionCache existingCache = naturalIdResolutionCacheMap.putIfAbsent( persister, entityNaturalIdResolutionCache );
if ( existingCache != null ) {
entityNaturalIdResolutionCache = existingCache;
}
} }
entityNaturalIdResolutionCache.pkToNaturalIdMap.put( pk, cachedNaturalId ); entityNaturalIdResolutionCache.pkToNaturalIdMap.put( pk, cachedNaturalId );

View File

@ -37,6 +37,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.ConcurrentMap;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
@ -109,7 +110,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
// private Map<Object,EntityEntry> entityEntries; // private Map<Object,EntityEntry> entityEntries;
// Entity proxies, by EntityKey // Entity proxies, by EntityKey
private Map<EntityKey, Object> proxiesByKey; private ConcurrentMap<EntityKey, Object> proxiesByKey;
// Snapshots of current database state for entities // Snapshots of current database state for entities
// that have *not* been loaded // that have *not* been loaded
@ -563,9 +564,7 @@ public class StatefulPersistenceContext implements PersistenceContext {
final EntityPersister persister = session.getFactory().getEntityPersister( li.getEntityName() ); final EntityPersister persister = session.getFactory().getEntityPersister( li.getEntityName() );
final EntityKey key = session.generateEntityKey( li.getIdentifier(), persister ); final EntityKey key = session.generateEntityKey( li.getIdentifier(), persister );
// any earlier proxy takes precedence // any earlier proxy takes precedence
if ( !proxiesByKey.containsKey( key ) ) { proxiesByKey.putIfAbsent( key, proxy );
proxiesByKey.put( key, proxy );
}
proxy.getHibernateLazyInitializer().setSession( session ); proxy.getHibernateLazyInitializer().setSession( session );
} }
} }

View File

@ -71,6 +71,7 @@ public class DriverManagerConnectionProviderImpl
private int poolSize; private int poolSize;
private boolean autocommit; private boolean autocommit;
//Access guarded by synchronization on the pool instance
private final ArrayList<Connection> pool = new ArrayList<Connection>(); private final ArrayList<Connection> pool = new ArrayList<Connection>();
private final AtomicInteger checkedOut = new AtomicInteger(); private final AtomicInteger checkedOut = new AtomicInteger();
@ -170,6 +171,7 @@ public class DriverManagerConnectionProviderImpl
public void stop() { public void stop() {
LOG.cleaningUpConnectionPool( url ); LOG.cleaningUpConnectionPool( url );
synchronized ( pool ) {
for ( Connection connection : pool ) { for ( Connection connection : pool ) {
try { try {
connection.close(); connection.close();
@ -179,6 +181,7 @@ public class DriverManagerConnectionProviderImpl
} }
} }
pool.clear(); pool.clear();
}
stopped = true; stopped = true;
} }

View File

@ -152,7 +152,7 @@ public class HiLoOptimizer extends AbstractOptimizer {
} }
@Override @Override
public IntegralDataTypeHolder getLastSourceValue() { public synchronized IntegralDataTypeHolder getLastSourceValue() {
return noTenantGenerationState().lastSourceValue; return noTenantGenerationState().lastSourceValue;
} }
@ -168,7 +168,7 @@ public class HiLoOptimizer extends AbstractOptimizer {
* *
* @return Value for property 'lastValue'. * @return Value for property 'lastValue'.
*/ */
public IntegralDataTypeHolder getLastValue() { public synchronized IntegralDataTypeHolder getLastValue() {
return noTenantGenerationState().value.copy().decrement(); return noTenantGenerationState().value.copy().decrement();
} }
@ -179,7 +179,7 @@ public class HiLoOptimizer extends AbstractOptimizer {
* *
* @return Value for property 'upperLimit'. * @return Value for property 'upperLimit'.
*/ */
public IntegralDataTypeHolder getHiValue() { public synchronized IntegralDataTypeHolder getHiValue() {
return noTenantGenerationState().upperLimit; return noTenantGenerationState().upperLimit;
} }
} }

View File

@ -125,7 +125,7 @@ public class LegacyHiLoAlgorithmOptimizer extends AbstractOptimizer {
} }
@Override @Override
public IntegralDataTypeHolder getLastSourceValue() { public synchronized IntegralDataTypeHolder getLastSourceValue() {
return noTenantGenerationState().lastSourceValue.copy(); return noTenantGenerationState().lastSourceValue.copy();
} }
@ -142,7 +142,7 @@ public class LegacyHiLoAlgorithmOptimizer extends AbstractOptimizer {
* @return Value for property 'lastValue'. * @return Value for property 'lastValue'.
*/ */
@SuppressWarnings( {"UnusedDeclaration"}) @SuppressWarnings( {"UnusedDeclaration"})
public IntegralDataTypeHolder getLastValue() { public synchronized IntegralDataTypeHolder getLastValue() {
return noTenantGenerationState().value; return noTenantGenerationState().value;
} }
} }

View File

@ -53,7 +53,8 @@ public final class StringHelper {
public static int lastIndexOfLetter(String string) { public static int lastIndexOfLetter(String string) {
for ( int i=0; i<string.length(); i++ ) { for ( int i=0; i<string.length(); i++ ) {
char character = string.charAt(i); char character = string.charAt(i);
if ( !Character.isLetter(character) /*&& !('_'==character)*/ ) return i-1; // Include "_". See HHH-8073
if ( !Character.isLetter(character) && !('_'==character) ) return i-1;
} }
return string.length()-1; return string.length()-1;
} }

View File

@ -594,6 +594,17 @@ public class CustomLoader extends Loader {
rowProcessor.columnProcessors[i].performDiscovery( metadata, types, aliases ); rowProcessor.columnProcessors[i].performDiscovery( metadata, types, aliases );
} }
validateAliases( aliases );
resultTypes = ArrayHelper.toTypeArray( types );
transformerAliases = ArrayHelper.toStringArray( aliases );
}
catch ( SQLException e ) {
throw new HibernateException( "Exception while trying to autodiscover types.", e );
}
}
private void validateAliases(List<String> aliases) {
// lets make sure we did not end up with duplicate aliases. this can occur when the user supplied query // lets make sure we did not end up with duplicate aliases. this can occur when the user supplied query
// did not rename same-named columns. e.g.: // did not rename same-named columns. e.g.:
// select u.username, u2.username from t_user u, t_user u2 ... // select u.username, u2.username from t_user u, t_user u2 ...
@ -603,6 +614,7 @@ public class CustomLoader extends Loader {
// troublesome condition, so lets throw an error. See HHH-5992 // troublesome condition, so lets throw an error. See HHH-5992
final HashSet<String> aliasesSet = new HashSet<String>(); final HashSet<String> aliasesSet = new HashSet<String>();
for ( String alias : aliases ) { for ( String alias : aliases ) {
validateAlias( alias );
boolean alreadyExisted = !aliasesSet.add( alias ); boolean alreadyExisted = !aliasesSet.add( alias );
if ( alreadyExisted ) { if ( alreadyExisted ) {
throw new NonUniqueDiscoveredSqlAliasException( throw new NonUniqueDiscoveredSqlAliasException(
@ -611,13 +623,9 @@ public class CustomLoader extends Loader {
); );
} }
} }
}
resultTypes = ArrayHelper.toTypeArray( types ); protected void validateAlias(String alias) {
transformerAliases = ArrayHelper.toStringArray( aliases );
}
catch ( SQLException e ) {
throw new HibernateException( "Exception while trying to autodiscover types.", e );
}
} }
private static class Metadata { private static class Metadata {

View File

@ -111,7 +111,7 @@ public class Column implements Selectable, Serializable, Cloneable {
@Override @Override
public String getAlias(Dialect dialect) { public String getAlias(Dialect dialect) {
final int lastLetter = StringHelper.lastIndexOfLetter( name ); final int lastLetter = StringHelper.lastIndexOfLetter( name );
String suffix = Integer.toString(uniqueInteger) + '_'; final String suffix = Integer.toString(uniqueInteger) + '_';
String alias = name; String alias = name;
if ( lastLetter == -1 ) { if ( lastLetter == -1 ) {

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure;
import org.hibernate.HibernateException;
/**
* @author Steve Ebersole
*/
public class NoSuchParameterException extends HibernateException {
public NoSuchParameterException(String message) {
super( message );
}
}

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.procedure;
import org.hibernate.HibernateException;
/**
* @author Steve Ebersole
*/
public class ParameterStrategyException extends HibernateException {
public ParameterStrategyException(String message) {
super( message );
}
}

View File

@ -82,6 +82,9 @@ public interface ProcedureCall extends BasicQueryContract, SynchronizeableQuery
* @param position The parameter position * @param position The parameter position
* *
* @return The parameter registration memento * @return The parameter registration memento
*
* @throws ParameterStrategyException If the ProcedureCall is defined using named parameters
* @throws NoSuchParameterException If no parameter with that position exists
*/ */
public ParameterRegistration getParameterRegistration(int position); public ParameterRegistration getParameterRegistration(int position);
@ -122,6 +125,9 @@ public interface ProcedureCall extends BasicQueryContract, SynchronizeableQuery
* @param name The parameter name * @param name The parameter name
* *
* @return The parameter registration memento * @return The parameter registration memento
*
* @throws ParameterStrategyException If the ProcedureCall is defined using positional parameters
* @throws NoSuchParameterException If no parameter with that name exists
*/ */
public ParameterRegistration getParameterRegistration(String name); public ParameterRegistration getParameterRegistration(String name);
@ -134,13 +140,14 @@ public interface ProcedureCall extends BasicQueryContract, SynchronizeableQuery
/** /**
* Retrieves access to outputs of this procedure call. Can be called multiple times, returning the same * Retrieves access to outputs of this procedure call. Can be called multiple times, returning the same
* Output instance each time. * ProcedureResult instance each time.
* <p/> * <p/>
* Note that the procedure will not actually be executed until the outputs are actually accessed. * If the procedure call has not actually be executed yet, it will be executed and then the ProcedureResult
* will be returned.
* *
* @return The outputs representation * @return The ProcedureResult representation
*/ */
public ProcedureResult getResult(); public ProcedureOutputs getResult();
/** /**
* Extract the disconnected representation of this call. Used in HEM to allow redefining a named query * Extract the disconnected representation of this call. Used in HEM to allow redefining a named query

View File

@ -29,7 +29,7 @@ import org.hibernate.Session;
import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.SessionImplementor;
/** /**
* Represents a "memento" of a ProcedureCall * Represents a "memento" (disconnected, externalizable form) of a ProcedureCall
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */

View File

@ -23,20 +23,20 @@
*/ */
package org.hibernate.procedure; package org.hibernate.procedure;
import org.hibernate.result.Result; import org.hibernate.result.Outputs;
/** /**
* Specialization of the {@link Result} contract providing access to the stored procedure's registered * Specialization of the {@link org.hibernate.result.Outputs} contract providing access to the stored procedure's registered
* output parameters. * output parameters.
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface ProcedureResult extends Result { public interface ProcedureOutputs extends Outputs {
/** /**
* Retrieve the value of an OUTPUT parameter by the parameter's registration memento. * Retrieve the value of an OUTPUT parameter by the parameter's registration memento.
* <p/> * <p/>
* Should NOT be called for parameters registered as REF_CURSOR. REF_CURSOR parameters should be * Should NOT be called for parameters registered as REF_CURSOR. REF_CURSOR parameters should be
* accessed via the returns (see {@link #getNextReturn} * accessed via the returns (see {@link #getNextOutput}
* *
* @param parameterRegistration The parameter's registration memento. * @param parameterRegistration The parameter's registration memento.
* *
@ -53,6 +53,9 @@ public interface ProcedureResult extends Result {
* *
* @return The output value. * @return The output value.
* *
* @throws ParameterStrategyException If the ProcedureCall is defined using positional parameters
* @throws NoSuchParameterException If no parameter with that name exists
*
* @see ProcedureCall#registerParameter(String, Class, javax.persistence.ParameterMode) * @see ProcedureCall#registerParameter(String, Class, javax.persistence.ParameterMode)
*/ */
public Object getOutputParameterValue(String name); public Object getOutputParameterValue(String name);
@ -64,6 +67,9 @@ public interface ProcedureResult extends Result {
* *
* @return The output value. * @return The output value.
* *
* @throws ParameterStrategyException If the ProcedureCall is defined using named parameters
* @throws NoSuchParameterException If no parameter with that position exists
*
* @see ProcedureCall#registerParameter(int, Class, javax.persistence.ParameterMode) * @see ProcedureCall#registerParameter(int, Class, javax.persistence.ParameterMode)
*/ */
public Object getOutputParameterValue(int position); public Object getOutputParameterValue(int position);

View File

@ -50,10 +50,12 @@ import org.hibernate.internal.util.StringHelper;
import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.CollectionHelper;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
import org.hibernate.procedure.NamedParametersNotSupportedException; import org.hibernate.procedure.NamedParametersNotSupportedException;
import org.hibernate.procedure.NoSuchParameterException;
import org.hibernate.procedure.ParameterRegistration; import org.hibernate.procedure.ParameterRegistration;
import org.hibernate.procedure.ParameterStrategyException;
import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureCall;
import org.hibernate.procedure.ProcedureCallMemento; import org.hibernate.procedure.ProcedureCallMemento;
import org.hibernate.procedure.ProcedureResult; import org.hibernate.procedure.ProcedureOutputs;
import org.hibernate.result.spi.ResultContext; import org.hibernate.result.spi.ResultContext;
import org.hibernate.type.Type; import org.hibernate.type.Type;
@ -75,7 +77,7 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
private Set<String> synchronizedQuerySpaces; private Set<String> synchronizedQuerySpaces;
private ProcedureResultImpl outputs; private ProcedureOutputsImpl outputs;
/** /**
* The no-returns form. * The no-returns form.
@ -321,21 +323,22 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
@Override @Override
public ParameterRegistrationImplementor getParameterRegistration(int position) { public ParameterRegistrationImplementor getParameterRegistration(int position) {
if ( parameterStrategy != ParameterStrategy.POSITIONAL ) { if ( parameterStrategy != ParameterStrategy.POSITIONAL ) {
throw new IllegalArgumentException( "Positions were not used to register parameters with this stored procedure call" ); throw new ParameterStrategyException(
"Attempt to access positional parameter [" + position + "] but ProcedureCall using named parameters"
);
} }
try { for ( ParameterRegistrationImplementor parameter : registeredParameters ) {
return registeredParameters.get( position ); if ( position == parameter.getPosition() ) {
return parameter;
} }
catch ( Exception e ) {
throw new QueryException( "Could not locate parameter registered using that position [" + position + "]" );
} }
throw new NoSuchParameterException( "Could not locate parameter registered using that position [" + position + "]" );
} }
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> ParameterRegistration<T> registerParameter(String name, Class<T> type, ParameterMode mode) { public <T> ParameterRegistration<T> registerParameter(String name, Class<T> type, ParameterMode mode) {
final NamedParameterRegistration parameterRegistration final NamedParameterRegistration parameterRegistration = new NamedParameterRegistration( this, name, mode, type );
= new NamedParameterRegistration( this, name, mode, type );
registerParameter( parameterRegistration ); registerParameter( parameterRegistration );
return parameterRegistration; return parameterRegistration;
} }
@ -350,14 +353,14 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
@Override @Override
public ParameterRegistrationImplementor getParameterRegistration(String name) { public ParameterRegistrationImplementor getParameterRegistration(String name) {
if ( parameterStrategy != ParameterStrategy.NAMED ) { if ( parameterStrategy != ParameterStrategy.NAMED ) {
throw new IllegalArgumentException( "Names were not used to register parameters with this stored procedure call" ); throw new ParameterStrategyException( "Names were not used to register parameters with this stored procedure call" );
} }
for ( ParameterRegistrationImplementor parameter : registeredParameters ) { for ( ParameterRegistrationImplementor parameter : registeredParameters ) {
if ( name.equals( parameter.getName() ) ) { if ( name.equals( parameter.getName() ) ) {
return parameter; return parameter;
} }
} }
throw new IllegalArgumentException( "Could not locate parameter registered under that name [" + name + "]" ); throw new NoSuchParameterException( "Could not locate parameter registered under that name [" + name + "]" );
} }
@Override @Override
@ -367,7 +370,7 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
} }
@Override @Override
public ProcedureResult getResult() { public ProcedureOutputs getResult() {
if ( outputs == null ) { if ( outputs == null ) {
outputs = buildOutputs(); outputs = buildOutputs();
} }
@ -375,7 +378,7 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
return outputs; return outputs;
} }
private ProcedureResultImpl buildOutputs() { private ProcedureOutputsImpl buildOutputs() {
// todo : going to need a very specialized Loader for this. // todo : going to need a very specialized Loader for this.
// or, might be a good time to look at splitting Loader up into: // or, might be a good time to look at splitting Loader up into:
// 1) building statement objects // 1) building statement objects
@ -385,6 +388,11 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
// for now assume there are no resultClasses nor mappings defined.. // for now assume there are no resultClasses nor mappings defined..
// TOTAL PROOF-OF-CONCEPT!!!!!! // TOTAL PROOF-OF-CONCEPT!!!!!!
// todo : how to identify calls which should be in the form `{? = call procName...}` ??? (note leading param marker)
// more than likely this will need to be a method on the native API. I can see this as a trigger to
// both: (1) add the `? = ` part and also (2) register a REFCURSOR parameter for DBs (Oracle, PGSQL) that
// need it.
final StringBuilder buffer = new StringBuilder().append( "{call " ) final StringBuilder buffer = new StringBuilder().append( "{call " )
.append( procedureName ) .append( procedureName )
.append( "(" ); .append( "(" );
@ -414,7 +422,7 @@ public class ProcedureCallImpl extends AbstractBasicQueryContractImpl implements
i += parameter.getSqlTypes().length; i += parameter.getSqlTypes().length;
} }
return new ProcedureResultImpl( this, statement ); return new ProcedureOutputsImpl( this, statement );
} }
catch (SQLException e) { catch (SQLException e) {
throw getSession().getFactory().getSQLExceptionHelper().convert( throw getSession().getFactory().getSQLExceptionHelper().convert(

View File

@ -25,28 +25,26 @@ package org.hibernate.procedure.internal;
import java.sql.CallableStatement; import java.sql.CallableStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException;
import org.hibernate.JDBCException;
import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport; import org.hibernate.engine.jdbc.cursor.spi.RefCursorSupport;
import org.hibernate.procedure.ParameterRegistration; import org.hibernate.procedure.ParameterRegistration;
import org.hibernate.procedure.ProcedureResult; import org.hibernate.procedure.ProcedureOutputs;
import org.hibernate.result.Return; import org.hibernate.result.Output;
import org.hibernate.result.internal.ResultImpl; import org.hibernate.result.internal.OutputsImpl;
/** /**
* Implementation of ProcedureResult. Defines centralized access to all of the results of a procedure call. * Implementation of ProcedureResult. Defines centralized access to all of the results of a procedure call.
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class ProcedureResultImpl extends ResultImpl implements ProcedureResult { public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutputs {
private final ProcedureCallImpl procedureCall; private final ProcedureCallImpl procedureCall;
private final CallableStatement callableStatement; private final CallableStatement callableStatement;
private final ParameterRegistrationImplementor[] refCursorParameters; private final ParameterRegistrationImplementor[] refCursorParameters;
private int refCursorParamIndex; private int refCursorParamIndex;
ProcedureResultImpl(ProcedureCallImpl procedureCall, CallableStatement callableStatement) { ProcedureOutputsImpl(ProcedureCallImpl procedureCall, CallableStatement callableStatement) {
super( procedureCall, callableStatement ); super( procedureCall, callableStatement );
this.procedureCall = procedureCall; this.procedureCall = procedureCall;
this.callableStatement = callableStatement; this.callableStatement = callableStatement;
@ -70,48 +68,45 @@ public class ProcedureResultImpl extends ResultImpl implements ProcedureResult {
} }
@Override @Override
protected CurrentReturnDescriptor buildCurrentReturnDescriptor(boolean isResultSet, int updateCount) { protected CurrentReturnState buildCurrentReturnState(boolean isResultSet, int updateCount) {
return new ProcedureCurrentReturnDescriptor( isResultSet, updateCount, refCursorParamIndex ); return new ProcedureCurrentReturnState( isResultSet, updateCount, refCursorParamIndex );
} }
protected boolean hasMoreReturns(CurrentReturnDescriptor descriptor) { protected class ProcedureCurrentReturnState extends CurrentReturnState {
return super.hasMoreReturns( descriptor ) private final int refCursorParamIndex;
|| ( (ProcedureCurrentReturnDescriptor) descriptor ).refCursorParamIndex < refCursorParameters.length;
private ProcedureCurrentReturnState(boolean isResultSet, int updateCount, int refCursorParamIndex) {
super( isResultSet, updateCount );
this.refCursorParamIndex = refCursorParamIndex;
} }
@Override @Override
protected Return buildExtendedReturn(CurrentReturnDescriptor returnDescriptor) { public boolean indicatesMoreOutputs() {
this.refCursorParamIndex++; return super.indicatesMoreOutputs()
final int refCursorParamIndex = ( (ProcedureCurrentReturnDescriptor) returnDescriptor ).refCursorParamIndex; || ProcedureOutputsImpl.this.refCursorParamIndex < ProcedureOutputsImpl.this.refCursorParameters.length;
final ParameterRegistrationImplementor refCursorParam = refCursorParameters[refCursorParamIndex]; }
@Override
protected boolean hasExtendedReturns() {
return refCursorParamIndex < refCursorParameters.length;
}
@Override
protected Output buildExtendedReturn() {
ProcedureOutputsImpl.this.refCursorParamIndex++;
final ParameterRegistrationImplementor refCursorParam = ProcedureOutputsImpl.this.refCursorParameters[refCursorParamIndex];
ResultSet resultSet; ResultSet resultSet;
if ( refCursorParam.getName() != null ) { if ( refCursorParam.getName() != null ) {
resultSet = procedureCall.getSession().getFactory().getServiceRegistry() resultSet = ProcedureOutputsImpl.this.procedureCall.getSession().getFactory().getServiceRegistry()
.getService( RefCursorSupport.class ) .getService( RefCursorSupport.class )
.getResultSet( callableStatement, refCursorParam.getName() ); .getResultSet( ProcedureOutputsImpl.this.callableStatement, refCursorParam.getName() );
} }
else { else {
resultSet = procedureCall.getSession().getFactory().getServiceRegistry() resultSet = ProcedureOutputsImpl.this.procedureCall.getSession().getFactory().getServiceRegistry()
.getService( RefCursorSupport.class ) .getService( RefCursorSupport.class )
.getResultSet( callableStatement, refCursorParam.getPosition() ); .getResultSet( ProcedureOutputsImpl.this.callableStatement, refCursorParam.getPosition() );
} }
return new ResultSetReturn( this, resultSet ); return buildResultSetOutput( extractResults( resultSet ) );
}
protected JDBCException convert(SQLException e, String message) {
return procedureCall.getSession().getFactory().getSQLExceptionHelper().convert(
e,
message,
procedureCall.getProcedureName()
);
}
protected static class ProcedureCurrentReturnDescriptor extends CurrentReturnDescriptor {
private final int refCursorParamIndex;
private ProcedureCurrentReturnDescriptor(boolean isResultSet, int updateCount, int refCursorParamIndex) {
super( isResultSet, updateCount );
this.refCursorParamIndex = refCursorParamIndex;
} }
} }

View File

@ -32,4 +32,8 @@ public class NoMoreReturnsException extends HibernateException {
public NoMoreReturnsException(String message) { public NoMoreReturnsException(String message) {
super( message ); super( message );
} }
public NoMoreReturnsException() {
super( "Results have been exhausted" );
}
} }

View File

@ -24,18 +24,18 @@
package org.hibernate.result; package org.hibernate.result;
/** /**
* Common contract for individual return objects which can be either results ({@link ResultSetReturn}) or update * Common contract for individual return objects which can be either results ({@link ResultSetOutput}) or update
* counts ({@link UpdateCountReturn}). * counts ({@link UpdateCountOutput}).
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface Return { public interface Output {
/** /**
* Determine if this return is a result (castable to {@link ResultSetReturn}). The alternative is that it is * Determine if this return is a result (castable to {@link ResultSetOutput}). The alternative is that it is
* an update count (castable to {@link UpdateCountReturn}). * an update count (castable to {@link UpdateCountOutput}).
* *
* @return {@code true} indicates that {@code this} can be safely cast to {@link ResultSetReturn}), other wise * @return {@code true} indicates that {@code this} can be safely cast to {@link ResultSetOutput}), other wise
* it can be cast to {@link UpdateCountReturn}. * it can be cast to {@link UpdateCountOutput}.
*/ */
public boolean isResultSet(); public boolean isResultSet();
} }

View File

@ -24,27 +24,32 @@
package org.hibernate.result; package org.hibernate.result;
/** /**
* Represents the result of executing a JDBC statement accounting for mixing of result sets and update counts hiding the * Represents the outputs of executing a JDBC statement accounting for mixing of result sets and update counts
* complexity (IMO) of how this is exposed in the JDBC API. * hiding the complexity (IMO) of how this is exposed in the JDBC API.
* * <p/>
* A result is made up of group of {@link Return} objects, each representing a single result set or update count. * The outputs are exposed as a group of {@link Output} objects, each representing a single result set or update count.
* Conceptually, Result presents those Returns as an iterator. * Conceptually, Result presents those Returns as an iterator.
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface Result { public interface Outputs {
/** /**
* Are there any more returns associated with this result? * Retrieve the current Output object.
* *
* @return {@code true} means there are more returns available via {@link #getNextReturn()}; {@code false} * @return The current Output object. Can be {@code null}
* indicates that calling {@link #getNextReturn()} will certainly result in an exception.
*/ */
public boolean hasMoreReturns(); public Output getCurrent();
/** /**
* Retrieve the next return. * Go to the next Output object (if any), returning an indication of whether there was another (aka, will
* the next call to {@link #getCurrent()} return {@code null}?
* *
* @return The next return. * @return {@code true} if the next call to {@link #getCurrent()} will return a non-{@code null} value.
*/ */
public Return getNextReturn() throws NoMoreReturnsException; public boolean goToNext();
/**
* Eagerly release any resources held by this Outputs.
*/
public void release();
} }

View File

@ -30,7 +30,7 @@ import java.util.List;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface ResultSetReturn extends Return { public interface ResultSetOutput extends Output {
/** /**
* Consume the underlying {@link java.sql.ResultSet} and return the resulting List. * Consume the underlying {@link java.sql.ResultSet} and return the resulting List.
* *

View File

@ -28,7 +28,7 @@ package org.hibernate.result;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface UpdateCountReturn extends Return { public interface UpdateCountOutput extends Output {
/** /**
* Retrieve the associated update count * Retrieve the associated update count
* *

View File

@ -0,0 +1,321 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.result.internal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.jboss.logging.Logger;
import org.hibernate.JDBCException;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.internal.CoreLogging;
import org.hibernate.loader.custom.CustomLoader;
import org.hibernate.loader.custom.CustomQuery;
import org.hibernate.loader.custom.sql.SQLQueryReturnProcessor;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.result.NoMoreReturnsException;
import org.hibernate.result.Outputs;
import org.hibernate.result.Output;
import org.hibernate.result.spi.ResultContext;
/**
* @author Steve Ebersole
*/
public class OutputsImpl implements Outputs {
private static final Logger log = CoreLogging.logger( OutputsImpl.class );
private final ResultContext context;
private final PreparedStatement jdbcStatement;
private final CustomLoaderExtension loader;
private CurrentReturnState currentReturnState;
public OutputsImpl(ResultContext context, PreparedStatement jdbcStatement) {
this.context = context;
this.jdbcStatement = jdbcStatement;
// For now... but see the LoadPlan work; eventually this should just be a ResultSetProcessor.
this.loader = buildSpecializedCustomLoader( context );
try {
final boolean isResultSet = jdbcStatement.execute();
currentReturnState = buildCurrentReturnState( isResultSet );
}
catch (SQLException e) {
throw convert( e, "Error calling CallableStatement.getMoreResults" );
}
}
private CurrentReturnState buildCurrentReturnState(boolean isResultSet) {
int updateCount = -1;
if ( ! isResultSet ) {
try {
updateCount = jdbcStatement.getUpdateCount();
}
catch (SQLException e) {
throw convert( e, "Error calling CallableStatement.getUpdateCount" );
}
}
return buildCurrentReturnState( isResultSet, updateCount );
}
protected CurrentReturnState buildCurrentReturnState(boolean isResultSet, int updateCount) {
return new CurrentReturnState( isResultSet, updateCount );
}
protected JDBCException convert(SQLException e, String message) {
return context.getSession().getFactory().getSQLExceptionHelper().convert(
e,
message,
context.getSql()
);
}
@Override
public Output getCurrent() {
if ( currentReturnState == null ) {
return null;
}
return currentReturnState.getOutput();
}
@Override
public boolean goToNext() {
if ( currentReturnState == null ) {
return false;
}
if ( currentReturnState.indicatesMoreOutputs() )
// prepare the next return state
try {
final boolean isResultSet = jdbcStatement.getMoreResults();
currentReturnState = buildCurrentReturnState( isResultSet );
}
catch (SQLException e) {
throw convert( e, "Error calling CallableStatement.getMoreResults" );
}
// and return
return currentReturnState != null && currentReturnState.indicatesMoreOutputs();
}
@Override
public void release() {
try {
jdbcStatement.close();
}
catch (SQLException e) {
log.debug( "Unable to close PreparedStatement", e );
}
}
private List extractCurrentResults() {
try {
return extractResults( jdbcStatement.getResultSet() );
}
catch (SQLException e) {
throw convert( e, "Error calling CallableStatement.getResultSet" );
}
}
protected List extractResults(ResultSet resultSet) {
try {
return loader.processResultSet( resultSet );
}
catch (SQLException e) {
throw convert( e, "Error extracting results from CallableStatement" );
}
}
/**
* Encapsulates the information needed to interpret the current return within a result
*/
protected class CurrentReturnState {
private final boolean isResultSet;
private final int updateCount;
private Output rtn;
protected CurrentReturnState(boolean isResultSet, int updateCount) {
this.isResultSet = isResultSet;
this.updateCount = updateCount;
}
public boolean indicatesMoreOutputs() {
return isResultSet() || getUpdateCount() >= 0;
}
public boolean isResultSet() {
return isResultSet;
}
public int getUpdateCount() {
return updateCount;
}
public Output getOutput() {
if ( rtn == null ) {
rtn = buildOutput();
}
return rtn;
}
protected Output buildOutput() {
if ( log.isDebugEnabled() ) {
log.debugf(
"Building Return [isResultSet=%s, updateCount=%s, extendedReturn=%s",
isResultSet(),
getUpdateCount(),
hasExtendedReturns()
);
}
// todo : temporary for tck testing...
System.out.println(
String.format(
"Building Return [isResultSet=%s, updateCount=%s, extendedReturn=%s",
isResultSet(),
getUpdateCount(),
hasExtendedReturns()
)
);
if ( isResultSet() ) {
return buildResultSetOutput( extractCurrentResults() );
}
else if ( getUpdateCount() >= 0 ) {
return buildUpdateCountOutput( updateCount );
}
else if ( hasExtendedReturns() ) {
return buildExtendedReturn();
}
throw new NoMoreReturnsException();
}
// hooks for stored procedure (out param) processing ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
protected Output buildResultSetOutput(List list) {
return new ResultSetOutputImpl( list );
}
protected Output buildUpdateCountOutput(int updateCount) {
return new UpdateCountOutputImpl( updateCount );
}
protected boolean hasExtendedReturns() {
return false;
}
protected Output buildExtendedReturn() {
throw new IllegalStateException( "State does not define extended returns" );
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Hooks into Hibernate's Loader hierarchy for ResultSet -> Object mapping
private static CustomLoaderExtension buildSpecializedCustomLoader(final ResultContext context) {
// might be better to just manually construct the Return(s).. SQLQueryReturnProcessor does a lot of
// work that is really unnecessary here.
final SQLQueryReturnProcessor processor = new SQLQueryReturnProcessor(
context.getQueryReturns(),
context.getSession().getFactory()
);
processor.process();
final List<org.hibernate.loader.custom.Return> customReturns = processor.generateCustomReturns( false );
CustomQuery customQuery = new CustomQuery() {
@Override
public String getSQL() {
return context.getSql();
}
@Override
public Set<String> getQuerySpaces() {
return context.getSynchronizedQuerySpaces();
}
@Override
public Map getNamedParameterBindPoints() {
// no named parameters in terms of embedded in the SQL string
return null;
}
@Override
public List<org.hibernate.loader.custom.Return> getCustomQueryReturns() {
return customReturns;
}
};
return new CustomLoaderExtension(
customQuery,
context.getQueryParameters(),
context.getSession()
);
}
private static class CustomLoaderExtension extends CustomLoader {
private QueryParameters queryParameters;
private SessionImplementor session;
private boolean needsDiscovery = true;
public CustomLoaderExtension(
CustomQuery customQuery,
QueryParameters queryParameters,
SessionImplementor session) {
super( customQuery, session.getFactory() );
this.queryParameters = queryParameters;
this.session = session;
}
// todo : this would be a great way to add locking to stored procedure support (at least where returning entities).
public List processResultSet(ResultSet resultSet) throws SQLException {
if ( needsDiscovery ) {
super.autoDiscoverTypes( resultSet );
// todo : EntityAliases discovery
needsDiscovery = false;
}
return super.processResultSet(
resultSet,
queryParameters,
session,
true,
null,
Integer.MAX_VALUE,
Collections.<AfterLoadAction>emptyList()
);
}
}
}

View File

@ -1,293 +0,0 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.result.internal;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.hibernate.JDBCException;
import org.hibernate.engine.spi.QueryParameters;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.loader.custom.CustomLoader;
import org.hibernate.loader.custom.CustomQuery;
import org.hibernate.loader.custom.sql.SQLQueryReturnProcessor;
import org.hibernate.loader.spi.AfterLoadAction;
import org.hibernate.result.NoMoreReturnsException;
import org.hibernate.result.Result;
import org.hibernate.result.Return;
import org.hibernate.result.spi.ResultContext;
/**
* @author Steve Ebersole
*/
public class ResultImpl implements Result {
private final ResultContext context;
private final PreparedStatement jdbcStatement;
private final CustomLoaderExtension loader;
private CurrentReturnDescriptor currentReturnDescriptor;
private boolean executed = false;
public ResultImpl(ResultContext context, PreparedStatement jdbcStatement) {
this.context = context;
this.jdbcStatement = jdbcStatement;
// For now...
this.loader = buildSpecializedCustomLoader( context );
}
@Override
public boolean hasMoreReturns() {
if ( currentReturnDescriptor == null ) {
final boolean isResultSet;
if ( executed ) {
try {
isResultSet = jdbcStatement.getMoreResults();
}
catch (SQLException e) {
throw convert( e, "Error calling CallableStatement.getMoreResults" );
}
}
else {
try {
isResultSet = jdbcStatement.execute();
}
catch (SQLException e) {
throw convert( e, "Error calling CallableStatement.execute" );
}
executed = true;
}
int updateCount = -1;
if ( ! isResultSet ) {
try {
updateCount = jdbcStatement.getUpdateCount();
}
catch (SQLException e) {
throw convert( e, "Error calling CallableStatement.getUpdateCount" );
}
}
currentReturnDescriptor = buildCurrentReturnDescriptor( isResultSet, updateCount );
}
return hasMoreReturns( currentReturnDescriptor );
}
protected CurrentReturnDescriptor buildCurrentReturnDescriptor(boolean isResultSet, int updateCount) {
return new CurrentReturnDescriptor( isResultSet, updateCount );
}
protected boolean hasMoreReturns(CurrentReturnDescriptor descriptor) {
return descriptor.isResultSet
|| descriptor.updateCount >= 0;
}
@Override
public Return getNextReturn() {
if ( currentReturnDescriptor == null ) {
if ( executed ) {
throw new IllegalStateException( "Unexpected condition" );
}
else {
throw new IllegalStateException( "hasMoreReturns() not called before getNextReturn()" );
}
}
if ( ! hasMoreReturns( currentReturnDescriptor ) ) {
throw new NoMoreReturnsException( "Results have been exhausted" );
}
CurrentReturnDescriptor copyReturnDescriptor = currentReturnDescriptor;
currentReturnDescriptor = null;
if ( copyReturnDescriptor.isResultSet ) {
try {
return new ResultSetReturn( this, jdbcStatement.getResultSet() );
}
catch (SQLException e) {
throw convert( e, "Error calling CallableStatement.getResultSet" );
}
}
else if ( copyReturnDescriptor.updateCount >= 0 ) {
return new UpdateCountReturn( this, copyReturnDescriptor.updateCount );
}
else {
return buildExtendedReturn( copyReturnDescriptor );
}
}
protected Return buildExtendedReturn(CurrentReturnDescriptor copyReturnDescriptor) {
throw new NoMoreReturnsException( "Results have been exhausted" );
}
protected JDBCException convert(SQLException e, String message) {
return context.getSession().getFactory().getSQLExceptionHelper().convert(
e,
message,
context.getSql()
);
}
protected static class CurrentReturnDescriptor {
private final boolean isResultSet;
private final int updateCount;
protected CurrentReturnDescriptor(boolean isResultSet, int updateCount) {
this.isResultSet = isResultSet;
this.updateCount = updateCount;
}
}
protected static class ResultSetReturn implements org.hibernate.result.ResultSetReturn {
private final ResultImpl storedProcedureOutputs;
private final ResultSet resultSet;
public ResultSetReturn(ResultImpl storedProcedureOutputs, ResultSet resultSet) {
this.storedProcedureOutputs = storedProcedureOutputs;
this.resultSet = resultSet;
}
@Override
public boolean isResultSet() {
return true;
}
@Override
@SuppressWarnings("unchecked")
public List getResultList() {
try {
return storedProcedureOutputs.loader.processResultSet( resultSet );
}
catch (SQLException e) {
throw storedProcedureOutputs.convert( e, "Error calling ResultSet.next" );
}
}
@Override
public Object getSingleResult() {
List results = getResultList();
if ( results == null || results.isEmpty() ) {
return null;
}
else {
return results.get( 0 );
}
}
}
protected static class UpdateCountReturn implements org.hibernate.result.UpdateCountReturn {
private final ResultImpl result;
private final int updateCount;
public UpdateCountReturn(ResultImpl result, int updateCount) {
this.result = result;
this.updateCount = updateCount;
}
@Override
public int getUpdateCount() {
return updateCount;
}
@Override
public boolean isResultSet() {
return false;
}
}
private static CustomLoaderExtension buildSpecializedCustomLoader(final ResultContext context) {
final SQLQueryReturnProcessor processor = new SQLQueryReturnProcessor(
context.getQueryReturns(),
context.getSession().getFactory()
);
processor.process();
final List<org.hibernate.loader.custom.Return> customReturns = processor.generateCustomReturns( false );
CustomQuery customQuery = new CustomQuery() {
@Override
public String getSQL() {
return context.getSql();
}
@Override
public Set<String> getQuerySpaces() {
return context.getSynchronizedQuerySpaces();
}
@Override
public Map getNamedParameterBindPoints() {
// no named parameters in terms of embedded in the SQL string
return null;
}
@Override
public List<org.hibernate.loader.custom.Return> getCustomQueryReturns() {
return customReturns;
}
};
return new CustomLoaderExtension(
customQuery,
context.getQueryParameters(),
context.getSession()
);
}
private static class CustomLoaderExtension extends CustomLoader {
private QueryParameters queryParameters;
private SessionImplementor session;
public CustomLoaderExtension(
CustomQuery customQuery,
QueryParameters queryParameters,
SessionImplementor session) {
super( customQuery, session.getFactory() );
this.queryParameters = queryParameters;
this.session = session;
}
// todo : this would be a great way to add locking to stored procedure support (at least where returning entities).
public List processResultSet(ResultSet resultSet) throws SQLException {
super.autoDiscoverTypes( resultSet );
return super.processResultSet(
resultSet,
queryParameters,
session,
true,
null,
Integer.MAX_VALUE,
Collections.<AfterLoadAction>emptyList()
);
}
}
}

View File

@ -0,0 +1,63 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.result.internal;
import java.util.List;
import org.hibernate.result.ResultSetOutput;
/**
* Implementation of ResultSetOutput
*
* @author Steve Ebersole
*/
class ResultSetOutputImpl implements ResultSetOutput {
private final List results;
public ResultSetOutputImpl(List results) {
this.results = results;
}
@Override
public boolean isResultSet() {
return true;
}
@Override
@SuppressWarnings("unchecked")
public List getResultList() {
return results;
}
@Override
public Object getSingleResult() {
final List results = getResultList();
if ( results == null || results.isEmpty() ) {
return null;
}
else {
return results.get( 0 );
}
}
}

View File

@ -0,0 +1,49 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.result.internal;
import org.hibernate.result.UpdateCountOutput;
/**
* Implementation of UpdateCountOutput
*
* @author Steve Ebersole
*/
class UpdateCountOutputImpl implements UpdateCountOutput {
private final int updateCount;
public UpdateCountOutputImpl(int updateCount) {
this.updateCount = updateCount;
}
@Override
public int getUpdateCount() {
return updateCount;
}
@Override
public boolean isResultSet() {
return false;
}
}

View File

@ -38,6 +38,7 @@ import org.hibernate.testing.junit4.BaseCoreFunctionalTestMethod;
import org.hibernate.testing.junit4.BaseUnitTestCase; import org.hibernate.testing.junit4.BaseUnitTestCase;
import org.hibernate.type.SerializationException; import org.hibernate.type.SerializationException;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
@ -66,7 +67,11 @@ public class SessionFactorySerializationTest extends BaseCoreFunctionalTestMetho
SessionFactory factory2 = (SessionFactory) SerializationHelper.clone( factory ); SessionFactory factory2 = (SessionFactory) SerializationHelper.clone( factory );
assertSame( factory, factory2 ); assertSame( factory, factory2 );
SessionFactoryRegistry.INSTANCE.removeSessionFactory( "some-other-uuid", NAME, false, null );
factory.close(); factory.close();
assertFalse( SessionFactoryRegistry.INSTANCE.hasRegistrations() );
} }
@Test @Test
@ -82,17 +87,20 @@ public class SessionFactorySerializationTest extends BaseCoreFunctionalTestMetho
StringRefAddr refAddr = (StringRefAddr) reference.get( "uuid" ); StringRefAddr refAddr = (StringRefAddr) reference.get( "uuid" );
String uuid = (String) refAddr.getContent(); String uuid = (String) refAddr.getContent();
// deregister under this uuid... // deregister under this uuid...
SessionFactoryRegistry.INSTANCE.removeSessionFactory( uuid, NAME, false, null ); SessionFactoryRegistry.INSTANCE.removeSessionFactory( uuid, null, false, null );
// and then register under a different uuid... // and then register under a different uuid...
SessionFactoryRegistry.INSTANCE.addSessionFactory( "some-other-uuid", NAME, false, factory, null ); SessionFactoryRegistry.INSTANCE.addSessionFactory( "some-other-uuid", null, false, factory, null );
try { try {
SerializationHelper.clone( factory ); SerializationHelper.clone( factory );
fail( "Expecting an error" ); fail( "Expecting an error" );
} }
catch ( SerializationException expected ) { catch ( SerializationException expected ) {
} }
SessionFactoryRegistry.INSTANCE.removeSessionFactory( "some-other-uuid", null, false, null );
factory.close(); factory.close();
assertFalse( SessionFactoryRegistry.INSTANCE.hasRegistrations() );
} }
} }

View File

@ -56,12 +56,11 @@ public class EmbeddableIntegratorTest extends BaseUnitTestCase {
*/ */
@Test(expected = GenericJDBCException.class) @Test(expected = GenericJDBCException.class)
public void testWithoutIntegrator() { public void testWithoutIntegrator() {
ServiceRegistry reg = new StandardServiceRegistryBuilder( new BootstrapServiceRegistryImpl() ).build(); ServiceRegistry reg = new StandardServiceRegistryBuilder( new BootstrapServiceRegistryImpl() ).build();
SessionFactory sf = new Configuration().addAnnotatedClass( Investor.class ) SessionFactory sf = new Configuration().addAnnotatedClass( Investor.class )
.setProperty( "hibernate.hbm2ddl.auto", "create-drop" ).buildSessionFactory( reg ); .setProperty( "hibernate.hbm2ddl.auto", "create-drop" ).buildSessionFactory( reg );
try {
Session sess = sf.openSession(); Session sess = sf.openSession();
Investor myInv = getInvestor(); Investor myInv = getInvestor();
myInv.setId( 1L ); myInv.setId( 1L );
@ -74,18 +73,22 @@ public class EmbeddableIntegratorTest extends BaseUnitTestCase {
assertEquals( new BigDecimal( "100" ), inv.getInvestments().get( 0 ).getAmount().getAmount() ); assertEquals( new BigDecimal( "100" ), inv.getInvestments().get( 0 ).getAmount().getAmount() );
sess.close(); sess.close();
}
finally {
sf.close(); sf.close();
StandardServiceRegistryBuilder.destroy( reg ); StandardServiceRegistryBuilder.destroy( reg );
} }
}
@Test @Test
public void testWithIntegrator() { public void testWithIntegrator() {
StandardServiceRegistry reg = new StandardServiceRegistryBuilder( new BootstrapServiceRegistryBuilder().with( StandardServiceRegistry reg = new StandardServiceRegistryBuilder(
new InvestorIntegrator() ).build() ).build(); new BootstrapServiceRegistryBuilder().with( new InvestorIntegrator() ).build()
).build();
SessionFactory sf = new Configuration().addAnnotatedClass( Investor.class ) SessionFactory sf = new Configuration().addAnnotatedClass( Investor.class )
.setProperty( "hibernate.hbm2ddl.auto", "create-drop" ).buildSessionFactory( reg ); .setProperty( "hibernate.hbm2ddl.auto", "create-drop" ).buildSessionFactory( reg );
try {
Session sess = sf.openSession(); Session sess = sf.openSession();
Investor myInv = getInvestor(); Investor myInv = getInvestor();
myInv.setId( 2L ); myInv.setId( 2L );
@ -98,9 +101,12 @@ public class EmbeddableIntegratorTest extends BaseUnitTestCase {
assertEquals( new BigDecimal( "100" ), inv.getInvestments().get( 0 ).getAmount().getAmount() ); assertEquals( new BigDecimal( "100" ), inv.getInvestments().get( 0 ).getAmount().getAmount() );
sess.close(); sess.close();
}
finally {
sf.close(); sf.close();
StandardServiceRegistryBuilder.destroy( reg ); StandardServiceRegistryBuilder.destroy( reg );
} }
}
private Investor getInvestor() { private Investor getInvestor() {
Investor i = new Investor(); Investor i = new Investor();

View File

@ -70,6 +70,9 @@ public class OrmVersion1SupportedTest extends BaseCoreFunctionalTestCase {
s.close(); s.close();
assertEquals( "HHH00196 should not be called", 0, BytemanHelper.getAndResetInvocationCount() ); assertEquals( "HHH00196 should not be called", 0, BytemanHelper.getAndResetInvocationCount() );
// which means we also need to close it manually
releaseSessionFactory();
} }
@Override @Override

View File

@ -25,7 +25,6 @@ package org.hibernate.test.cfg.cache;
import org.junit.Test; import org.junit.Test;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Configuration;
import org.hibernate.testing.FailureExpectedWithNewMetamodel; import org.hibernate.testing.FailureExpectedWithNewMetamodel;
import org.hibernate.testing.junit4.BaseUnitTestCase; import org.hibernate.testing.junit4.BaseUnitTestCase;
@ -44,7 +43,8 @@ public class CacheConfigurationTest extends BaseUnitTestCase {
if(isMetadataUsed()){ if(isMetadataUsed()){
throw new IllegalStateException( "what should we do here " ); throw new IllegalStateException( "what should we do here " );
} }
// we only care if the SF builds successfully.
Configuration cfg = new Configuration().configure(CFG_XML); Configuration cfg = new Configuration().configure(CFG_XML);
SessionFactory sessionFactory = cfg.buildSessionFactory(); cfg.buildSessionFactory().close();
} }
} }

View File

@ -37,14 +37,15 @@ import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.CriteriaImpl; import org.hibernate.internal.CriteriaImpl;
import org.hibernate.loader.criteria.CriteriaQueryTranslator; import org.hibernate.loader.criteria.CriteriaQueryTranslator;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
/** /**
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class CriterionTest extends BaseCoreFunctionalTestCase { public class CriterionTest extends BaseUnitTestCase {
@Test @Test
public void testIlikeRendering() { public void testIlikeRendering() {
SessionFactory sf = new Configuration() SessionFactory sf = new Configuration()
@ -52,6 +53,7 @@ public class CriterionTest extends BaseCoreFunctionalTestCase {
.setProperty( AvailableSettings.DIALECT, IlikeSupportingDialect.class.getName() ) .setProperty( AvailableSettings.DIALECT, IlikeSupportingDialect.class.getName() )
.setProperty( Environment.HBM2DDL_AUTO, "create-drop" ) .setProperty( Environment.HBM2DDL_AUTO, "create-drop" )
.buildSessionFactory(); .buildSessionFactory();
try {
final Criteria criteria = sf.openSession().createCriteria( IrrelevantEntity.class ); final Criteria criteria = sf.openSession().createCriteria( IrrelevantEntity.class );
final CriteriaQueryTranslator translator = new CriteriaQueryTranslator( final CriteriaQueryTranslator translator = new CriteriaQueryTranslator(
(SessionFactoryImplementor) sf, (SessionFactoryImplementor) sf,
@ -63,6 +65,10 @@ public class CriterionTest extends BaseCoreFunctionalTestCase {
final String ilikeExpressionSqlFragment = ilikeExpression.toSqlString( criteria, translator ); final String ilikeExpressionSqlFragment = ilikeExpression.toSqlString( criteria, translator );
assertEquals( "a.name insensitiveLike ?", ilikeExpressionSqlFragment ); assertEquals( "a.name insensitiveLike ?", ilikeExpressionSqlFragment );
} }
finally {
sf.close();
}
}
@Test @Test
public void testIlikeMimicing() { public void testIlikeMimicing() {
@ -71,6 +77,7 @@ public class CriterionTest extends BaseCoreFunctionalTestCase {
.setProperty( AvailableSettings.DIALECT, NonIlikeSupportingDialect.class.getName() ) .setProperty( AvailableSettings.DIALECT, NonIlikeSupportingDialect.class.getName() )
.setProperty( Environment.HBM2DDL_AUTO, "create-drop" ) .setProperty( Environment.HBM2DDL_AUTO, "create-drop" )
.buildSessionFactory(); .buildSessionFactory();
try {
final Criteria criteria = sf.openSession().createCriteria( IrrelevantEntity.class ); final Criteria criteria = sf.openSession().createCriteria( IrrelevantEntity.class );
final CriteriaQueryTranslator translator = new CriteriaQueryTranslator( final CriteriaQueryTranslator translator = new CriteriaQueryTranslator(
(SessionFactoryImplementor) sf, (SessionFactoryImplementor) sf,
@ -82,6 +89,10 @@ public class CriterionTest extends BaseCoreFunctionalTestCase {
final String ilikeExpressionSqlFragment = ilikeExpression.toSqlString( criteria, translator ); final String ilikeExpressionSqlFragment = ilikeExpression.toSqlString( criteria, translator );
assertEquals( "lowLowLow(a.name) like ?", ilikeExpressionSqlFragment ); assertEquals( "lowLowLow(a.name) like ?", ilikeExpressionSqlFragment );
} }
finally {
sf.close();
}
}
public static class IlikeSupportingDialect extends Dialect { public static class IlikeSupportingDialect extends Dialect {
@Override @Override

View File

@ -21,15 +21,19 @@
package org.hibernate.test.mapping; package org.hibernate.test.mapping;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.mapping.Table; import org.hibernate.mapping.Table;
import org.hibernate.metamodel.spi.relational.TableSpecification; import org.hibernate.metamodel.spi.relational.TableSpecification;
import org.hibernate.test.util.SchemaUtil; import org.hibernate.test.util.SchemaUtil;
import org.hibernate.testing.TestForIssue; import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import static org.junit.Assert.assertTrue;
/** /**
* Column aliases utilize {@link Table#getUniqueInteger()} for naming. The * Column aliases utilize {@link Table#getUniqueInteger()} for naming. The
* unique integer used to be statically generated by the Table class, meaning * unique integer used to be statically generated by the Table class, meaning
@ -41,18 +45,64 @@ import static org.junit.Assert.assertTrue;
* *
* @author Brett Meyer * @author Brett Meyer
*/ */
@TestForIssue( jiraKey = "HHH-2448" )
public class AliasTest extends BaseCoreFunctionalTestCase { public class AliasTest extends BaseCoreFunctionalTestCase {
/**
* Column aliases utilize {@link Table#getUniqueInteger()} for naming. The unique integer used to be statically
* generated by the Table class, meaning it was dependent on mapping order. HHH-2448 made the alias names
* deterministic by having Configuration determine the unique integers on its second pass over the Tables tree map.
* AliasTest and {@link MappingReorderedAliasTest} ensure that the unique integers are the same, regardless of
* mapping ordering.
*/
@Test @Test
@TestForIssue( jiraKey = "HHH-2448" )
public void testAliasOrdering() { public void testAliasOrdering() {
TableSpecification table1 = SchemaUtil.getTable( "Table1", metadata() ); TableSpecification table1 = SchemaUtil.getTable( "Table1", metadata() );
TableSpecification table2 = SchemaUtil.getTable( "Table2", metadata() ); TableSpecification table2 = SchemaUtil.getTable( "Table2", metadata() );
assertTrue( table1.getTableNumber() < table2.getTableNumber() ); assertTrue( table1.getTableNumber() < table2.getTableNumber() );
} }
@Test
@TestForIssue( jiraKey = "HHH-8371" )
public final void testUnderscoreInColumnName() throws Throwable {
final Session s = openSession();
s.getTransaction().begin();
UserEntity user = new UserEntity();
user.setName( "foo" );
s.persist(user);
final ConfEntity conf = new ConfEntity();
conf.setConfKey("counter");
conf.setConfValue("3");
final UserConfEntity uc = new UserConfEntity();
uc.setUser(user);
uc.setConf(conf);
conf.getUserConf().add(uc);
s.persist(conf);
s.getTransaction().commit();
s.clear();
s.getTransaction().begin();
user = (UserEntity) s.get(UserEntity.class, user.getId());
try {
s.flush();
}
catch ( HibernateException e ) {
// original issue from HHH-8371
fail( "The explicit column name's underscore(s) were not considered during alias creation." );
}
assertNotNull( user );
assertEquals( user.getName(), "foo" );
s.getTransaction().commit();
s.close();
}
@Override @Override
protected Class<?>[] getAnnotatedClasses() { protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { Table1.class, Table2.class }; return new Class<?>[] { Table1.class, Table2.class, ConfEntity.class, UserConfEntity.class, UserEntity.class };
} }
} }

View File

@ -0,0 +1,54 @@
package org.hibernate.test.mapping;
import static javax.persistence.CascadeType.ALL;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.OneToMany;
import javax.persistence.Table;
@Entity
@Table(name = "CONF")
@IdClass(ConfId.class)
public class ConfEntity implements Serializable{
private static final long serialVersionUID = -5089484717715507169L;
@Id
@Column(name = "confKey")
private String confKey;
@Id
@Column(name = "confValue")
private String confValue;
@OneToMany(mappedBy="conf", cascade = ALL, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<UserConfEntity> userConf = new HashSet<UserConfEntity>();
public String getConfKey() {
return confKey;
}
public void setConfKey(String confKey) {
this.confKey = confKey;
}
public String getConfValue() {
return confValue;
}
public void setConfValue(String confValue) {
this.confValue = confValue;
}
public Set<UserConfEntity> getUserConf() {
return userConf;
}
}

View File

@ -0,0 +1,67 @@
package org.hibernate.test.mapping;
import java.io.Serializable;
public class ConfId implements Serializable{
private static final long serialVersionUID = -6722022851594514199L;
private String confKey;
private String confValue;
public ConfId(){
}
public ConfId(String confKey, String confValue) {
this.confKey = confKey;
this.confValue = confValue;
}
public String getConfKey() {
return confKey;
}
public void setConfKey(String confKey) {
this.confKey = confKey;
}
public String getConfValue() {
return confValue;
}
public void setConfValue(String confValue) {
this.confValue = confValue;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((confKey == null) ? 0 : confKey.hashCode());
result = prime * result + ((confValue == null) ? 0 : confValue.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ConfId other = (ConfId) obj;
if (confKey == null) {
if (other.confKey != null)
return false;
} else if (!confKey.equals(other.confKey))
return false;
else if (confValue == null) {
if (other.confValue != null)
return false;
} else if (!confValue.equals(other.confValue))
return false;
return true;
}
}

View File

@ -27,6 +27,6 @@ public class MappingReorderedAliasTest extends AliasTest {
@Override @Override
protected Class<?>[] getAnnotatedClasses() { protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { Table2.class, Table1.class }; return new Class<?>[] { Table2.class, Table1.class, ConfEntity.class, UserConfEntity.class, UserEntity.class };
} }
} }

View File

@ -0,0 +1,48 @@
package org.hibernate.test.mapping;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.JoinColumn;
import javax.persistence.JoinColumns;
import javax.persistence.ManyToOne;
import javax.persistence.Table;
@Entity
@Table(name = "USER_CONFS")
@IdClass(UserConfId.class)
public class UserConfEntity implements Serializable{
private static final long serialVersionUID = 9153314908821604322L;
@Id
@ManyToOne
@JoinColumn(name="user_id")
private UserEntity user;
@Id
@ManyToOne
@JoinColumns({
@JoinColumn(name="cnf_key", referencedColumnName="confKey"),
@JoinColumn(name="cnf_value", referencedColumnName="confValue")})
private ConfEntity conf;
public ConfEntity getConf() {
return conf;
}
public void setConf(ConfEntity conf) {
this.conf = conf;
}
public UserEntity getUser() {
return user;
}
public void setUser(UserEntity user) {
this.user = user;
}
}

View File

@ -0,0 +1,70 @@
package org.hibernate.test.mapping;
import java.io.Serializable;
public class UserConfId implements Serializable{
private static final long serialVersionUID = -161134972658451944L;
private Long user;
private ConfId conf;
public UserConfId(){
}
public UserConfId(Long user, ConfId conf) {
this.user = user;
this.conf = conf;
}
public Long getUser() {
return user;
}
public void setUser(Long user) {
this.user = user;
}
public ConfId getConf() {
return conf;
}
public void setConf(ConfId conf) {
this.conf = conf;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((conf == null) ? 0 : conf.hashCode());
result = prime * result + ((user == null) ? 0 : user.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
UserConfId other = (UserConfId) obj;
if (conf == null) {
if (other.conf != null)
return false;
} else if (!conf.equals(other.conf))
return false;
if (user == null) {
if (other.user != null)
return false;
} else if (!user.equals(other.user))
return false;
return true;
}
}

View File

@ -0,0 +1,58 @@
package org.hibernate.test.mapping;
import static javax.persistence.CascadeType.ALL;
import static javax.persistence.FetchType.EAGER;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.OrderColumn;
import javax.persistence.Table;
@Entity
@Table(name = "USER")
public class UserEntity implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
@Column(name = "user_id")
private Long id;
@OrderColumn(name = "cnf_order")
@OneToMany(mappedBy="user", fetch = EAGER, cascade = ALL, orphanRemoval = true)
private Set<UserConfEntity> confs = new HashSet<UserConfEntity>();
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Set<UserConfEntity> getConfs() {
return confs;
}
public void setConfs(Set<UserConfEntity> confs) {
this.confs = confs;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -30,24 +30,20 @@ import org.junit.Test;
import org.hibernate.JDBCException; import org.hibernate.JDBCException;
import org.hibernate.Session; import org.hibernate.Session;
import org.hibernate.cfg.Configuration;
import org.hibernate.dialect.Dialect; import org.hibernate.dialect.Dialect;
import org.hibernate.dialect.H2Dialect; import org.hibernate.dialect.H2Dialect;
import org.hibernate.engine.spi.Mapping;
import org.hibernate.mapping.AuxiliaryDatabaseObject;
import org.hibernate.metamodel.spi.MetadataImplementor; import org.hibernate.metamodel.spi.MetadataImplementor;
import org.hibernate.metamodel.spi.relational.Database; import org.hibernate.metamodel.spi.relational.Database;
import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureCall;
import org.hibernate.procedure.ProcedureResult; import org.hibernate.procedure.ProcedureOutputs;
import org.hibernate.result.ResultSetReturn; import org.hibernate.result.ResultSetOutput;
import org.hibernate.result.Return; import org.hibernate.result.Output;
import org.hibernate.testing.RequiresDialect; import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.hibernate.testing.junit4.ExtraAssertions;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
/** /**
@ -305,13 +301,11 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
Session session = openSession(); Session session = openSession();
session.beginTransaction(); session.beginTransaction();
ProcedureCall query = session.createStoredProcedureCall( "user"); ProcedureCall procedureCall = session.createStoredProcedureCall( "user");
ProcedureResult procedureResult = query.getResult(); ProcedureOutputs procedureOutputs = procedureCall.getResult();
assertTrue( "Checking ProcedureResult has more returns", procedureResult.hasMoreReturns() ); Output currentOutput = procedureOutputs.getCurrent();
Return nextReturn = procedureResult.getNextReturn(); assertNotNull( currentOutput );
assertNotNull( nextReturn ); ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput );
ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() );
ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn;
String name = (String) resultSetReturn.getSingleResult(); String name = (String) resultSetReturn.getSingleResult();
assertEquals( "SA", name ); assertEquals( "SA", name );
@ -325,14 +319,12 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
session.beginTransaction(); session.beginTransaction();
ProcedureCall query = session.createStoredProcedureCall( "findOneUser" ); ProcedureCall query = session.createStoredProcedureCall( "findOneUser" );
ProcedureResult procedureResult = query.getResult(); ProcedureOutputs procedureResult = query.getResult();
assertTrue( "Checking ProcedureResult has more returns", procedureResult.hasMoreReturns() ); Output currentOutput = procedureResult.getCurrent();
Return nextReturn = procedureResult.getNextReturn(); assertNotNull( currentOutput );
assertNotNull( nextReturn ); ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput );
ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() );
ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn;
Object result = resultSetReturn.getSingleResult(); Object result = resultSetReturn.getSingleResult();
ExtraAssertions.assertTyping( Object[].class, result ); assertTyping( Object[].class, result );
String name = (String) ( (Object[]) result )[1]; String name = (String) ( (Object[]) result )[1];
assertEquals( "Steve", name ); assertEquals( "Steve", name );
@ -346,17 +338,15 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
session.beginTransaction(); session.beginTransaction();
ProcedureCall query = session.createStoredProcedureCall( "findUsers" ); ProcedureCall query = session.createStoredProcedureCall( "findUsers" );
ProcedureResult procedureResult = query.getResult(); ProcedureOutputs procedureResult = query.getResult();
assertTrue( "Checking ProcedureResult has more returns", procedureResult.hasMoreReturns() ); Output currentOutput = procedureResult.getCurrent();
Return nextReturn = procedureResult.getNextReturn(); assertNotNull( currentOutput );
assertNotNull( nextReturn ); ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput );
ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() );
ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn;
List results = resultSetReturn.getResultList(); List results = resultSetReturn.getResultList();
assertEquals( 3, results.size() ); assertEquals( 3, results.size() );
for ( Object result : results ) { for ( Object result : results ) {
ExtraAssertions.assertTyping( Object[].class, result ); assertTyping( Object[].class, result );
Integer id = (Integer) ( (Object[]) result )[0]; Integer id = (Integer) ( (Object[]) result )[0];
String name = (String) ( (Object[]) result )[1]; String name = (String) ( (Object[]) result )[1];
if ( id.equals( 1 ) ) { if ( id.equals( 1 ) ) {
@ -385,16 +375,14 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
ProcedureCall query = session.createStoredProcedureCall( "findUserRange" ); ProcedureCall query = session.createStoredProcedureCall( "findUserRange" );
query.registerParameter( "start", Integer.class, ParameterMode.IN ).bindValue( 1 ); query.registerParameter( "start", Integer.class, ParameterMode.IN ).bindValue( 1 );
query.registerParameter( "end", Integer.class, ParameterMode.IN ).bindValue( 2 ); query.registerParameter( "end", Integer.class, ParameterMode.IN ).bindValue( 2 );
ProcedureResult procedureResult = query.getResult(); ProcedureOutputs procedureResult = query.getResult();
assertTrue( "Checking ProcedureResult has more returns", procedureResult.hasMoreReturns() ); Output currentOutput = procedureResult.getCurrent();
Return nextReturn = procedureResult.getNextReturn(); assertNotNull( currentOutput );
assertNotNull( nextReturn ); ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput );
ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() );
ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn;
List results = resultSetReturn.getResultList(); List results = resultSetReturn.getResultList();
assertEquals( 1, results.size() ); assertEquals( 1, results.size() );
Object result = results.get( 0 ); Object result = results.get( 0 );
ExtraAssertions.assertTyping( Object[].class, result ); assertTyping( Object[].class, result );
Integer id = (Integer) ( (Object[]) result )[0]; Integer id = (Integer) ( (Object[]) result )[0];
String name = (String) ( (Object[]) result )[1]; String name = (String) ( (Object[]) result )[1];
assertEquals( 1, (int) id ); assertEquals( 1, (int) id );
@ -412,16 +400,14 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
ProcedureCall query = session.createStoredProcedureCall( "findUserRange" ); ProcedureCall query = session.createStoredProcedureCall( "findUserRange" );
query.registerParameter( 1, Integer.class, ParameterMode.IN ).bindValue( 1 ); query.registerParameter( 1, Integer.class, ParameterMode.IN ).bindValue( 1 );
query.registerParameter( 2, Integer.class, ParameterMode.IN ).bindValue( 2 ); query.registerParameter( 2, Integer.class, ParameterMode.IN ).bindValue( 2 );
ProcedureResult procedureResult = query.getResult(); ProcedureOutputs procedureResult = query.getResult();
assertTrue( "Checking ProcedureResult has more returns", procedureResult.hasMoreReturns() ); Output currentOutput = procedureResult.getCurrent();
Return nextReturn = procedureResult.getNextReturn(); assertNotNull( currentOutput );
assertNotNull( nextReturn ); ResultSetOutput resultSetReturn = assertTyping( ResultSetOutput.class, currentOutput );
ExtraAssertions.assertClassAssignability( ResultSetReturn.class, nextReturn.getClass() );
ResultSetReturn resultSetReturn = (ResultSetReturn) nextReturn;
List results = resultSetReturn.getResultList(); List results = resultSetReturn.getResultList();
assertEquals( 1, results.size() ); assertEquals( 1, results.size() );
Object result = results.get( 0 ); Object result = results.get( 0 );
ExtraAssertions.assertTyping( Object[].class, result ); assertTyping( Object[].class, result );
Integer id = (Integer) ( (Object[]) result )[0]; Integer id = (Integer) ( (Object[]) result )[0];
String name = (String) ( (Object[]) result )[1]; String name = (String) ( (Object[]) result )[1];
assertEquals( 1, (int) id ); assertEquals( 1, (int) id );
@ -443,9 +429,8 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
ProcedureCall query = session.createStoredProcedureCall( "findUserRange" ); ProcedureCall query = session.createStoredProcedureCall( "findUserRange" );
query.registerParameter( 1, Integer.class, ParameterMode.IN ); query.registerParameter( 1, Integer.class, ParameterMode.IN );
query.registerParameter( 2, Integer.class, ParameterMode.IN ).bindValue( 2 ); query.registerParameter( 2, Integer.class, ParameterMode.IN ).bindValue( 2 );
ProcedureResult procedureResult = query.getResult();
try { try {
procedureResult.hasMoreReturns(); query.getResult();
fail( "Expecting failure due to missing parameter bind" ); fail( "Expecting failure due to missing parameter bind" );
} }
catch (JDBCException expected) { catch (JDBCException expected) {
@ -456,9 +441,8 @@ public class StoredProcedureTest extends BaseCoreFunctionalTestCase {
ProcedureCall query = session.createStoredProcedureCall( "findUserRange" ); ProcedureCall query = session.createStoredProcedureCall( "findUserRange" );
query.registerParameter( "start", Integer.class, ParameterMode.IN ); query.registerParameter( "start", Integer.class, ParameterMode.IN );
query.registerParameter( "end", Integer.class, ParameterMode.IN ).bindValue( 2 ); query.registerParameter( "end", Integer.class, ParameterMode.IN ).bindValue( 2 );
ProcedureResult procedureResult = query.getResult();
try { try {
procedureResult.hasMoreReturns(); query.getResult();
fail( "Expecting failure due to missing parameter bind" ); fail( "Expecting failure due to missing parameter bind" );
} }
catch (JDBCException expected) { catch (JDBCException expected) {

View File

@ -34,6 +34,7 @@ import org.hibernate.Session;
import org.hibernate.SessionFactory; import org.hibernate.SessionFactory;
import org.hibernate.Transaction; import org.hibernate.Transaction;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder; import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchStyle;
@ -58,56 +59,59 @@ public class StatsTest extends BaseCoreFunctionalTestCase {
public String[] getMappings() { public String[] getMappings() {
return new String[] { "stats/Continent.hbm.xml" }; return new String[] { "stats/Continent.hbm.xml" };
} }
@Override @Override
public void configure(Configuration cfg) { protected void initialize(){
super.configure( cfg ); super.initialize();
cfg.setProperty( Environment.GENERATE_STATISTICS, "true" ); getTestConfiguration().getProperties().setProperty( Environment.GENERATE_STATISTICS, "true" );
getTestConfiguration().getProperties().setProperty( AvailableSettings.HBM2DDL_AUTO, "create");
} }
@Override @Override
protected void prepareStandardServiceRegistryBuilder(StandardServiceRegistryBuilder serviceRegistryBuilder) { protected boolean createSchema() {
serviceRegistryBuilder.applySetting( Environment.GENERATE_STATISTICS, "true" ); return false;
} }
@Test @Test
@SuppressWarnings( {"UnusedAssignment"}) @SuppressWarnings( {"UnusedAssignment"})
public void testCollectionFetchVsLoad() throws Exception { public void testCollectionFetchVsLoad() throws Exception {
Statistics stats = sessionFactory().getStatistics(); SessionFactory sf =sessionFactory();
stats.clear();
Session s = openSession(); Session s = sf.openSession();
Transaction tx = s.beginTransaction(); Transaction tx = s.beginTransaction();
Continent europe = fillDb(s); Continent europe = fillDb(s);
tx.commit(); tx.commit();
s.clear(); s.close();
s = sf.openSession();
tx = s.beginTransaction(); tx = s.beginTransaction();
assertEquals(0, stats.getCollectionLoadCount() ); assertEquals(0, sf.getStatistics().getCollectionLoadCount() );
assertEquals(0, stats.getCollectionFetchCount() ); assertEquals(0, sf.getStatistics().getCollectionFetchCount() );
Continent europe2 = (Continent) s.get( Continent.class, europe.getId() ); Continent europe2 = (Continent) s.get( Continent.class, europe.getId() );
assertEquals("Lazy true: no collection should be loaded", 0, stats.getCollectionLoadCount() ); assertEquals("Lazy true: no collection should be loaded", 0, sf.getStatistics().getCollectionLoadCount() );
assertEquals( 0, stats.getCollectionFetchCount() ); assertEquals( 0, sf.getStatistics().getCollectionFetchCount() );
europe2.getCountries().size(); europe2.getCountries().size();
assertEquals( 1, stats.getCollectionLoadCount() ); assertEquals( 1, sf.getStatistics().getCollectionLoadCount() );
assertEquals("Explicit fetch of the collection state", 1, stats.getCollectionFetchCount() ); assertEquals("Explicit fetch of the collection state", 1, sf.getStatistics().getCollectionFetchCount() );
tx.commit(); tx.commit();
s.close(); s.close();
s = openSession(); sf.getStatistics().clear();
s = sf.openSession();
tx = s.beginTransaction(); tx = s.beginTransaction();
stats.clear();
europe = fillDb(s); europe = fillDb(s);
tx.commit(); tx.commit();
s.clear(); s.clear();
tx = s.beginTransaction(); tx = s.beginTransaction();
assertEquals( 0, stats.getCollectionLoadCount() ); assertEquals( 0, sf.getStatistics().getCollectionLoadCount() );
assertEquals( 0, stats.getCollectionFetchCount() ); assertEquals( 0, sf.getStatistics().getCollectionFetchCount() );
europe2 = (Continent) s.createQuery( europe2 = (Continent) s.createQuery(
"from " + Continent.class.getName() + " a join fetch a.countries where a.id = " + europe.getId() "from " + Continent.class.getName() + " a join fetch a.countries where a.id = " + europe.getId()
).uniqueResult(); ).uniqueResult();
assertEquals( 1, stats.getCollectionLoadCount() ); assertEquals( 1, sf.getStatistics().getCollectionLoadCount() );
assertEquals( "collection should be loaded in the same query as its parent", 0, stats.getCollectionFetchCount() ); assertEquals( "collection should be loaded in the same query as its parent", 0, sf.getStatistics().getCollectionFetchCount() );
tx.commit(); tx.commit();
s.close(); s.close();
@ -115,21 +119,23 @@ public class StatsTest extends BaseCoreFunctionalTestCase {
PluralAttributeBinding coll = SchemaUtil.getCollection( Continent.class, "countries", metadata() ); PluralAttributeBinding coll = SchemaUtil.getCollection( Continent.class, "countries", metadata() );
coll.setFetchStyle( FetchStyle.JOIN ); coll.setFetchStyle( FetchStyle.JOIN );
coll.setFetchTiming( FetchTiming.IMMEDIATE ); coll.setFetchTiming( FetchTiming.IMMEDIATE );
SessionFactory sf = metadata().getSessionFactoryBuilder().build(); sf = metadata().getSessionFactoryBuilder().build();
stats = sf.getStatistics(); Statistics stats = sf.getStatistics();
stats.clear(); stats.clear();
stats.setStatisticsEnabled(true); stats.setStatisticsEnabled(true);
s = sf.openSession(); s = sf.openSession();
tx = s.beginTransaction(); tx = s.beginTransaction();
europe = fillDb(s); europe = fillDb(s);
tx.commit(); tx.commit();
s.clear(); s.close();
s = sf.openSession();
tx = s.beginTransaction(); tx = s.beginTransaction();
assertEquals( 0, stats.getCollectionLoadCount() ); assertEquals( 0, sf.getStatistics().getCollectionLoadCount() );
assertEquals( 0, stats.getCollectionFetchCount() ); assertEquals( 0, sf.getStatistics().getCollectionFetchCount() );
europe2 = (Continent) s.get( Continent.class, europe.getId() ); europe2 = (Continent) s.get( Continent.class, europe.getId() );
assertEquals( 1, stats.getCollectionLoadCount() ); assertEquals( 1, sf.getStatistics().getCollectionLoadCount() );
assertEquals( "Should do direct load, not indirect second load when lazy false and JOIN", 0, stats.getCollectionFetchCount() ); assertEquals( "Should do direct load, not indirect second load when lazy false and JOIN", 0, sf.getStatistics().getCollectionFetchCount() );
tx.commit(); tx.commit();
s.close(); s.close();
sf.close(); sf.close();
@ -146,42 +152,45 @@ public class StatsTest extends BaseCoreFunctionalTestCase {
tx = s.beginTransaction(); tx = s.beginTransaction();
europe = fillDb(s); europe = fillDb(s);
tx.commit(); tx.commit();
s.clear(); s.close();
s = sf.openSession();
tx = s.beginTransaction(); tx = s.beginTransaction();
assertEquals( 0, stats.getCollectionLoadCount() ); assertEquals( 0, sf.getStatistics().getCollectionLoadCount() );
assertEquals( 0, stats.getCollectionFetchCount() ); assertEquals( 0, sf.getStatistics().getCollectionFetchCount() );
europe2 = (Continent) s.get( Continent.class, europe.getId() ); europe2 = (Continent) s.get( Continent.class, europe.getId() );
assertEquals( 1, stats.getCollectionLoadCount() ); assertEquals( 1, sf.getStatistics().getCollectionLoadCount() );
assertEquals( "Should do explicit collection load, not part of the first one", 1, stats.getCollectionFetchCount() ); assertEquals( "Should do explicit collection load, not part of the first one", 1, sf.getStatistics().getCollectionFetchCount() );
for ( Object o : europe2.getCountries() ) { for ( Object o : europe2.getCountries() ) {
s.delete( o ); s.delete( o );
} }
cleanDb( s ); cleanDb( s );
tx.commit(); tx.commit();
s.close(); s.close();
sf.close();
} }
@Test @Test
public void testQueryStatGathering() { public void testQueryStatGathering() {
Statistics stats = sessionFactory().getStatistics(); SessionFactory sf = sessionFactory();
stats.clear();
Session s = openSession(); Session s = sf.openSession();
Transaction tx = s.beginTransaction(); Transaction tx = s.beginTransaction();
fillDb(s); fillDb(s);
tx.commit(); tx.commit();
s.close(); s.close();
s = openSession(); s = sf.openSession();
tx = s.beginTransaction(); tx = s.beginTransaction();
final String continents = "from Continent"; final String continents = "from Continent";
int results = s.createQuery( continents ).list().size(); int results = s.createQuery( continents ).list().size();
QueryStatistics continentStats = stats.getQueryStatistics( continents ); QueryStatistics continentStats = sf.getStatistics().getQueryStatistics( continents );
assertNotNull( "stats were null", continentStats ); assertNotNull( "stats were null", continentStats );
assertEquals( "unexpected execution count", 1, continentStats.getExecutionCount() ); assertEquals( "unexpected execution count", 1, continentStats.getExecutionCount() );
assertEquals( "unexpected row count", results, continentStats.getExecutionRowCount() ); assertEquals( "unexpected row count", results, continentStats.getExecutionRowCount() );
long maxTime = continentStats.getExecutionMaxTime(); long maxTime = continentStats.getExecutionMaxTime();
assertEquals( maxTime, stats.getQueryExecutionMaxTime() ); assertEquals( maxTime, sf.getStatistics().getQueryExecutionMaxTime() );
// assertEquals( continents, stats.getQueryExecutionMaxTimeQueryString() ); // assertEquals( continents, stats.getQueryExecutionMaxTimeQueryString() );
Iterator itr = s.createQuery( continents ).iterate(); Iterator itr = s.createQuery( continents ).iterate();
@ -206,44 +215,47 @@ public class StatsTest extends BaseCoreFunctionalTestCase {
// explicitly check that statistics for "split queries" get collected // explicitly check that statistics for "split queries" get collected
// under the original query // under the original query
stats.clear(); sf.getStatistics().clear();
s = openSession();
s = sf.openSession();
tx = s.beginTransaction(); tx = s.beginTransaction();
final String localities = "from Locality"; final String localities = "from Locality";
results = s.createQuery( localities ).list().size(); results = s.createQuery( localities ).list().size();
QueryStatistics localityStats = stats.getQueryStatistics( localities ); QueryStatistics localityStats = sf.getStatistics().getQueryStatistics( localities );
assertNotNull( "stats were null", localityStats ); assertNotNull( "stats were null", localityStats );
// ...one for each split query // ...one for each split query
assertEquals( "unexpected execution count", 2, localityStats.getExecutionCount() ); assertEquals( "unexpected execution count", 2, localityStats.getExecutionCount() );
assertEquals( "unexpected row count", results, localityStats.getExecutionRowCount() ); assertEquals( "unexpected row count", results, localityStats.getExecutionRowCount() );
maxTime = localityStats.getExecutionMaxTime(); maxTime = localityStats.getExecutionMaxTime();
assertEquals( maxTime, stats.getQueryExecutionMaxTime() ); assertEquals( maxTime, sf.getStatistics().getQueryExecutionMaxTime() );
// assertEquals( localities, stats.getQueryExecutionMaxTimeQueryString() ); // assertEquals( localities, stats.getQueryExecutionMaxTimeQueryString() );
tx.commit(); tx.commit();
s.close(); s.close();
assertFalse( s.isOpen() ); assertFalse( s.isOpen() );
// native sql queries // native sql queries
stats.clear(); sf.getStatistics().clear();
s = openSession();
s = sf.openSession();
tx = s.beginTransaction(); tx = s.beginTransaction();
final String sql = "select id, name from Country"; final String sql = "select id, name from Country";
results = s.createSQLQuery( sql ).addEntity( Country.class ).list().size(); results = s.createSQLQuery( sql ).addEntity( Country.class ).list().size();
QueryStatistics sqlStats = stats.getQueryStatistics( sql ); QueryStatistics sqlStats = sf.getStatistics().getQueryStatistics( sql );
assertNotNull( "sql stats were null", sqlStats ); assertNotNull( "sql stats were null", sqlStats );
assertEquals( "unexpected execution count", 1, sqlStats.getExecutionCount() ); assertEquals( "unexpected execution count", 1, sqlStats.getExecutionCount() );
assertEquals( "unexpected row count", results, sqlStats.getExecutionRowCount() ); assertEquals( "unexpected row count", results, sqlStats.getExecutionRowCount() );
maxTime = sqlStats.getExecutionMaxTime(); maxTime = sqlStats.getExecutionMaxTime();
assertEquals( maxTime, stats.getQueryExecutionMaxTime() ); assertEquals( maxTime, sf.getStatistics().getQueryExecutionMaxTime() );
// assertEquals( sql, stats.getQueryExecutionMaxTimeQueryString() ); // assertEquals( sql, stats.getQueryExecutionMaxTimeQueryString() );
tx.commit(); tx.commit();
s.close(); s.close();
s = openSession(); s = sf.openSession();
tx = s.beginTransaction(); tx = s.beginTransaction();
cleanDb( s ); cleanDb( s );
tx.commit(); tx.commit();
s.close(); s.close();
sf.close();
} }
private Continent fillDb(Session s) { private Continent fillDb(Session s) {

View File

@ -22,6 +22,9 @@ dependencies {
testRuntime( libraries.validator ) testRuntime( libraries.validator )
testRuntime( "org.jboss.spec.javax.ejb:jboss-ejb-api_3.2_spec:1.0.0.Alpha2" ) testRuntime( "org.jboss.spec.javax.ejb:jboss-ejb-api_3.2_spec:1.0.0.Alpha2" )
// testRuntime( "org.glassfish.web:el-impl:2.1.2-b04" ) // testRuntime( "org.glassfish.web:el-impl:2.1.2-b04" )
// for testing stored procedure support
testCompile( libraries.derby )
} }
def pomName() { def pomName() {

View File

@ -24,7 +24,6 @@
package org.hibernate.jpa.event.internal.jpa; package org.hibernate.jpa.event.internal.jpa;
import javax.persistence.PersistenceException; import javax.persistence.PersistenceException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import org.hibernate.jpa.event.spi.jpa.ListenerFactory; import org.hibernate.jpa.event.spi.jpa.ListenerFactory;
@ -36,7 +35,8 @@ import org.hibernate.jpa.event.spi.jpa.ListenerFactory;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public class StandardListenerFactory implements ListenerFactory { public class StandardListenerFactory implements ListenerFactory {
private Map listenerInstances = new ConcurrentHashMap();
private final ConcurrentHashMap listenerInstances = new ConcurrentHashMap();
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -52,7 +52,10 @@ public class StandardListenerFactory implements ListenerFactory {
e e
); );
} }
listenerInstances.put( listenerClass, listenerInstance ); Object existing = listenerInstances.putIfAbsent( listenerClass, listenerInstance );
if ( existing != null ) {
listenerInstance = existing;
}
} }
return (T) listenerInstance; return (T) listenerInstance;
} }

View File

@ -269,6 +269,8 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory {
@Override @Override
public EntityManager createEntityManager(SynchronizationType synchronizationType, Map map) { public EntityManager createEntityManager(SynchronizationType synchronizationType, Map map) {
validateNotClosed();
//TODO support discardOnClose, persistencecontexttype?, interceptor, //TODO support discardOnClose, persistencecontexttype?, interceptor,
return new EntityManagerImpl( return new EntityManagerImpl(
this, this,
@ -282,42 +284,49 @@ public class EntityManagerFactoryImpl implements HibernateEntityManagerFactory {
} }
public CriteriaBuilder getCriteriaBuilder() { public CriteriaBuilder getCriteriaBuilder() {
validateNotClosed();
return criteriaBuilder; return criteriaBuilder;
} }
public Metamodel getMetamodel() { public Metamodel getMetamodel() {
validateNotClosed();
return metamodel; return metamodel;
} }
public void close() { public void close() {
// The spec says so, that's why :(
validateNotClosed();
sessionFactory.close(); sessionFactory.close();
EntityManagerFactoryRegistry.INSTANCE.removeEntityManagerFactory(entityManagerFactoryName, this); EntityManagerFactoryRegistry.INSTANCE.removeEntityManagerFactory(entityManagerFactoryName, this);
} }
public Map<String, Object> getProperties() { public Map<String, Object> getProperties() {
validateNotClosed();
return properties; return properties;
} }
public Cache getCache() { public Cache getCache() {
validateNotClosed();
// TODO : cache the cache reference? // TODO : cache the cache reference?
if ( ! isOpen() ) {
throw new IllegalStateException("EntityManagerFactory is closed");
}
return new JPACache( sessionFactory ); return new JPACache( sessionFactory );
} }
public PersistenceUnitUtil getPersistenceUnitUtil() { protected void validateNotClosed() {
if ( ! isOpen() ) { if ( ! isOpen() ) {
throw new IllegalStateException("EntityManagerFactory is closed"); throw new IllegalStateException( "EntityManagerFactory is closed" );
} }
}
public PersistenceUnitUtil getPersistenceUnitUtil() {
validateNotClosed();
return util; return util;
} }
@Override @Override
public void addNamedQuery(String name, Query query) { public void addNamedQuery(String name, Query query) {
if ( ! isOpen() ) { validateNotClosed();
throw new IllegalStateException( "EntityManagerFactory is closed" );
}
if ( StoredProcedureQueryImpl.class.isInstance( query ) ) { if ( StoredProcedureQueryImpl.class.isInstance( query ) ) {
final ProcedureCall procedureCall = ( (StoredProcedureQueryImpl) query ).getHibernateProcedureCall(); final ProcedureCall procedureCall = ( (StoredProcedureQueryImpl) query ).getHibernateProcedureCall();

View File

@ -197,7 +197,7 @@ public class EntityManagerImpl extends AbstractEntityManagerImpl implements Sess
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T> EntityGraph<T> getEntityGraph(String graphName) { public EntityGraph<?> getEntityGraph(String graphName) {
checkOpen(); checkOpen();
final EntityGraphImpl named = getEntityManagerFactory().findEntityGraphByName( graphName ); final EntityGraphImpl named = getEntityManagerFactory().findEntityGraphByName( graphName );
if ( named == null ) { if ( named == null ) {

View File

@ -23,13 +23,10 @@
*/ */
package org.hibernate.jpa.internal; package org.hibernate.jpa.internal;
import javax.persistence.NoResultException; import static javax.persistence.TemporalType.DATE;
import javax.persistence.NonUniqueResultException; import static javax.persistence.TemporalType.TIME;
import javax.persistence.ParameterMode; import static javax.persistence.TemporalType.TIMESTAMP;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import javax.persistence.TemporalType;
import javax.persistence.TypedQuery;
import java.util.Calendar; import java.util.Calendar;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -39,7 +36,13 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.jboss.logging.Logger; import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.ParameterMode;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import javax.persistence.TemporalType;
import javax.persistence.TypedQuery;
import org.hibernate.CacheMode; import org.hibernate.CacheMode;
import org.hibernate.FlushMode; import org.hibernate.FlushMode;
@ -59,10 +62,8 @@ import org.hibernate.jpa.internal.util.LockModeTypeHelper;
import org.hibernate.jpa.spi.AbstractEntityManagerImpl; import org.hibernate.jpa.spi.AbstractEntityManagerImpl;
import org.hibernate.jpa.spi.AbstractQueryImpl; import org.hibernate.jpa.spi.AbstractQueryImpl;
import org.hibernate.type.CompositeCustomType; import org.hibernate.type.CompositeCustomType;
import org.hibernate.type.Type;
import static javax.persistence.TemporalType.DATE; import org.jboss.logging.Logger;
import static javax.persistence.TemporalType.TIME;
import static javax.persistence.TemporalType.TIMESTAMP;
/** /**
* Hibernate implementation of both the {@link Query} and {@link TypedQuery} contracts. * Hibernate implementation of both the {@link Query} and {@link TypedQuery} contracts.
@ -103,7 +104,7 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
for ( String name : (Set<String>) parameterMetadata.getNamedParameterNames() ) { for ( String name : (Set<String>) parameterMetadata.getNamedParameterNames() ) {
final NamedParameterDescriptor descriptor = parameterMetadata.getNamedParameterDescriptor( name ); final NamedParameterDescriptor descriptor = parameterMetadata.getNamedParameterDescriptor( name );
Class javaType = namedParameterTypeRedefinition.get( name ); Class javaType = namedParameterTypeRedefinition.get( name );
if ( javaType != null && mightNeedRedefinition( javaType, descriptor.getExpectedType().getClass() ) ) { if ( javaType != null && mightNeedRedefinition( javaType, descriptor.getExpectedType() ) ) {
descriptor.resetExpectedType( descriptor.resetExpectedType(
sfi().getTypeResolver().heuristicType( javaType.getName() ) sfi().getTypeResolver().heuristicType( javaType.getName() )
); );
@ -136,10 +137,13 @@ public class QueryImpl<X> extends AbstractQueryImpl<X> implements TypedQuery<X>,
return (SessionFactoryImplementor) getEntityManager().getFactory().getSessionFactory(); return (SessionFactoryImplementor) getEntityManager().getFactory().getSessionFactory();
} }
private boolean mightNeedRedefinition(Class javaType, Class expectedType) { private boolean mightNeedRedefinition(Class javaType, Type expectedType) {
// only redefine dates/times/timestamps that are not wrapped in a CompositeCustomType // only redefine dates/times/timestamps that are not wrapped in a CompositeCustomType
if ( expectedType == null )
return java.util.Date.class.isAssignableFrom( javaType );
else
return java.util.Date.class.isAssignableFrom( javaType ) return java.util.Date.class.isAssignableFrom( javaType )
&& !CompositeCustomType.class.isAssignableFrom( expectedType ); && !CompositeCustomType.class.isAssignableFrom( expectedType.getClass() );
} }
private static class ParameterRegistrationImpl<T> implements ParameterRegistration<T> { private static class ParameterRegistrationImpl<T> implements ParameterRegistration<T> {

View File

@ -25,11 +25,15 @@ package org.hibernate.jpa.internal;
import javax.persistence.FlushModeType; import javax.persistence.FlushModeType;
import javax.persistence.LockModeType; import javax.persistence.LockModeType;
import javax.persistence.NoResultException;
import javax.persistence.NonUniqueResultException;
import javax.persistence.Parameter; import javax.persistence.Parameter;
import javax.persistence.ParameterMode; import javax.persistence.ParameterMode;
import javax.persistence.PersistenceException;
import javax.persistence.Query; import javax.persistence.Query;
import javax.persistence.StoredProcedureQuery; import javax.persistence.StoredProcedureQuery;
import javax.persistence.TemporalType; import javax.persistence.TemporalType;
import javax.persistence.TransactionRequiredException;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -37,11 +41,15 @@ import java.util.List;
import org.hibernate.CacheMode; import org.hibernate.CacheMode;
import org.hibernate.FlushMode; import org.hibernate.FlushMode;
import org.hibernate.LockMode; import org.hibernate.LockMode;
import org.hibernate.procedure.NoSuchParameterException;
import org.hibernate.procedure.ParameterStrategyException;
import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureCall;
import org.hibernate.procedure.ProcedureResult; import org.hibernate.procedure.ProcedureCallMemento;
import org.hibernate.result.ResultSetReturn; import org.hibernate.procedure.ProcedureOutputs;
import org.hibernate.result.Return; import org.hibernate.result.NoMoreReturnsException;
import org.hibernate.result.UpdateCountReturn; import org.hibernate.result.ResultSetOutput;
import org.hibernate.result.Output;
import org.hibernate.result.UpdateCountOutput;
import org.hibernate.jpa.spi.BaseQueryImpl; import org.hibernate.jpa.spi.BaseQueryImpl;
import org.hibernate.jpa.spi.HibernateEntityManagerImplementor; import org.hibernate.jpa.spi.HibernateEntityManagerImplementor;
@ -50,13 +58,28 @@ import org.hibernate.jpa.spi.HibernateEntityManagerImplementor;
*/ */
public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredProcedureQuery { public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredProcedureQuery {
private final ProcedureCall procedureCall; private final ProcedureCall procedureCall;
private ProcedureResult procedureResult; private ProcedureOutputs procedureResult;
public StoredProcedureQueryImpl(ProcedureCall procedureCall, HibernateEntityManagerImplementor entityManager) { public StoredProcedureQueryImpl(ProcedureCall procedureCall, HibernateEntityManagerImplementor entityManager) {
super( entityManager ); super( entityManager );
this.procedureCall = procedureCall; this.procedureCall = procedureCall;
} }
/**
* This form is used to build a StoredProcedureQueryImpl from a memento (usually from a NamedStoredProcedureQuery).
*
* @param memento The memento
* @param entityManager The EntityManager
*/
@SuppressWarnings("unchecked")
public StoredProcedureQueryImpl(ProcedureCallMemento memento, HibernateEntityManagerImplementor entityManager) {
super( entityManager );
this.procedureCall = memento.makeProcedureCall( entityManager.getSession() );
for ( org.hibernate.procedure.ParameterRegistration nativeParamReg : procedureCall.getRegisteredParameters() ) {
registerParameter( new ParameterRegistrationImpl( nativeParamReg ) );
}
}
@Override @Override
protected boolean applyTimeoutHint(int timeout) { protected boolean applyTimeoutHint(int timeout) {
procedureCall.setTimeout( timeout ); procedureCall.setTimeout( timeout );
@ -178,81 +201,162 @@ public class StoredProcedureQueryImpl extends BaseQueryImpl implements StoredPro
// outputs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // outputs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private ProcedureResult outputs() { @Override
public boolean execute() {
try {
final Output rtn = outputs().getCurrent();
return rtn != null && ResultSetOutput.class.isInstance( rtn );
}
catch (NoMoreReturnsException e) {
return false;
}
}
protected ProcedureOutputs outputs() {
if ( procedureResult == null ) { if ( procedureResult == null ) {
procedureResult = procedureCall.getResult(); procedureResult = procedureCall.getResult();
} }
return procedureResult; return procedureResult;
} }
@Override
public int executeUpdate() {
if ( ! entityManager().isTransactionInProgress() ) {
throw new TransactionRequiredException( "javax.persistence.Query.executeUpdate requires active transaction" );
}
// the expectation is that there is just one Output, of type UpdateCountOutput
try {
execute();
return getUpdateCount();
}
finally {
outputs().release();
}
}
@Override @Override
public Object getOutputParameterValue(int position) { public Object getOutputParameterValue(int position) {
try {
return outputs().getOutputParameterValue( position ); return outputs().getOutputParameterValue( position );
} }
catch (ParameterStrategyException e) {
throw new IllegalArgumentException( "Invalid mix of named and positional parameters", e );
}
catch (NoSuchParameterException e) {
throw new IllegalArgumentException( e.getMessage(), e );
}
}
@Override @Override
public Object getOutputParameterValue(String parameterName) { public Object getOutputParameterValue(String parameterName) {
try {
return outputs().getOutputParameterValue( parameterName ); return outputs().getOutputParameterValue( parameterName );
} }
catch (ParameterStrategyException e) {
@Override throw new IllegalArgumentException( "Invalid mix of named and positional parameters", e );
public boolean execute() { }
return outputs().hasMoreReturns(); catch (NoSuchParameterException e) {
throw new IllegalArgumentException( e.getMessage(), e );
} }
@Override
public int executeUpdate() {
return getUpdateCount();
} }
@Override @Override
public boolean hasMoreResults() { public boolean hasMoreResults() {
return outputs().hasMoreReturns(); return outputs().goToNext() && ResultSetOutput.class.isInstance( outputs().getCurrent() );
} }
@Override @Override
public int getUpdateCount() { public int getUpdateCount() {
final Return nextReturn = outputs().getNextReturn(); try {
if ( nextReturn.isResultSet() ) { final Output rtn = outputs().getCurrent();
if ( rtn == null ) {
return -1;
}
else if ( UpdateCountOutput.class.isInstance( rtn ) ) {
return ( (UpdateCountOutput) rtn ).getUpdateCount();
}
else {
return -1;
}
}
catch (NoMoreReturnsException e) {
return -1; return -1;
} }
return ( (UpdateCountReturn) nextReturn ).getUpdateCount();
} }
@Override @Override
public List getResultList() { public List getResultList() {
final Return nextReturn = outputs().getNextReturn(); try {
if ( ! nextReturn.isResultSet() ) { final Output rtn = outputs().getCurrent();
return null; // todo : what should be thrown/returned here? if ( ! ResultSetOutput.class.isInstance( rtn ) ) {
throw new IllegalStateException( "Current CallableStatement ou was not a ResultSet, but getResultList was called" );
}
return ( (ResultSetOutput) rtn ).getResultList();
}
catch (NoMoreReturnsException e) {
// todo : the spec is completely silent on these type of edge-case scenarios.
// Essentially here we'd have a case where there are no more results (ResultSets nor updateCount) but
// getResultList was called.
return null;
} }
return ( (ResultSetReturn) nextReturn ).getResultList();
} }
@Override @Override
public Object getSingleResult() { public Object getSingleResult() {
final Return nextReturn = outputs().getNextReturn(); final List resultList = getResultList();
if ( ! nextReturn.isResultSet() ) { if ( resultList == null || resultList.isEmpty() ) {
return null; // todo : what should be thrown/returned here? throw new NoResultException(
String.format(
"Call to stored procedure [%s] returned no results",
procedureCall.getProcedureName()
)
);
} }
return ( (ResultSetReturn) nextReturn ).getSingleResult(); else if ( resultList.size() > 1 ) {
throw new NonUniqueResultException(
String.format(
"Call to stored procedure [%s] returned multiple results",
procedureCall.getProcedureName()
)
);
}
return resultList.get( 0 );
} }
@Override @Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> cls) { public <T> T unwrap(Class<T> cls) {
return null; if ( ProcedureCall.class.isAssignableFrom( cls ) ) {
return (T) procedureCall;
}
else if ( ProcedureOutputs.class.isAssignableFrom( cls ) ) {
return (T) outputs();
}
else if ( BaseQueryImpl.class.isAssignableFrom( cls ) ) {
return (T) this;
} }
throw new PersistenceException(
// ugh ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ String.format(
"Unsure how to unwrap %s impl [%s] as requested type [%s]",
StoredProcedureQuery.class.getSimpleName(),
this.getClass().getName(),
cls.getName()
)
);
}
@Override @Override
public Query setLockMode(LockModeType lockMode) { public Query setLockMode(LockModeType lockMode) {
return null; throw new IllegalStateException( "javax.persistence.Query.setLockMode not valid on javax.persistence.StoredProcedureQuery" );
} }
@Override @Override
public LockModeType getLockMode() { public LockModeType getLockMode() {
return null; throw new IllegalStateException( "javax.persistence.Query.getLockMode not valid on javax.persistence.StoredProcedureQuery" );
} }

View File

@ -28,6 +28,7 @@ import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess; import org.hibernate.engine.jdbc.connections.spi.JdbcConnectionAccess;
import org.jboss.logging.Logger;
import org.hibernate.engine.jdbc.internal.DDLFormatterImpl; import org.hibernate.engine.jdbc.internal.DDLFormatterImpl;
import org.hibernate.engine.jdbc.spi.SqlStatementLogger; import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
@ -38,6 +39,8 @@ import org.hibernate.engine.jdbc.spi.SqlStatementLogger;
* @author Steve Ebersole * @author Steve Ebersole
*/ */
class JdbcConnectionContext { class JdbcConnectionContext {
private static final Logger log = Logger.getLogger( JdbcConnectionContext.class );
private final JdbcConnectionAccess jdbcConnectionAccess; private final JdbcConnectionAccess jdbcConnectionAccess;
private final SqlStatementLogger sqlStatementLogger; private final SqlStatementLogger sqlStatementLogger;
@ -62,6 +65,15 @@ class JdbcConnectionContext {
public void release() { public void release() {
if ( jdbcConnection != null ) { if ( jdbcConnection != null ) {
try {
if ( ! jdbcConnection.getAutoCommit() ) {
jdbcConnection.commit();
}
}
catch (SQLException e) {
log.debug( "Unable to commit JDBC transaction used for JPA schema export; may or may not be a problem" );
}
try { try {
jdbcConnectionAccess.releaseConnection( jdbcConnection ); jdbcConnectionAccess.releaseConnection( jdbcConnection );
} }

View File

@ -838,8 +838,7 @@ public abstract class AbstractEntityManagerImpl implements HibernateEntityManage
if ( memento == null ) { if ( memento == null ) {
throw new IllegalArgumentException( "No @NamedStoredProcedureQuery was found with that name : " + name ); throw new IllegalArgumentException( "No @NamedStoredProcedureQuery was found with that name : " + name );
} }
final ProcedureCall procedureCall = memento.makeProcedureCall( internalGetSession() ); final StoredProcedureQueryImpl jpaImpl = new StoredProcedureQueryImpl( memento, this );
final StoredProcedureQueryImpl jpaImpl = new StoredProcedureQueryImpl( procedureCall, this );
// apply hints // apply hints
if ( memento.getHintsMap() != null ) { if ( memento.getHintsMap() != null ) {
for ( Map.Entry<String,Object> hintEntry : memento.getHintsMap().entrySet() ) { for ( Map.Entry<String,Object> hintEntry : memento.getHintsMap().entrySet() ) {

View File

@ -51,6 +51,8 @@ import org.hibernate.jpa.internal.EntityManagerMessageLogger;
import org.hibernate.jpa.internal.util.CacheModeHelper; import org.hibernate.jpa.internal.util.CacheModeHelper;
import org.hibernate.jpa.internal.util.ConfigurationHelper; import org.hibernate.jpa.internal.util.ConfigurationHelper;
import org.hibernate.jpa.internal.util.LockModeTypeHelper; import org.hibernate.jpa.internal.util.LockModeTypeHelper;
import org.hibernate.procedure.NoSuchParameterException;
import org.hibernate.procedure.ParameterStrategyException;
import static org.hibernate.jpa.QueryHints.HINT_CACHEABLE; import static org.hibernate.jpa.QueryHints.HINT_CACHEABLE;
import static org.hibernate.jpa.QueryHints.HINT_CACHE_MODE; import static org.hibernate.jpa.QueryHints.HINT_CACHE_MODE;
@ -439,7 +441,8 @@ public abstract class BaseQueryImpl implements Query {
protected abstract boolean isJpaPositionalParameter(int position); protected abstract boolean isJpaPositionalParameter(int position);
/** /**
* Hibernate specific extension to the JPA {@link javax.persistence.Parameter} contract. * Hibernate specific extension to the JPA {@link javax.persistence.Parameter} contract. Used here to track
* information known about the parameter.
*/ */
protected static interface ParameterRegistration<T> extends Parameter<T> { protected static interface ParameterRegistration<T> extends Parameter<T> {
/** /**
@ -450,6 +453,12 @@ public abstract class BaseQueryImpl implements Query {
*/ */
public ParameterMode getMode(); public ParameterMode getMode();
/**
* Can we bind (set) values on this parameter? Generally this is {@code true}, but would not be in the case
* of parameters with OUT or REF_CURSOR mode.
*
* @return Whether the parameter is bindable (can set be called).
*/
public boolean isBindable(); public boolean isBindable();
public void bindValue(T value); public void bindValue(T value);
@ -459,6 +468,11 @@ public abstract class BaseQueryImpl implements Query {
public ParameterBind<T> getBind(); public ParameterBind<T> getBind();
} }
/**
* Represents the value currently bound to a particular parameter.
*
* @param <T>
*/
protected static interface ParameterBind<T> { protected static interface ParameterBind<T> {
public T getValue(); public T getValue();
@ -648,6 +662,12 @@ public abstract class BaseQueryImpl implements Query {
try { try {
findParameterRegistration( position ).bindValue( value, temporalType ); findParameterRegistration( position ).bindValue( value, temporalType );
} }
catch (ParameterStrategyException e) {
throw new IllegalArgumentException( "Invalid mix of named and positional parameters", e );
}
catch (NoSuchParameterException e) {
throw new IllegalArgumentException( e.getMessage(), e );
}
catch (QueryParameterException e) { catch (QueryParameterException e) {
throw new IllegalArgumentException( e ); throw new IllegalArgumentException( e );
} }
@ -755,18 +775,22 @@ public abstract class BaseQueryImpl implements Query {
checkOpen( false ); checkOpen( false );
final ParameterRegistration<T> registration = findParameterRegistration( param ); final ParameterRegistration<T> registration = findParameterRegistration( param );
if ( registration != null ) { if ( registration == null ) {
throw new IllegalArgumentException( "Passed parameter [" + param + "] is not a (registered) parameter of this query" );
}
if ( ! registration.isBindable() ) { if ( ! registration.isBindable() ) {
throw new IllegalArgumentException( "Passed parameter [" + param + "] is not bindable" ); throw new IllegalStateException( "Passed parameter [" + param + "] is not bindable" );
} }
final ParameterBind<T> bind = registration.getBind(); final ParameterBind<T> bind = registration.getBind();
if ( bind != null ) { if ( bind == null ) {
return bind.getValue();
}
}
throw new IllegalStateException( "Parameter [" + param + "] has not yet been bound" ); throw new IllegalStateException( "Parameter [" + param + "] has not yet been bound" );
} }
return bind.getValue();
}
@Override @Override
public Object getParameterValue(String name) { public Object getParameterValue(String name) {
checkOpen( false ); checkOpen( false );

View File

@ -32,6 +32,7 @@ import java.util.List;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import javax.persistence.TypedQuery; import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.ParameterExpression; import javax.persistence.criteria.ParameterExpression;
import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Predicate;
@ -103,4 +104,26 @@ public class BasicCriteriaUsageTest extends BaseEntityManagerFunctionalTestCase
em.getTransaction().commit(); em.getTransaction().commit();
em.close(); em.close();
} }
@Test
@TestForIssue(jiraKey = "HHH-8373")
public void testFunctionCriteria() {
Wall wall = new Wall();
wall.setColor( "yellow" );
EntityManager em = getOrCreateEntityManager();
em.getTransaction().begin();
em.persist( wall );
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Wall> query = cb.createQuery( Wall.class );
Root<Wall> root = query.from( Wall.class );
query.select( root ).where( cb.equal( root.get( "color" ), cb.lower( cb.literal( "YELLOW" ) ) ) );
Wall resultItem = em.createQuery( query ).getSingleResult();
assertNotNull( resultItem );
em.getTransaction().commit();
em.close();
}
} }

View File

@ -91,7 +91,7 @@ public class BasicEntityGraphTests extends BaseEntityManagerFunctionalTestCase {
em.getEntityManagerFactory().addNamedEntityGraph( "immutable", graphRoot ); em.getEntityManagerFactory().addNamedEntityGraph( "immutable", graphRoot );
graphRoot = em.getEntityGraph( "immutable" ); graphRoot = (EntityGraph<Entity1>) em.getEntityGraph( "immutable" );
assertEquals( "immutable", graphRoot.getName() ); assertEquals( "immutable", graphRoot.getName() );
assertEquals( 2, graphRoot.getAttributeNodes().size() ); assertEquals( 2, graphRoot.getAttributeNodes().size() );

View File

@ -52,13 +52,13 @@ public abstract class AbstractStoredProcedureTest extends BaseEntityManagerFunct
list = m2.getParameterDeclarations(); list = m2.getParameterDeclarations();
memento = list.get( 0 ); memento = list.get( 0 );
assertEquals( Integer.valueOf( 0 ), memento.getPosition() ); assertEquals( Integer.valueOf( 1 ), memento.getPosition() );
assertEquals( javax.persistence.ParameterMode.INOUT, memento.getMode() ); assertEquals( javax.persistence.ParameterMode.INOUT, memento.getMode() );
assertEquals( StringType.INSTANCE, memento.getHibernateType() ); assertEquals( StringType.INSTANCE, memento.getHibernateType() );
assertEquals( String.class, memento.getType() ); assertEquals( String.class, memento.getType() );
memento = list.get( 1 ); memento = list.get( 1 );
assertEquals( Integer.valueOf( 1 ), memento.getPosition() ); assertEquals( Integer.valueOf( 2 ), memento.getPosition() );
assertEquals( javax.persistence.ParameterMode.INOUT, memento.getMode() ); assertEquals( javax.persistence.ParameterMode.INOUT, memento.getMode() );
assertEquals( LongType.INSTANCE, memento.getHibernateType() ); assertEquals( LongType.INSTANCE, memento.getHibernateType() );
assertEquals( Long.class, memento.getType() ); assertEquals( Long.class, memento.getType() );

View File

@ -0,0 +1,467 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* Copyright (c) 2013, Red Hat Inc. or third-party contributors as
* indicated by the @author tags or express copyright attribution
* statements applied by the authors. All third-party contributions are
* distributed under license by Red Hat Inc.
*
* This copyrighted material is made available to anyone wishing to use, modify,
* copy, or redistribute it subject to the terms and conditions of the GNU
* Lesser General Public License, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution; if not, write to:
* Free Software Foundation, Inc.
* 51 Franklin Street, Fifth Floor
* Boston, MA 02110-1301 USA
*/
package org.hibernate.jpa.test.procedure;
import javax.persistence.EntityManager;
import javax.persistence.StoredProcedureQuery;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.dialect.DerbyTenSevenDialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.HibernateEntityManagerFactory;
import org.hibernate.jpa.boot.spi.Bootstrap;
import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor;
import org.hibernate.jpa.internal.EntityManagerFactoryImpl;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.hibernate.testing.FailureExpected;
import org.hibernate.testing.junit4.BaseUnitTestCase;
import static org.hibernate.testing.junit4.ExtraAssertions.assertTyping;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* Tests various JPA usage scenarios for performing stored procedures. Inspired by the awesomely well-done JPA TCK
*
* @author Steve Ebersole
*/
public class JpaTckUsageTest extends BaseUnitTestCase {
@Test
public void testMultipleGetUpdateCountCalls() {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser" );
// this is what the TCK attempts to do, don't shoot the messenger...
query.getUpdateCount();
// yep, twice
query.getUpdateCount();
}
finally {
em.getTransaction().commit();
em.close();
}
}
@Test
public void testBasicScalarResults() {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser" );
boolean isResult = query.execute();
assertTrue( isResult );
int updateCount = query.getUpdateCount();
boolean results = false;
do {
List list = query.getResultList();
assertEquals( 1, list.size() );
results = query.hasMoreResults();
// and it only sets the updateCount once lol
} while ( results || updateCount != -1);
}
finally {
em.getTransaction().commit();
em.close();
}
}
@Test
@FailureExpected( jiraKey = "HHH-8416", message = "JPA TCK challenge" )
public void testHasMoreResultsHandlingTckChallenge() {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser", User.class );
assertTrue( query.execute() );
assertTrue( query.hasMoreResults() );
query.getResultList();
assertFalse( query.hasMoreResults() );
}
finally {
em.getTransaction().commit();
em.close();
}
}
@Test
public void testHasMoreResultsHandling() {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser", User.class );
assertTrue( query.execute() );
query.getResultList();
assertFalse( query.hasMoreResults() );
}
finally {
em.getTransaction().commit();
em.close();
}
}
@Test
public void testResultClassHandling() {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
StoredProcedureQuery query = em.createStoredProcedureQuery( "findOneUser", User.class );
boolean isResult = query.execute();
assertTrue( isResult );
int updateCount = query.getUpdateCount();
boolean results = false;
do {
List list = query.getResultList();
assertEquals( 1, list.size() );
assertTyping( User.class, list.get( 0 ) );
results = query.hasMoreResults();
// and it only sets the updateCount once lol
} while ( results || updateCount != -1);
}
finally {
em.getTransaction().commit();
em.close();
}
}
@Test
public void testSettingInParamDefinedOnNamedStoredProcedureQuery() {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
StoredProcedureQuery query = em.createNamedStoredProcedureQuery( "positional-param" );
query.setParameter( 1, 1 );
}
finally {
em.getTransaction().commit();
em.close();
}
}
@Test
public void testSettingNonExistingParams() {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
// non-existing positional param
try {
StoredProcedureQuery query = em.createNamedStoredProcedureQuery( "positional-param" );
query.setParameter( 99, 1 );
fail( "Expecting an exception" );
}
catch (IllegalArgumentException expected) {
// this is the expected condition
}
// non-existing named param
try {
StoredProcedureQuery query = em.createNamedStoredProcedureQuery( "positional-param" );
query.setParameter( "does-not-exist", 1 );
fail( "Expecting an exception" );
}
catch (IllegalArgumentException expected) {
// this is the expected condition
}
}
finally {
em.getTransaction().commit();
em.close();
}
}
@Test
@FailureExpected( jiraKey = "HHH-8395", message = "Out of the frying pan into the fire: https://issues.apache.org/jira/browse/DERBY-211" )
public void testExecuteUpdate() {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
try {
StoredProcedureQuery query = em.createStoredProcedureQuery( "deleteAllUsers" );
int count = query.executeUpdate();
// this fails because the Derby EmbeddedDriver is returning zero here rather than the actual updateCount :(
// https://issues.apache.org/jira/browse/DERBY-211
assertEquals( 1, count );
}
finally {
em.getTransaction().commit();
em.close();
}
}
public void testParameterRegistration() {
}
// todo : look at ways to allow "Auxiliary DB Objects" to the db via EMF bootstrapping.
// public static final String findOneUser_CREATE_CMD = "CREATE ALIAS findOneUser AS $$\n" +
// "import org.h2.tools.SimpleResultSet;\n" +
// "import java.sql.*;\n" +
// "@CODE\n" +
// "ResultSet findOneUser() {\n" +
// " SimpleResultSet rs = new SimpleResultSet();\n" +
// " rs.addColumn(\"ID\", Types.INTEGER, 10, 0);\n" +
// " rs.addColumn(\"NAME\", Types.VARCHAR, 255, 0);\n" +
// " rs.addRow(1, \"Steve\");\n" +
// " return rs;\n" +
// "}\n" +
// "$$";
// public static final String findOneUser_DROP_CMD = "DROP ALIAS findOneUser IF EXISTS";
//
// public static final String deleteAllUsers_CREATE_CMD = "CREATE ALIAS deleteAllUsers AS $$\n" +
// "@CODE\n" +
// "int deleteAllUsers() {\n" +
// " return 156;" +
// "}\n" +
// "$$";
// public static final String deleteAllUsers_DROP_CMD = "DROP ALIAS deleteAllUsers IF EXISTS";
HibernateEntityManagerFactory entityManagerFactory;
@Before
public void startUp() {
// create the EMF
entityManagerFactory = (EntityManagerFactoryImpl) Bootstrap.getEntityManagerFactoryBuilder(
buildPersistenceUnitDescriptor(),
buildSettingsMap()
).build();
// create the procedures
createTestUser( entityManagerFactory );
createProcedures( entityManagerFactory );
}
private PersistenceUnitDescriptor buildPersistenceUnitDescriptor() {
return new BaseEntityManagerFunctionalTestCase.TestingPersistenceUnitDescriptorImpl( getClass().getSimpleName() );
}
@SuppressWarnings("unchecked")
private Map buildSettingsMap() {
Map settings = new HashMap();
settings.put( AvailableSettings.LOADED_CLASSES, Collections.singletonList( User.class ) );
settings.put( org.hibernate.cfg.AvailableSettings.DIALECT, DerbyTenSevenDialect.class );
settings.put( org.hibernate.cfg.AvailableSettings.DRIVER, org.apache.derby.jdbc.EmbeddedDriver.class.getName() );
// settings.put( org.hibernate.cfg.AvailableSettings.URL, "jdbc:derby:/tmp/hibernate-orm-testing;create=true" );
settings.put( org.hibernate.cfg.AvailableSettings.URL, "jdbc:derby:memory:hibernate-orm-testing;create=true" );
settings.put( org.hibernate.cfg.AvailableSettings.USER, "" );
settings.put( org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO, "create-drop" );
settings.put( org.hibernate.cfg.AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "true" );
settings.put( org.hibernate.cfg.AvailableSettings.DIALECT, DerbyTenSevenDialect.class.getName() );
return settings;
}
@After
public void tearDown() {
if ( entityManagerFactory == null ) {
return;
}
deleteTestUser( entityManagerFactory );
dropProcedures( entityManagerFactory );
entityManagerFactory.close();
}
private void createProcedures(HibernateEntityManagerFactory emf) {
final SessionFactoryImplementor sf = emf.unwrap( SessionFactoryImplementor.class );
final Connection conn;
try {
conn = sf.getConnectionProvider().getConnection();
conn.setAutoCommit( false );
try {
Statement statement = conn.createStatement();
// drop them, just to be sure
try {
dropProcedures( statement );
}
catch (SQLException ignore) {
}
createProcedureFindOneUser( statement );
createProcedureDeleteAllUsers( statement );
try {
statement.close();
}
catch (SQLException ignore) {
}
}
finally {
try {
conn.commit();
}
catch (SQLException e) {
System.out.println( "Unable to commit transaction after creating creating procedures");
}
try {
sf.getConnectionProvider().closeConnection( conn );
}
catch (SQLException ignore) {
}
}
}
catch (SQLException e) {
throw new RuntimeException( "Unable to create stored procedures", e );
}
}
private void dropProcedures(Statement statement) throws SQLException {
statement.execute( "DROP PROCEDURE findOneUser" );
statement.execute( "DROP PROCEDURE deleteAllUsers" );
}
private void createProcedureFindOneUser(Statement statement) throws SQLException {
statement.execute(
"CREATE PROCEDURE findOneUser() " +
"language java " +
"dynamic result sets 1 " +
"external name 'org.hibernate.jpa.test.procedure.JpaTckUsageTest.findOneUser' " +
"parameter style java"
);
}
private void createProcedureDeleteAllUsers(Statement statement) throws SQLException {
statement.execute(
"CREATE PROCEDURE deleteAllUsers() " +
"language java " +
"external name 'org.hibernate.jpa.test.procedure.JpaTckUsageTest.deleteAllUsers' " +
"parameter style java"
);
}
public static void findOneUser(ResultSet[] results) throws SQLException {
Connection conn = DriverManager.getConnection( "jdbc:default:connection" );
PreparedStatement ps = conn.prepareStatement( "select id, name from t_user where name=?" );
ps.setString( 1, "steve" );
results[0] = ps.executeQuery();
conn.close();
}
public static void findUserIds(ResultSet[] results) throws SQLException {
Connection conn = DriverManager.getConnection( "jdbc:default:connection" );
PreparedStatement ps = conn.prepareStatement( "select id from t_user" );
results[0] = ps.executeQuery();
conn.close();
}
public static void deleteAllUsers() throws SQLException {
// afaict the only way to return update counts here is to actually perform some DML
Connection conn = DriverManager.getConnection( "jdbc:default:connection" );
System.out.println( "Preparing delete all" );
PreparedStatement ps = conn.prepareStatement( "delete from t_user" );
System.out.println( "Executing delete all" );
int count = ps.executeUpdate();
System.out.println( "Count : " + count );
System.out.println( "Closing resources" );
ps.close();
conn.close();
}
private void createTestUser(HibernateEntityManagerFactory entityManagerFactory) {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
em.persist( new User( 1, "steve" ) );
em.getTransaction().commit();
em.close();
}
private void deleteTestUser(HibernateEntityManagerFactory entityManagerFactory) {
EntityManager em = entityManagerFactory.createEntityManager();
em.getTransaction().begin();
em.createQuery( "delete from User" ).executeUpdate();
em.getTransaction().commit();
em.close();
}
private void dropProcedures(HibernateEntityManagerFactory emf) {
final SessionFactoryImplementor sf = emf.unwrap( SessionFactoryImplementor.class );
final Connection conn;
try {
conn = sf.getConnectionProvider().getConnection();
conn.setAutoCommit( false );
try {
Statement statement = conn.createStatement();
dropProcedures( statement );
try {
statement.close();
}
catch (SQLException ignore) {
}
}
finally {
try {
conn.commit();
}
catch (SQLException e) {
System.out.println( "Unable to commit transaction after creating dropping procedures");
}
try {
sf.getConnectionProvider().closeConnection( conn );
}
catch (SQLException ignore) {
}
}
}
catch (SQLException e) {
throw new RuntimeException( "Unable to drop stored procedures", e );
}
}
}

View File

@ -9,6 +9,7 @@ import javax.persistence.NamedStoredProcedureQuery;
import javax.persistence.ParameterMode; import javax.persistence.ParameterMode;
import javax.persistence.SqlResultSetMapping; import javax.persistence.SqlResultSetMapping;
import javax.persistence.StoredProcedureParameter; import javax.persistence.StoredProcedureParameter;
import javax.persistence.Table;
/** /**
* @author Strong Liu <stliu@hibernate.org> * @author Strong Liu <stliu@hibernate.org>
@ -43,6 +44,13 @@ import javax.persistence.StoredProcedureParameter;
}, },
resultSetMappings = { "srms" } resultSetMappings = { "srms" }
),
@NamedStoredProcedureQuery(
name = "positional-param",
procedureName = "positionalParameterTesting",
parameters = {
@StoredProcedureParameter( mode = ParameterMode.IN, type = Integer.class )
}
) )
} }
) )
@ -54,11 +62,20 @@ import javax.persistence.StoredProcedureParameter;
}) })
} }
) )
@Table( name = "T_USER" )
public class User { public class User {
@Id @Id
private int id; private int id;
private String name; private String name;
public User() {
}
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() { public int getId() {
return id; return id;
} }

View File

@ -21,6 +21,7 @@
# 51 Franklin Street, Fifth Floor # 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301 USA # Boston, MA 02110-1301 USA
# #
hibernate.dialect org.hibernate.dialect.H2Dialect hibernate.dialect org.hibernate.dialect.H2Dialect
hibernate.connection.driver_class org.h2.Driver hibernate.connection.driver_class org.h2.Driver
hibernate.connection.url jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE;LOCK_TIMEOUT=10000 hibernate.connection.url jdbc:h2:mem:db1;DB_CLOSE_DELAY=-1;MVCC=TRUE;LOCK_TIMEOUT=10000

View File

@ -125,4 +125,10 @@ public interface EnversSettings {
* Name of column used for storing ordinal of the change in sets of embeddable elements. Defaults to {@literal SETORDINAL}. * Name of column used for storing ordinal of the change in sets of embeddable elements. Defaults to {@literal SETORDINAL}.
*/ */
public static final String EMBEDDABLE_SET_ORDINAL_FIELD_NAME = "org.hibernate.envers.embeddable_set_ordinal_field_name"; public static final String EMBEDDABLE_SET_ORDINAL_FIELD_NAME = "org.hibernate.envers.embeddable_set_ordinal_field_name";
/**
* Guarantees proper validity audit strategy behavior when application reuses identifiers of deleted entities.
* Exactly one row with {@code null} end date exists for each identifier.
*/
public static final String ALLOW_IDENTIFIER_REUSE = "org.hibernate.envers.allow_identifier_reuse";
} }

View File

@ -75,6 +75,9 @@ public class GlobalConfiguration {
// Use revision entity with native id generator // Use revision entity with native id generator
private final boolean useRevisionEntityWithNativeId; private final boolean useRevisionEntityWithNativeId;
// Support reused identifiers of previously deleted entities
private final boolean allowIdentifierReuse;
/* /*
Which operator to use in correlated subqueries (when we want a property to be equal to the result of Which operator to use in correlated subqueries (when we want a property to be equal to the result of
a correlated subquery, for example: e.p <operator> (select max(e2.p) where e2.p2 = e.p2 ...). a correlated subquery, for example: e.p <operator> (select max(e2.p) where e2.p2 = e.p2 ...).
@ -131,6 +134,10 @@ public class GlobalConfiguration {
else { else {
revisionListenerClass = null; revisionListenerClass = null;
} }
allowIdentifierReuse = ConfigurationHelper.getBoolean(
EnversSettings.ALLOW_IDENTIFIER_REUSE, properties, false
);
} }
public boolean isGenerateRevisionsForCollections() { public boolean isGenerateRevisionsForCollections() {
@ -184,4 +191,8 @@ public class GlobalConfiguration {
public boolean isUseRevisionEntityWithNativeId() { public boolean isUseRevisionEntityWithNativeId() {
return useRevisionEntityWithNativeId; return useRevisionEntityWithNativeId;
} }
public boolean isAllowIdentifierReuse() {
return allowIdentifierReuse;
}
} }

View File

@ -73,7 +73,7 @@ public final class OneAuditEntityQueryGenerator extends AbstractRelationQueryGen
final QueryBuilder validQuery = commonPart.deepCopy(); final QueryBuilder validQuery = commonPart.deepCopy();
final QueryBuilder removedQuery = commonPart.deepCopy(); final QueryBuilder removedQuery = commonPart.deepCopy();
createValidDataRestrictions( createValidDataRestrictions(
globalCfg, auditStrategy, referencedIdData, validQuery, validQuery.getRootParameters(), true globalCfg, auditStrategy, referencedIdData, validQuery, validQuery.getRootParameters()
); );
createValidAndRemovedDataRestrictions( globalCfg, auditStrategy, referencedIdData, removedQuery ); createValidAndRemovedDataRestrictions( globalCfg, auditStrategy, referencedIdData, removedQuery );
@ -99,8 +99,7 @@ public final class OneAuditEntityQueryGenerator extends AbstractRelationQueryGen
*/ */
private void createValidDataRestrictions( private void createValidDataRestrictions(
GlobalConfiguration globalCfg, AuditStrategy auditStrategy, GlobalConfiguration globalCfg, AuditStrategy auditStrategy,
MiddleIdData referencedIdData, QueryBuilder qb, Parameters rootParameters, MiddleIdData referencedIdData, QueryBuilder qb, Parameters rootParameters) {
boolean inclusive) {
final String revisionPropertyPath = verEntCfg.getRevisionNumberPath(); final String revisionPropertyPath = verEntCfg.getRevisionNumberPath();
// (selecting e entities at revision :revision) // (selecting e entities at revision :revision)
// --> based on auditStrategy (see above) // --> based on auditStrategy (see above)
@ -108,7 +107,7 @@ public final class OneAuditEntityQueryGenerator extends AbstractRelationQueryGen
globalCfg, qb, rootParameters, revisionPropertyPath, globalCfg, qb, rootParameters, revisionPropertyPath,
verEntCfg.getRevisionEndFieldName(), true, referencedIdData, revisionPropertyPath, verEntCfg.getRevisionEndFieldName(), true, referencedIdData, revisionPropertyPath,
verEntCfg.getOriginalIdPropName(), REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR, verEntCfg.getOriginalIdPropName(), REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR,
inclusive true
); );
// e.revision_type != DEL // e.revision_type != DEL
rootParameters.addWhereWithNamedParam( getRevisionTypePath(), false, "!=", DEL_REVISION_TYPE_PARAMETER ); rootParameters.addWhereWithNamedParam( getRevisionTypePath(), false, "!=", DEL_REVISION_TYPE_PARAMETER );
@ -126,7 +125,7 @@ public final class OneAuditEntityQueryGenerator extends AbstractRelationQueryGen
// Restrictions to match all rows deleted at exactly given revision. // Restrictions to match all rows deleted at exactly given revision.
final Parameters removed = disjoint.addSubParameters( "and" ); final Parameters removed = disjoint.addSubParameters( "and" );
// Excluding current revision, because we need to match data valid at the previous one. // Excluding current revision, because we need to match data valid at the previous one.
createValidDataRestrictions( globalCfg, auditStrategy, referencedIdData, remQb, valid, false ); createValidDataRestrictions( globalCfg, auditStrategy, referencedIdData, remQb, valid );
// e.revision = :revision // e.revision = :revision
removed.addWhereWithNamedParam( verEntCfg.getRevisionNumberPath(), false, "=", REVISION_PARAMETER ); removed.addWhereWithNamedParam( verEntCfg.getRevisionNumberPath(), false, "=", REVISION_PARAMETER );
// e.revision_type = DEL // e.revision_type = DEL

View File

@ -180,7 +180,7 @@ public final class ThreeEntityQueryGenerator extends AbstractRelationQueryGenera
originalIdPropertyName, originalIdPropertyName,
REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS,
REFERENCED_ENTITY_ALIAS_DEF_AUD_STR, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR,
inclusive true
); );
// (selecting f entities at revision :revision) // (selecting f entities at revision :revision)
// --> based on auditStrategy (see above) // --> based on auditStrategy (see above)
@ -188,15 +188,15 @@ public final class ThreeEntityQueryGenerator extends AbstractRelationQueryGenera
globalCfg, globalCfg,
qb, qb,
rootParameters, rootParameters,
REFERENCED_ENTITY_ALIAS + "." + revisionPropertyPath, INDEX_ENTITY_ALIAS + "." + revisionPropertyPath,
REFERENCED_ENTITY_ALIAS + "." + verEntCfg.getRevisionEndFieldName(), INDEX_ENTITY_ALIAS + "." + verEntCfg.getRevisionEndFieldName(),
false, false,
referencedIdData, referencedIdData,
revisionPropertyPath, revisionPropertyPath,
originalIdPropertyName, originalIdPropertyName,
INDEX_ENTITY_ALIAS, INDEX_ENTITY_ALIAS,
INDEX_ENTITY_ALIAS_DEF_AUD_STR, INDEX_ENTITY_ALIAS_DEF_AUD_STR,
inclusive true
); );
// (with ee association at revision :revision) // (with ee association at revision :revision)
// --> based on auditStrategy (see above) // --> based on auditStrategy (see above)

View File

@ -152,7 +152,7 @@ public final class TwoEntityQueryGenerator extends AbstractRelationQueryGenerato
originalIdPropertyName, originalIdPropertyName,
REFERENCED_ENTITY_ALIAS, REFERENCED_ENTITY_ALIAS,
REFERENCED_ENTITY_ALIAS_DEF_AUD_STR, REFERENCED_ENTITY_ALIAS_DEF_AUD_STR,
inclusive true
); );
// (with ee association at revision :revision) // (with ee association at revision :revision)
// --> based on auditStrategy (see above) // --> based on auditStrategy (see above)

View File

@ -98,8 +98,15 @@ public class ValidityAuditStrategy implements AuditStrategy {
session.save( auditedEntityName, data ); session.save( auditedEntityName, data );
sessionCacheCleaner.scheduleAuditDataRemoval( session, data ); sessionCacheCleaner.scheduleAuditDataRemoval( session, data );
// Update the end date of the previous row if this operation is expected to have a previous row // Update the end date of the previous row.
if ( getRevisionType( auditCfg, data ) != RevisionType.ADD ) { //
// When application reuses identifiers of previously removed entities:
// The UPDATE statement will no-op if an entity with a given identifier has been
// inserted for the first time. But in case a deleted primary key value was
// reused, this guarantees correct strategy behavior: exactly one row with
// null end date exists for each identifier.
final boolean reuseEntityIdentifier = auditCfg.getGlobalCfg().isAllowIdentifierReuse();
if ( reuseEntityIdentifier || getRevisionType( auditCfg, data ) != RevisionType.ADD ) {
final Queryable productionEntityQueryable = getQueryable( entityName, sessionImplementor ); final Queryable productionEntityQueryable = getQueryable( entityName, sessionImplementor );
final Queryable rootProductionEntityQueryable = getQueryable( final Queryable rootProductionEntityQueryable = getQueryable(
productionEntityQueryable.getRootEntityName(), productionEntityQueryable.getRootEntityName(),
@ -238,7 +245,7 @@ public class ValidityAuditStrategy implements AuditStrategy {
} }
); );
if ( rowCount != 1 ) { if ( rowCount != 1 && ( !reuseEntityIdentifier || ( getRevisionType( auditCfg, data ) != RevisionType.ADD ) ) ) {
throw new RuntimeException( throw new RuntimeException(
"Cannot update previous revision for entity " + auditedEntityName + " and id " + id "Cannot update previous revision for entity " + auditedEntityName + " and id " + id
); );

View File

@ -58,9 +58,9 @@ public class CollectionRefIngEntity implements Serializable {
this.reference = reference; this.reference = reference;
} }
public CollectionRefIngEntity(String data, CollectionRefEdEntity reference) { public CollectionRefIngEntity(Integer id, String data) {
this.id = id;
this.data = data; this.data = data;
this.reference = reference;
} }
public Integer getId() { public Integer getId() {

View File

@ -106,7 +106,6 @@ public class NotOwnedBidirectional extends BaseEnversJPAFunctionalTestCase {
@Test @Test
public void testHistoryOfPersonalContact() { public void testHistoryOfPersonalContact() {
System.out.println( getAuditReader().find( PersonalContact.class, pc_id, 1 ).getAddresses() );
assert getAuditReader().find( PersonalContact.class, pc_id, 1 ).getAddresses().equals( assert getAuditReader().find( PersonalContact.class, pc_id, 1 ).getAddresses().equals(
TestTools.makeSet( new Address( a1_id, "a1" ) ) TestTools.makeSet( new Address( a1_id, "a1" ) )
); );

View File

@ -106,7 +106,6 @@ public class NotOwnedBidirectional extends BaseEnversJPAFunctionalTestCase {
@Test @Test
public void testHistoryOfPersonalContact() { public void testHistoryOfPersonalContact() {
System.out.println( getAuditReader().find( PersonalContact.class, pc_id, 1 ).getAddresses() );
assert getAuditReader().find( PersonalContact.class, pc_id, 1 ).getAddresses().equals( assert getAuditReader().find( PersonalContact.class, pc_id, 1 ).getAddresses().equals(
TestTools.makeSet( new Address( a1_id, "a1" ) ) TestTools.makeSet( new Address( a1_id, "a1" ) )
); );

View File

@ -156,10 +156,10 @@ public class BasicList extends BaseEnversJPAFunctionalTestCase {
ListOwnedEntity rev5 = getAuditReader().find( ListOwnedEntity.class, ed1_id, 5 ); ListOwnedEntity rev5 = getAuditReader().find( ListOwnedEntity.class, ed1_id, 5 );
assert rev1.getReferencing().equals( Collections.EMPTY_LIST ); assert rev1.getReferencing().equals( Collections.EMPTY_LIST );
assert TestTools.checkList( rev2.getReferencing(), ing1, ing2 ); assert TestTools.checkCollection( rev2.getReferencing(), ing1, ing2 );
assert TestTools.checkList( rev3.getReferencing(), ing1, ing2 ); assert TestTools.checkCollection( rev3.getReferencing(), ing1, ing2 );
assert TestTools.checkList( rev4.getReferencing(), ing2 ); assert TestTools.checkCollection( rev4.getReferencing(), ing2 );
assert TestTools.checkList( rev5.getReferencing(), ing2 ); assert TestTools.checkCollection( rev5.getReferencing(), ing2 );
} }
@Test @Test
@ -174,10 +174,10 @@ public class BasicList extends BaseEnversJPAFunctionalTestCase {
ListOwnedEntity rev5 = getAuditReader().find( ListOwnedEntity.class, ed2_id, 5 ); ListOwnedEntity rev5 = getAuditReader().find( ListOwnedEntity.class, ed2_id, 5 );
assert rev1.getReferencing().equals( Collections.EMPTY_LIST ); assert rev1.getReferencing().equals( Collections.EMPTY_LIST );
assert TestTools.checkList( rev2.getReferencing(), ing2 ); assert TestTools.checkCollection( rev2.getReferencing(), ing2 );
assert TestTools.checkList( rev3.getReferencing(), ing1, ing2 ); assert TestTools.checkCollection( rev3.getReferencing(), ing1, ing2 );
assert TestTools.checkList( rev4.getReferencing(), ing1, ing2 ); assert TestTools.checkCollection( rev4.getReferencing(), ing1, ing2 );
assert TestTools.checkList( rev5.getReferencing(), ing2 ); assert TestTools.checkCollection( rev5.getReferencing(), ing2 );
} }
@Test @Test
@ -192,9 +192,9 @@ public class BasicList extends BaseEnversJPAFunctionalTestCase {
ListOwningEntity rev5 = getAuditReader().find( ListOwningEntity.class, ing1_id, 5 ); ListOwningEntity rev5 = getAuditReader().find( ListOwningEntity.class, ing1_id, 5 );
assert rev1.getReferences().equals( Collections.EMPTY_LIST ); assert rev1.getReferences().equals( Collections.EMPTY_LIST );
assert TestTools.checkList( rev2.getReferences(), ed1 ); assert TestTools.checkCollection( rev2.getReferences(), ed1 );
assert TestTools.checkList( rev3.getReferences(), ed1, ed2 ); assert TestTools.checkCollection( rev3.getReferences(), ed1, ed2 );
assert TestTools.checkList( rev4.getReferences(), ed2 ); assert TestTools.checkCollection( rev4.getReferences(), ed2 );
assert rev5.getReferences().equals( Collections.EMPTY_LIST ); assert rev5.getReferences().equals( Collections.EMPTY_LIST );
} }
@ -210,9 +210,9 @@ public class BasicList extends BaseEnversJPAFunctionalTestCase {
ListOwningEntity rev5 = getAuditReader().find( ListOwningEntity.class, ing2_id, 5 ); ListOwningEntity rev5 = getAuditReader().find( ListOwningEntity.class, ing2_id, 5 );
assert rev1.getReferences().equals( Collections.EMPTY_LIST ); assert rev1.getReferences().equals( Collections.EMPTY_LIST );
assert TestTools.checkList( rev2.getReferences(), ed1, ed2 ); assert TestTools.checkCollection( rev2.getReferences(), ed1, ed2 );
assert TestTools.checkList( rev3.getReferences(), ed1, ed2 ); assert TestTools.checkCollection( rev3.getReferences(), ed1, ed2 );
assert TestTools.checkList( rev4.getReferences(), ed1, ed2 ); assert TestTools.checkCollection( rev4.getReferences(), ed1, ed2 );
assert TestTools.checkList( rev5.getReferences(), ed1, ed2 ); assert TestTools.checkCollection( rev5.getReferences(), ed1, ed2 );
} }
} }

View File

@ -150,16 +150,16 @@ public class BasicWhereJoinTable extends BaseEnversJPAFunctionalTestCase {
WhereJoinTableEntity rev4 = getAuditReader().find( WhereJoinTableEntity.class, wjte1_id, 4 ); WhereJoinTableEntity rev4 = getAuditReader().find( WhereJoinTableEntity.class, wjte1_id, 4 );
// Checking 1st list // Checking 1st list
assert TestTools.checkList( rev1.getReferences1() ); assert TestTools.checkCollection( rev1.getReferences1() );
assert TestTools.checkList( rev2.getReferences1(), ite1_1 ); assert TestTools.checkCollection( rev2.getReferences1(), ite1_1 );
assert TestTools.checkList( rev3.getReferences1(), ite1_1 ); assert TestTools.checkCollection( rev3.getReferences1(), ite1_1 );
assert TestTools.checkList( rev4.getReferences1() ); assert TestTools.checkCollection( rev4.getReferences1() );
// Checking 2nd list // Checking 2nd list
assert TestTools.checkList( rev1.getReferences2() ); assert TestTools.checkCollection( rev1.getReferences2() );
assert TestTools.checkList( rev2.getReferences2(), ite2_1 ); assert TestTools.checkCollection( rev2.getReferences2(), ite2_1 );
assert TestTools.checkList( rev3.getReferences2(), ite2_1 ); assert TestTools.checkCollection( rev3.getReferences2(), ite2_1 );
assert TestTools.checkList( rev4.getReferences2(), ite2_1 ); assert TestTools.checkCollection( rev4.getReferences2(), ite2_1 );
} }
@Test @Test
@ -174,15 +174,15 @@ public class BasicWhereJoinTable extends BaseEnversJPAFunctionalTestCase {
WhereJoinTableEntity rev4 = getAuditReader().find( WhereJoinTableEntity.class, wjte2_id, 4 ); WhereJoinTableEntity rev4 = getAuditReader().find( WhereJoinTableEntity.class, wjte2_id, 4 );
// Checking 1st list // Checking 1st list
assert TestTools.checkList( rev1.getReferences1() ); assert TestTools.checkCollection( rev1.getReferences1() );
assert TestTools.checkList( rev2.getReferences1() ); assert TestTools.checkCollection( rev2.getReferences1() );
assert TestTools.checkList( rev3.getReferences1(), ite1_1, ite1_2 ); assert TestTools.checkCollection( rev3.getReferences1(), ite1_1, ite1_2 );
assert TestTools.checkList( rev4.getReferences1(), ite1_1, ite1_2 ); assert TestTools.checkCollection( rev4.getReferences1(), ite1_1, ite1_2 );
// Checking 2nd list // Checking 2nd list
assert TestTools.checkList( rev1.getReferences2() ); assert TestTools.checkCollection( rev1.getReferences2() );
assert TestTools.checkList( rev2.getReferences2() ); assert TestTools.checkCollection( rev2.getReferences2() );
assert TestTools.checkList( rev3.getReferences2() ); assert TestTools.checkCollection( rev3.getReferences2() );
assert TestTools.checkList( rev4.getReferences2(), ite2_2 ); assert TestTools.checkCollection( rev4.getReferences2(), ite2_2 );
} }
} }

View File

@ -163,11 +163,11 @@ public class BasicBiowned extends BaseEnversJPAFunctionalTestCase {
ListBiowning1Entity rev4 = getAuditReader().find( ListBiowning1Entity.class, o1_1_id, 4 ); ListBiowning1Entity rev4 = getAuditReader().find( ListBiowning1Entity.class, o1_1_id, 4 );
ListBiowning1Entity rev5 = getAuditReader().find( ListBiowning1Entity.class, o1_1_id, 5 ); ListBiowning1Entity rev5 = getAuditReader().find( ListBiowning1Entity.class, o1_1_id, 5 );
assert TestTools.checkList( rev1.getReferences() ); assert TestTools.checkCollection( rev1.getReferences() );
assert TestTools.checkList( rev2.getReferences(), o2_1 ); assert TestTools.checkCollection( rev2.getReferences(), o2_1 );
assert TestTools.checkList( rev3.getReferences(), o2_1, o2_2 ); assert TestTools.checkCollection( rev3.getReferences(), o2_1, o2_2 );
assert TestTools.checkList( rev4.getReferences() ); assert TestTools.checkCollection( rev4.getReferences() );
assert TestTools.checkList( rev5.getReferences(), o2_2 ); assert TestTools.checkCollection( rev5.getReferences(), o2_2 );
} }
@Test @Test
@ -181,12 +181,11 @@ public class BasicBiowned extends BaseEnversJPAFunctionalTestCase {
ListBiowning1Entity rev4 = getAuditReader().find( ListBiowning1Entity.class, o1_2_id, 4 ); ListBiowning1Entity rev4 = getAuditReader().find( ListBiowning1Entity.class, o1_2_id, 4 );
ListBiowning1Entity rev5 = getAuditReader().find( ListBiowning1Entity.class, o1_2_id, 5 ); ListBiowning1Entity rev5 = getAuditReader().find( ListBiowning1Entity.class, o1_2_id, 5 );
assert TestTools.checkList( rev1.getReferences() ); assert TestTools.checkCollection( rev1.getReferences() );
assert TestTools.checkList( rev2.getReferences(), o2_2 ); assert TestTools.checkCollection( rev2.getReferences(), o2_2 );
assert TestTools.checkList( rev3.getReferences(), o2_2 ); assert TestTools.checkCollection( rev3.getReferences(), o2_2 );
assert TestTools.checkList( rev4.getReferences(), o2_1, o2_2 ); assert TestTools.checkCollection( rev4.getReferences(), o2_1, o2_2 );
System.out.println( "rev5.getReferences() = " + rev5.getReferences() ); assert TestTools.checkCollection( rev5.getReferences(), o2_2 );
assert TestTools.checkList( rev5.getReferences(), o2_2 );
} }
@Test @Test
@ -200,11 +199,11 @@ public class BasicBiowned extends BaseEnversJPAFunctionalTestCase {
ListBiowning2Entity rev4 = getAuditReader().find( ListBiowning2Entity.class, o2_1_id, 4 ); ListBiowning2Entity rev4 = getAuditReader().find( ListBiowning2Entity.class, o2_1_id, 4 );
ListBiowning2Entity rev5 = getAuditReader().find( ListBiowning2Entity.class, o2_1_id, 5 ); ListBiowning2Entity rev5 = getAuditReader().find( ListBiowning2Entity.class, o2_1_id, 5 );
assert TestTools.checkList( rev1.getReferences() ); assert TestTools.checkCollection( rev1.getReferences() );
assert TestTools.checkList( rev2.getReferences(), o1_1 ); assert TestTools.checkCollection( rev2.getReferences(), o1_1 );
assert TestTools.checkList( rev3.getReferences(), o1_1 ); assert TestTools.checkCollection( rev3.getReferences(), o1_1 );
assert TestTools.checkList( rev4.getReferences(), o1_2 ); assert TestTools.checkCollection( rev4.getReferences(), o1_2 );
assert TestTools.checkList( rev5.getReferences() ); assert TestTools.checkCollection( rev5.getReferences() );
} }
@Test @Test
@ -218,10 +217,10 @@ public class BasicBiowned extends BaseEnversJPAFunctionalTestCase {
ListBiowning2Entity rev4 = getAuditReader().find( ListBiowning2Entity.class, o2_2_id, 4 ); ListBiowning2Entity rev4 = getAuditReader().find( ListBiowning2Entity.class, o2_2_id, 4 );
ListBiowning2Entity rev5 = getAuditReader().find( ListBiowning2Entity.class, o2_2_id, 5 ); ListBiowning2Entity rev5 = getAuditReader().find( ListBiowning2Entity.class, o2_2_id, 5 );
assert TestTools.checkList( rev1.getReferences() ); assert TestTools.checkCollection( rev1.getReferences() );
assert TestTools.checkList( rev2.getReferences(), o1_2 ); assert TestTools.checkCollection( rev2.getReferences(), o1_2 );
assert TestTools.checkList( rev3.getReferences(), o1_1, o1_2 ); assert TestTools.checkCollection( rev3.getReferences(), o1_1, o1_2 );
assert TestTools.checkList( rev4.getReferences(), o1_2 ); assert TestTools.checkCollection( rev4.getReferences(), o1_2 );
assert TestTools.checkList( rev5.getReferences(), o1_1, o1_2 ); assert TestTools.checkCollection( rev5.getReferences(), o1_1, o1_2 );
} }
} }

View File

@ -199,17 +199,17 @@ public class BasicSametable extends BaseEnversJPAFunctionalTestCase {
ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p1_id, 4 ); ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p1_id, 4 );
ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p1_id, 5 ); ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p1_id, 5 );
assert TestTools.checkList( rev1.getChildren1() ); assert TestTools.checkCollection( rev1.getChildren1() );
assert TestTools.checkList( rev2.getChildren1(), c1_1 ); assert TestTools.checkCollection( rev2.getChildren1(), c1_1 );
assert TestTools.checkList( rev3.getChildren1(), c1_1, c1_2 ); assert TestTools.checkCollection( rev3.getChildren1(), c1_1, c1_2 );
assert TestTools.checkList( rev4.getChildren1(), c1_2 ); assert TestTools.checkCollection( rev4.getChildren1(), c1_2 );
assert TestTools.checkList( rev5.getChildren1() ); assert TestTools.checkCollection( rev5.getChildren1() );
assert TestTools.checkList( rev1.getChildren2() ); assert TestTools.checkCollection( rev1.getChildren2() );
assert TestTools.checkList( rev2.getChildren2() ); assert TestTools.checkCollection( rev2.getChildren2() );
assert TestTools.checkList( rev3.getChildren2(), c2_2 ); assert TestTools.checkCollection( rev3.getChildren2(), c2_2 );
assert TestTools.checkList( rev4.getChildren2(), c2_2 ); assert TestTools.checkCollection( rev4.getChildren2(), c2_2 );
assert TestTools.checkList( rev5.getChildren2(), c2_2 ); assert TestTools.checkCollection( rev5.getChildren2(), c2_2 );
} }
@Test @Test
@ -224,17 +224,17 @@ public class BasicSametable extends BaseEnversJPAFunctionalTestCase {
ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p2_id, 4 ); ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p2_id, 4 );
ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p2_id, 5 ); ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p2_id, 5 );
assert TestTools.checkList( rev1.getChildren1() ); assert TestTools.checkCollection( rev1.getChildren1() );
assert TestTools.checkList( rev2.getChildren1() ); assert TestTools.checkCollection( rev2.getChildren1() );
assert TestTools.checkList( rev3.getChildren1(), c1_1 ); assert TestTools.checkCollection( rev3.getChildren1(), c1_1 );
assert TestTools.checkList( rev4.getChildren1(), c1_1 ); assert TestTools.checkCollection( rev4.getChildren1(), c1_1 );
assert TestTools.checkList( rev5.getChildren1(), c1_1 ); assert TestTools.checkCollection( rev5.getChildren1(), c1_1 );
assert TestTools.checkList( rev1.getChildren2() ); assert TestTools.checkCollection( rev1.getChildren2() );
assert TestTools.checkList( rev2.getChildren2(), c2_1 ); assert TestTools.checkCollection( rev2.getChildren2(), c2_1 );
assert TestTools.checkList( rev3.getChildren2(), c2_1 ); assert TestTools.checkCollection( rev3.getChildren2(), c2_1 );
assert TestTools.checkList( rev4.getChildren2(), c2_1, c2_2 ); assert TestTools.checkCollection( rev4.getChildren2(), c2_1, c2_2 );
assert TestTools.checkList( rev5.getChildren2(), c2_1 ); assert TestTools.checkCollection( rev5.getChildren2(), c2_1 );
} }
@Test @Test
@ -248,11 +248,11 @@ public class BasicSametable extends BaseEnversJPAFunctionalTestCase {
Child1Entity rev4 = getAuditReader().find( Child1Entity.class, c1_1_id, 4 ); Child1Entity rev4 = getAuditReader().find( Child1Entity.class, c1_1_id, 4 );
Child1Entity rev5 = getAuditReader().find( Child1Entity.class, c1_1_id, 5 ); Child1Entity rev5 = getAuditReader().find( Child1Entity.class, c1_1_id, 5 );
assert TestTools.checkList( rev1.getParents() ); assert TestTools.checkCollection( rev1.getParents() );
assert TestTools.checkList( rev2.getParents(), p1 ); assert TestTools.checkCollection( rev2.getParents(), p1 );
assert TestTools.checkList( rev3.getParents(), p1, p2 ); assert TestTools.checkCollection( rev3.getParents(), p1, p2 );
assert TestTools.checkList( rev4.getParents(), p2 ); assert TestTools.checkCollection( rev4.getParents(), p2 );
assert TestTools.checkList( rev5.getParents(), p2 ); assert TestTools.checkCollection( rev5.getParents(), p2 );
} }
// TODO: was disabled? // TODO: was disabled?
@ -266,11 +266,11 @@ public class BasicSametable extends BaseEnversJPAFunctionalTestCase {
Child1Entity rev4 = getAuditReader().find( Child1Entity.class, c1_2_id, 4 ); Child1Entity rev4 = getAuditReader().find( Child1Entity.class, c1_2_id, 4 );
Child1Entity rev5 = getAuditReader().find( Child1Entity.class, c1_2_id, 5 ); Child1Entity rev5 = getAuditReader().find( Child1Entity.class, c1_2_id, 5 );
assert TestTools.checkList( rev1.getParents() ); assert TestTools.checkCollection( rev1.getParents() );
assert TestTools.checkList( rev2.getParents() ); assert TestTools.checkCollection( rev2.getParents() );
assert TestTools.checkList( rev3.getParents(), p1 ); assert TestTools.checkCollection( rev3.getParents(), p1 );
assert TestTools.checkList( rev4.getParents(), p1 ); assert TestTools.checkCollection( rev4.getParents(), p1 );
assert TestTools.checkList( rev5.getParents() ); assert TestTools.checkCollection( rev5.getParents() );
} }
@Test @Test
@ -283,11 +283,11 @@ public class BasicSametable extends BaseEnversJPAFunctionalTestCase {
Child2Entity rev4 = getAuditReader().find( Child2Entity.class, c2_1_id, 4 ); Child2Entity rev4 = getAuditReader().find( Child2Entity.class, c2_1_id, 4 );
Child2Entity rev5 = getAuditReader().find( Child2Entity.class, c2_1_id, 5 ); Child2Entity rev5 = getAuditReader().find( Child2Entity.class, c2_1_id, 5 );
assert TestTools.checkList( rev1.getParents() ); assert TestTools.checkCollection( rev1.getParents() );
assert TestTools.checkList( rev2.getParents(), p2 ); assert TestTools.checkCollection( rev2.getParents(), p2 );
assert TestTools.checkList( rev3.getParents(), p2 ); assert TestTools.checkCollection( rev3.getParents(), p2 );
assert TestTools.checkList( rev4.getParents(), p2 ); assert TestTools.checkCollection( rev4.getParents(), p2 );
assert TestTools.checkList( rev5.getParents(), p2 ); assert TestTools.checkCollection( rev5.getParents(), p2 );
} }
@Test @Test
@ -301,10 +301,10 @@ public class BasicSametable extends BaseEnversJPAFunctionalTestCase {
Child2Entity rev4 = getAuditReader().find( Child2Entity.class, c2_2_id, 4 ); Child2Entity rev4 = getAuditReader().find( Child2Entity.class, c2_2_id, 4 );
Child2Entity rev5 = getAuditReader().find( Child2Entity.class, c2_2_id, 5 ); Child2Entity rev5 = getAuditReader().find( Child2Entity.class, c2_2_id, 5 );
assert TestTools.checkList( rev1.getParents() ); assert TestTools.checkCollection( rev1.getParents() );
assert TestTools.checkList( rev2.getParents() ); assert TestTools.checkCollection( rev2.getParents() );
assert TestTools.checkList( rev3.getParents(), p1 ); assert TestTools.checkCollection( rev3.getParents(), p1 );
assert TestTools.checkList( rev4.getParents(), p1, p2 ); assert TestTools.checkCollection( rev4.getParents(), p1, p2 );
assert TestTools.checkList( rev5.getParents(), p1 ); assert TestTools.checkCollection( rev5.getParents(), p1 );
} }
} }

View File

@ -151,9 +151,9 @@ public class BasicUniList extends BaseEnversJPAFunctionalTestCase {
ListUniEntity rev5 = getAuditReader().find( ListUniEntity.class, ing1_id, 5 ); ListUniEntity rev5 = getAuditReader().find( ListUniEntity.class, ing1_id, 5 );
assert rev1.getReferences().equals( Collections.EMPTY_LIST ); assert rev1.getReferences().equals( Collections.EMPTY_LIST );
assert TestTools.checkList( rev2.getReferences(), ed1 ); assert TestTools.checkCollection( rev2.getReferences(), ed1 );
assert TestTools.checkList( rev3.getReferences(), ed1, ed2 ); assert TestTools.checkCollection( rev3.getReferences(), ed1, ed2 );
assert TestTools.checkList( rev4.getReferences(), ed2 ); assert TestTools.checkCollection( rev4.getReferences(), ed2 );
assert rev5.getReferences().equals( Collections.EMPTY_LIST ); assert rev5.getReferences().equals( Collections.EMPTY_LIST );
} }
@ -169,9 +169,9 @@ public class BasicUniList extends BaseEnversJPAFunctionalTestCase {
ListUniEntity rev5 = getAuditReader().find( ListUniEntity.class, ing2_id, 5 ); ListUniEntity rev5 = getAuditReader().find( ListUniEntity.class, ing2_id, 5 );
assert rev1.getReferences().equals( Collections.EMPTY_LIST ); assert rev1.getReferences().equals( Collections.EMPTY_LIST );
assert TestTools.checkList( rev2.getReferences(), ed1, ed2 ); assert TestTools.checkCollection( rev2.getReferences(), ed1, ed2 );
assert TestTools.checkList( rev3.getReferences(), ed1, ed2 ); assert TestTools.checkCollection( rev3.getReferences(), ed1, ed2 );
assert TestTools.checkList( rev4.getReferences(), ed1, ed2 ); assert TestTools.checkCollection( rev4.getReferences(), ed1, ed2 );
assert TestTools.checkList( rev5.getReferences(), ed1, ed2 ); assert TestTools.checkCollection( rev5.getReferences(), ed1, ed2 );
} }
} }

View File

@ -11,7 +11,7 @@ import org.hibernate.envers.test.entities.manytomany.unidirectional.M2MIndexedLi
import org.junit.Test; import org.junit.Test;
import static org.hibernate.envers.test.tools.TestTools.checkList; import static org.hibernate.envers.test.tools.TestTools.checkCollection;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -114,9 +114,9 @@ public class M2MIndexedListNotAuditedTarget extends BaseEnversJPAFunctionalTestC
3 3
); );
assertTrue( checkList( rev1.getReferences(), uste1, uste2 ) ); assertTrue( checkCollection( rev1.getReferences(), uste1, uste2 ) );
assertTrue( checkList( rev2.getReferences(), uste1, uste2 ) ); assertTrue( checkCollection( rev2.getReferences(), uste1, uste2 ) );
assertTrue( checkList( rev3.getReferences(), uste2, uste1 ) ); assertTrue( checkCollection( rev3.getReferences(), uste2, uste1 ) );
} }
@Test @Test
@ -138,7 +138,7 @@ public class M2MIndexedListNotAuditedTarget extends BaseEnversJPAFunctionalTestC
); );
assertNull( rev1 ); assertNull( rev1 );
assertTrue( checkList( rev2.getReferences(), uste2 ) ); assertTrue( checkCollection( rev2.getReferences(), uste2 ) );
assertTrue( checkList( rev3.getReferences(), uste2 ) ); assertTrue( checkCollection( rev3.getReferences(), uste2 ) );
} }
} }

View File

@ -35,7 +35,7 @@ import org.hibernate.envers.test.entities.manytomany.unidirectional.M2MTargetNot
import org.junit.Test; import org.junit.Test;
import static org.hibernate.envers.test.tools.TestTools.checkList; import static org.hibernate.envers.test.tools.TestTools.checkCollection;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
/** /**
@ -153,10 +153,10 @@ public class M2MRelationNotAuditedTarget extends BaseEnversJPAFunctionalTestCase
M2MTargetNotAuditedEntity rev3 = getAuditReader().find( M2MTargetNotAuditedEntity.class, tnae1_id, 3 ); M2MTargetNotAuditedEntity rev3 = getAuditReader().find( M2MTargetNotAuditedEntity.class, tnae1_id, 3 );
M2MTargetNotAuditedEntity rev4 = getAuditReader().find( M2MTargetNotAuditedEntity.class, tnae1_id, 4 ); M2MTargetNotAuditedEntity rev4 = getAuditReader().find( M2MTargetNotAuditedEntity.class, tnae1_id, 4 );
assertTrue( checkList( rev1.getReferences() ) ); assertTrue( checkCollection( rev1.getReferences() ) );
assertTrue( checkList( rev2.getReferences(), uste1 ) ); assertTrue( checkCollection( rev2.getReferences(), uste1 ) );
assertTrue( checkList( rev3.getReferences(), uste1 ) ); assertTrue( checkCollection( rev3.getReferences(), uste1 ) );
assertTrue( checkList( rev4.getReferences(), uste1, uste2 ) ); assertTrue( checkCollection( rev4.getReferences(), uste1, uste2 ) );
} }
@Test @Test
@ -169,9 +169,9 @@ public class M2MRelationNotAuditedTarget extends BaseEnversJPAFunctionalTestCase
M2MTargetNotAuditedEntity rev3 = getAuditReader().find( M2MTargetNotAuditedEntity.class, tnae2_id, 3 ); M2MTargetNotAuditedEntity rev3 = getAuditReader().find( M2MTargetNotAuditedEntity.class, tnae2_id, 3 );
M2MTargetNotAuditedEntity rev4 = getAuditReader().find( M2MTargetNotAuditedEntity.class, tnae2_id, 4 ); M2MTargetNotAuditedEntity rev4 = getAuditReader().find( M2MTargetNotAuditedEntity.class, tnae2_id, 4 );
assertTrue( checkList( rev1.getReferences(), uste1, uste2 ) ); assertTrue( checkCollection( rev1.getReferences(), uste1, uste2 ) );
assertTrue( checkList( rev2.getReferences(), uste2 ) ); assertTrue( checkCollection( rev2.getReferences(), uste2 ) );
assertTrue( checkList( rev3.getReferences() ) ); assertTrue( checkCollection( rev3.getReferences() ) );
assertTrue( checkList( rev4.getReferences(), uste1 ) ); assertTrue( checkCollection( rev4.getReferences(), uste1 ) );
} }
} }

View File

@ -118,9 +118,9 @@ public class BasicList extends BaseEnversJPAFunctionalTestCase {
ListRefEdEntity rev2 = getAuditReader().find( ListRefEdEntity.class, ed1_id, 2 ); ListRefEdEntity rev2 = getAuditReader().find( ListRefEdEntity.class, ed1_id, 2 );
ListRefEdEntity rev3 = getAuditReader().find( ListRefEdEntity.class, ed1_id, 3 ); ListRefEdEntity rev3 = getAuditReader().find( ListRefEdEntity.class, ed1_id, 3 );
assert TestTools.checkList( rev1.getReffering(), ing1, ing2 ); assert TestTools.checkCollection( rev1.getReffering(), ing1, ing2 );
assert TestTools.checkList( rev2.getReffering(), ing2 ); assert TestTools.checkCollection( rev2.getReffering(), ing2 );
assert TestTools.checkList( rev3.getReffering() ); assert TestTools.checkCollection( rev3.getReffering() );
} }
@Test @Test
@ -132,9 +132,9 @@ public class BasicList extends BaseEnversJPAFunctionalTestCase {
ListRefEdEntity rev2 = getAuditReader().find( ListRefEdEntity.class, ed2_id, 2 ); ListRefEdEntity rev2 = getAuditReader().find( ListRefEdEntity.class, ed2_id, 2 );
ListRefEdEntity rev3 = getAuditReader().find( ListRefEdEntity.class, ed2_id, 3 ); ListRefEdEntity rev3 = getAuditReader().find( ListRefEdEntity.class, ed2_id, 3 );
assert TestTools.checkList( rev1.getReffering() ); assert TestTools.checkCollection( rev1.getReffering() );
assert TestTools.checkList( rev2.getReffering(), ing1 ); assert TestTools.checkCollection( rev2.getReffering(), ing1 );
assert TestTools.checkList( rev3.getReffering(), ing1, ing2 ); assert TestTools.checkCollection( rev3.getReffering(), ing1, ing2 );
} }
@Test @Test

View File

@ -126,10 +126,10 @@ public class BasicDetachedList extends BaseEnversJPAFunctionalTestCase {
ListRefCollEntity rev3 = getAuditReader().find( ListRefCollEntity.class, coll1_id, 3 ); ListRefCollEntity rev3 = getAuditReader().find( ListRefCollEntity.class, coll1_id, 3 );
ListRefCollEntity rev4 = getAuditReader().find( ListRefCollEntity.class, coll1_id, 4 ); ListRefCollEntity rev4 = getAuditReader().find( ListRefCollEntity.class, coll1_id, 4 );
assert TestTools.checkList( rev1.getCollection(), str1 ); assert TestTools.checkCollection( rev1.getCollection(), str1 );
assert TestTools.checkList( rev2.getCollection(), str1, str2 ); assert TestTools.checkCollection( rev2.getCollection(), str1, str2 );
assert TestTools.checkList( rev3.getCollection(), str2 ); assert TestTools.checkCollection( rev3.getCollection(), str2 );
assert TestTools.checkList( rev4.getCollection() ); assert TestTools.checkCollection( rev4.getCollection() );
assert "coll1".equals( rev1.getData() ); assert "coll1".equals( rev1.getData() );
assert "coll1".equals( rev2.getData() ); assert "coll1".equals( rev2.getData() );

View File

@ -34,7 +34,7 @@ import org.hibernate.envers.test.entities.onetomany.detached.DoubleListJoinColum
import org.junit.Test; import org.junit.Test;
import static org.hibernate.envers.test.tools.TestTools.checkList; import static org.hibernate.envers.test.tools.TestTools.checkCollection;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -266,15 +266,15 @@ public class DoubleJoinColumnBidirectionalList extends BaseEnversJPAFunctionalTe
4 4
); );
assertTrue( checkList( rev1.getReferences1(), ed1_1_fromRev1 ) ); assertTrue( checkCollection( rev1.getReferences1(), ed1_1_fromRev1 ) );
assertTrue( checkList( rev2.getReferences1(), ed1_1_fromRev1, ed1_2 ) ); assertTrue( checkCollection( rev2.getReferences1(), ed1_1_fromRev1, ed1_2 ) );
assertTrue( checkList( rev3.getReferences1(), ed1_1_fromRev3, ed1_2 ) ); assertTrue( checkCollection( rev3.getReferences1(), ed1_1_fromRev3, ed1_2 ) );
assertTrue( checkList( rev4.getReferences1() ) ); assertTrue( checkCollection( rev4.getReferences1() ) );
assertTrue( checkList( rev1.getReferences2(), ed2_1 ) ); assertTrue( checkCollection( rev1.getReferences2(), ed2_1 ) );
assertTrue( checkList( rev2.getReferences2(), ed2_1, ed2_2_fromRev1 ) ); assertTrue( checkCollection( rev2.getReferences2(), ed2_1, ed2_2_fromRev1 ) );
assertTrue( checkList( rev3.getReferences2(), ed2_1, ed2_2_fromRev3 ) ); assertTrue( checkCollection( rev3.getReferences2(), ed2_1, ed2_2_fromRev3 ) );
assertTrue( checkList( rev4.getReferences2(), ed2_2_fromRev3 ) ); assertTrue( checkCollection( rev4.getReferences2(), ed2_2_fromRev3 ) );
} }
@Test @Test
@ -319,15 +319,15 @@ public class DoubleJoinColumnBidirectionalList extends BaseEnversJPAFunctionalTe
4 4
); );
assertTrue( checkList( rev1.getReferences1(), ed1_2 ) ); assertTrue( checkCollection( rev1.getReferences1(), ed1_2 ) );
assertTrue( checkList( rev2.getReferences1() ) ); assertTrue( checkCollection( rev2.getReferences1() ) );
assertTrue( checkList( rev3.getReferences1() ) ); assertTrue( checkCollection( rev3.getReferences1() ) );
assertTrue( checkList( rev4.getReferences1(), ed1_1_fromRev3, ed1_2 ) ); assertTrue( checkCollection( rev4.getReferences1(), ed1_1_fromRev3, ed1_2 ) );
assertTrue( checkList( rev1.getReferences2(), ed2_2_fromRev1 ) ); assertTrue( checkCollection( rev1.getReferences2(), ed2_2_fromRev1 ) );
assertTrue( checkList( rev2.getReferences2() ) ); assertTrue( checkCollection( rev2.getReferences2() ) );
assertTrue( checkList( rev3.getReferences2() ) ); assertTrue( checkCollection( rev3.getReferences2() ) );
assertTrue( checkList( rev4.getReferences2(), ed2_1 ) ); assertTrue( checkCollection( rev4.getReferences2(), ed2_1 ) );
} }
@Test @Test

View File

@ -33,7 +33,7 @@ import org.hibernate.envers.test.entities.onetomany.detached.ListJoinColumnBidir
import org.junit.Test; import org.junit.Test;
import static org.hibernate.envers.test.tools.TestTools.checkList; import static org.hibernate.envers.test.tools.TestTools.checkCollection;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -199,10 +199,10 @@ public class JoinColumnBidirectionalList extends BaseEnversJPAFunctionalTestCase
4 4
); );
assertTrue( checkList( rev1.getReferences(), ed1_fromRev1 ) ); assertTrue( checkCollection( rev1.getReferences(), ed1_fromRev1 ) );
assertTrue( checkList( rev2.getReferences(), ed1_fromRev1, ed2 ) ); assertTrue( checkCollection( rev2.getReferences(), ed1_fromRev1, ed2 ) );
assertTrue( checkList( rev3.getReferences(), ed1_fromRev3, ed2 ) ); assertTrue( checkCollection( rev3.getReferences(), ed1_fromRev3, ed2 ) );
assertTrue( checkList( rev4.getReferences() ) ); assertTrue( checkCollection( rev4.getReferences() ) );
} }
@Test @Test
@ -237,10 +237,10 @@ public class JoinColumnBidirectionalList extends BaseEnversJPAFunctionalTestCase
4 4
); );
assertTrue( checkList( rev1.getReferences(), ed2 ) ); assertTrue( checkCollection( rev1.getReferences(), ed2 ) );
assertTrue( checkList( rev2.getReferences() ) ); assertTrue( checkCollection( rev2.getReferences() ) );
assertTrue( checkList( rev3.getReferences() ) ); assertTrue( checkCollection( rev3.getReferences() ) );
assertTrue( checkList( rev4.getReferences(), ed1, ed2 ) ); assertTrue( checkCollection( rev4.getReferences(), ed1, ed2 ) );
} }
@Test @Test

View File

@ -34,7 +34,7 @@ import org.hibernate.envers.test.entities.onetomany.detached.ListJoinColumnBidir
import org.junit.Test; import org.junit.Test;
import static org.hibernate.envers.test.tools.TestTools.checkList; import static org.hibernate.envers.test.tools.TestTools.checkCollection;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -169,8 +169,8 @@ public class JoinColumnBidirectionalListWithInheritance extends BaseEnversJPAFun
2 2
); );
assertTrue( checkList( rev1.getReferences(), ed1 ) ); assertTrue( checkCollection( rev1.getReferences(), ed1 ) );
assertTrue( checkList( rev2.getReferences(), ed1, ed2 ) ); assertTrue( checkCollection( rev2.getReferences(), ed1, ed2 ) );
} }
@Test @Test
@ -191,8 +191,8 @@ public class JoinColumnBidirectionalListWithInheritance extends BaseEnversJPAFun
2 2
); );
assertTrue( checkList( rev1.getReferences(), ed2 ) ); assertTrue( checkCollection( rev1.getReferences(), ed2 ) );
assertTrue( checkList( rev2.getReferences() ) ); assertTrue( checkCollection( rev2.getReferences() ) );
} }
@Test @Test

View File

@ -75,17 +75,17 @@ public class HierarchyTest extends BaseEnversJPAFunctionalTestCase {
Node ver1 = getAuditReader().find( Node.class, parentId, 1 ); Node ver1 = getAuditReader().find( Node.class, parentId, 1 );
Assert.assertEquals( parent, ver1 ); Assert.assertEquals( parent, ver1 );
Assert.assertTrue( TestTools.checkList( ver1.getChildren(), child1, child2 ) ); Assert.assertTrue( TestTools.checkCollection( ver1.getChildren(), child1, child2 ) );
child1.setData( "child1 modified" ); child1.setData( "child1 modified" );
Node ver2 = getAuditReader().find( Node.class, parentId, 2 ); Node ver2 = getAuditReader().find( Node.class, parentId, 2 );
Assert.assertEquals( parent, ver2 ); Assert.assertEquals( parent, ver2 );
Assert.assertTrue( TestTools.checkList( ver2.getChildren(), child1, child2 ) ); Assert.assertTrue( TestTools.checkCollection( ver2.getChildren(), child1, child2 ) );
Node ver3 = getAuditReader().find( Node.class, parentId, 3 ); Node ver3 = getAuditReader().find( Node.class, parentId, 3 );
Assert.assertEquals( parent, ver3 ); Assert.assertEquals( parent, ver3 );
Assert.assertTrue( TestTools.checkList( ver3.getChildren(), child1 ) ); Assert.assertTrue( TestTools.checkCollection( ver3.getChildren(), child1 ) );
} }
@Test @Test

View File

@ -1,6 +1,7 @@
package org.hibernate.envers.test.integration.proxy; package org.hibernate.envers.test.integration.proxy;
import javax.persistence.EntityManager; import javax.persistence.EntityManager;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -17,9 +18,13 @@ import org.hibernate.envers.test.entities.IntTestPrivSeqEntity;
import org.hibernate.envers.test.entities.StrTestPrivSeqEntity; import org.hibernate.envers.test.entities.StrTestPrivSeqEntity;
import org.hibernate.envers.test.entities.UnversionedStrTestEntity; import org.hibernate.envers.test.entities.UnversionedStrTestEntity;
import org.hibernate.envers.test.entities.collection.StringSetEntity; import org.hibernate.envers.test.entities.collection.StringSetEntity;
import org.hibernate.envers.test.entities.manytomany.ListOwnedEntity;
import org.hibernate.envers.test.entities.manytomany.ListOwningEntity;
import org.hibernate.envers.test.entities.manytomany.SetOwnedEntity; import org.hibernate.envers.test.entities.manytomany.SetOwnedEntity;
import org.hibernate.envers.test.entities.manytomany.SetOwningEntity; import org.hibernate.envers.test.entities.manytomany.SetOwningEntity;
import org.hibernate.envers.test.entities.manytomany.unidirectional.M2MIndexedListTargetNotAuditedEntity; import org.hibernate.envers.test.entities.manytomany.unidirectional.M2MIndexedListTargetNotAuditedEntity;
import org.hibernate.envers.test.entities.onetomany.CollectionRefEdEntity;
import org.hibernate.envers.test.entities.onetomany.CollectionRefIngEntity;
import org.hibernate.envers.test.entities.onetomany.SetRefEdEntity; import org.hibernate.envers.test.entities.onetomany.SetRefEdEntity;
import org.hibernate.envers.test.entities.onetomany.SetRefIngEntity; import org.hibernate.envers.test.entities.onetomany.SetRefIngEntity;
import org.hibernate.envers.test.integration.manytomany.ternary.TernaryMapEntity; import org.hibernate.envers.test.integration.manytomany.ternary.TernaryMapEntity;
@ -55,7 +60,8 @@ public class RemovedObjectQueryTest extends BaseEnversJPAFunctionalTestCase {
return new Class<?>[] { return new Class<?>[] {
SetRefEdEntity.class, SetRefIngEntity.class, SetOwnedEntity.class, SetOwningEntity.class, SetRefEdEntity.class, SetRefIngEntity.class, SetOwnedEntity.class, SetOwningEntity.class,
StringSetEntity.class, UnversionedStrTestEntity.class, M2MIndexedListTargetNotAuditedEntity.class, StringSetEntity.class, UnversionedStrTestEntity.class, M2MIndexedListTargetNotAuditedEntity.class,
TernaryMapEntity.class, StrTestPrivSeqEntity.class, IntTestPrivSeqEntity.class TernaryMapEntity.class, StrTestPrivSeqEntity.class, IntTestPrivSeqEntity.class,
CollectionRefEdEntity.class, CollectionRefIngEntity.class, ListOwnedEntity.class, ListOwningEntity.class
}; };
} }
@ -212,30 +218,108 @@ public class RemovedObjectQueryTest extends BaseEnversJPAFunctionalTestCase {
ternaryMapId = mapEntity.getId(); ternaryMapId = mapEntity.getId();
// Revision 16 - removing ternary map // Revision 16 - updating ternary map
em.getTransaction().begin();
intEntity2 = em.find( IntTestPrivSeqEntity.class, intEntity2.getId() );
intEntity2.setNumber( 3 );
intEntity2 = em.merge( intEntity2 );
stringEntity2 = em.find( StrTestPrivSeqEntity.class, stringEntity2.getId() );
stringEntity2.setStr( "Value 3" );
stringEntity2 = em.merge( stringEntity2 );
em.getTransaction().commit();
// Revision 17 - removing ternary map
em.getTransaction().begin(); em.getTransaction().begin();
mapEntity = em.find( TernaryMapEntity.class, mapEntity.getId() ); mapEntity = em.find( TernaryMapEntity.class, mapEntity.getId() );
em.remove( mapEntity ); em.remove( mapEntity );
em.getTransaction().commit(); em.getTransaction().commit();
CollectionRefEdEntity collEd1 = new CollectionRefEdEntity( 1, "data_ed_1" );
CollectionRefIngEntity collIng1 = new CollectionRefIngEntity( 2, "data_ing_1", collEd1 );
collEd1.setReffering( new ArrayList<CollectionRefIngEntity>() );
collEd1.getReffering().add( collIng1 );
// Revision 18 - testing one-to-many collection
em.getTransaction().begin();
em.persist( collEd1 );
em.persist( collIng1 );
em.getTransaction().commit();
// Revision 19
em.getTransaction().begin();
collIng1 = em.find( CollectionRefIngEntity.class, collIng1.getId() );
collIng1.setData( "modified data_ing_1" );
collIng1 = em.merge( collIng1 );
em.getTransaction().commit();
// Revision 20
em.getTransaction().begin();
collEd1 = em.find( CollectionRefEdEntity.class, collEd1.getId() );
collIng1 = em.find( CollectionRefIngEntity.class, collIng1.getId() );
em.remove( collIng1 );
em.remove( collEd1 );
em.getTransaction().commit();
ListOwnedEntity listEd1 = new ListOwnedEntity( 1, "data_ed_1" );
ListOwningEntity listIng1 = new ListOwningEntity( 2, "data_ing_1" );
listEd1.setReferencing( new ArrayList<ListOwningEntity>() );
listIng1.setReferences( new ArrayList<ListOwnedEntity>() );
listEd1.getReferencing().add( listIng1 );
listIng1.getReferences().add( listEd1 );
// Revision 21 - testing many-to-many collection
em.getTransaction().begin();
em.persist( listEd1 );
em.persist( listIng1 );
em.getTransaction().commit();
// Revision 22
em.getTransaction().begin();
listIng1 = em.find( ListOwningEntity.class, listIng1.getId() );
listIng1.setData( "modified data_ing_1" );
listIng1 = em.merge( listIng1 );
em.getTransaction().commit();
// Revision 23
em.getTransaction().begin();
listIng1 = em.find( ListOwningEntity.class, listIng1.getId() );
listEd1 = em.find( ListOwnedEntity.class, listEd1.getId() );
em.remove( listIng1 );
em.remove( listEd1 );
em.getTransaction().commit();
em.close(); em.close();
} }
@Test @Test
public void testTernaryMap() { public void testTernaryMap() {
final TernaryMapEntity ternaryMap = new TernaryMapEntity();
ternaryMap.setId( ternaryMapId );
ternaryMap.getMap().put( intEntity1, stringEntity1 );
ternaryMap.getMap().put( new IntTestPrivSeqEntity( 2, intEntity2.getId() ) , new StrTestPrivSeqEntity( "Value 2", stringEntity2.getId() ) );
TernaryMapEntity entity = getAuditReader().find( TernaryMapEntity.class, ternaryMapId, 15 );
Assert.assertEquals( ternaryMap.getMap(), entity.getMap() );
ternaryMap.getMap().clear();
ternaryMap.getMap().put( intEntity1, stringEntity1 );
ternaryMap.getMap().put( intEntity2, stringEntity2 );
entity = getAuditReader().find( TernaryMapEntity.class, ternaryMapId, 16 );
Assert.assertEquals( ternaryMap.getMap(), entity.getMap() );
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( TernaryMapEntity.class, false, true ) List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( TernaryMapEntity.class, false, true )
.add( AuditEntity.id().eq( ternaryMapId ) ) .add( AuditEntity.id().eq( ternaryMapId ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) ) .add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList(); .getResultList();
Object[] objArray = (Object[]) queryResult.get( 0 ); Object[] objArray = (Object[]) queryResult.get( 0 );
Assert.assertEquals( 16, getRevisionNumber( objArray[1] ) ); Assert.assertEquals( 17, getRevisionNumber( objArray[1] ) );
TernaryMapEntity mapEntity = (TernaryMapEntity) objArray[0]; entity = (TernaryMapEntity) objArray[0];
Assert.assertEquals( Assert.assertEquals( ternaryMap.getMap(), entity.getMap() );
TestTools.makeMap( intEntity1, stringEntity1, intEntity2, stringEntity2 ),
mapEntity.getMap()
);
} }
@Test @Test
@ -251,7 +335,7 @@ public class RemovedObjectQueryTest extends BaseEnversJPAFunctionalTestCase {
M2MIndexedListTargetNotAuditedEntity relationNotAuditedEntity = (M2MIndexedListTargetNotAuditedEntity) objArray[0]; M2MIndexedListTargetNotAuditedEntity relationNotAuditedEntity = (M2MIndexedListTargetNotAuditedEntity) objArray[0];
Assert.assertTrue( Assert.assertTrue(
TestTools.checkList( TestTools.checkCollection(
relationNotAuditedEntity.getReferences(), relationNotAuditedEntity.getReferences(),
unversionedEntity1, unversionedEntity2 unversionedEntity1, unversionedEntity2
) )
@ -274,6 +358,34 @@ public class RemovedObjectQueryTest extends BaseEnversJPAFunctionalTestCase {
// One to many tests. // One to many tests.
@Test
public void testOneToManyCollectionSemantics() {
final CollectionRefEdEntity edVer1 = new CollectionRefEdEntity( 1, "data_ed_1" );
final CollectionRefIngEntity ingVer1 = new CollectionRefIngEntity( 2, "data_ing_1" );
final CollectionRefIngEntity ingVer2 = new CollectionRefIngEntity( 2, "modified data_ing_1" );
CollectionRefEdEntity entity = getAuditReader().find( CollectionRefEdEntity.class, 1, 18 );
Assert.assertEquals( edVer1, entity );
Assert.assertTrue( TestTools.checkCollection( entity.getReffering(), ingVer1 ) );
entity = getAuditReader().find( CollectionRefEdEntity.class, 1, 19 );
Assert.assertTrue( TestTools.checkCollection( entity.getReffering(), ingVer2 ) );
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( CollectionRefEdEntity.class, false, true )
.add( AuditEntity.id().eq( 1 ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();
Object[] objArray = (Object[]) queryResult.get( 0 );
Assert.assertEquals( 20, getRevisionNumber( objArray[1] ) );
entity = (CollectionRefEdEntity) objArray[0];
Assert.assertEquals( "data_ed_1", entity.getData() );
Assert.assertTrue( TestTools.checkCollection( entity.getReffering(), ingVer2 ) );
}
@Test @Test
public void testReferencedOneToManySameRevision() { public void testReferencedOneToManySameRevision() {
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetRefIngEntity.class, false, true ) List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetRefIngEntity.class, false, true )
@ -360,6 +472,34 @@ public class RemovedObjectQueryTest extends BaseEnversJPAFunctionalTestCase {
// Many to many tests. // Many to many tests.
@Test
public void testManyToManyCollectionSemantics() {
final ListOwnedEntity edVer1 = new ListOwnedEntity( 1, "data_ed_1" );
final ListOwningEntity ingVer1 = new ListOwningEntity( 2, "data_ing_1" );
final ListOwningEntity ingVer2 = new ListOwningEntity( 2, "modified data_ing_1" );
ListOwnedEntity entity = getAuditReader().find( ListOwnedEntity.class, 1, 21 );
Assert.assertEquals( edVer1, entity );
Assert.assertTrue( TestTools.checkCollection( entity.getReferencing(), ingVer1 ) );
entity = getAuditReader().find( ListOwnedEntity.class, 1, 22 );
Assert.assertTrue( TestTools.checkCollection( entity.getReferencing(), ingVer2 ) );
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( ListOwnedEntity.class, false, true )
.add( AuditEntity.id().eq( 1 ) )
.add( AuditEntity.revisionType().eq( RevisionType.DEL ) )
.getResultList();
Object[] objArray = (Object[]) queryResult.get( 0 );
Assert.assertEquals( 23, getRevisionNumber( objArray[1] ) );
entity = (ListOwnedEntity) objArray[0];
Assert.assertEquals( "data_ed_1", entity.getData() );
Assert.assertTrue( TestTools.checkCollection( entity.getReferencing(), ingVer2 ) );
}
@Test @Test
public void testOwnedManyToManySameRevision() { public void testOwnedManyToManySameRevision() {
List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetOwningEntity.class, false, true ) List queryResult = getAuditReader().createQuery().forRevisionsOfEntity( SetOwningEntity.class, false, true )

View File

@ -134,7 +134,7 @@ public class AggregateQuery extends BaseEnversJPAFunctionalTestCase {
.add( AuditEntity.id().between( 2, 3 ) ) .add( AuditEntity.id().between( 2, 3 ) )
.getResultList(); .getResultList();
Assert.assertTrue( Assert.assertTrue(
TestTools.checkList( TestTools.checkCollection(
list, list,
new IntTestEntity( 10, 2 ), new IntTestEntity( 8, 3 ), new IntTestEntity( 52, 2 ) new IntTestEntity( 10, 2 ), new IntTestEntity( 8, 3 ), new IntTestEntity( 52, 2 )
) )

View File

@ -311,7 +311,7 @@ public class SimpleQuery extends BaseEnversJPAFunctionalTestCase {
.addProjection( AuditEntity.revisionType() ).add( AuditEntity.id().eq( id1 ) ) .addProjection( AuditEntity.revisionType() ).add( AuditEntity.id().eq( id1 ) )
.getSingleResult(); .getSingleResult();
Assert.assertTrue( TestTools.checkList( result, site1, site2, site3 ) ); Assert.assertTrue( TestTools.checkCollection( result, site1, site2, site3 ) );
Assert.assertEquals( revisionType, RevisionType.ADD ); Assert.assertEquals( revisionType, RevisionType.ADD );
} }
@ -330,7 +330,7 @@ public class SimpleQuery extends BaseEnversJPAFunctionalTestCase {
.addProjection( AuditEntity.revisionType() ).add( AuditEntity.id().eq( id1 ) ) .addProjection( AuditEntity.revisionType() ).add( AuditEntity.id().eq( id1 ) )
.getSingleResult(); .getSingleResult();
Assert.assertTrue( TestTools.checkList( result, site1, site2 ) ); Assert.assertTrue( TestTools.checkCollection( result, site1, site2 ) );
Assert.assertEquals( revisionType, RevisionType.MOD ); Assert.assertEquals( revisionType, RevisionType.MOD );
} }
@ -348,7 +348,7 @@ public class SimpleQuery extends BaseEnversJPAFunctionalTestCase {
.addProjection( AuditEntity.revisionType() ).add( AuditEntity.id().eq( id1 ) ) .addProjection( AuditEntity.revisionType() ).add( AuditEntity.id().eq( id1 ) )
.getSingleResult(); .getSingleResult();
Assert.assertTrue( TestTools.checkList( result, site1 ) ); Assert.assertTrue( TestTools.checkCollection( result, site1 ) );
Assert.assertEquals( revisionType, RevisionType.DEL ); Assert.assertEquals( revisionType, RevisionType.DEL );
} }

View File

@ -91,14 +91,14 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase
StrTestEntity ste = new StrTestEntity( "x", steId ); StrTestEntity ste = new StrTestEntity( "x", steId );
StrIntTestEntity site = new StrIntTestEntity( "y", 1, siteId ); StrIntTestEntity site = new StrIntTestEntity( "y", 1, siteId );
assert TestTools.checkList( getCrossTypeRevisionChangesReader().findEntities( 1 ), ste, site ); assert TestTools.checkCollection( getCrossTypeRevisionChangesReader().findEntities( 1 ), ste, site );
} }
@Test @Test
public void testTrackModifiedEntities() { public void testTrackModifiedEntities() {
StrIntTestEntity site = new StrIntTestEntity( "y", 2, siteId ); StrIntTestEntity site = new StrIntTestEntity( "y", 2, siteId );
assert TestTools.checkList( getCrossTypeRevisionChangesReader().findEntities( 2 ), site ); assert TestTools.checkCollection( getCrossTypeRevisionChangesReader().findEntities( 2 ), site );
} }
@Test @Test
@ -106,7 +106,7 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase
StrTestEntity ste = new StrTestEntity( null, steId ); StrTestEntity ste = new StrTestEntity( null, steId );
StrIntTestEntity site = new StrIntTestEntity( null, null, siteId ); StrIntTestEntity site = new StrIntTestEntity( null, null, siteId );
assert TestTools.checkList( getCrossTypeRevisionChangesReader().findEntities( 3 ), site, ste ); assert TestTools.checkCollection( getCrossTypeRevisionChangesReader().findEntities( 3 ), site, ste );
} }
@Test @Test
@ -120,9 +120,9 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase
StrIntTestEntity site = new StrIntTestEntity( "y", 1, siteId ); StrIntTestEntity site = new StrIntTestEntity( "y", 1, siteId );
Map<RevisionType, List<Object>> result = getCrossTypeRevisionChangesReader().findEntitiesGroupByRevisionType( 1 ); Map<RevisionType, List<Object>> result = getCrossTypeRevisionChangesReader().findEntitiesGroupByRevisionType( 1 );
assert TestTools.checkList( result.get( RevisionType.ADD ), site, ste ); assert TestTools.checkCollection( result.get( RevisionType.ADD ), site, ste );
assert TestTools.checkList( result.get( RevisionType.MOD ) ); assert TestTools.checkCollection( result.get( RevisionType.MOD ) );
assert TestTools.checkList( result.get( RevisionType.DEL ) ); assert TestTools.checkCollection( result.get( RevisionType.DEL ) );
} }
@Test @Test
@ -130,9 +130,9 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase
StrIntTestEntity site = new StrIntTestEntity( "y", 2, siteId ); StrIntTestEntity site = new StrIntTestEntity( "y", 2, siteId );
Map<RevisionType, List<Object>> result = getCrossTypeRevisionChangesReader().findEntitiesGroupByRevisionType( 2 ); Map<RevisionType, List<Object>> result = getCrossTypeRevisionChangesReader().findEntitiesGroupByRevisionType( 2 );
assert TestTools.checkList( result.get( RevisionType.ADD ) ); assert TestTools.checkCollection( result.get( RevisionType.ADD ) );
assert TestTools.checkList( result.get( RevisionType.MOD ), site ); assert TestTools.checkCollection( result.get( RevisionType.MOD ), site );
assert TestTools.checkList( result.get( RevisionType.DEL ) ); assert TestTools.checkCollection( result.get( RevisionType.DEL ) );
} }
@Test @Test
@ -141,9 +141,9 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase
StrIntTestEntity site = new StrIntTestEntity( null, null, siteId ); StrIntTestEntity site = new StrIntTestEntity( null, null, siteId );
Map<RevisionType, List<Object>> result = getCrossTypeRevisionChangesReader().findEntitiesGroupByRevisionType( 3 ); Map<RevisionType, List<Object>> result = getCrossTypeRevisionChangesReader().findEntitiesGroupByRevisionType( 3 );
assert TestTools.checkList( result.get( RevisionType.ADD ) ); assert TestTools.checkCollection( result.get( RevisionType.ADD ) );
assert TestTools.checkList( result.get( RevisionType.MOD ) ); assert TestTools.checkCollection( result.get( RevisionType.MOD ) );
assert TestTools.checkList( result.get( RevisionType.DEL ), site, ste ); assert TestTools.checkCollection( result.get( RevisionType.DEL ), site, ste );
} }
@Test @Test
@ -151,7 +151,7 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase
StrTestEntity ste = new StrTestEntity( "x", steId ); StrTestEntity ste = new StrTestEntity( "x", steId );
StrIntTestEntity site = new StrIntTestEntity( "y", 1, siteId ); StrIntTestEntity site = new StrIntTestEntity( "y", 1, siteId );
assert TestTools.checkList( assert TestTools.checkCollection(
getCrossTypeRevisionChangesReader().findEntities( 1, RevisionType.ADD ), getCrossTypeRevisionChangesReader().findEntities( 1, RevisionType.ADD ),
ste, ste,
site site
@ -162,7 +162,10 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase
public void testFindChangedEntitiesByRevisionTypeMOD() { public void testFindChangedEntitiesByRevisionTypeMOD() {
StrIntTestEntity site = new StrIntTestEntity( "y", 2, siteId ); StrIntTestEntity site = new StrIntTestEntity( "y", 2, siteId );
assert TestTools.checkList( getCrossTypeRevisionChangesReader().findEntities( 2, RevisionType.MOD ), site ); assert TestTools.checkCollection(
getCrossTypeRevisionChangesReader().findEntities( 2, RevisionType.MOD ),
site
);
} }
@Test @Test
@ -170,7 +173,7 @@ public class DefaultTrackingEntitiesTest extends BaseEnversJPAFunctionalTestCase
StrTestEntity ste = new StrTestEntity( null, steId ); StrTestEntity ste = new StrTestEntity( null, steId );
StrIntTestEntity site = new StrIntTestEntity( null, null, siteId ); StrIntTestEntity site = new StrIntTestEntity( null, null, siteId );
assert TestTools.checkList( assert TestTools.checkCollection(
getCrossTypeRevisionChangesReader().findEntities( 3, RevisionType.DEL ), getCrossTypeRevisionChangesReader().findEntities( 3, RevisionType.DEL ),
ste, ste,
site site

View File

@ -0,0 +1,73 @@
package org.hibernate.envers.test.integration.strategy;
import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import java.util.Arrays;
import java.util.Map;
import org.hibernate.envers.configuration.EnversSettings;
import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase;
import org.hibernate.envers.test.entities.IntNoAutoIdTestEntity;
import org.junit.Test;
import org.hibernate.testing.TestForIssue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
/**
* Tests that reusing identifiers doesn't cause auditing misbehavior.
*
* @author adar
*/
@TestForIssue(jiraKey = "HHH-8280")
public class IdentifierReuseTest extends BaseEnversJPAFunctionalTestCase {
@Override
protected void addConfigOptions(Map options) {
options.put( EnversSettings.ALLOW_IDENTIFIER_REUSE, "true" );
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { IntNoAutoIdTestEntity.class };
}
@Test
public void testIdentifierReuse() {
final Integer reusedId = 1;
EntityManager entityManager = getEntityManager();
saveUpdateAndRemoveEntity( entityManager, reusedId );
saveUpdateAndRemoveEntity( entityManager, reusedId );
entityManager.close();
assertEquals(
Arrays.asList( 1, 2, 3, 4, 5, 6 ),
getAuditReader().getRevisions( IntNoAutoIdTestEntity.class, reusedId )
);
}
private void saveUpdateAndRemoveEntity(EntityManager entityManager, Integer id) {
EntityTransaction transaction = entityManager.getTransaction();
transaction.begin();
IntNoAutoIdTestEntity entity = new IntNoAutoIdTestEntity( 0, id );
entityManager.persist( entity );
assertEquals( id, entity.getId() );
transaction.commit();
transaction.begin();
entity = entityManager.find( IntNoAutoIdTestEntity.class, id );
entity.setNumVal( 1 );
entity = entityManager.merge( entity );
assertEquals( id, entity.getId() );
transaction.commit();
transaction.begin();
entity = entityManager.find( IntNoAutoIdTestEntity.class, id );
assertNotNull( entity );
entityManager.remove( entity );
transaction.commit();
}
}

View File

@ -291,17 +291,17 @@ public class ValidityAuditStrategyRevEndTestCustomRevEnt extends BaseEnversJPAFu
ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p1_id, 4 ); ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p1_id, 4 );
ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p1_id, 5 ); ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p1_id, 5 );
assert TestTools.checkList( rev1.getChildren1() ); assert TestTools.checkCollection( rev1.getChildren1() );
assert TestTools.checkList( rev2.getChildren1(), c1_1 ); assert TestTools.checkCollection( rev2.getChildren1(), c1_1 );
assert TestTools.checkList( rev3.getChildren1(), c1_1, c1_2 ); assert TestTools.checkCollection( rev3.getChildren1(), c1_1, c1_2 );
assert TestTools.checkList( rev4.getChildren1(), c1_2 ); assert TestTools.checkCollection( rev4.getChildren1(), c1_2 );
assert TestTools.checkList( rev5.getChildren1() ); assert TestTools.checkCollection( rev5.getChildren1() );
assert TestTools.checkList( rev1.getChildren2() ); assert TestTools.checkCollection( rev1.getChildren2() );
assert TestTools.checkList( rev2.getChildren2() ); assert TestTools.checkCollection( rev2.getChildren2() );
assert TestTools.checkList( rev3.getChildren2(), c2_2 ); assert TestTools.checkCollection( rev3.getChildren2(), c2_2 );
assert TestTools.checkList( rev4.getChildren2(), c2_2 ); assert TestTools.checkCollection( rev4.getChildren2(), c2_2 );
assert TestTools.checkList( rev5.getChildren2(), c2_2 ); assert TestTools.checkCollection( rev5.getChildren2(), c2_2 );
} }
@Test @Test
@ -319,17 +319,17 @@ public class ValidityAuditStrategyRevEndTestCustomRevEnt extends BaseEnversJPAFu
ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p2_id, 4 ); ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p2_id, 4 );
ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p2_id, 5 ); ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p2_id, 5 );
assert TestTools.checkList( rev1.getChildren1() ); assert TestTools.checkCollection( rev1.getChildren1() );
assert TestTools.checkList( rev2.getChildren1() ); assert TestTools.checkCollection( rev2.getChildren1() );
assert TestTools.checkList( rev3.getChildren1(), c1_1 ); assert TestTools.checkCollection( rev3.getChildren1(), c1_1 );
assert TestTools.checkList( rev4.getChildren1(), c1_1 ); assert TestTools.checkCollection( rev4.getChildren1(), c1_1 );
assert TestTools.checkList( rev5.getChildren1(), c1_1 ); assert TestTools.checkCollection( rev5.getChildren1(), c1_1 );
assert TestTools.checkList( rev1.getChildren2() ); assert TestTools.checkCollection( rev1.getChildren2() );
assert TestTools.checkList( rev2.getChildren2(), c2_1 ); assert TestTools.checkCollection( rev2.getChildren2(), c2_1 );
assert TestTools.checkList( rev3.getChildren2(), c2_1 ); assert TestTools.checkCollection( rev3.getChildren2(), c2_1 );
assert TestTools.checkList( rev4.getChildren2(), c2_1, c2_2 ); assert TestTools.checkCollection( rev4.getChildren2(), c2_1, c2_2 );
assert TestTools.checkList( rev5.getChildren2(), c2_1 ); assert TestTools.checkCollection( rev5.getChildren2(), c2_1 );
} }
@Test @Test
@ -358,11 +358,11 @@ public class ValidityAuditStrategyRevEndTestCustomRevEnt extends BaseEnversJPAFu
5 5
); );
assert TestTools.checkList( rev1.getParents() ); assert TestTools.checkCollection( rev1.getParents() );
assert TestTools.checkList( rev2.getParents(), p1 ); assert TestTools.checkCollection( rev2.getParents(), p1 );
assert TestTools.checkList( rev3.getParents(), p1, p2 ); assert TestTools.checkCollection( rev3.getParents(), p1, p2 );
assert TestTools.checkList( rev4.getParents(), p2 ); assert TestTools.checkCollection( rev4.getParents(), p2 );
assert TestTools.checkList( rev5.getParents(), p2 ); assert TestTools.checkCollection( rev5.getParents(), p2 );
} }
// TODO: this was disabled? // TODO: this was disabled?
@ -391,11 +391,11 @@ public class ValidityAuditStrategyRevEndTestCustomRevEnt extends BaseEnversJPAFu
5 5
); );
assert TestTools.checkList( rev1.getParents() ); assert TestTools.checkCollection( rev1.getParents() );
assert TestTools.checkList( rev2.getParents() ); assert TestTools.checkCollection( rev2.getParents() );
assert TestTools.checkList( rev3.getParents(), p1 ); assert TestTools.checkCollection( rev3.getParents(), p1 );
assert TestTools.checkList( rev4.getParents(), p1 ); assert TestTools.checkCollection( rev4.getParents(), p1 );
assert TestTools.checkList( rev5.getParents() ); assert TestTools.checkCollection( rev5.getParents() );
} }
@Test @Test
@ -423,11 +423,11 @@ public class ValidityAuditStrategyRevEndTestCustomRevEnt extends BaseEnversJPAFu
5 5
); );
assert TestTools.checkList( rev1.getParents() ); assert TestTools.checkCollection( rev1.getParents() );
assert TestTools.checkList( rev2.getParents(), p2 ); assert TestTools.checkCollection( rev2.getParents(), p2 );
assert TestTools.checkList( rev3.getParents(), p2 ); assert TestTools.checkCollection( rev3.getParents(), p2 );
assert TestTools.checkList( rev4.getParents(), p2 ); assert TestTools.checkCollection( rev4.getParents(), p2 );
assert TestTools.checkList( rev5.getParents(), p2 ); assert TestTools.checkCollection( rev5.getParents(), p2 );
} }
@Test @Test
@ -456,11 +456,11 @@ public class ValidityAuditStrategyRevEndTestCustomRevEnt extends BaseEnversJPAFu
5 5
); );
assert TestTools.checkList( rev1.getParents() ); assert TestTools.checkCollection( rev1.getParents() );
assert TestTools.checkList( rev2.getParents() ); assert TestTools.checkCollection( rev2.getParents() );
assert TestTools.checkList( rev3.getParents(), p1 ); assert TestTools.checkCollection( rev3.getParents(), p1 );
assert TestTools.checkList( rev4.getParents(), p1, p2 ); assert TestTools.checkCollection( rev4.getParents(), p1, p2 );
assert TestTools.checkList( rev5.getParents(), p1 ); assert TestTools.checkCollection( rev5.getParents(), p1 );
} }
private List<Map<String, Object>> getRevisions( private List<Map<String, Object>> getRevisions(

View File

@ -289,17 +289,17 @@ public class ValidityAuditStrategyRevEndTsTest extends BaseEnversJPAFunctionalTe
ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p1_id, 4 ); ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p1_id, 4 );
ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p1_id, 5 ); ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p1_id, 5 );
assert TestTools.checkList( rev1.getChildren1() ); assert TestTools.checkCollection( rev1.getChildren1() );
assert TestTools.checkList( rev2.getChildren1(), c1_1 ); assert TestTools.checkCollection( rev2.getChildren1(), c1_1 );
assert TestTools.checkList( rev3.getChildren1(), c1_1, c1_2 ); assert TestTools.checkCollection( rev3.getChildren1(), c1_1, c1_2 );
assert TestTools.checkList( rev4.getChildren1(), c1_2 ); assert TestTools.checkCollection( rev4.getChildren1(), c1_2 );
assert TestTools.checkList( rev5.getChildren1() ); assert TestTools.checkCollection( rev5.getChildren1() );
assert TestTools.checkList( rev1.getChildren2() ); assert TestTools.checkCollection( rev1.getChildren2() );
assert TestTools.checkList( rev2.getChildren2() ); assert TestTools.checkCollection( rev2.getChildren2() );
assert TestTools.checkList( rev3.getChildren2(), c2_2 ); assert TestTools.checkCollection( rev3.getChildren2(), c2_2 );
assert TestTools.checkList( rev4.getChildren2(), c2_2 ); assert TestTools.checkCollection( rev4.getChildren2(), c2_2 );
assert TestTools.checkList( rev5.getChildren2(), c2_2 ); assert TestTools.checkCollection( rev5.getChildren2(), c2_2 );
} }
@Test @Test
@ -317,17 +317,17 @@ public class ValidityAuditStrategyRevEndTsTest extends BaseEnversJPAFunctionalTe
ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p2_id, 4 ); ParentEntity rev4 = getAuditReader().find( ParentEntity.class, p2_id, 4 );
ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p2_id, 5 ); ParentEntity rev5 = getAuditReader().find( ParentEntity.class, p2_id, 5 );
assert TestTools.checkList( rev1.getChildren1() ); assert TestTools.checkCollection( rev1.getChildren1() );
assert TestTools.checkList( rev2.getChildren1() ); assert TestTools.checkCollection( rev2.getChildren1() );
assert TestTools.checkList( rev3.getChildren1(), c1_1 ); assert TestTools.checkCollection( rev3.getChildren1(), c1_1 );
assert TestTools.checkList( rev4.getChildren1(), c1_1 ); assert TestTools.checkCollection( rev4.getChildren1(), c1_1 );
assert TestTools.checkList( rev5.getChildren1(), c1_1 ); assert TestTools.checkCollection( rev5.getChildren1(), c1_1 );
assert TestTools.checkList( rev1.getChildren2() ); assert TestTools.checkCollection( rev1.getChildren2() );
assert TestTools.checkList( rev2.getChildren2(), c2_1 ); assert TestTools.checkCollection( rev2.getChildren2(), c2_1 );
assert TestTools.checkList( rev3.getChildren2(), c2_1 ); assert TestTools.checkCollection( rev3.getChildren2(), c2_1 );
assert TestTools.checkList( rev4.getChildren2(), c2_1, c2_2 ); assert TestTools.checkCollection( rev4.getChildren2(), c2_1, c2_2 );
assert TestTools.checkList( rev5.getChildren2(), c2_1 ); assert TestTools.checkCollection( rev5.getChildren2(), c2_1 );
} }
@Test @Test
@ -356,11 +356,11 @@ public class ValidityAuditStrategyRevEndTsTest extends BaseEnversJPAFunctionalTe
5 5
); );
assert TestTools.checkList( rev1.getParents() ); assert TestTools.checkCollection( rev1.getParents() );
assert TestTools.checkList( rev2.getParents(), p1 ); assert TestTools.checkCollection( rev2.getParents(), p1 );
assert TestTools.checkList( rev3.getParents(), p1, p2 ); assert TestTools.checkCollection( rev3.getParents(), p1, p2 );
assert TestTools.checkList( rev4.getParents(), p2 ); assert TestTools.checkCollection( rev4.getParents(), p2 );
assert TestTools.checkList( rev5.getParents(), p2 ); assert TestTools.checkCollection( rev5.getParents(), p2 );
} }
// TODO: this was disabled? // TODO: this was disabled?
@ -389,11 +389,11 @@ public class ValidityAuditStrategyRevEndTsTest extends BaseEnversJPAFunctionalTe
5 5
); );
assert TestTools.checkList( rev1.getParents() ); assert TestTools.checkCollection( rev1.getParents() );
assert TestTools.checkList( rev2.getParents() ); assert TestTools.checkCollection( rev2.getParents() );
assert TestTools.checkList( rev3.getParents(), p1 ); assert TestTools.checkCollection( rev3.getParents(), p1 );
assert TestTools.checkList( rev4.getParents(), p1 ); assert TestTools.checkCollection( rev4.getParents(), p1 );
assert TestTools.checkList( rev5.getParents() ); assert TestTools.checkCollection( rev5.getParents() );
} }
@Test @Test
@ -421,11 +421,11 @@ public class ValidityAuditStrategyRevEndTsTest extends BaseEnversJPAFunctionalTe
5 5
); );
assert TestTools.checkList( rev1.getParents() ); assert TestTools.checkCollection( rev1.getParents() );
assert TestTools.checkList( rev2.getParents(), p2 ); assert TestTools.checkCollection( rev2.getParents(), p2 );
assert TestTools.checkList( rev3.getParents(), p2 ); assert TestTools.checkCollection( rev3.getParents(), p2 );
assert TestTools.checkList( rev4.getParents(), p2 ); assert TestTools.checkCollection( rev4.getParents(), p2 );
assert TestTools.checkList( rev5.getParents(), p2 ); assert TestTools.checkCollection( rev5.getParents(), p2 );
} }
@Test @Test
@ -454,11 +454,11 @@ public class ValidityAuditStrategyRevEndTsTest extends BaseEnversJPAFunctionalTe
5 5
); );
assert TestTools.checkList( rev1.getParents() ); assert TestTools.checkCollection( rev1.getParents() );
assert TestTools.checkList( rev2.getParents() ); assert TestTools.checkCollection( rev2.getParents() );
assert TestTools.checkList( rev3.getParents(), p1 ); assert TestTools.checkCollection( rev3.getParents(), p1 );
assert TestTools.checkList( rev4.getParents(), p1, p2 ); assert TestTools.checkCollection( rev4.getParents(), p1, p2 );
assert TestTools.checkList( rev5.getParents(), p1 ); assert TestTools.checkCollection( rev5.getParents(), p1 );
} }
private List<Map<String, Object>> getRevisions( private List<Map<String, Object>> getRevisions(

View File

@ -25,6 +25,7 @@ package org.hibernate.envers.test.tools;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -63,7 +64,7 @@ public class TestTools {
return ret; return ret;
} }
public static <T> boolean checkList(List<T> list, T... objects) { public static <T> boolean checkCollection(Collection<T> list, T... objects) {
if ( list.size() != objects.length ) { if ( list.size() != objects.length ) {
return false; return false;
} }

View File

@ -35,12 +35,10 @@ dependencies {
// Local copies of all jars needed fur the OSGi runtime. // Local copies of all jars needed fur the OSGi runtime.
osgiRuntime( "org.jboss.arquillian.osgi:arquillian-osgi-bundle:1.0.3.Final" ) osgiRuntime( "org.jboss.arquillian.osgi:arquillian-osgi-bundle:1.0.3.Final" )
osgiRuntime( "org.ops4j.pax.url:pax-url-wrap:1.5.2" ) osgiRuntime( libraries.jpa )
// TODO: Temporarily using a pre-built jar in src/test/resources. It's needed for a recent manifest change. Once this is available in Maven, use it.
//osgiRuntime( "org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0-SNAPSHOT" )
osgiRuntime( "javax.enterprise:cdi-api:1.1-PFD" ) osgiRuntime( "javax.enterprise:cdi-api:1.1-PFD" )
osgiRuntime( "org.jboss.spec.javax.interceptor:jboss-interceptors-api_1.2_spec:1.0.0.Alpha1" ) osgiRuntime( "org.jboss.spec.javax.interceptor:jboss-interceptors-api_1.2_spec:1.0.0.Alpha1" )
osgiRuntime( "org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec:1.0.0.Alpha1" ) osgiRuntime( libraries.jta )
osgiRuntime( "commons-collections:commons-collections:3.2.1" ) osgiRuntime( "commons-collections:commons-collections:3.2.1" )
osgiRuntime( "commons-pool:commons-pool:1.6" ) osgiRuntime( "commons-pool:commons-pool:1.6" )
osgiRuntime( "commons-dbcp:commons-dbcp:1.4" ) osgiRuntime( "commons-dbcp:commons-dbcp:1.4" )
@ -93,7 +91,7 @@ task copyBnd(type: Copy) {
task runBnd(type: JavaExec){ task runBnd(type: JavaExec){
main = "-jar" main = "-jar"
args "$buildDir/osgi-lib/bnd/bnd.jar", "$buildDir/osgi-lib/bnd/cdi-api.bnd", "$buildDir/osgi-lib/bnd/el-api.bnd", "$buildDir/osgi-lib/bnd/jandex.bnd", "$buildDir/osgi-lib/bnd/javassist.bnd", "$buildDir/osgi-lib/bnd/serp.bnd" args "$buildDir/osgi-lib/bnd/bnd.jar", "$buildDir/osgi-lib/bnd/cdi-api.bnd", "$buildDir/osgi-lib/bnd/el-api.bnd", "$buildDir/osgi-lib/bnd/jandex.bnd", "$buildDir/osgi-lib/bnd/serp.bnd"
} }
task copyToLib(type: Copy) { task copyToLib(type: Copy) {

View File

@ -35,6 +35,7 @@ import org.hibernate.jpa.HibernatePersistenceProvider;
import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil; import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.ServiceRegistration;
/** /**
* This BundleActivator provides three different uses of Hibernate in OSGi * This BundleActivator provides three different uses of Hibernate in OSGi
@ -59,6 +60,10 @@ import org.osgi.framework.FrameworkUtil;
*/ */
@SuppressWarnings("UnusedDeclaration") @SuppressWarnings("UnusedDeclaration")
public class HibernateBundleActivator implements BundleActivator { public class HibernateBundleActivator implements BundleActivator {
private ServiceRegistration<?> persistenceProviderService;
private ServiceRegistration<?> sessionFactoryService;
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public void start(BundleContext context) throws Exception { public void start(BundleContext context) throws Exception {
@ -75,12 +80,12 @@ public class HibernateBundleActivator implements BundleActivator {
final Dictionary properties = new Hashtable(); final Dictionary properties = new Hashtable();
// In order to support existing persistence.xml files, register using the legacy provider name. // In order to support existing persistence.xml files, register using the legacy provider name.
properties.put( "javax.persistence.provider", HibernatePersistenceProvider.class.getName() ); properties.put( "javax.persistence.provider", HibernatePersistenceProvider.class.getName() );
context.registerService( persistenceProviderService = context.registerService(
PersistenceProvider.class.getName(), PersistenceProvider.class.getName(),
new OsgiPersistenceProviderService( osgiClassLoader, osgiJtaPlatform, context ), new OsgiPersistenceProviderService( osgiClassLoader, osgiJtaPlatform, context ),
properties properties
); );
context.registerService( sessionFactoryService = context.registerService(
SessionFactory.class.getName(), SessionFactory.class.getName(),
new OsgiSessionFactoryService( osgiClassLoader, osgiJtaPlatform, context ), new OsgiSessionFactoryService( osgiClassLoader, osgiJtaPlatform, context ),
new Hashtable() new Hashtable()
@ -89,6 +94,11 @@ public class HibernateBundleActivator implements BundleActivator {
@Override @Override
public void stop(BundleContext context) throws Exception { public void stop(BundleContext context) throws Exception {
// Nothing else to do? persistenceProviderService.unregister();
persistenceProviderService = null;
sessionFactoryService.unregister();
sessionFactoryService = null;
ClassLoaderHelper.overridenClassLoader = null;
} }
} }

View File

@ -18,13 +18,17 @@ package org.hibernate.osgi.test;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import java.io.InputStream; import java.io.InputStream;
import org.hibernate.osgi.OsgiPersistenceProviderService;
import org.hibernate.osgi.OsgiSessionFactoryService;
import org.hibernate.osgi.test.result.OsgiTestResults; import org.hibernate.osgi.test.result.OsgiTestResults;
import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.junit.Arquillian; import org.jboss.arquillian.junit.Arquillian;
import org.jboss.arquillian.junit.InSequence;
import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.osgi.metadata.OSGiManifestBuilder; import org.jboss.osgi.metadata.OSGiManifestBuilder;
import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.ShrinkWrap;
@ -94,6 +98,8 @@ public class OsgiTestCase {
builder.addBundleSymbolicName( archive.getName() ); builder.addBundleSymbolicName( archive.getName() );
builder.addBundleManifestVersion( 2 ); builder.addBundleManifestVersion( 2 );
builder.addImportPackages( OsgiTestResults.class ); builder.addImportPackages( OsgiTestResults.class );
// needed primarily to test service cleanup in #testStop
builder.addImportPackages( OsgiSessionFactoryService.class );
return builder.openStream(); return builder.openStream();
} }
} ); } );
@ -107,19 +113,16 @@ public class OsgiTestCase {
* @throws Exception * @throws Exception
*/ */
@Test @Test
@InSequence(1)
public void testClientBundle() throws Exception { public void testClientBundle() throws Exception {
assertNotNull( "BundleContext injected", context ); commonTests();
assertEquals( "System Bundle ID", 0, context.getBundle().getBundleId() );
testHibernateBundle( "org.hibernate.core" );
testHibernateBundle( "org.hibernate.entitymanager" );
final Bundle testClientBundle = findHibernateBundle( "testClientBundle" ); final Bundle testClientBundle = findHibernateBundle( "testClientBundle" );
assertNotNull( "The test client bundle was not found!", testClientBundle ); assertNotNull( "The test client bundle was not found!", testClientBundle );
testClientBundle.start(); testClientBundle.start();
assertEquals( "The test client bundle was not activated!", Bundle.ACTIVE, testClientBundle.getState() ); assertEquals( "The test client bundle was not activated!", Bundle.ACTIVE, testClientBundle.getState() );
final ServiceReference serviceReference = context.getServiceReference( OsgiTestResults.class.getName() ); final ServiceReference<?> serviceReference = context.getServiceReference( OsgiTestResults.class.getName() );
final OsgiTestResults testResults = (OsgiTestResults) context.getService( serviceReference ); final OsgiTestResults testResults = (OsgiTestResults) context.getService( serviceReference );
if ( testResults.getFailures().size() > 0 ) { if ( testResults.getFailures().size() > 0 ) {
@ -127,6 +130,36 @@ public class OsgiTestCase {
} }
} }
/**
* Test that stopping the hibernate-osgi bundle happens cleanly.
*
* TODO: This will be really simplistic at first, but should be expanded upon.
*
* @throws Exception
*/
@Test
// Arquillian does not restart the container between runs (afaik). Without the ordering, the tests will
// intermittently fail since this method stops the bundle.
@InSequence(2)
public void testStop() throws Exception {
commonTests();
findHibernateBundle( "org.hibernate.osgi" ).stop();
testHibernateBundle( "org.hibernate.osgi", Bundle.RESOLVED );
assertNull( context.getServiceReference( OsgiSessionFactoryService.class ) );
assertNull( context.getServiceReference( OsgiPersistenceProviderService.class ) );
}
private void commonTests() {
assertNotNull( "BundleContext injected", context );
assertEquals( "System Bundle ID", 0, context.getBundle().getBundleId() );
testHibernateBundle( "org.hibernate.core", Bundle.ACTIVE );
testHibernateBundle( "org.hibernate.entitymanager", Bundle.ACTIVE );
testHibernateBundle( "org.hibernate.osgi", Bundle.ACTIVE );
}
private Bundle findHibernateBundle(String symbolicName) { private Bundle findHibernateBundle(String symbolicName) {
for ( Bundle bundle : context.getBundles() ) { for ( Bundle bundle : context.getBundles() ) {
if ( bundle.getSymbolicName().equals( symbolicName ) ) { if ( bundle.getSymbolicName().equals( symbolicName ) ) {
@ -136,10 +169,10 @@ public class OsgiTestCase {
return null; return null;
} }
private void testHibernateBundle(String symbolicName) { private void testHibernateBundle(String symbolicName, int state) {
final Bundle bundle = findHibernateBundle( symbolicName ); final Bundle bundle = findHibernateBundle( symbolicName );
assertNotNull( "Bundle " + symbolicName + " was not found!", bundle ); assertNotNull( "Bundle " + symbolicName + " was not found!", bundle );
assertEquals( "Bundle " + symbolicName + " was not activated!", Bundle.ACTIVE, bundle.getState() ); assertEquals( "Bundle " + symbolicName + " was not in the expected state!", state, bundle.getState() );
} }
} }

View File

@ -1,6 +0,0 @@
Bundle-SymbolicName: javassist
Bundle-Version: 3.18.0
Include-Resource: @../javassist-3.18.0-GA.jar
Import-Package: com.sun.jdi;resolution:=optional,com.sun.jdi.connect;resolution:=optional,com.sun.jdi.event;resolution:=optional,com.sun.jdi.request;resolution:=optional
-exportcontents: javassist,javassist.bytecode,javassist.bytecode.analysis,javassist.bytecode.annotation,javassist.bytecode.stackmap,javassist.compiler,javassist.compiler.ast,javassist.convert,javassist.expr,javassist.runtime,javassist.scopedpool,javassist.tools,javassist.tools.reflect,javassist.tools.rmi,javassist.tools.web,javassist.util,javassist.util.proxy

View File

@ -14,11 +14,9 @@ felix.log.level=4
felix.auto.install.1=\ felix.auto.install.1=\
file:target/osgi-lib/testClientBundle.jar file:target/osgi-lib/testClientBundle.jar
# TODO: Temporarily using a pre-built hibernate-jpa-2.1-api-1.0.0-SNAPSHOT.jar in src/test/resources.
# It's needed for a recent manifest change. Once this is available in Maven, use it.
felix.auto.start.1=\ felix.auto.start.1=\
file:target/osgi-lib/arquillian-osgi-bundle-1.0.3.Final.jar \ file:target/osgi-lib/arquillian-osgi-bundle-1.0.3.Final.jar \
file:src/test/resources/hibernate-jpa-2.1-api-1.0.0-SNAPSHOT.jar \ file:target/osgi-lib/hibernate-jpa-2.1-api-1.0.0.Final.jar \
file:target/osgi-lib/bnd/el-api-2.2.0.jar \ file:target/osgi-lib/bnd/el-api-2.2.0.jar \
file:target/osgi-lib/bnd/cdi-api-1.1.0.jar \ file:target/osgi-lib/bnd/cdi-api-1.1.0.jar \
file:target/osgi-lib/jboss-interceptors-api_1.2_spec-1.0.0.Alpha1.jar \ file:target/osgi-lib/jboss-interceptors-api_1.2_spec-1.0.0.Alpha1.jar \
@ -30,7 +28,7 @@ felix.auto.start.1=\
file:target/osgi-lib/bnd/serp-1.14.1.jar \ file:target/osgi-lib/bnd/serp-1.14.1.jar \
file:target/osgi-lib/h2-1.3.170.jar \ file:target/osgi-lib/h2-1.3.170.jar \
file:target/osgi-lib/org.apache.servicemix.bundles.antlr-2.7.7_5.jar \ file:target/osgi-lib/org.apache.servicemix.bundles.antlr-2.7.7_5.jar \
file:target/osgi-lib/bnd/javassist-3.18.0.jar \ file:target/osgi-lib/javassist-3.18.1-Beta1.jar \
file:target/osgi-lib/org.apache.servicemix.specs.jsr303-api-1.0.0-2.2.0.jar \ file:target/osgi-lib/org.apache.servicemix.specs.jsr303-api-1.0.0-2.2.0.jar \
file:target/osgi-lib/org.apache.servicemix.bundles.ant-1.8.2_2.jar \ file:target/osgi-lib/org.apache.servicemix.bundles.ant-1.8.2_2.jar \
file:target/osgi-lib/org.apache.servicemix.specs.stax-api-1.2-2.2.0.jar \ file:target/osgi-lib/org.apache.servicemix.specs.stax-api-1.2-2.2.0.jar \

Some files were not shown because too many files have changed in this diff Show More