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

This commit is contained in:
Andrea Boriero 2020-02-14 11:36:06 +00:00
commit 2660007df9
41 changed files with 1304 additions and 209 deletions

View File

@ -3,6 +3,76 @@ Hibernate 5 Changelog
Note: Please refer to JIRA to learn more about each issue. Note: Please refer to JIRA to learn more about each issue.
hanges in 5.4.12.Final (February 13, 2020)
------------------------------------------------------------------------------------------------------------------------
https://hibernate.atlassian.net/projects/HHH/versions/31827/tab/release-report-done
** Bug
* [HHH-13858] - Fix Oracle failing tests
* [HHH-13859] - NPE on scanning for entities in a project having module-info.class resources
** New Feature
* [HHH-13861] - Expose the doWork() and doReturningWork() APIs on StatelessSession as well
* [HHH-13863] - Introduce a module to distribute some helpers useful to compile Hibernate ORM to GraalVM native images
** Improvement
* [HHH-13864] - Cosmetic change of format when reporting version number
Changes in 5.4.11.Final (February 07, 2020)
------------------------------------------------------------------------------------------------------------------------
https://hibernate.atlassian.net/projects/HHH/versions/31818/tab/release-report-done
** Bug
* [HHH-6615] - int type in Revision number
* [HHH-6686] - JPQL operator "is empty" failes for @ElementCollection
* [HHH-10844] - Resolve columnDefinition to appropriate sql-type for audit mappings
* [HHH-13373] - Hibernate report query hibernate_sequence table error in spring-boot application starting on a multi-database mariadb server
* [HHH-13456] - ForeignGenerator Throws ClassCastException When Using StatelessSession
* [HHH-13472] - Error creating hibernate_sequence in MariaDB 10.3
* [HHH-13644] - NullPointerException when calling StoredProcedureQuery.getResultStream() instead of StoredProcedureQuery.getResultList()
* [HHH-13677] - org.hibernate.flushMode property not applied
* [HHH-13704] - Make sure javassist is really an optional dependency
* [HHH-13752] - Delete doesn't work when many-to-many uses non-primary key for join table
* [HHH-13759] - Bytecode enhancement fails for an embedded field in a MappedSuperclass
* [HHH-13760] - Envers tries to use relationship's entity as value for column instead of numeric identifier (cast class exception happens) for LAZY @ManyToOne
* [HHH-13770] - Envers - modified flag column value set to null from 5.4.7 onwards
* [HHH-13780] - Allow NamedQuery to set hint QueryHints.PASS_DISTINCT_THROUGH
* [HHH-13783] - org.hibernate.MappingException: The increment size of the sequence is set to [10] in the entity mapping while … size is [1]
* [HHH-13792] - L2 entity cache is evicted prior to committing transaction for HQL/native updates
* [HHH-13796] - Missing from clause in query from BinaryLogicOperatorNode row value constructor translation
* [HHH-13804] - HibernateProxy might need to be instantiated even with build-time enhancement
* [HHH-13806] - CoreMessageLogger#unableToLoadCommand is not printing the cause of the error
* [HHH-13808] - Incorrect String format in log
* [HHH-13831] - Replaced listener is not called when EventListenerGroup#fireEventOnEachListener is called
** Task
* [HHH-13726] - Extract org.hibernate.internal.SessionFactoryImpl#prepareEventListeners from SessionFactoryImpl
* [HHH-13767] - Remove mention of Oracle and DB2 not being in MC
* [HHH-13821] - Update to Byte Buddy 1.10.7
* [HHH-13822] - OSGi integration tests need to be able to download dependencies from Maven Central using HTTPS
* [HHH-13823] - Various visibility changes to help prototyping of Hibernate RX
* [HHH-13833] - Byte Buddy enhancer should use ASM7 opcodes to improve compatibility with code compiled for Java 11
* [HHH-13837] - Initialize the Hibernate VERSION as a real constant
* [HHH-13838] - Allow extension of PersistenceXmlParser
* [HHH-13849] - Convert ProxyFactoryFactory and BytecodeProvider into a Service
** Improvement
* [HHH-8776] - Ability for JPA entity-graphs to handle non-lazy attributes as lazy
* [HHH-11958] - Apply QueryHints.HINT_READONLY to load operations
* [HHH-12856] - Upgrade DB2400 dialect to use the DB2 for i improvements
* [HHH-13390] - Upgrade JPA MetaModel Generator (jpamodelgen) to support Gradle Incremental Compile
* [HHH-13800] - Correct some typos in the javadocs of hibernate-core module
* [HHH-13802] - fix javadoc warnings in 'hibernate-core'
* [HHH-13809] - Various improvements in the user guides
* [HHH-13830] - Fixing typo on the build task description
* [HHH-13832] - Optimise setting of default Flush Mode on a newly created Session
* [HHH-13850] - Clear the BytecodeProvider caches both after SessionFactory creation and stop
* [HHH-13851] - Rework initialization of ProxyFactoryFactory to move responsibility out of PojoEntityTuplizer
* [HHH-13854] - Allow extensions of StandardServiceRegistryBuilder to ignore Environment variables
Changes in 6.0.0.Alpha4 (December 20, 2019) Changes in 6.0.0.Alpha4 (December 20, 2019)
------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------
@ -22,8 +92,6 @@ https://hibernate.atlassian.net/projects/HHH/versions/31817
* [HHH-13769] - Avoid unnecessary joins * [HHH-13769] - Avoid unnecessary joins
Changes in 5.4.10.Final (December 05, 2019) Changes in 5.4.10.Final (December 05, 2019)
------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------

View File

@ -48,6 +48,9 @@ ext {
jaxbApiVersionOsgiRange = "[2.2,3)" jaxbApiVersionOsgiRange = "[2.2,3)"
jaxbRuntimeVersion = '2.3.1' jaxbRuntimeVersion = '2.3.1'
//GraalVM
graalvmVersion = '19.3.1'
libraries = [ libraries = [
// Ant // Ant
ant: 'org.apache.ant:ant:1.8.2', ant: 'org.apache.ant:ant:1.8.2',
@ -143,7 +146,7 @@ ext {
// EL required by Hibernate Validator at test runtime // EL required by Hibernate Validator at test runtime
expression_language: "org.glassfish:javax.el:${elVersion}", expression_language: "org.glassfish:javax.el:${elVersion}",
c3p0: "com.mchange:c3p0:0.9.5.2", c3p0: "com.mchange:c3p0:0.9.5.3",
ehcache: "net.sf.ehcache:ehcache:2.10.6", ehcache: "net.sf.ehcache:ehcache:2.10.6",
ehcache3: "org.ehcache:ehcache:3.6.1", ehcache3: "org.ehcache:ehcache:3.6.1",
jcache: "javax.cache:cache-api:1.0.0", jcache: "javax.cache:cache-api:1.0.0",
@ -179,10 +182,11 @@ ext {
jboss_ejb_spec_jar : 'org.jboss.spec.javax.ejb:jboss-ejb-api_3.2_spec:1.0.0.Final', jboss_ejb_spec_jar : 'org.jboss.spec.javax.ejb:jboss-ejb-api_3.2_spec:1.0.0.Final',
jboss_annotation_spec_jar : 'org.jboss.spec.javax.annotation:jboss-annotations-api_1.2_spec:1.0.0.Final', jboss_annotation_spec_jar : 'org.jboss.spec.javax.annotation:jboss-annotations-api_1.2_spec:1.0.0.Final',
asciidoclet : 'org.asciidoctor:asciidoclet:1.+' asciidoclet : 'org.asciidoctor:asciidoclet:1.+',
//asciidoclet : 'org.asciidoctor:asciidoclet:1.5.7-SNAPSHOT' //asciidoclet : 'org.asciidoctor:asciidoclet:1.5.7-SNAPSHOT'
] graalvm_nativeimage : "org.graalvm.nativeimage:svm:${graalvmVersion}"
]
} }
configurations.all { configurations.all {

View File

@ -18,8 +18,6 @@ import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate; import javax.persistence.criteria.CriteriaUpdate;
import org.hibernate.graph.RootGraph; import org.hibernate.graph.RootGraph;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import org.hibernate.stat.SessionStatistics; import org.hibernate.stat.SessionStatistics;
/** /**
@ -940,27 +938,6 @@ public interface Session extends SharedSessionContract, EntityManager, AutoClose
*/ */
void setReadOnly(Object entityOrProxy, boolean readOnly); void setReadOnly(Object entityOrProxy, boolean readOnly);
/**
* Controller for allowing users to perform JDBC related work using the Connection managed by this Session.
*
* @param work The work to be performed.
* @throws HibernateException Generally indicates wrapped {@link java.sql.SQLException}
*/
void doWork(Work work) throws HibernateException;
/**
* Controller for allowing users to perform JDBC related work using the Connection managed by this Session. After
* execution returns the result of the {@link ReturningWork#execute} call.
*
* @param work The work to be performed.
* @param <T> The type of the result returned from the work
*
* @return the result from calling {@link ReturningWork#execute}.
*
* @throws HibernateException Generally indicates wrapped {@link java.sql.SQLException}
*/
<T> T doReturningWork(ReturningWork<T> work) throws HibernateException;
@Override @Override
<T> RootGraph<T> createEntityGraph(Class<T> rootType); <T> RootGraph<T> createEntityGraph(Class<T> rootType);

View File

@ -13,6 +13,8 @@ import javax.persistence.criteria.CriteriaDelete;
import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.CriteriaUpdate; import javax.persistence.criteria.CriteriaUpdate;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import org.hibernate.procedure.ProcedureCall; import org.hibernate.procedure.ProcedureCall;
import org.hibernate.query.QueryProducer; import org.hibernate.query.QueryProducer;
@ -163,4 +165,30 @@ public interface SharedSessionContract extends QueryProducer, Serializable {
org.hibernate.query.Query createQuery(CriteriaDelete deleteQuery); org.hibernate.query.Query createQuery(CriteriaDelete deleteQuery);
<T> org.hibernate.query.Query<T> createNamedQuery(String name, Class<T> resultType); <T> org.hibernate.query.Query<T> createNamedQuery(String name, Class<T> resultType);
/**
* Controller for allowing users to perform JDBC related work using the Connection managed by this Session.
*
* @param work The work to be performed.
* @throws HibernateException Generally indicates wrapped {@link java.sql.SQLException}
*/
default void doWork(Work work) throws HibernateException {
throw new UnsupportedOperationException( "The doWork method has not been implemented in this implementation of org.hibernate.engine.spi.SharedSessionContractImplemento" );
}
/**
* Controller for allowing users to perform JDBC related work using the Connection managed by this Session. After
* execution returns the result of the {@link ReturningWork#execute} call.
*
* @param work The work to be performed.
* @param <T> The type of the result returned from the work
*
* @return the result from calling {@link ReturningWork#execute}.
*
* @throws HibernateException Generally indicates wrapped {@link java.sql.SQLException}
*/
default <T> T doReturningWork(ReturningWork<T> work) throws HibernateException {
throw new UnsupportedOperationException( "The doReturningWork method has not been implemented in this implementation of org.hibernate.engine.spi.SharedSessionContractImplemento" );
}
} }

View File

@ -52,6 +52,6 @@ public final class Version {
*/ */
@AllowSysOut @AllowSysOut
public static void main(String[] args) { public static void main(String[] args) {
System.out.println( "Hibernate Core {" + getVersionString() + "}" ); System.out.println( "Hibernate ORM core version " + getVersionString() );
} }
} }

View File

@ -0,0 +1,24 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.boot.archive.scan.internal;
import org.hibernate.boot.archive.spi.ArchiveContext;
import org.hibernate.boot.archive.spi.ArchiveEntry;
import org.hibernate.boot.archive.spi.ArchiveEntryHandler;
public final class NoopEntryHandler implements ArchiveEntryHandler {
public static final ArchiveEntryHandler NOOP_INSTANCE = new NoopEntryHandler();
private NoopEntryHandler() {
//Use the singleton.
}
@Override
public void handleEntry(ArchiveEntry entry, ArchiveContext context) {
}
}

View File

@ -10,6 +10,7 @@ import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import org.hibernate.boot.archive.scan.internal.NoopEntryHandler;
import org.hibernate.boot.archive.scan.internal.ScanResultCollector; import org.hibernate.boot.archive.scan.internal.ScanResultCollector;
import org.hibernate.boot.archive.spi.ArchiveContext; import org.hibernate.boot.archive.spi.ArchiveContext;
import org.hibernate.boot.archive.spi.ArchiveDescriptor; import org.hibernate.boot.archive.spi.ArchiveDescriptor;
@ -132,6 +133,12 @@ public abstract class AbstractScannerImpl implements Scanner {
if ( nameWithinArchive.endsWith( "package-info.class" ) ) { if ( nameWithinArchive.endsWith( "package-info.class" ) ) {
return packageEntryHandler; return packageEntryHandler;
} }
else if ( nameWithinArchive.endsWith( "module-info.class" ) ) {
//There's two reasons to skip this: the most important one is that Jandex
//is unable to analyze them, so we need to dodge it.
//Secondarily, we have no use for these so let's save the effort.
return NoopEntryHandler.NOOP_INSTANCE;
}
else if ( nameWithinArchive.endsWith( ".class" ) ) { else if ( nameWithinArchive.endsWith( ".class" ) ) {
return classEntryHandler; return classEntryHandler;
} }

View File

@ -6,7 +6,6 @@
*/ */
package org.hibernate.boot.archive.scan.spi; package org.hibernate.boot.archive.scan.spi;
import java.io.DataInputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import javax.persistence.Converter; import javax.persistence.Converter;

View File

@ -20,10 +20,13 @@ import org.hibernate.SessionFactory;
import org.hibernate.SessionFactoryObserver; import org.hibernate.SessionFactoryObserver;
import org.hibernate.boot.SessionFactoryBuilder; import org.hibernate.boot.SessionFactoryBuilder;
import org.hibernate.boot.TempTableDdlTransactionHandling; import org.hibernate.boot.TempTableDdlTransactionHandling;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.spi.BootstrapContext; import org.hibernate.boot.spi.BootstrapContext;
import org.hibernate.boot.spi.MetadataImplementor; import org.hibernate.boot.spi.MetadataImplementor;
import org.hibernate.boot.spi.SessionFactoryBuilderImplementor; import org.hibernate.boot.spi.SessionFactoryBuilderImplementor;
import org.hibernate.boot.spi.SessionFactoryOptions; import org.hibernate.boot.spi.SessionFactoryOptions;
import org.hibernate.bytecode.internal.SessionFactoryObserverForBytecodeEnhancer;
import org.hibernate.bytecode.spi.BytecodeProvider;
import org.hibernate.cache.spi.TimestampsCacheFactory; import org.hibernate.cache.spi.TimestampsCacheFactory;
import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
import org.hibernate.internal.SessionFactoryImpl; import org.hibernate.internal.SessionFactoryImpl;
@ -450,6 +453,9 @@ public class SessionFactoryBuilderImpl implements SessionFactoryBuilderImplement
@Override @Override
public SessionFactory build() { public SessionFactory build() {
metadata.validate(); metadata.validate();
final StandardServiceRegistry serviceRegistry = metadata.getMetadataBuildingOptions().getServiceRegistry();
BytecodeProvider bytecodeProvider = serviceRegistry.getService( BytecodeProvider.class );
addSessionFactoryObservers( new SessionFactoryObserverForBytecodeEnhancer( bytecodeProvider ) );
return new SessionFactoryImpl( metadata, buildSessionFactoryOptions() ); return new SessionFactoryImpl( metadata, buildSessionFactoryOptions() );
} }

View File

@ -97,11 +97,12 @@ public class StandardServiceRegistryBuilder {
} }
/** /**
* Intended for use exclusively from JPA boot-strapping. * Intended for use exclusively from JPA boot-strapping, or extensions of
* this class. Consider this an SPI.
* *
* @see #forJpa * @see #forJpa
*/ */
private StandardServiceRegistryBuilder( protected StandardServiceRegistryBuilder(
BootstrapServiceRegistry bootstrapServiceRegistry, BootstrapServiceRegistry bootstrapServiceRegistry,
Map settings, Map settings,
LoadedConfig loadedConfig) { LoadedConfig loadedConfig) {

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.bytecode.internal;
import java.util.Map;
import org.hibernate.boot.registry.StandardServiceInitiator;
import org.hibernate.bytecode.spi.BytecodeProvider;
import org.hibernate.cfg.Environment;
import org.hibernate.service.spi.ServiceRegistryImplementor;
public final class BytecodeProviderInitiator implements StandardServiceInitiator<BytecodeProvider> {
/**
* Singleton access
*/
public static final StandardServiceInitiator<BytecodeProvider> INSTANCE = new BytecodeProviderInitiator();
@Override
public BytecodeProvider initiateService(Map configurationValues, ServiceRegistryImplementor registry) {
// TODO in 6 this will no longer use Environment, which is configured via global environment variables,
// but move to a component which can be reconfigured differently in each registry.
return Environment.getBytecodeProvider();
}
@Override
public Class<BytecodeProvider> getServiceInitiated() {
return BytecodeProvider.class;
}
}

View File

@ -0,0 +1,39 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.bytecode.internal;
import java.util.Map;
import org.hibernate.boot.registry.StandardServiceInitiator;
import org.hibernate.bytecode.spi.BytecodeProvider;
import org.hibernate.bytecode.spi.ProxyFactoryFactory;
import org.hibernate.service.spi.ServiceRegistryImplementor;
/**
* Most commonly the {@link ProxyFactoryFactory} will depend directly on the chosen {@link BytecodeProvider},
* however by registering them as two separate services we can allow to override either one
* or both of them.
* @author Sanne Grinovero
*/
public final class ProxyFactoryFactoryInitiator implements StandardServiceInitiator<ProxyFactoryFactory> {
/**
* Singleton access
*/
public static final StandardServiceInitiator<ProxyFactoryFactory> INSTANCE = new ProxyFactoryFactoryInitiator();
@Override
public ProxyFactoryFactory initiateService(Map configurationValues, ServiceRegistryImplementor registry) {
final BytecodeProvider bytecodeProvider = registry.getService( BytecodeProvider.class );
return bytecodeProvider.getProxyFactoryFactory();
}
@Override
public Class<ProxyFactoryFactory> getServiceInitiated() {
return ProxyFactoryFactory.class;
}
}

View File

@ -0,0 +1,35 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.bytecode.internal;
import org.hibernate.SessionFactory;
import org.hibernate.SessionFactoryObserver;
import org.hibernate.bytecode.spi.BytecodeProvider;
public final class SessionFactoryObserverForBytecodeEnhancer implements SessionFactoryObserver {
private final BytecodeProvider bytecodeProvider;
public SessionFactoryObserverForBytecodeEnhancer(BytecodeProvider bytecodeProvider) {
this.bytecodeProvider = bytecodeProvider;
}
@Override
public void sessionFactoryCreated(final SessionFactory factory) {
this.bytecodeProvider.resetCaches();
}
@Override
public void sessionFactoryClosing(final SessionFactory factory) {
this.bytecodeProvider.resetCaches();
}
@Override
public void sessionFactoryClosed(final SessionFactory factory) {
this.bytecodeProvider.resetCaches();
}
}

View File

@ -159,7 +159,7 @@ public final class ByteBuddyState {
/** /**
* Wipes out all known caches used by ByteBuddy. This implies it might trigger the need * Wipes out all known caches used by ByteBuddy. This implies it might trigger the need
* to re-create some helpers if used at runtime, especially as this state is shared by * to re-create some helpers if used at runtime, especially as this state might be shared by
* multiple SessionFactory instances, but at least ensures we cleanup anything which is no * multiple SessionFactory instances, but at least ensures we cleanup anything which is no
* longer needed after a SessionFactory close. * longer needed after a SessionFactory close.
* The assumption is that closing SessionFactories is a rare event; in this perspective the cost * The assumption is that closing SessionFactories is a rare event; in this perspective the cost

View File

@ -8,6 +8,7 @@ package org.hibernate.bytecode.spi;
import org.hibernate.bytecode.enhance.spi.EnhancementContext; import org.hibernate.bytecode.enhance.spi.EnhancementContext;
import org.hibernate.bytecode.enhance.spi.Enhancer; import org.hibernate.bytecode.enhance.spi.Enhancer;
import org.hibernate.service.Service;
/** /**
* Contract for providers of bytecode services to Hibernate. * Contract for providers of bytecode services to Hibernate.
@ -19,7 +20,7 @@ import org.hibernate.bytecode.enhance.spi.Enhancer;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface BytecodeProvider { public interface BytecodeProvider extends Service {
/** /**
* Retrieve the specific factory for this provider capable of * Retrieve the specific factory for this provider capable of
* generating run-time proxies for lazy-loading purposes. * generating run-time proxies for lazy-loading purposes.

View File

@ -6,11 +6,9 @@
*/ */
package org.hibernate.bytecode.spi; package org.hibernate.bytecode.spi;
import org.hibernate.SessionFactory;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.proxy.ProxyFactory; import org.hibernate.proxy.ProxyFactory;
import org.hibernate.service.ServiceRegistry; import org.hibernate.service.Service;
/** /**
* An interface for factories of {@link ProxyFactory proxy factory} instances. * An interface for factories of {@link ProxyFactory proxy factory} instances.
@ -20,7 +18,7 @@ import org.hibernate.service.ServiceRegistry;
* *
* @author Steve Ebersole * @author Steve Ebersole
*/ */
public interface ProxyFactoryFactory { public interface ProxyFactoryFactory extends Service {
/** /**
* Build a proxy factory specifically for handling runtime * Build a proxy factory specifically for handling runtime
* lazy loading. * lazy loading.

View File

@ -198,13 +198,18 @@ public abstract class AbstractReadWriteAccess extends AbstractCachedDomainDataAc
public void remove(SharedSessionContractImplementor session, Object key) { public void remove(SharedSessionContractImplementor session, Object key) {
if ( getStorageAccess().getFromCache( key, session ) instanceof SoftLock ) { if ( getStorageAccess().getFromCache( key, session ) instanceof SoftLock ) {
log.debugf( "Skipping #remove call in read-write access to maintain SoftLock : %s", key ); log.debugf( "Skipping #remove call in read-write access to maintain SoftLock : %s", key );
// don'tm do anything... we want the SoftLock to remain in place // don't do anything... we want the SoftLock to remain in place
} }
else { else {
super.remove( session, key ); super.remove( session, key );
} }
} }
@Override
public void removeAll(SharedSessionContractImplementor session) {
// A no-op
}
/** /**
* Interface type implemented by all wrapper objects in the cache. * Interface type implemented by all wrapper objects in the cache.
*/ */

View File

@ -151,9 +151,4 @@ public class EntityReadWriteAccess extends AbstractReadWriteAccess implements En
public SoftLock lockRegion() { public SoftLock lockRegion() {
return null; return null;
} }
@Override
public void unlockRegion(SoftLock lock) {
}
} }

View File

@ -351,8 +351,15 @@ public final class Environment implements AvailableSettings {
LOG.bytecodeProvider( providerName ); LOG.bytecodeProvider( providerName );
// todo : allow a custom class name - just check if the config is a FQN // there is no need to support plugging in a custom BytecodeProvider via FQCN:
// currently we assume it is only ever the Strings "javassist" or "bytebuddy"... // - the static helper methods on this class are deprecated
// - it's possible to plug a custom BytecodeProvider directly into the ServiceRegistry
//
// This also allows integrators to inject a BytecodeProvider instance which has some
// state; particularly useful to inject proxy definitions which have been prepared in
// advance.
// See also https://hibernate.atlassian.net/browse/HHH-13804 and how this was solved in
// Quarkus.
LOG.unknownBytecodeProvider( providerName, BYTECODE_PROVIDER_NAME_DEFAULT ); LOG.unknownBytecodeProvider( providerName, BYTECODE_PROVIDER_NAME_DEFAULT );
return new org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl(); return new org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl();

View File

@ -51,6 +51,9 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.engine.transaction.internal.TransactionImpl; import org.hibernate.engine.transaction.internal.TransactionImpl;
import org.hibernate.engine.transaction.spi.TransactionImplementor; import org.hibernate.engine.transaction.spi.TransactionImplementor;
import org.hibernate.id.uuid.StandardRandomStrategy; import org.hibernate.id.uuid.StandardRandomStrategy;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import org.hibernate.jdbc.WorkExecutorVisitable;
import org.hibernate.jpa.QueryHints; import org.hibernate.jpa.QueryHints;
import org.hibernate.jpa.internal.util.FlushModeTypeHelper; import org.hibernate.jpa.internal.util.FlushModeTypeHelper;
import org.hibernate.persister.entity.EntityPersister; import org.hibernate.persister.entity.EntityPersister;
@ -728,6 +731,28 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
return buildNamedQuery( name, resultClass ); return buildNamedQuery( name, resultClass );
} }
@Override
public void doWork(final Work work) throws HibernateException {
WorkExecutorVisitable<Void> realWork = (workExecutor, connection) -> {
workExecutor.executeWork( work, connection );
return null;
};
doWork( realWork );
}
@Override
public <T> T doReturningWork(final ReturningWork<T> work) throws HibernateException {
WorkExecutorVisitable<T> realWork = (workExecutor, connection) -> workExecutor.executeReturningWork(
work,
connection
);
return doWork( realWork );
}
private <T> T doWork(WorkExecutorVisitable<T> work) throws HibernateException {
return getJdbcCoordinator().coordinateWork( work );
}
protected <T> QueryImplementor<T> buildNamedQuery(String queryName, Class<T> resultType) { protected <T> QueryImplementor<T> buildNamedQuery(String queryName, Class<T> resultType) {
checkOpen(); checkOpen();
pulseTransactionCoordinator(); pulseTransactionCoordinator();
@ -794,7 +819,6 @@ public abstract class AbstractSharedSessionContract implements SharedSessionCont
throw getExceptionConverter().convert( new IllegalArgumentException( "No query defined for that name [" + queryName + "]" ) ); throw getExceptionConverter().convert( new IllegalArgumentException( "No query defined for that name [" + queryName + "]" ) );
} }
@Override @Override
@SuppressWarnings("UnnecessaryLocalVariable") @SuppressWarnings("UnnecessaryLocalVariable")
public ProcedureCall getNamedProcedureCall(String name) { public ProcedureCall getNamedProcedureCall(String name) {

View File

@ -1439,7 +1439,7 @@ public interface CoreMessageLogger extends BasicLogger {
void validatorNotFound(); void validatorNotFound();
@LogMessage(level = INFO) @LogMessage(level = INFO)
@Message(value = "Hibernate Core {%s}", id = 412) @Message(value = "Hibernate ORM core version %s", id = 412)
void version(String versionString); void version(String versionString);
@LogMessage(level = WARN) @LogMessage(level = WARN)

View File

@ -770,8 +770,6 @@ public class SessionFactoryImpl implements SessionFactoryImplementor {
* @throws HibernateException * @throws HibernateException
*/ */
public void close() throws HibernateException { public void close() throws HibernateException {
//This is an idempotent operation so we can do it even before the checks (it won't hurt):
Environment.getBytecodeProvider().resetCaches();
synchronized (this) { synchronized (this) {
if ( isClosed ) { if ( isClosed ) {
if ( getSessionFactoryOptions().getJpaCompliance().isJpaClosedComplianceEnabled() ) { if ( getSessionFactoryOptions().getJpaCompliance().isJpaClosedComplianceEnabled() ) {

View File

@ -115,10 +115,6 @@ import org.hibernate.graph.RootGraph;
import org.hibernate.graph.internal.RootGraphImpl; import org.hibernate.graph.internal.RootGraphImpl;
import org.hibernate.graph.spi.GraphImplementor; import org.hibernate.graph.spi.GraphImplementor;
import org.hibernate.graph.spi.RootGraphImplementor; import org.hibernate.graph.spi.RootGraphImplementor;
import org.hibernate.jdbc.ReturningWork;
import org.hibernate.jdbc.Work;
import org.hibernate.jdbc.WorkExecutor;
import org.hibernate.jdbc.WorkExecutorVisitable;
import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.AvailableSettings;
import org.hibernate.jpa.QueryHints; import org.hibernate.jpa.QueryHints;
import org.hibernate.jpa.internal.util.CacheModeHelper; import org.hibernate.jpa.internal.util.CacheModeHelper;
@ -1745,33 +1741,6 @@ public class SessionImpl
persistenceContext.setReadOnly( entity, readOnly ); persistenceContext.setReadOnly( entity, readOnly );
} }
@Override
public void doWork(final Work work) throws HibernateException {
WorkExecutorVisitable<Void> realWork = new WorkExecutorVisitable<Void>() {
@Override
public Void accept(WorkExecutor<Void> workExecutor, Connection connection) throws SQLException {
workExecutor.executeWork( work, connection );
return null;
}
};
doWork( realWork );
}
@Override
public <T> T doReturningWork(final ReturningWork<T> work) throws HibernateException {
WorkExecutorVisitable<T> realWork = new WorkExecutorVisitable<T>() {
@Override
public T accept(WorkExecutor<T> workExecutor, Connection connection) throws SQLException {
return workExecutor.executeReturningWork( work, connection );
}
};
return doWork( realWork );
}
private <T> T doWork(WorkExecutorVisitable<T> work) throws HibernateException {
return getJdbcCoordinator().coordinateWork( work );
}
@Override @Override
public void afterScrollOperation() { public void afterScrollOperation() {
// nothing to do in a stateful session // nothing to do in a stateful session

View File

@ -3921,7 +3921,7 @@ public abstract class AbstractEntityPersister
final String[] deleteStrings; final String[] deleteStrings;
if ( isImpliedOptimisticLocking && loadedState != null ) { if ( isImpliedOptimisticLocking && loadedState != null ) {
// we need to utilize dynamic delete statements // we need to utilize dynamic delete statements
deleteStrings = generateSQLDeletStrings( loadedState ); deleteStrings = generateSQLDeleteStrings( loadedState );
} }
else { else {
// otherwise, utilize the static delete statements // otherwise, utilize the static delete statements
@ -3939,7 +3939,7 @@ public abstract class AbstractEntityPersister
|| entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.ALL; || entityMetamodel.getOptimisticLockStyle() == OptimisticLockStyle.ALL;
} }
private String[] generateSQLDeletStrings(Object[] loadedState) { private String[] generateSQLDeleteStrings(Object[] loadedState) {
int span = getTableSpan(); int span = getTableSpan();
String[] deleteStrings = new String[span]; String[] deleteStrings = new String[span];
for ( int j = span - 1; j >= 0; j-- ) { for ( int j = span - 1; j >= 0; j-- ) {

View File

@ -0,0 +1,115 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.proxy.pojo;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.Set;
import org.hibernate.MappingException;
import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property;
import org.hibernate.mapping.Subclass;
import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.Setter;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.tuple.entity.PojoEntityTuplizer;
/**
* Most of this code was originally an internal detail of {@link PojoEntityTuplizer},
* then extracted to make it easier for integrators to initialize a custom
* {@link org.hibernate.proxy.ProxyFactory}.
*/
public final class ProxyFactoryHelper {
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( ProxyFactoryHelper.class );
private ProxyFactoryHelper() {
//not meant to be instantiated
}
public static Set<Class> extractProxyInterfaces(final PersistentClass persistentClass, final String entityName) {
/*
* We need to preserve the order of the interfaces they were put into the set, since javassist will choose the
* first one's class-loader to construct the proxy class with. This is also the reason why HibernateProxy.class
* should be the last one in the order (on JBossAS7 its class-loader will be org.hibernate module's class-
* loader, which will not see the classes inside deployed apps. See HHH-3078
*/
final Set<Class> proxyInterfaces = new java.util.LinkedHashSet<Class>();
final Class mappedClass = persistentClass.getMappedClass();
final Class proxyInterface = persistentClass.getProxyInterface();
if ( proxyInterface != null && !mappedClass.equals( proxyInterface ) ) {
if ( !proxyInterface.isInterface() ) {
throw new MappingException(
"proxy must be either an interface, or the class itself: " + entityName
);
}
proxyInterfaces.add( proxyInterface );
}
if ( mappedClass.isInterface() ) {
proxyInterfaces.add( mappedClass );
}
Iterator<Subclass> subclasses = persistentClass.getSubclassIterator();
while ( subclasses.hasNext() ) {
final Subclass subclass = subclasses.next();
final Class subclassProxy = subclass.getProxyInterface();
final Class subclassClass = subclass.getMappedClass();
if ( subclassProxy != null && !subclassClass.equals( subclassProxy ) ) {
if ( !subclassProxy.isInterface() ) {
throw new MappingException(
"proxy must be either an interface, or the class itself: " + subclass.getEntityName()
);
}
proxyInterfaces.add( subclassProxy );
}
}
proxyInterfaces.add( HibernateProxy.class );
return proxyInterfaces;
}
public static void validateProxyability(final PersistentClass persistentClass) {
Iterator properties = persistentClass.getPropertyIterator();
Class clazz = persistentClass.getMappedClass();
while ( properties.hasNext() ) {
Property property = (Property) properties.next();
Method method = property.getGetter( clazz ).getMethod();
if ( method != null && Modifier.isFinal( method.getModifiers() ) ) {
LOG.gettersOfLazyClassesCannotBeFinal( persistentClass.getEntityName(), property.getName() );
}
method = property.getSetter( clazz ).getMethod();
if ( method != null && Modifier.isFinal( method.getModifiers() ) ) {
LOG.settersOfLazyClassesCannotBeFinal( persistentClass.getEntityName(), property.getName() );
}
}
}
public static Method extractProxySetIdentifierMethod(final Setter idSetter, final Class proxyInterface) {
Method idSetterMethod = idSetter == null ? null : idSetter.getMethod();
Method proxySetIdentifierMethod = idSetterMethod == null || proxyInterface == null ?
null :
ReflectHelper.getMethod( proxyInterface, idSetterMethod );
return proxySetIdentifierMethod;
}
public static Method extractProxyGetIdentifierMethod(final Getter idGetter, final Class proxyInterface) {
Method idGetterMethod = idGetter == null ? null : idGetter.getMethod();
Method proxyGetIdentifierMethod = idGetterMethod == null || proxyInterface == null ?
null :
ReflectHelper.getMethod( proxyInterface, idGetterMethod );
return proxyGetIdentifierMethod;
}
}

View File

@ -12,6 +12,8 @@ import java.util.List;
import org.hibernate.boot.cfgxml.internal.CfgXmlAccessServiceInitiator; import org.hibernate.boot.cfgxml.internal.CfgXmlAccessServiceInitiator;
import org.hibernate.boot.registry.StandardServiceInitiator; import org.hibernate.boot.registry.StandardServiceInitiator;
import org.hibernate.bytecode.internal.BytecodeProviderInitiator;
import org.hibernate.bytecode.internal.ProxyFactoryFactoryInitiator;
import org.hibernate.cache.internal.RegionFactoryInitiator; import org.hibernate.cache.internal.RegionFactoryInitiator;
import org.hibernate.engine.config.internal.ConfigurationServiceInitiator; import org.hibernate.engine.config.internal.ConfigurationServiceInitiator;
import org.hibernate.engine.jdbc.batch.internal.BatchBuilderInitiator; import org.hibernate.engine.jdbc.batch.internal.BatchBuilderInitiator;
@ -51,6 +53,9 @@ public final class StandardServiceInitiators {
private static List<StandardServiceInitiator> buildStandardServiceInitiatorList() { private static List<StandardServiceInitiator> buildStandardServiceInitiatorList() {
final ArrayList<StandardServiceInitiator> serviceInitiators = new ArrayList<StandardServiceInitiator>(); final ArrayList<StandardServiceInitiator> serviceInitiators = new ArrayList<StandardServiceInitiator>();
serviceInitiators.add( BytecodeProviderInitiator.INSTANCE );
serviceInitiators.add( ProxyFactoryFactoryInitiator.INSTANCE );
serviceInitiators.add( CfgXmlAccessServiceInitiator.INSTANCE ); serviceInitiators.add( CfgXmlAccessServiceInitiator.INSTANCE );
serviceInitiators.add( ConfigurationServiceInitiator.INSTANCE ); serviceInitiators.add( ConfigurationServiceInitiator.INSTANCE );
serviceInitiators.add( PropertyAccessStrategyResolverInitiator.INSTANCE ); serviceInitiators.add( PropertyAccessStrategyResolverInitiator.INSTANCE );

View File

@ -5,12 +5,15 @@
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.tuple.component; package org.hibernate.tuple.component;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import org.hibernate.AssertionFailure; import org.hibernate.AssertionFailure;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.bytecode.spi.BasicProxyFactory; import org.hibernate.bytecode.spi.BasicProxyFactory;
import org.hibernate.bytecode.spi.BytecodeProvider;
import org.hibernate.bytecode.spi.ProxyFactoryFactory;
import org.hibernate.bytecode.spi.ReflectionOptimizer; import org.hibernate.bytecode.spi.ReflectionOptimizer;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionFactoryImplementor;
@ -67,9 +70,8 @@ public class PojoComponentTuplizer extends AbstractComponentTuplizer {
optimizer = null; optimizer = null;
} }
else { else {
// TODO: here is why we need to make bytecode provider global :( final BytecodeProvider bytecodeProvider = component.getServiceRegistry().getService( BytecodeProvider.class );
// TODO : again, fix this after HHH-1907 is complete optimizer = bytecodeProvider.getReflectionOptimizer(
optimizer = Environment.getBytecodeProvider().getReflectionOptimizer(
componentClass, getterNames, setterNames, propTypes componentClass, getterNames, setterNames, propTypes
); );
} }
@ -124,7 +126,8 @@ public class PojoComponentTuplizer extends AbstractComponentTuplizer {
protected Instantiator buildInstantiator(Component component) { protected Instantiator buildInstantiator(Component component) {
if ( component.isEmbedded() && ReflectHelper.isAbstractClass( this.componentClass ) ) { if ( component.isEmbedded() && ReflectHelper.isAbstractClass( this.componentClass ) ) {
return new ProxiedInstantiator( this.componentClass ); ProxyFactoryFactory proxyFactoryFactory = component.getServiceRegistry().getService( ProxyFactoryFactory.class );
return new ProxiedInstantiator( this.componentClass, proxyFactoryFactory );
} }
if ( optimizer == null ) { if ( optimizer == null ) {
return new PojoInstantiator( this.componentClass, null ); return new PojoInstantiator( this.componentClass, null );
@ -151,16 +154,14 @@ public class PojoComponentTuplizer extends AbstractComponentTuplizer {
private final Class proxiedClass; private final Class proxiedClass;
private final BasicProxyFactory factory; private final BasicProxyFactory factory;
public ProxiedInstantiator(Class componentClass) { public ProxiedInstantiator(Class componentClass, ProxyFactoryFactory proxyFactoryFactory) {
proxiedClass = componentClass; proxiedClass = componentClass;
if ( proxiedClass.isInterface() ) { if ( proxiedClass.isInterface() ) {
factory = Environment.getBytecodeProvider() factory = proxyFactoryFactory
.getProxyFactoryFactory()
.buildBasicProxyFactory( null, new Class[] { proxiedClass } ); .buildBasicProxyFactory( null, new Class[] { proxiedClass } );
} }
else { else {
factory = Environment.getBytecodeProvider() factory = proxyFactoryFactory
.getProxyFactoryFactory()
.buildBasicProxyFactory( proxiedClass, null ); .buildBasicProxyFactory( proxiedClass, null );
} }
} }

View File

@ -7,17 +7,16 @@
package org.hibernate.tuple.entity; package org.hibernate.tuple.entity;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.hibernate.EntityMode; import org.hibernate.EntityMode;
import org.hibernate.EntityNameResolver; import org.hibernate.EntityNameResolver;
import org.hibernate.HibernateException; import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor; import org.hibernate.bytecode.enhance.spi.interceptor.BytecodeLazyAttributeInterceptor;
import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor; import org.hibernate.bytecode.enhance.spi.interceptor.EnhancementAsProxyLazinessInterceptor;
import org.hibernate.bytecode.spi.BytecodeProvider;
import org.hibernate.bytecode.spi.ProxyFactoryFactory;
import org.hibernate.bytecode.spi.ReflectionOptimizer; import org.hibernate.bytecode.spi.ReflectionOptimizer;
import org.hibernate.cfg.Environment; import org.hibernate.cfg.Environment;
import org.hibernate.classic.Lifecycle; import org.hibernate.classic.Lifecycle;
@ -27,14 +26,12 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor; import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.internal.CoreLogging; import org.hibernate.internal.CoreLogging;
import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.CoreMessageLogger;
import org.hibernate.internal.util.ReflectHelper;
import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.PersistentClass;
import org.hibernate.mapping.Property; import org.hibernate.mapping.Property;
import org.hibernate.mapping.Subclass;
import org.hibernate.property.access.spi.Getter; import org.hibernate.property.access.spi.Getter;
import org.hibernate.property.access.spi.Setter; import org.hibernate.property.access.spi.Setter;
import org.hibernate.proxy.HibernateProxy;
import org.hibernate.proxy.ProxyFactory; import org.hibernate.proxy.ProxyFactory;
import org.hibernate.proxy.pojo.ProxyFactoryHelper;
import org.hibernate.tuple.Instantiator; import org.hibernate.tuple.Instantiator;
import org.hibernate.type.CompositeType; import org.hibernate.type.CompositeType;
@ -52,15 +49,11 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer {
private final boolean lifecycleImplementor; private final boolean lifecycleImplementor;
private final ReflectionOptimizer optimizer; private final ReflectionOptimizer optimizer;
private final boolean isBytecodeEnhanced;
public PojoEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) { public PojoEntityTuplizer(EntityMetamodel entityMetamodel, PersistentClass mappedEntity) {
super( entityMetamodel, mappedEntity ); super( entityMetamodel, mappedEntity );
this.mappedClass = mappedEntity.getMappedClass(); this.mappedClass = mappedEntity.getMappedClass();
this.proxyInterface = mappedEntity.getProxyInterface(); this.proxyInterface = mappedEntity.getProxyInterface();
this.lifecycleImplementor = Lifecycle.class.isAssignableFrom( mappedClass ); this.lifecycleImplementor = Lifecycle.class.isAssignableFrom( mappedClass );
this.isBytecodeEnhanced = entityMetamodel.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading();
String[] getterNames = new String[propertySpan]; String[] getterNames = new String[propertySpan];
String[] setterNames = new String[propertySpan]; String[] setterNames = new String[propertySpan];
@ -75,16 +68,13 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer {
optimizer = null; optimizer = null;
} }
else { else {
// todo : YUCK!!! final BytecodeProvider bytecodeProvider = entityMetamodel.getSessionFactory().getServiceRegistry().getService( BytecodeProvider.class );
optimizer = Environment.getBytecodeProvider().getReflectionOptimizer( optimizer = bytecodeProvider.getReflectionOptimizer(
mappedClass, mappedClass,
getterNames, getterNames,
setterNames, setterNames,
propTypes propTypes
); );
// optimizer = getFactory().getSettings().getBytecodeProvider().getReflectionOptimizer(
// mappedClass, getterNames, setterNames, propTypes
// );
} }
} }
@ -92,76 +82,21 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer {
protected ProxyFactory buildProxyFactory(PersistentClass persistentClass, Getter idGetter, Setter idSetter) { protected ProxyFactory buildProxyFactory(PersistentClass persistentClass, Getter idGetter, Setter idSetter) {
// determine the id getter and setter methods from the proxy interface (if any) // determine the id getter and setter methods from the proxy interface (if any)
// determine all interfaces needed by the resulting proxy // determine all interfaces needed by the resulting proxy
final String entityName = getEntityName();
final Class mappedClass = persistentClass.getMappedClass();
final Class proxyInterface = persistentClass.getProxyInterface();
/* final Set<Class> proxyInterfaces = ProxyFactoryHelper.extractProxyInterfaces( persistentClass, entityName );
* We need to preserve the order of the interfaces they were put into the set, since javassist will choose the
* first one's class-loader to construct the proxy class with. This is also the reason why HibernateProxy.class
* should be the last one in the order (on JBossAS7 its class-loader will be org.hibernate module's class-
* loader, which will not see the classes inside deployed apps. See HHH-3078
*/
Set<Class> proxyInterfaces = new java.util.LinkedHashSet<Class>();
Class mappedClass = persistentClass.getMappedClass(); ProxyFactoryHelper.validateProxyability( persistentClass );
Class proxyInterface = persistentClass.getProxyInterface();
if ( proxyInterface != null && !mappedClass.equals( proxyInterface ) ) { Method proxyGetIdentifierMethod = ProxyFactoryHelper.extractProxyGetIdentifierMethod( idGetter, proxyInterface );
if ( !proxyInterface.isInterface() ) { Method proxySetIdentifierMethod = ProxyFactoryHelper.extractProxySetIdentifierMethod( idSetter, proxyInterface );
throw new MappingException(
"proxy must be either an interface, or the class itself: " + getEntityName()
);
}
proxyInterfaces.add( proxyInterface );
}
if ( mappedClass.isInterface() ) {
proxyInterfaces.add( mappedClass );
}
Iterator<Subclass> subclasses = persistentClass.getSubclassIterator();
while ( subclasses.hasNext() ) {
final Subclass subclass = subclasses.next();
final Class subclassProxy = subclass.getProxyInterface();
final Class subclassClass = subclass.getMappedClass();
if ( subclassProxy != null && !subclassClass.equals( subclassProxy ) ) {
if ( !subclassProxy.isInterface() ) {
throw new MappingException(
"proxy must be either an interface, or the class itself: " + subclass.getEntityName()
);
}
proxyInterfaces.add( subclassProxy );
}
}
proxyInterfaces.add( HibernateProxy.class );
Iterator properties = persistentClass.getPropertyIterator();
Class clazz = persistentClass.getMappedClass();
while ( properties.hasNext() ) {
Property property = (Property) properties.next();
Method method = property.getGetter( clazz ).getMethod();
if ( method != null && Modifier.isFinal( method.getModifiers() ) ) {
LOG.gettersOfLazyClassesCannotBeFinal( persistentClass.getEntityName(), property.getName() );
}
method = property.getSetter( clazz ).getMethod();
if ( method != null && Modifier.isFinal( method.getModifiers() ) ) {
LOG.settersOfLazyClassesCannotBeFinal( persistentClass.getEntityName(), property.getName() );
}
}
Method idGetterMethod = idGetter == null ? null : idGetter.getMethod();
Method idSetterMethod = idSetter == null ? null : idSetter.getMethod();
Method proxyGetIdentifierMethod = idGetterMethod == null || proxyInterface == null ?
null :
ReflectHelper.getMethod( proxyInterface, idGetterMethod );
Method proxySetIdentifierMethod = idSetterMethod == null || proxyInterface == null ?
null :
ReflectHelper.getMethod( proxyInterface, idSetterMethod );
ProxyFactory pf = buildProxyFactoryInternal( persistentClass, idGetter, idSetter ); ProxyFactory pf = buildProxyFactoryInternal( persistentClass, idGetter, idSetter );
try { try {
pf.postInstantiate( pf.postInstantiate(
getEntityName(), entityName,
mappedClass, mappedClass,
proxyInterfaces, proxyInterfaces,
proxyGetIdentifierMethod, proxyGetIdentifierMethod,
@ -172,7 +107,7 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer {
); );
} }
catch (HibernateException he) { catch (HibernateException he) {
LOG.unableToCreateProxyFactory( getEntityName(), he ); LOG.unableToCreateProxyFactory( entityName, he );
pf = null; pf = null;
} }
return pf; return pf;
@ -182,9 +117,8 @@ public class PojoEntityTuplizer extends AbstractEntityTuplizer {
PersistentClass persistentClass, PersistentClass persistentClass,
Getter idGetter, Getter idGetter,
Setter idSetter) { Setter idSetter) {
// TODO : YUCK!!! fix after HHH-1907 is complete ProxyFactoryFactory proxyFactory = getFactory().getServiceRegistry().getService( ProxyFactoryFactory.class );
return Environment.getBytecodeProvider().getProxyFactoryFactory().buildProxyFactory( getFactory() ); return proxyFactory.buildProxyFactory( getFactory() );
// return getFactory().getSettings().getBytecodeProvider().getProxyFactoryFactory().buildProxyFactory();
} }
@Override @Override

View File

@ -0,0 +1,310 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.cache.spi;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import javax.persistence.Cacheable;
import javax.persistence.Entity;
import javax.persistence.Id;
import org.hibernate.EmptyInterceptor;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.cfg.Configuration;
import org.hibernate.testing.TestForIssue;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.Before;
import org.junit.Test;
import static org.hibernate.testing.transaction.TransactionUtil.doInHibernate;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
/**
* @author Frank Doherty
*/
public class ReadWriteCacheTest extends BaseCoreFunctionalTestCase {
private static final String ORIGINAL_TITLE = "Original Title";
private static final String UPDATED_TITLE = "Updated Title";
private long bookId;
private CountDownLatch endLatch;
private AtomicBoolean interceptTransaction;
@Override
public void buildSessionFactory() {
buildSessionFactory( getCacheConfig() );
}
@Before
public void init() {
endLatch = new CountDownLatch( 1 );
interceptTransaction = new AtomicBoolean();
}
@Override
public void rebuildSessionFactory() {
rebuildSessionFactory( getCacheConfig() );
}
@Test
public void testDelete() throws InterruptedException {
bookId = 1L;
doInHibernate( this::sessionFactory, session -> {
createBook( bookId, session );
} );
doInHibernate( this::sessionFactory, session -> {
log.info( "Delete Book" );
Book book = session.get( Book.class, bookId );
session.delete( book );
interceptTransaction.set( true );
} );
endLatch.await();
interceptTransaction.set( false );
doInHibernate( this::sessionFactory, session -> {
assertBookNotFound( bookId, session );
} );
}
@Test
@TestForIssue(jiraKey = "HHH-13792")
public void testDeleteHQL() throws InterruptedException {
bookId = 2L;
doInHibernate( this::sessionFactory, session -> {
createBook( bookId, session );
} );
doInHibernate( this::sessionFactory, session -> {
log.info( "Delete Book using HQL" );
int numRows = session.createQuery( "delete from Book where id = :id" )
.setParameter( "id", bookId )
.executeUpdate();
assertEquals( 1, numRows );
interceptTransaction.set( true );
} );
endLatch.await();
interceptTransaction.set( false );
doInHibernate( this::sessionFactory, session -> {
assertBookNotFound( bookId, session );
} );
}
@Test
@TestForIssue(jiraKey = "HHH-13792")
public void testDeleteNativeQuery() throws InterruptedException {
bookId = 3L;
doInHibernate( this::sessionFactory, session -> {
createBook( bookId, session );
} );
doInHibernate( this::sessionFactory, session -> {
log.info( "Delete Book using NativeQuery" );
int numRows = session.createNativeQuery( "delete from Book where id = :id" )
.setParameter( "id", bookId )
.addSynchronizedEntityClass( Book.class )
.executeUpdate();
assertEquals( 1, numRows );
interceptTransaction.set( true );
} );
endLatch.await();
interceptTransaction.set( false );
doInHibernate( this::sessionFactory, session -> {
assertBookNotFound( bookId, session );
} );
}
@Test
public void testUpdate() throws InterruptedException {
bookId = 4L;
doInHibernate( this::sessionFactory, session -> {
createBook( bookId, session );
} );
doInHibernate( this::sessionFactory, session -> {
log.info( "Update Book" );
Book book = session.get( Book.class, bookId );
book.setTitle( UPDATED_TITLE );
session.save( book );
interceptTransaction.set( true );
} );
endLatch.await();
interceptTransaction.set( false );
doInHibernate( this::sessionFactory, session -> {
loadBook( bookId, session );
} );
}
@Test
@TestForIssue(jiraKey = "HHH-13792")
public void testUpdateHQL() throws InterruptedException {
bookId = 5L;
doInHibernate( this::sessionFactory, session -> {
createBook( bookId, session );
} );
doInHibernate( this::sessionFactory, session -> {
log.info( "Update Book using HQL" );
int numRows = session.createQuery( "update Book set title = :title where id = :id" )
.setParameter( "title", UPDATED_TITLE )
.setParameter( "id", bookId )
.executeUpdate();
assertEquals( 1, numRows );
interceptTransaction.set( true );
} );
endLatch.await();
interceptTransaction.set( false );
doInHibernate( this::sessionFactory, session -> {
loadBook( bookId, session );
} );
}
@Test
@TestForIssue(jiraKey = "HHH-13792")
public void testUpdateNativeQuery() throws InterruptedException {
bookId = 6L;
doInHibernate( this::sessionFactory, session -> {
createBook( bookId, session );
} );
doInHibernate( this::sessionFactory, session -> {
log.info( "Update Book using NativeQuery" );
int numRows = session.createNativeQuery( "update Book set title = :title where id = :id" )
.setParameter( "title", UPDATED_TITLE )
.setParameter( "id", bookId )
.addSynchronizedEntityClass( Book.class )
.executeUpdate();
assertEquals( 1, numRows );
interceptTransaction.set( true );
} );
endLatch.await();
interceptTransaction.set( false );
doInHibernate( this::sessionFactory, session -> {
loadBook( bookId, session );
} );
}
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] {
Book.class,
};
}
@Override
protected String getCacheConcurrencyStrategy() {
return "read-write";
}
private void assertBookNotFound(long bookId, Session session) {
log.info( "Load Book" );
Book book = session.get( Book.class, bookId );
assertNull( book );
}
private void createBook(long bookId, Session session) {
log.info( "Create Book" );
Book book = new Book();
book.setId( bookId );
book.setTitle( ORIGINAL_TITLE );
session.save( book );
}
private Consumer<Configuration> getCacheConfig() {
return configuration -> configuration.setInterceptor( new TransactionInterceptor() );
}
private void loadBook(long bookId, Session session) {
log.info( "Load Book" );
Book book = session.get( Book.class, bookId );
assertNotNull( book );
assertEquals( "Found old value", UPDATED_TITLE, book.getTitle() );
}
@Entity(name = "Book")
@Cacheable
@org.hibernate.annotations.Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
private static final class Book {
@Id
private Long id;
private String title;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String toString() {
return "Book[id=" + id + ",title=" + title + "]";
}
}
private final class TransactionInterceptor extends EmptyInterceptor {
@Override
public void beforeTransactionCompletion(Transaction tx) {
if ( interceptTransaction.get() ) {
try {
log.info( "Fetch Book" );
executeSync( () -> {
Session session = sessionFactory()
.openSession();
Book book = session.get( Book.class, bookId );
assertNotNull( book );
log.infof( "Fetched %s", book );
session.close();
} );
assertTrue( sessionFactory().getCache()
.containsEntity( Book.class, bookId ) );
}
finally {
endLatch.countDown();
}
}
}
}
}

View File

@ -22,13 +22,13 @@ import java.util.Set;
name = "company.location", name = "company.location",
fetchOverrides = { fetchOverrides = {
@FetchProfile.FetchOverride( @FetchProfile.FetchOverride(
entity = CompanyWithFetchProfile.class, entity = CompanyFetchProfile.class,
association = "location", association = "location",
mode = FetchMode.JOIN mode = FetchMode.JOIN
) )
} }
) )
public class CompanyWithFetchProfile { public class CompanyFetchProfile {
@Id @GeneratedValue @Id @GeneratedValue
public long id; public long id;
@ -42,6 +42,7 @@ public class CompanyWithFetchProfile {
public Set<Market> markets = new HashSet<Market>(); public Set<Market> markets = new HashSet<Market>();
@ElementCollection(fetch = FetchType.EAGER) @ElementCollection(fetch = FetchType.EAGER)
@JoinTable(name= "companyfp_phonenos")
public Set<String> phoneNumbers = new HashSet<String>(); public Set<String> phoneNumbers = new HashSet<String>();
public Location getLocation() { public Location getLocation() {

View File

@ -1,4 +1,4 @@
package org.hibernate.jpa.test.graphs.mapped_by_id; package org.hibernate.jpa.test.graphs.mappedbyid;
import org.hibernate.Hibernate; import org.hibernate.Hibernate;
import org.hibernate.Session; import org.hibernate.Session;
@ -99,12 +99,12 @@ public class FetchGraphFindByIdTest extends BaseEntityManagerFunctionalTestCase
entityManager.unwrap( Session.class ).enableFetchProfile("company.location"); entityManager.unwrap( Session.class ).enableFetchProfile("company.location");
EntityGraph<CompanyWithFetchProfile> entityGraph = entityManager.createEntityGraph( CompanyWithFetchProfile.class ); EntityGraph<CompanyFetchProfile> entityGraph = entityManager.createEntityGraph( CompanyFetchProfile.class );
entityGraph.addAttributeNodes( "markets" ); entityGraph.addAttributeNodes( "markets" );
Map<String, Object> properties = Collections.singletonMap( "javax.persistence.fetchgraph", entityGraph ); Map<String, Object> properties = Collections.singletonMap( "javax.persistence.fetchgraph", entityGraph );
CompanyWithFetchProfile company = entityManager.find( CompanyWithFetchProfile.class, companyWithFetchProfileId, properties ); CompanyFetchProfile company = entityManager.find( CompanyFetchProfile.class, companyWithFetchProfileId, properties );
entityManager.getTransaction().commit(); entityManager.getTransaction().commit();
entityManager.close(); entityManager.close();
@ -126,7 +126,7 @@ public class FetchGraphFindByIdTest extends BaseEntityManagerFunctionalTestCase
subSubgraph.addAttributeNodes( "managers" ); subSubgraph.addAttributeNodes( "managers" );
subSubgraph.addAttributeNodes( "friends" ); subSubgraph.addAttributeNodes( "friends" );
company = entityManager.find( CompanyWithFetchProfile.class, companyWithFetchProfileId, properties ); company = entityManager.find( CompanyFetchProfile.class, companyWithFetchProfileId, properties );
entityManager.getTransaction().commit(); entityManager.getTransaction().commit();
entityManager.close(); entityManager.close();
@ -189,17 +189,17 @@ public class FetchGraphFindByIdTest extends BaseEntityManagerFunctionalTestCase
entityManager.persist( company ); entityManager.persist( company );
companyId = company.id; companyId = company.id;
CompanyWithFetchProfile companyWithFetchProfile = new CompanyWithFetchProfile(); CompanyFetchProfile companyFetchProfile = new CompanyFetchProfile();
companyWithFetchProfile.employees.add( employee ); companyFetchProfile.employees.add( employee );
companyWithFetchProfile.employees.add( manager1 ); companyFetchProfile.employees.add( manager1 );
companyWithFetchProfile.employees.add( manager2 ); companyFetchProfile.employees.add( manager2 );
companyWithFetchProfile.location = location; companyFetchProfile.location = location;
companyWithFetchProfile.markets.add( Market.SERVICES ); companyFetchProfile.markets.add( Market.SERVICES );
companyWithFetchProfile.markets.add( Market.TECHNOLOGY ); companyFetchProfile.markets.add( Market.TECHNOLOGY );
companyWithFetchProfile.phoneNumbers.add( "012-345-6789" ); companyFetchProfile.phoneNumbers.add( "012-345-6789" );
companyWithFetchProfile.phoneNumbers.add( "987-654-3210" ); companyFetchProfile.phoneNumbers.add( "987-654-3210" );
entityManager.persist( companyWithFetchProfile ); entityManager.persist( companyFetchProfile );
companyWithFetchProfileId = companyWithFetchProfile.id; companyWithFetchProfileId = companyFetchProfile.id;
entityManager.getTransaction().commit(); entityManager.getTransaction().commit();
entityManager.close(); entityManager.close();
@ -207,7 +207,7 @@ public class FetchGraphFindByIdTest extends BaseEntityManagerFunctionalTestCase
@Override @Override
protected Class<?>[] getAnnotatedClasses() { protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { Company.class, CompanyWithFetchProfile.class, Employee.class, Manager.class, Location.class, Course.class, Student.class }; return new Class<?>[] { Company.class, CompanyFetchProfile.class, Employee.class, Manager.class, Location.class, Course.class, Student.class };
} }
} }

View File

@ -4,7 +4,7 @@
* License: GNU Lesser General Public License (LGPL), version 2.1 or later. * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>. * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/ */
package org.hibernate.jpa.test.graphs.mapped_by_id; package org.hibernate.jpa.test.graphs.mappedbyid;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;

View File

@ -175,11 +175,11 @@ public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCas
entityManager.unwrap( Session.class ).enableFetchProfile( "company.location" ); entityManager.unwrap( Session.class ).enableFetchProfile( "company.location" );
EntityGraph<CompanyWithFetchProfile> entityGraph = entityManager.createEntityGraph( CompanyWithFetchProfile.class ); EntityGraph<CompanyFetchProfile> entityGraph = entityManager.createEntityGraph( CompanyFetchProfile.class );
entityGraph.addAttributeNodes( "markets" ); entityGraph.addAttributeNodes( "markets" );
Query query = entityManager.createQuery( "from " + CompanyWithFetchProfile.class.getName() ); Query query = entityManager.createQuery( "from " + CompanyFetchProfile.class.getName() );
query.setHint( QueryHints.HINT_FETCHGRAPH, entityGraph ); query.setHint( QueryHints.HINT_FETCHGRAPH, entityGraph );
CompanyWithFetchProfile company = (CompanyWithFetchProfile) query.getSingleResult(); CompanyFetchProfile company = (CompanyFetchProfile) query.getSingleResult();
entityManager.getTransaction().commit(); entityManager.getTransaction().commit();
entityManager.close(); entityManager.close();
@ -201,9 +201,9 @@ public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCas
subSubgraph.addAttributeNodes( "managers" ); subSubgraph.addAttributeNodes( "managers" );
subSubgraph.addAttributeNodes( "friends" ); subSubgraph.addAttributeNodes( "friends" );
query = entityManager.createQuery( "from " + CompanyWithFetchProfile.class.getName() ); query = entityManager.createQuery( "from " + CompanyFetchProfile.class.getName() );
query.setHint( QueryHints.HINT_FETCHGRAPH, entityGraph ); query.setHint( QueryHints.HINT_FETCHGRAPH, entityGraph );
company = (CompanyWithFetchProfile) query.getSingleResult(); company = (CompanyFetchProfile) query.getSingleResult();
entityManager.getTransaction().commit(); entityManager.getTransaction().commit();
entityManager.close(); entityManager.close();
@ -508,16 +508,16 @@ public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCas
company.phoneNumbers.add( "987-654-3210" ); company.phoneNumbers.add( "987-654-3210" );
entityManager.persist( company ); entityManager.persist( company );
CompanyWithFetchProfile companyWithFetchProfile = new CompanyWithFetchProfile(); CompanyFetchProfile companyFetchProfile = new CompanyFetchProfile();
companyWithFetchProfile.employees.add( employee ); companyFetchProfile.employees.add( employee );
companyWithFetchProfile.employees.add( manager1 ); companyFetchProfile.employees.add( manager1 );
companyWithFetchProfile.employees.add( manager2 ); companyFetchProfile.employees.add( manager2 );
companyWithFetchProfile.location = location; companyFetchProfile.location = location;
companyWithFetchProfile.markets.add( Market.SERVICES ); companyFetchProfile.markets.add( Market.SERVICES );
companyWithFetchProfile.markets.add( Market.TECHNOLOGY ); companyFetchProfile.markets.add( Market.TECHNOLOGY );
companyWithFetchProfile.phoneNumbers.add( "012-345-6789" ); companyFetchProfile.phoneNumbers.add( "012-345-6789" );
companyWithFetchProfile.phoneNumbers.add( "987-654-3210" ); companyFetchProfile.phoneNumbers.add( "987-654-3210" );
entityManager.persist( companyWithFetchProfile ); entityManager.persist( companyFetchProfile );
entityManager.getTransaction().commit(); entityManager.getTransaction().commit();
entityManager.close(); entityManager.close();
@ -525,6 +525,6 @@ public class QueryHintEntityGraphTest extends BaseEntityManagerFunctionalTestCas
@Override @Override
protected Class<?>[] getAnnotatedClasses() { protected Class<?>[] getAnnotatedClasses() {
return new Class<?>[] { Company.class, CompanyWithFetchProfile.class, Employee.class, Manager.class, Location.class, Course.class, Student.class }; return new Class<?>[] { Company.class, CompanyFetchProfile.class, Employee.class, Manager.class, Location.class, Course.class, Student.class };
} }
} }

View File

@ -0,0 +1,148 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
*/
package org.hibernate.test.stateless;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import org.hibernate.StatelessSession;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
/**
* @author Andrea Boriero
*/
@RequiresDialect(H2Dialect.class)
public class StatelessDoWorkTest extends BaseCoreFunctionalTestCase {
public static final String EXPECTED_ENTITY_NAME = "test";
public static final Integer PERSISTED_TEST_ENTITY_ID = 1;
@Override
protected Class<?>[] getAnnotatedClasses() {
return new Class[] { TestEntity.class };
}
@Before
public void setUp() {
inTransaction(
session -> {
TestEntity entity = new TestEntity( PERSISTED_TEST_ENTITY_ID, EXPECTED_ENTITY_NAME );
session.save( entity );
}
);
}
@After
public void tearDown() {
inTransaction(
session -> {
session.createQuery( "delete from TestEntity" ).executeUpdate();
}
);
}
@Test
public void testDoReturningWork() {
String retrievedEntityName;
try (StatelessSession statelessSession = sessionFactory().openStatelessSession()) {
retrievedEntityName = statelessSession.doReturningWork(
(connection) -> {
try (PreparedStatement preparedStatement = connection.prepareStatement(
"SELECT NAME FROM TEST_ENTITY WHERE ID = ?" )) {
preparedStatement.setInt( 1, PERSISTED_TEST_ENTITY_ID );
ResultSet resultSet = preparedStatement.executeQuery();
String name = null;
if ( resultSet.next() ) {
name = resultSet.getString( 1 );
}
return name;
}
}
);
}
assertThat( retrievedEntityName, is( EXPECTED_ENTITY_NAME ) );
}
@Test
public void testDoWork() {
try (StatelessSession statelessSession = sessionFactory().openStatelessSession()) {
statelessSession.doWork(
(connection) -> {
try (PreparedStatement preparedStatement = connection.prepareStatement(
"DELETE FROM TEST_ENTITY " )) {
preparedStatement.execute();
}
}
);
}
assertThatAllTestEntitiesHaveBeenDeleted();
}
private void assertThatAllTestEntitiesHaveBeenDeleted() {
inTransaction( session -> {
List results = session.createQuery( "from TestEntity" ).list();
assertThat( results.size(), is( 0 ) );
} );
}
@Entity(name = "TestEntity")
@Table(name = "TEST_ENTITY")
public static class TestEntity {
@Id
@Column(name = "ID")
private Integer id;
@Column(name = "NAME")
private String name;
public TestEntity() {
}
public TestEntity(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}

View File

@ -0,0 +1,17 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
apply from: rootProject.file( 'gradle/published-java-module.gradle' )
description = "Experimental extension to make it easier to compile applications into a GraalVM native image"
dependencies {
//No need for transitive dependencies: this is all just metadata to be used as companion jar.
compileOnly project( ':hibernate-core' )
compileOnly( libraries.graalvm_nativeimage )
testCompile( project( ':hibernate-core' ) )
}

View File

@ -0,0 +1,59 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.graalvm.internal;
import java.lang.reflect.Constructor;
import java.lang.reflect.Executable;
import java.util.ArrayList;
import org.hibernate.internal.util.ReflectHelper;
import com.oracle.svm.core.annotate.AutomaticFeature;
import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.RuntimeReflection;
/**
* This is a best effort, untested experimental GraalVM feature to help people getting Hibernate ORM
* to work with GraalVM native images.
* There are multiple reasons for this to be untested. One is that for tests to be effective they would
* need very extensive coverage of all functionality: the point of this class being a list of all things
* being initialized reflectively, it's not possible to ensure that the list is comprehensive without the
* tests being comprehensive as well.
* The other problem is that this is listing just that "static needs" of Hibernate ORM: it will very likely
* also need to access reflectively the user's domain model and the various extension points, depending on
* configurations. Such configuration - and especially the domain model - is dynamic by its very own nature,
* and therefore this list is merely provided as a useful starting point, but it needs to be extended;
* such extensions could be automated, or will need to be explicitly passed to the native-image arguments.
* <p>
* In conclusion, it's not possible to provide a fully comprehensive list: take this as a hopefully
* useful building block.
* </p>
* @author Sanne Grinovero
*/
@AutomaticFeature
public class GraalVMStaticAutofeature implements Feature {
public void beforeAnalysis(Feature.BeforeAnalysisAccess before) {
final Class<?>[] needsHavingSimpleConstructors = StaticClassLists.typesNeedingDefaultConstructorAccessible();
final Class[] neddingAllConstructorsAccessible = StaticClassLists.typesNeedingAllConstructorsAccessible();
//Size formula is just a reasonable guess:
ArrayList<Executable> executables = new ArrayList<>( needsHavingSimpleConstructors.length + neddingAllConstructorsAccessible.length * 3 );
for ( Class c : needsHavingSimpleConstructors ) {
executables.add( ReflectHelper.getDefaultConstructor( c ) );
}
for ( Class c : neddingAllConstructorsAccessible ) {
for ( Constructor declaredConstructor : c.getDeclaredConstructors() ) {
executables.add( declaredConstructor );
}
}
RuntimeReflection.register( needsHavingSimpleConstructors );
RuntimeReflection.register( neddingAllConstructorsAccessible );
RuntimeReflection.register( StaticClassLists.typesNeedingArrayCopy() );
RuntimeReflection.register( executables.toArray(new Executable[0]) );
}
}

View File

@ -0,0 +1,146 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.graalvm.internal;
import org.hibernate.tool.hbm2ddl.MultipleLinesSqlCommandExtractor;
import org.hibernate.type.EnumType;
/**
* The place to list all "static" types we know of that need to be possible to
* construct at runtime via reflection.
* This is useful for GraalVM native images - but is not intenteded to be an
* exhaustive list: take these as an helpful starting point.
*/
final class StaticClassLists {
public static Class[] typesNeedingAllConstructorsAccessible() {
return new Class[] {
//The CoreMessageLogger is sometimes looked up without it necessarily being a field, so we're
//not processing it the same way as other Logger lookups.
org.hibernate.internal.CoreMessageLogger_$logger.class,
org.hibernate.tuple.component.PojoComponentTuplizer.class,
org.hibernate.tuple.component.DynamicMapComponentTuplizer.class,
org.hibernate.tuple.entity.DynamicMapEntityTuplizer.class,
org.hibernate.persister.collection.OneToManyPersister.class,
org.hibernate.persister.collection.BasicCollectionPersister.class,
org.hibernate.persister.entity.JoinedSubclassEntityPersister.class,
org.hibernate.persister.entity.UnionSubclassEntityPersister.class,
org.hibernate.persister.entity.SingleTableEntityPersister.class,
org.hibernate.tuple.entity.PojoEntityTuplizer.class,
//ANTLR special ones:
org.hibernate.hql.internal.ast.tree.EntityJoinFromElement.class,
org.hibernate.hql.internal.ast.tree.MapKeyEntityFromElement.class,
org.hibernate.hql.internal.ast.tree.ComponentJoin.class,
};
}
public static Class[] typesNeedingDefaultConstructorAccessible() {
return new Class[] {
//Support for @OrderBy
org.hibernate.sql.ordering.antlr.NodeSupport.class,
org.hibernate.sql.ordering.antlr.OrderByFragment.class,
org.hibernate.sql.ordering.antlr.SortSpecification.class,
org.hibernate.sql.ordering.antlr.OrderingSpecification.class,
org.hibernate.sql.ordering.antlr.CollationSpecification.class,
org.hibernate.sql.ordering.antlr.SortKey.class,
//ANTLR tokens:
org.hibernate.hql.internal.ast.tree.SelectClause.class,
org.hibernate.hql.internal.ast.tree.HqlSqlWalkerNode.class,
org.hibernate.hql.internal.ast.tree.MethodNode.class,
org.hibernate.hql.internal.ast.tree.UnaryLogicOperatorNode.class,
org.hibernate.hql.internal.ast.tree.NullNode.class,
org.hibernate.hql.internal.ast.tree.IntoClause.class,
org.hibernate.hql.internal.ast.tree.UpdateStatement.class,
org.hibernate.hql.internal.ast.tree.SelectExpressionImpl.class,
org.hibernate.hql.internal.ast.tree.CastFunctionNode.class,
org.hibernate.hql.internal.ast.tree.DeleteStatement.class,
org.hibernate.hql.internal.ast.tree.SqlNode.class,
org.hibernate.hql.internal.ast.tree.SearchedCaseNode.class,
org.hibernate.hql.internal.ast.tree.FromElement.class,
org.hibernate.hql.internal.ast.tree.JavaConstantNode.class,
org.hibernate.hql.internal.ast.tree.SqlFragment.class,
org.hibernate.hql.internal.ast.tree.MapKeyNode.class,
org.hibernate.hql.internal.ast.tree.ImpliedFromElement.class,
org.hibernate.hql.internal.ast.tree.IsNotNullLogicOperatorNode.class,
org.hibernate.hql.internal.ast.tree.InsertStatement.class,
org.hibernate.hql.internal.ast.tree.UnaryArithmeticNode.class,
org.hibernate.hql.internal.ast.tree.CollectionFunction.class,
org.hibernate.hql.internal.ast.tree.BinaryLogicOperatorNode.class,
org.hibernate.hql.internal.ast.tree.CountNode.class,
org.hibernate.hql.internal.ast.tree.IsNullLogicOperatorNode.class,
org.hibernate.hql.internal.ast.tree.IdentNode.class,
org.hibernate.hql.internal.ast.tree.ParameterNode.class,
org.hibernate.hql.internal.ast.tree.MapEntryNode.class,
org.hibernate.hql.internal.ast.tree.MapValueNode.class,
org.hibernate.hql.internal.ast.tree.InLogicOperatorNode.class,
org.hibernate.hql.internal.ast.tree.IndexNode.class,
org.hibernate.hql.internal.ast.tree.DotNode.class,
org.hibernate.hql.internal.ast.tree.ResultVariableRefNode.class,
org.hibernate.hql.internal.ast.tree.BetweenOperatorNode.class,
org.hibernate.hql.internal.ast.tree.AggregateNode.class,
org.hibernate.hql.internal.ast.tree.QueryNode.class,
org.hibernate.hql.internal.ast.tree.BooleanLiteralNode.class,
org.hibernate.hql.internal.ast.tree.SimpleCaseNode.class,
org.hibernate.hql.internal.ast.tree.OrderByClause.class,
org.hibernate.hql.internal.ast.tree.FromClause.class,
org.hibernate.hql.internal.ast.tree.ConstructorNode.class,
org.hibernate.hql.internal.ast.tree.LiteralNode.class,
org.hibernate.hql.internal.ast.tree.BinaryArithmeticOperatorNode.class,
//Various well known needs:
org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorBuilderImpl.class,
org.hibernate.id.enhanced.SequenceStyleGenerator.class,
org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl.class,
org.hibernate.resource.transaction.backend.jta.internal.JtaTransactionCoordinatorBuilderImpl.class,
EnumType.class,
MultipleLinesSqlCommandExtractor.class,
org.hibernate.hql.internal.ast.HqlToken.class,
org.hibernate.hql.internal.ast.tree.Node.class,
};
}
public static Class[] typesNeedingArrayCopy() {
return new Class[] {
//Eventlisteners need to be registered for reflection to allow creation via Array#newInstance ;
// types need to be in synch with those declared in org.hibernate.event.spi.EventType
org.hibernate.event.spi.LoadEventListener[].class,
org.hibernate.event.spi.ResolveNaturalIdEventListener[].class,
org.hibernate.event.spi.InitializeCollectionEventListener[].class,
org.hibernate.event.spi.SaveOrUpdateEventListener[].class,
org.hibernate.event.spi.PersistEventListener[].class,
org.hibernate.event.spi.MergeEventListener[].class,
org.hibernate.event.spi.DeleteEventListener[].class,
org.hibernate.event.spi.ReplicateEventListener[].class,
org.hibernate.event.spi.FlushEventListener[].class,
org.hibernate.event.spi.AutoFlushEventListener[].class,
org.hibernate.event.spi.DirtyCheckEventListener[].class,
org.hibernate.event.spi.FlushEntityEventListener[].class,
org.hibernate.event.spi.ClearEventListener[].class,
org.hibernate.event.spi.EvictEventListener[].class,
org.hibernate.event.spi.LockEventListener[].class,
org.hibernate.event.spi.RefreshEventListener[].class,
org.hibernate.event.spi.PreLoadEventListener[].class,
org.hibernate.event.spi.PreDeleteEventListener[].class,
org.hibernate.event.spi.PreUpdateEventListener[].class,
org.hibernate.event.spi.PreInsertEventListener[].class,
org.hibernate.event.spi.PostLoadEventListener[].class,
org.hibernate.event.spi.PostDeleteEventListener[].class,
org.hibernate.event.spi.PostUpdateEventListener[].class,
org.hibernate.event.spi.PostInsertEventListener[].class,
org.hibernate.event.spi.PreCollectionRecreateEventListener[].class,
org.hibernate.event.spi.PreCollectionRemoveEventListener[].class,
org.hibernate.event.spi.PreCollectionUpdateEventListener[].class,
org.hibernate.event.spi.PostCollectionRecreateEventListener[].class,
org.hibernate.event.spi.PostCollectionRemoveEventListener[].class,
org.hibernate.event.spi.PostCollectionUpdateEventListener[].class
};
}
}

View File

@ -0,0 +1,50 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.graalvm.internal;
import java.lang.reflect.Constructor;
import org.hibernate.internal.util.ReflectHelper;
import org.junit.Assert;
import org.junit.Test;
public class BasicConstructorsAvailableTest {
@Test
public void checkNonDefaultConstructorsCanBeLoaded() {
Class[] classes = StaticClassLists.typesNeedingAllConstructorsAccessible();
for ( Class c : classes ) {
Constructor[] declaredConstructors = c.getDeclaredConstructors();
Assert.assertTrue( declaredConstructors.length > 0 );
if ( declaredConstructors.length == 1 ) {
//If there's only one, let's check that this class wasn't placed in the wrong cathegory:
Assert.assertTrue( declaredConstructors[0].getParameterCount() > 0 );
}
}
}
@Test
public void checkDefaultConstructorsAreAvailable() {
Class[] classes = StaticClassLists.typesNeedingDefaultConstructorAccessible();
for ( Class c : classes ) {
Constructor constructor = ReflectHelper.getDefaultConstructor( c );
Assert.assertNotNull( "Failed for class: " + c.getName(), constructor );
}
}
@Test
public void checkArraysAreArrays() {
Class[] classes = StaticClassLists.typesNeedingArrayCopy();
for ( Class c : classes ) {
Assert.assertTrue( "Wrong category for type: " + c.getName(), c.isArray() );
Constructor[] constructors = c.getConstructors();
Assert.assertEquals( 0, constructors.length );
}
}
}

View File

@ -0,0 +1,46 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.integrationtest.java.module.test;
import java.net.URL;
import java.util.Set;
import org.hibernate.boot.archive.internal.StandardArchiveDescriptorFactory;
import org.hibernate.boot.archive.scan.internal.StandardScanOptions;
import org.hibernate.boot.archive.scan.internal.StandardScanParameters;
import org.hibernate.boot.archive.scan.internal.StandardScanner;
import org.hibernate.boot.archive.scan.spi.ClassDescriptor;
import org.hibernate.boot.archive.scan.spi.ScanResult;
import org.hibernate.orm.integrationtest.java.module.entity.Author;
import org.junit.Assert;
import org.junit.Test;
/**
* We need to test that the scanner works, including when there is a module-info.class
* resource in the project. See also HHH-13859.
*/
public class ScannerTest {
@Test
public void verifyModuleInfoScanner() {
URL urlToThis = Author.class.getProtectionDomain().getCodeSource().getLocation();
StandardScanner standardScanner = new StandardScanner( StandardArchiveDescriptorFactory.INSTANCE );
ScanResult scan = standardScanner.scan(
new TestScanEnvironment( urlToThis ),
new StandardScanOptions(),
StandardScanParameters.INSTANCE
);
Set<ClassDescriptor> locatedClasses = scan.getLocatedClasses();
Assert.assertEquals( 1, locatedClasses.size() );
ClassDescriptor classDescriptor = locatedClasses.iterator().next();
Assert.assertNotNull( classDescriptor );
Assert.assertEquals( Author.class.getName(), classDescriptor.getName() );
Assert.assertEquals( ClassDescriptor.Categorization.MODEL, classDescriptor.getCategorization() );
}
}

View File

@ -0,0 +1,42 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.orm.integrationtest.java.module.test;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import org.hibernate.boot.archive.scan.spi.ScanEnvironment;
final class TestScanEnvironment implements ScanEnvironment {
private final URL root;
TestScanEnvironment(URL root) {
this.root = root;
}
@Override
public URL getRootUrl() {
return root;
}
@Override
public List<URL> getNonRootUrls() {
return Collections.emptyList();
}
@Override
public List<String> getExplicitlyListedClassNames() {
return Collections.emptyList();
}
@Override
public List<String> getExplicitlyListedMappingFiles() {
return Collections.emptyList();
}
}

View File

@ -31,6 +31,7 @@ include 'hibernate-infinispan'
include 'hibernate-jipijapa' include 'hibernate-jipijapa'
include 'hibernate-orm-modules' include 'hibernate-orm-modules'
include 'hibernate-graalvm'
if ( JavaVersion.current().isJava11Compatible() ) { if ( JavaVersion.current().isJava11Compatible() ) {
include 'hibernate-integrationtest-java-modules' include 'hibernate-integrationtest-java-modules'