diff --git a/build.gradle b/build.gradle index 98ddba5eb7..50c993a646 100644 --- a/build.gradle +++ b/build.gradle @@ -1,20 +1,25 @@ apply plugin: 'eclipse' apply plugin: 'idea' +apply from: "./libraries.gradle" allprojects { repositories { - mavenCentral() - mavenLocal() - mavenRepo name: 'jboss-nexus', url: "https://repository.jboss.org/nexus/content/groups/public/" + mavenCentral() + mavenLocal() + + + mavenRepo name: 'jboss-nexus', url: "http://repository.jboss.org/nexus/content/groups/public/" mavenRepo name: "jboss-snapshots", url: "http://snapshots.jboss.org/maven2/" } } buildscript { repositories { - mavenCentral() - mavenLocal() - mavenRepo name: 'jboss-nexus', url: "https://repository.jboss.org/nexus/content/groups/public/" + mavenCentral() + mavenLocal() + + + mavenRepo name: 'jboss-nexus', url: "http://repository.jboss.org/nexus/content/groups/public/" mavenRepo name: "jboss-snapshots", url: "http://snapshots.jboss.org/maven2/" } dependencies { @@ -22,11 +27,11 @@ buildscript { } } -hibernateTargetVersion = '4.2.0-SNAPSHOT' +ext.hibernateTargetVersion = '4.2.0-SNAPSHOT' idea { project { - jdkName = "1.6" + languageLevel = '1.6' ipr { withXml { provider -> provider.node.component.find { it.@name == 'VcsDirectoryMappings' }.mapping.@vcs = 'Git' @@ -46,64 +51,6 @@ idea { } } -// build a map of the dependency artifacts to use. Allows centralized definition of the version of artifacts to -// use. In that respect it serves a role similar to in Maven -junitVersion = '4.10' -h2Version = '1.2.145' -bytemanVersion = '1.5.2' - -libraries = [ - // Ant - ant: 'org.apache.ant:ant:1.8.2', - - // Antlr - antlr: 'antlr:antlr:2.7.7', - - // Annotations - commons_annotations: - 'org.hibernate.common:hibernate-commons-annotations:4.0.1.Final@jar', - jandex: 'org.jboss:jandex:1.1.0.Alpha1', - classmate: 'com.fasterxml:classmate:0.5.4', - - // Dom4J - dom4j: 'dom4j:dom4j:1.6.1@jar', - - // Javassist - javassist: 'org.javassist:javassist:3.15.0-GA', - - // javax - jpa: 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Draft-7plus', - jta: 'org.jboss.spec.javax.transaction:jboss-transaction-api_1.1_spec:1.0.0.Final', - validation: 'javax.validation:validation-api:1.0.0.GA', - jacc: 'org.jboss.spec.javax.security.jacc:jboss-jacc-api_1.4_spec:1.0.0.Final', - - // logging - logging: 'org.jboss.logging:jboss-logging:3.1.0.GA', - logging_processor: 'org.jboss.logging:jboss-logging-processor:1.0.0.Final', - - // jaxb task - jaxb: 'com.sun.xml.bind:jaxb-xjc:2.2.5', - jaxb2_basics: 'org.jvnet.jaxb2_commons:jaxb2-basics:0.6.3', - jaxb2_ant: 'org.jvnet.jaxb2_commons:jaxb2-basics-ant:0.6.3', - jaxb2_jaxb: 'org.jvnet.jaxb2_commons:jaxb2-basics-jaxb:2.2.4-1', - jaxb2_jaxb_xjc: 'org.jvnet.jaxb2_commons:jaxb2-basics-jaxb-xjc:2.2.4-1', - // ~~~~~~~~~~~~~~~~~~~~~~~~~~ testing - - // logging for testing - log4j: 'log4j:log4j:1.2.16', - - junit: "junit:junit:${junitVersion}", - byteman: "org.jboss.byteman:byteman:${bytemanVersion}", - byteman_install: "org.jboss.byteman:byteman-install:${bytemanVersion}", - byteman_bmunit: "org.jboss.byteman:byteman-bmunit:${bytemanVersion}", - jpa_modelgen: 'org.hibernate:hibernate-jpamodelgen:1.2.0.Final', - shrinkwrap_api: 'org.jboss.shrinkwrap:shrinkwrap-api:1.0.0-beta-6', - shrinkwrap: 'org.jboss.shrinkwrap:shrinkwrap-impl-base:1.0.0-beta-6', - validator: 'org.hibernate:hibernate-validator:4.3.0.Final', - h2: "com.h2database:h2:${h2Version}" -] - - subprojects { subProject -> apply plugin: 'idea' apply plugin: 'eclipse' @@ -142,7 +89,8 @@ subprojects { subProject -> } } - toolsJar = file("${System.getProperty('java.home')}/../lib/tools.jar") + + ext.toolsJar = file("${System.getProperty('java.home')}/../lib/tools.jar") // appropriately inject the common dependencies into each sub-project dependencies { compile( libraries.logging ) @@ -150,12 +98,18 @@ subprojects { subProject -> testCompile( libraries.byteman ) testCompile( libraries.byteman_install ) testCompile( libraries.byteman_bmunit ) - testCompile files( toolsJar ) - testRuntime( libraries.log4j ) + testRuntime( libraries.slf4j_api ) + testRuntime( libraries.slf4j_log4j12 ) + testRuntime( libraries.jcl_slf4j ) + testRuntime( libraries.jcl_api ) + testRuntime( libraries.jcl ) testRuntime( libraries.javassist ) testRuntime( libraries.h2 ) jbossLoggingTool( libraries.logging_processor ) hibernateJpaModelGenTool( libraries.jpa_modelgen ) + jaxb( libraries.jaxb ){ + exclude group: "javax.xml.stream" + } jaxb( libraries.jaxb ) jaxb( libraries.jaxb2_basics ) jaxb( libraries.jaxb2_ant ) @@ -163,16 +117,20 @@ subprojects { subProject -> jaxb( libraries.jaxb2_jaxb_xjc ) deployerJars "org.apache.maven.wagon:wagon-http:1.0" } - - aptDumpDir = subProject.file( "${buildDir}/tmp/apt" ) + if( ext.toolsJar.exists() ){ + dependencies{ + testCompile files( toolsJar ) + } + } + ext.aptDumpDir = subProject.file( "${buildDir}/tmp/apt" ) sourceSets.main { compileClasspath += configurations.provided } sourceSets.all { - originalJavaSrcDirs = java.srcDirs - generatedLoggingSrcDir = file( "${buildDir}/generated-src/logging/${name}" ) + ext.originalJavaSrcDirs = java.srcDirs + ext.generatedLoggingSrcDir = file( "${buildDir}/generated-src/logging/${name}" ) java.srcDir generatedLoggingSrcDir } @@ -225,6 +183,8 @@ subprojects { subProject -> maxHeapSize = "1024m" } + + processTestResources.doLast( { copy { from( sourceSets.test.java.srcDirs ) { @@ -310,7 +270,7 @@ subprojects { subProject -> } } - subProject.basePomConfig = pomConfig + subProject.ext.basePomConfig = pomConfig configure(install.repositories.mavenInstaller) { pom.project pomConfig @@ -339,7 +299,6 @@ subprojects { subProject -> } } - dependsOnChildren() // This is a task that generates the gradlew scripts, allowing users to run gradle without having gradle installed @@ -349,5 +308,5 @@ dependsOnChildren() // 2) /gradlew.bat which is the windows bat script for for executing builds // 3) /wrapper which is a directory named by the "jarPath" config which contains other needed files. task wrapper(type: Wrapper) { - gradleVersion = '1.0-milestone-8a' + gradleVersion = '1.1' } diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index dbc8a02872..d4a8169547 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -49,11 +49,10 @@ dependencies { idea { project { - jdkName = "1.6" + languageLevel = '1.6' } module { downloadSources = true downloadJavadoc = true - javaVersion = '1.6' } } diff --git a/buildSrc/src/main/groovy/org/hibernate/build/gradle/testing/database/JdbcDirectoryProfile.groovy b/buildSrc/src/main/groovy/org/hibernate/build/gradle/testing/database/JdbcDirectoryProfile.groovy index cd3f81f23c..69b984dfaa 100644 --- a/buildSrc/src/main/groovy/org/hibernate/build/gradle/testing/database/JdbcDirectoryProfile.groovy +++ b/buildSrc/src/main/groovy/org/hibernate/build/gradle/testing/database/JdbcDirectoryProfile.groovy @@ -26,7 +26,6 @@ package org.hibernate.build.gradle.testing.database; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; -import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency; /** * Database profile as defined by a directory named {@code jdbc} containing JDBC drivers. * @@ -34,22 +33,16 @@ import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDepend * @author Strong Liu */ public class JdbcDirectoryProfile extends AbstractDatabaseProfileImpl { - private final Configuration jdbcDependencies; + private final Configuration jdbcDependencies; - public JdbcDirectoryProfile(File jdbcDirectory, Project project) { - super( jdbcDirectory.getParentFile(), project ); - jdbcDependencies = prepareConfiguration( getName() ); - DefaultSelfResolvingDependency dependency = - new DefaultSelfResolvingDependency( project.files( jdbcDirectory.listFiles() ) ); -/* File [] jdbcDriverJars = jdbcDirectory.listFiles();*/ - jdbcDependencies.addDependency( dependency ); -/* project.dependencies { - jdbcDependency files(jdbcDriverJars) - }*/ - } + public JdbcDirectoryProfile(File jdbcDirectory, Project project) { + super( jdbcDirectory.getParentFile(), project ); + jdbcDependencies = prepareConfiguration( getName() ); + project.dependencies.add(getName(), project.files(jdbcDirectory.listFiles())) + } - @Override - public Configuration getTestingRuntimeConfiguration() { - return jdbcDependencies; - } + @Override + public Configuration getTestingRuntimeConfiguration() { + return jdbcDependencies; + } } diff --git a/buildSrc/src/main/groovy/org/hibernate/build/qalab/DatabaseAllocator.groovy b/buildSrc/src/main/groovy/org/hibernate/build/qalab/DatabaseAllocator.groovy index a1122b9d45..5cc4cc3988 100644 --- a/buildSrc/src/main/groovy/org/hibernate/build/qalab/DatabaseAllocator.groovy +++ b/buildSrc/src/main/groovy/org/hibernate/build/qalab/DatabaseAllocator.groovy @@ -93,7 +93,7 @@ class DatabaseAllocator { public static DatabaseAllocator locate(Project project) { if ( ! project.rootProject.hasProperty( DB_ALLOCATOR_KEY ) ) { - project.rootProject.setProperty( DB_ALLOCATOR_KEY, new DatabaseAllocator( project.rootProject ) ); + project.rootProject.ext.setProperty( DB_ALLOCATOR_KEY, new DatabaseAllocator( project.rootProject ) ); } return (DatabaseAllocator) project.rootProject.properties[ DB_ALLOCATOR_KEY ]; } diff --git a/documentation/documentation.gradle b/documentation/documentation.gradle index f6ee9cfe06..29345eb924 100644 --- a/documentation/documentation.gradle +++ b/documentation/documentation.gradle @@ -2,18 +2,18 @@ buildscript { repositories { mavenCentral() mavenLocal() - mavenRepo name: 'jboss-nexus', url: "https://repository.jboss.org/nexus/content/groups/public/" + mavenRepo name: 'jboss-nexus', url: "http://repository.jboss.org/nexus/content/groups/public/" } dependencies { - classpath "org.jboss.jdocbook:gradle-jdocbook:1.2.0" + classpath "org.jboss.jdocbook:gradle-jdocbook:1.2.1" } } apply plugin: "java" apply plugin: "jdocbook" - +ext.pressgangVersion = '3.0.0' dependencies { - pressgangVersion = '3.0.0' + jdocbookXsl "org.jboss.pressgang:pressgang-xslt-ns:${pressgangVersion}" jdocbookXsl "org.jboss.pressgang:pressgang-fonts:${pressgangVersion}" jdocbookStyles "org.jboss.pressgang:pressgang-jdocbook-style:${pressgangVersion}" diff --git a/documentation/src/main/docbook/devguide/en-US/appendix-Configuration_Properties.xml b/documentation/src/main/docbook/devguide/en-US/appendix-Configuration_Properties.xml index 71bea26a64..7a0d75a254 100644 --- a/documentation/src/main/docbook/devguide/en-US/appendix-Configuration_Properties.xml +++ b/documentation/src/main/docbook/devguide/en-US/appendix-Configuration_Properties.xml @@ -1,9 +1,57 @@ + xmlns:xlink="http://www.w3.org/1999/xlink"> + + Configuration properties + +
+ Strategy configurations + + Many configuration settings define pluggable strategies that Hibernate uses for various purposes. + The configuration of many of these strategy type settings accept definition in various forms. The + documentation of such configuration settings refer here. The types of forms available in such cases + include: + + + + short name (if defined) + + + Certain built-in strategy implementations have a corresponding short name. + + + + + strategy instance + + + An instance of the strategy implementation to use can be specified + + + + + strategy Class reference + + + A java.lang.Class reference of the strategy implementation to use can + be specified + + + + + strategy Class name + + + The class name (java.lang.String) of the strategy implementation to + use can be specified + + + + +
+ - Configuration properties
General Configuration @@ -67,10 +115,9 @@ hibernate.default_entity_mode - One of dynamic-map, dom4j, - pojo + dynamic-map or pojo Default mode for entity representation for all sessions opened from this - SessionFactory + SessionFactory, defaults to pojo. hibernate.order_updates @@ -229,9 +276,16 @@ hibernate.transaction.factory_class - A fully-qualified classname - The classname of a TransactionFactory to use with Hibernate Transaction API. The - default is JDBCTransactionFactory). + + jdbc or + + + + Names the org.hibernate.engine.transaction.spi.TransactionFactory + strategy implementation to use. See and + + + jta.UserTransaction @@ -359,8 +413,8 @@ For information on specific configuration of Proxool, refer to the Proxool documentation available from - . + .
-
\ No newline at end of file + diff --git a/documentation/src/main/docbook/devguide/en-US/chapters/services/Services.xml b/documentation/src/main/docbook/devguide/en-US/chapters/services/Services.xml index 2c188724e5..9ec01f2b97 100644 --- a/documentation/src/main/docbook/devguide/en-US/chapters/services/Services.xml +++ b/documentation/src/main/docbook/devguide/en-US/chapters/services/Services.xml @@ -774,14 +774,13 @@ Initiator - org.hibernate.stat.internal.StatisticsInitiator + org.hibernate.engine.transaction.internal.TransactionFactoryInitiator - Defines a hibernate.stats.factory setting to allow - configuring the - org.hibernate.stat.spi.StatisticsFactory to use internally - when building the actual - org.hibernate.stat.Statistics instance. + Defines a hibernate.transaction.factory_class setting to allow + configuring which TransactionFactory to use. + hibernate.transaction.factory_class follows the rules set forth + under . @@ -791,18 +790,20 @@ - org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory - - A JTA-based strategy in which Hibernate is not controlling the transactions. An - important distinction here is that interaction with the underlying JTA implementation - is done through the - javax.transaction.TransactionManager + org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory - + A non-JTA strategy in which the transactions are managed using the JDBC + java.sql.Connection. This implementation's short + name is jdbc. - org.hibernate.engine.transaction.internal.jdbc.JdbcTransactionFactory - - A non-JTA strategy in which the transactions are managed using the JDBC - java.sql.Connection + org.hibernate.engine.transaction.internal.jta.CMTTransactionFactory - + A JTA-based strategy in which Hibernate is not controlling the transactions. An + important distinction here is that interaction with the underlying JTA implementation + is done through the + javax.transaction.TransactionManager. This + implementation's short name is cmt. @@ -811,7 +812,8 @@ A JTA-based strategy in which Hibernate *may* be controlling the transactions. An important distinction here is that interaction with the underlying JTA implementation is done through the - javax.transaction.UserTransaction + javax.transaction.UserTransaction. This + implementation's short name is jta. diff --git a/documentation/src/main/docbook/manual/en-US/HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.xml b/documentation/src/main/docbook/manual/en-US/HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.xml index e028d5f4ce..167282fce2 100644 --- a/documentation/src/main/docbook/manual/en-US/HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.xml +++ b/documentation/src/main/docbook/manual/en-US/HIBERNATE_-_Relational_Persistence_for_Idiomatic_Java.xml @@ -65,7 +65,6 @@ - diff --git a/documentation/src/main/docbook/manual/en-US/content/configuration.xml b/documentation/src/main/docbook/manual/en-US/content/configuration.xml index 1baf0044e5..7532bb94a3 100644 --- a/documentation/src/main/docbook/manual/en-US/content/configuration.xml +++ b/documentation/src/main/docbook/manual/en-US/content/configuration.xml @@ -309,7 +309,7 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQL82Dialect above. - +
Hibernate Configuration Properties @@ -408,9 +408,10 @@ hibernate.dialect = org.hibernate.dialect.PostgreSQL82Dialect hibernate.default_entity_mode Sets a default mode for entity representation for all - sessions opened from this SessionFactory - dynamic-map, dom4j, - pojo + sessions opened from this SessionFactory, + defaults to pojo. + e.g. dynamic-map | + pojo diff --git a/documentation/src/main/docbook/manual/en-US/content/persistent_classes.xml b/documentation/src/main/docbook/manual/en-US/content/persistent_classes.xml index b9879ee020..5571855c14 100644 --- a/documentation/src/main/docbook/manual/en-US/content/persistent_classes.xml +++ b/documentation/src/main/docbook/manual/en-US/content/persistent_classes.xml @@ -322,7 +322,7 @@ public class DomesticCat extends Cat { key. -
+
Dynamic models @@ -335,8 +335,8 @@ public class DomesticCat extends Cat { Persistent entities do not necessarily have to be represented as POJO classes or as JavaBean objects at runtime. Hibernate also supports dynamic models (using Maps of Maps - at runtime) and the representation of entities as DOM4J trees. With this - approach, you do not write persistent classes, only mapping files. + at runtime). With this approach, you do not write persistent classes, + only mapping files. By default, Hibernate works in normal POJO mode. You can set a default entity representation mode for a particular @@ -444,9 +444,6 @@ dynamicSession.close() call flush() and close() on the secondary Session, and also leave the transaction and connection handling to the primary unit of work. - - More information about the XML representation capabilities can be - found in .
diff --git a/documentation/src/main/docbook/manual/en-US/content/transactions.xml b/documentation/src/main/docbook/manual/en-US/content/transactions.xml index c059115493..ec5dfec9c9 100644 --- a/documentation/src/main/docbook/manual/en-US/content/transactions.xml +++ b/documentation/src/main/docbook/manual/en-US/content/transactions.xml @@ -1,6 +1,6 @@ - + Transactions and Concurrency diff --git a/documentation/src/main/docbook/manual/en-US/content/xml.xml b/documentation/src/main/docbook/manual/en-US/content/xml.xml deleted file mode 100755 index cf989c5a64..0000000000 --- a/documentation/src/main/docbook/manual/en-US/content/xml.xml +++ /dev/null @@ -1,288 +0,0 @@ - - - - XML Mapping - - - XML Mapping is an experimental feature in Hibernate 3.0 and is currently under - active development. - - -
- Working with XML data - - - Hibernate allows you to work with persistent XML data in much the same way - you work with persistent POJOs. A parsed XML tree can be thought of - as another way of representing the relational data at the object level, - instead of POJOs. - - - - Hibernate supports dom4j as API for manipulating XML trees. You can write - queries that retrieve dom4j trees from the database and have any - modification you make to the tree automatically synchronized to the - database. You can even take an XML document, parse it using dom4j, and - write it to the database with any of Hibernate's basic operations: - persist(), saveOrUpdate(), merge(), delete(), replicate() - (merging is not yet supported). - - - - This feature has many applications including data import/export, - externalization of entity data via JMS or SOAP and XSLT-based reporting. - - - - A single mapping can be used to simultaneously map properties of a class - and nodes of an XML document to the database, or, if there is no class to map, - it can be used to map just the XML. - - -
- Specifying XML and class mapping together - - - Here is an example of mapping a POJO and XML simultaneously: - - - - - - - - - - - ... - -]]> -
- -
- Specifying only an XML mapping - - - Here is an example where there is no POJO class: - - - - - - - - - - - ... - -]]> - - - This mapping allows you to access the data as a dom4j tree, or as a graph of - property name/value pairs or java Maps. The property names - are purely logical constructs that can be referred to in HQL queries. - - -
- -
- -
- XML mapping metadata - - - A range of Hibernate mapping elements accept the node attribute. - This lets you specify the name of an XML attribute or element that holds the - property or entity data. The format of the node attribute - must be one of the following: - - - - - "element-name": map to the named XML element - - - "@attribute-name": map to the named XML attribute - - - ".": map to the parent element - - - - "element-name/@attribute-name": - map to the named attribute of the named element - - - - - - For collections and single valued associations, there is an additional - embed-xml attribute. If embed-xml="true", - the default, the XML tree for the associated entity (or collection of value type) - will be embedded directly in the XML tree for the entity that owns the association. - Otherwise, if embed-xml="false", then only the referenced - identifier value will appear in the XML for single point associations and - collections will not appear at all. - - - - Do not leave embed-xml="true" for - too many associations, since XML does not deal well with circularity. - - - - - - - - - - - - - - - - - - - ... - -]]> - - - In this case, the collection of account ids is embedded, but not - the actual account data. The following HQL query: - - - - - - would return datasets such as this: - - - - 987632567 - 985612323 - - Gavin - A - King - - ... -]]> - - - If you set embed-xml="true" on the <one-to-many> - mapping, the data might look more like this: - - - - - - 100.29 - - - - -2370.34 - - - Gavin - A - King - - ... -]]> - -
- - -
- Manipulating XML data - - - You can also re-read and update XML documents in the application. You can do this by - obtaining a dom4j session: - - - - - - - - When implementing XML-based data import/export, it is useful to combine this feature with Hibernate's replicate() - operation. - - -
- -
- diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 26ee107d40..7d9ec74379 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=http\://services.gradle.org/distributions/gradle-1.0-milestone-8a-bin.zip +distributionUrl=http\://services.gradle.org/distributions/gradle-1.1-bin.zip diff --git a/hibernate-c3p0/hibernate-c3p0.gradle b/hibernate-c3p0/hibernate-c3p0.gradle index cff4261a00..c68d582ff0 100644 --- a/hibernate-c3p0/hibernate-c3p0.gradle +++ b/hibernate-c3p0/hibernate-c3p0.gradle @@ -1,9 +1,7 @@ -apply plugin: 'java' - dependencies { provided( libraries.validation ) compile project( ':hibernate-core' ) - compile "c3p0:c3p0:0.9.1" + compile( libraries.c3p0 ) testCompile( libraries.validator ) { // for test runtime diff --git a/hibernate-core/hibernate-core.gradle b/hibernate-core/hibernate-core.gradle index 4f0d8f08f7..8a7b485a26 100644 --- a/hibernate-core/hibernate-core.gradle +++ b/hibernate-core/hibernate-core.gradle @@ -1,4 +1,3 @@ -apply plugin: 'java' apply plugin: 'antlr' apply plugin: org.hibernate.build.gradle.inject.InjectionPlugin apply plugin: org.hibernate.build.gradle.testing.matrix.MatrixTestingPlugin @@ -21,6 +20,9 @@ dependencies { testCompile( project(':hibernate-testing') ) testCompile( libraries.validation ) + testCompile( libraries.jandex ) + testCompile( libraries.classmate ) + testCompile( libraries.mockito ) testCompile( libraries.validator ) { // for test runtime transitive = true @@ -36,7 +38,7 @@ manifest.mainAttributes( ) sourceSets.main { - jaxbTargetDir = file( "${buildDir}/generated-src/jaxb/main" ) + ext.jaxbTargetDir = file( "${buildDir}/generated-src/jaxb/main" ) java.srcDir jaxbTargetDir originalJavaSrcDirs = java.srcDirs } @@ -53,18 +55,20 @@ idea { } task jaxb { - // output directory - jaxbTargetDir = file( "${buildDir}/generated-src/jaxb/main" ) + ext { + // output directory + jaxbTargetDir = file( "${buildDir}/generated-src/jaxb/main" ) - // input schemas - cfgXsd = file( 'src/main/resources/org/hibernate/hibernate-configuration-4.0.xsd') - hbmXsd = file( 'src/main/resources/org/hibernate/hibernate-mapping-4.0.xsd' ) - ormXsd = file( 'src/main/resources/org/hibernate/ejb/orm_2_0.xsd' ) + // input schemas + cfgXsd = file( 'src/main/resources/org/hibernate/hibernate-configuration-4.0.xsd') + hbmXsd = file( 'src/main/resources/org/hibernate/hibernate-mapping-4.0.xsd' ) + ormXsd = file( 'src/main/resources/org/hibernate/ejb/orm_2_0.xsd' ) - // input bindings - cfgXjb = file( 'src/main/xjb/hbm-configuration-bindings.xjb' ) - hbmXjb = file( 'src/main/xjb/hbm-mapping-bindings.xjb' ) - ormXjb = file( 'src/main/xjb/orm-bindings.xjb' ) + // input bindings + cfgXjb = file( 'src/main/xjb/hbm-configuration-bindings.xjb' ) + hbmXjb = file( 'src/main/xjb/hbm-mapping-bindings.xjb' ) + ormXjb = file( 'src/main/xjb/orm-bindings.xjb' ) + } // configure Gradle up-to-date checking inputs.files( [cfgXsd, hbmXsd, ormXsd, cfgXjb, hbmXjb, ormXjb] ) diff --git a/hibernate-core/src/main/java/org/hibernate/MultiTenancyStrategy.java b/hibernate-core/src/main/java/org/hibernate/MultiTenancyStrategy.java index 2d9dfd9aec..fff40398ad 100644 --- a/hibernate-core/src/main/java/org/hibernate/MultiTenancyStrategy.java +++ b/hibernate-core/src/main/java/org/hibernate/MultiTenancyStrategy.java @@ -36,7 +36,6 @@ import org.hibernate.internal.CoreMessageLogger; * @author Steve Ebersole */ public enum MultiTenancyStrategy { - /** * Multi-tenancy implemented by use of discriminator columns. */ @@ -59,19 +58,21 @@ public enum MultiTenancyStrategy { MultiTenancyStrategy.class.getName() ); - public static MultiTenancyStrategy fromConfigValue(Object value) { - if ( value == null ) { + public boolean requiresMultiTenantConnectionProvider() { + return this == DATABASE || this == SCHEMA; + } + + public static MultiTenancyStrategy determineMultiTenancyStrategy(Map properties) { + final Object strategy = properties.get( Environment.MULTI_TENANT ); + if ( strategy == null ) { return MultiTenancyStrategy.NONE; } - if ( MultiTenancyStrategy.class.isInstance( value ) ) { - return (MultiTenancyStrategy) value; + if ( MultiTenancyStrategy.class.isInstance( strategy ) ) { + return (MultiTenancyStrategy) strategy; } - return fromExternalName( value.toString() ); - } - - private static MultiTenancyStrategy fromExternalName(String strategyName) { + final String strategyName = strategy.toString(); try { return MultiTenancyStrategy.valueOf( strategyName.toUpperCase() ); } @@ -80,8 +81,4 @@ public enum MultiTenancyStrategy { return MultiTenancyStrategy.NONE; } } - - public static MultiTenancyStrategy determineMultiTenancyStrategy(Map properties) { - return fromConfigValue( properties.get( Environment.MULTI_TENANT ) ); - } } diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityDeleteAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityDeleteAction.java index be914bad83..ed1ae724f8 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityDeleteAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityDeleteAction.java @@ -37,6 +37,7 @@ import org.hibernate.event.service.spi.EventListenerGroup; import org.hibernate.event.spi.EventType; import org.hibernate.event.spi.PostDeleteEvent; import org.hibernate.event.spi.PostDeleteEventListener; +import org.hibernate.event.spi.PostInsertEventListener; import org.hibernate.event.spi.PreDeleteEvent; import org.hibernate.event.spi.PreDeleteEventListener; import org.hibernate.persister.entity.EntityPersister; @@ -189,6 +190,13 @@ public final class EntityDeleteAction extends EntityAction { @Override protected boolean hasPostCommitEventListeners() { - return ! listenerGroup( EventType.POST_COMMIT_DELETE ).isEmpty(); + final EventListenerGroup group = listenerGroup( EventType.POST_COMMIT_DELETE ); + for ( PostDeleteEventListener listener : group.listeners() ) { + if ( listener.requiresPostCommitHanding( getPersister() ) ) { + return true; + } + } + + return false; } } diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityIdentityInsertAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityIdentityInsertAction.java index 2f0c1bead4..54b325782f 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityIdentityInsertAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityIdentityInsertAction.java @@ -116,7 +116,14 @@ public final class EntityIdentityInsertAction extends AbstractEntityInsertAction @Override protected boolean hasPostCommitEventListeners() { - return ! listenerGroup( EventType.POST_COMMIT_INSERT ).isEmpty(); + final EventListenerGroup group = listenerGroup( EventType.POST_COMMIT_INSERT ); + for ( PostInsertEventListener listener : group.listeners() ) { + if ( listener.requiresPostCommitHanding( getPersister() ) ) { + return true; + } + } + + return false; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java index ba4a43e284..198a153c9b 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityInsertAction.java @@ -204,7 +204,14 @@ public final class EntityInsertAction extends AbstractEntityInsertAction { @Override protected boolean hasPostCommitEventListeners() { - return ! listenerGroup( EventType.POST_COMMIT_INSERT ).isEmpty(); + final EventListenerGroup group = listenerGroup( EventType.POST_COMMIT_INSERT ); + for ( PostInsertEventListener listener : group.listeners() ) { + if ( listener.requiresPostCommitHanding( getPersister() ) ) { + return true; + } + } + + return false; } private boolean isCachePutEnabled(EntityPersister persister, SessionImplementor session) { diff --git a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java index db48dd1d38..f37faf5a65 100644 --- a/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java +++ b/hibernate-core/src/main/java/org/hibernate/action/internal/EntityUpdateAction.java @@ -277,7 +277,14 @@ public final class EntityUpdateAction extends EntityAction { @Override protected boolean hasPostCommitEventListeners() { - return ! listenerGroup( EventType.POST_COMMIT_UPDATE ).isEmpty(); + final EventListenerGroup group = listenerGroup( EventType.POST_COMMIT_UPDATE ); + for ( PostUpdateEventListener listener : group.listeners() ) { + if ( listener.requiresPostCommitHanding( getPersister() ) ) { + return true; + } + } + + return false; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheKey.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheKey.java index a9b75234ae..3fa4b5058f 100755 --- a/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheKey.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/CacheKey.java @@ -30,11 +30,11 @@ import org.hibernate.internal.util.compare.EqualsHelper; import org.hibernate.type.Type; /** - * Allows multiple entity classes / collection roles to be - * stored in the same cache region. Also allows for composite + * Allows multiple entity classes / collection roles to be stored in the same cache region. Also allows for composite * keys which do not properly implement equals()/hashCode(). * * @author Gavin King + * @author Steve Ebersole */ public class CacheKey implements Serializable { private final Serializable key; @@ -64,33 +64,13 @@ public class CacheKey implements Serializable { this.type = type; this.entityOrRoleName = entityOrRoleName; this.tenantId = tenantId; - this.hashCode = type.getHashCode( key, factory ); + this.hashCode = calculateHashCode( type, factory ); } - @Override - public String toString() { - // Mainly for OSCache - return entityOrRoleName + '#' + key.toString();//"CacheKey#" + type.toString(key, sf); - } - - @Override - public boolean equals(Object other) { - if ( this == other ) { - return true; - } - if ( hashCode != other.hashCode() || !(other instanceof CacheKey) ) { - //hashCode is part of this check since it is pre-calculated and hash must match for equals to be true - return false; - } - CacheKey that = (CacheKey) other; - return entityOrRoleName.equals( that.entityOrRoleName ) && - type.isEqual( key, that.key ) && - EqualsHelper.equals( tenantId, that.tenantId ); - } - - @Override - public int hashCode() { - return hashCode; + private int calculateHashCode(Type type, SessionFactoryImplementor factory) { + int result = type.getHashCode( key, factory ); + result = 31 * result + (tenantId != null ? tenantId.hashCode() : 0); + return result; } public Serializable getKey() { @@ -101,4 +81,36 @@ public class CacheKey implements Serializable { return entityOrRoleName; } + public String getTenantId() { + return tenantId; + } + + @Override + public boolean equals(Object other) { + if ( other == null ) { + return false; + } + if ( this == other ) { + return true; + } + if ( hashCode != other.hashCode() || !( other instanceof CacheKey ) ) { + //hashCode is part of this check since it is pre-calculated and hash must match for equals to be true + return false; + } + CacheKey that = (CacheKey) other; + return EqualsHelper.equals( entityOrRoleName, that.entityOrRoleName ) && + type.isEqual( key, that.key ) && + EqualsHelper.equals( tenantId, that.tenantId ); + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public String toString() { + // Mainly for OSCache + return entityOrRoleName + '#' + key.toString();//"CacheKey#" + type.toString(key, sf); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/cache/spi/NaturalIdCacheKey.java b/hibernate-core/src/main/java/org/hibernate/cache/spi/NaturalIdCacheKey.java index 0eda0a7796..61c1ed9a6c 100644 --- a/hibernate-core/src/main/java/org/hibernate/cache/spi/NaturalIdCacheKey.java +++ b/hibernate-core/src/main/java/org/hibernate/cache/spi/NaturalIdCacheKey.java @@ -23,6 +23,8 @@ */ package org.hibernate.cache.spi; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Arrays; @@ -44,7 +46,7 @@ public class NaturalIdCacheKey implements Serializable { private final String entityName; private final String tenantId; private final int hashCode; - private final transient ValueHolder toString; + private transient ValueHolder toString; /** * Construct a new key for a caching natural identifier resolutions into the second level cache. @@ -73,7 +75,8 @@ public class NaturalIdCacheKey implements Serializable { result = prime * result + ( ( this.entityName == null ) ? 0 : this.entityName.hashCode() ); result = prime * result + ( ( this.tenantId == null ) ? 0 : this.tenantId.hashCode() ); for ( int i = 0; i < naturalIdValues.length; i++ ) { - final Type type = propertyTypes[naturalIdPropertyIndexes[i]]; + final int naturalIdPropertyIndex = naturalIdPropertyIndexes[i]; + final Type type = propertyTypes[naturalIdPropertyIndex]; final Object value = naturalIdValues[i]; result = prime * result + (value != null ? type.getHashCode( value, factory ) : 0); @@ -82,25 +85,29 @@ public class NaturalIdCacheKey implements Serializable { } this.hashCode = result; - this.toString = new ValueHolder( - new ValueHolder.DeferredInitializer() { - @Override - public String initialize() { - //Complex toString is needed as naturalIds for entities are not simply based on a single value like primary keys - //the only same way to differentiate the keys is to included the disassembled values in the string. - final StringBuilder toStringBuilder = new StringBuilder( entityName ).append( "##NaturalId[" ); - for ( int i = 0; i < naturalIdValues.length; i++ ) { - toStringBuilder.append( naturalIdValues[i] ); - if ( i + 1 < naturalIdValues.length ) { - toStringBuilder.append( ", " ); - } - } - toStringBuilder.append( "]" ); + initTransients(); + } + + private void initTransients() { + this.toString = new ValueHolder( + new ValueHolder.DeferredInitializer() { + @Override + public String initialize() { + //Complex toString is needed as naturalIds for entities are not simply based on a single value like primary keys + //the only same way to differentiate the keys is to included the disassembled values in the string. + final StringBuilder toStringBuilder = new StringBuilder( entityName ).append( "##NaturalId[" ); + for ( int i = 0; i < naturalIdValues.length; i++ ) { + toStringBuilder.append( naturalIdValues[i] ); + if ( i + 1 < naturalIdValues.length ) { + toStringBuilder.append( ", " ); + } + } + toStringBuilder.append( "]" ); - return toStringBuilder.toString(); - } - } - ); + return toStringBuilder.toString(); + } + } + ); } @SuppressWarnings( {"UnusedDeclaration"}) @@ -130,18 +137,27 @@ public class NaturalIdCacheKey implements Serializable { @Override public boolean equals(Object o) { + if ( o == null ) { + return false; + } if ( this == o ) { return true; } - - if ( hashCode != o.hashCode() || !(o instanceof NaturalIdCacheKey) ) { + + if ( hashCode != o.hashCode() || !( o instanceof NaturalIdCacheKey ) ) { //hashCode is part of this check since it is pre-calculated and hash must match for equals to be true return false; } final NaturalIdCacheKey other = (NaturalIdCacheKey) o; - return entityName.equals( other.entityName ) + return EqualsHelper.equals( entityName, other.entityName ) && EqualsHelper.equals( tenantId, other.tenantId ) && Arrays.deepEquals( this.naturalIdValues, other.naturalIdValues ); } + + private void readObject(ObjectInputStream ois) + throws ClassNotFoundException, IOException { + ois.defaultReadObject(); + initTransients(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AbstractPropertyHolder.java b/hibernate-core/src/main/java/org/hibernate/cfg/AbstractPropertyHolder.java index 46eb4a16e2..8676f63d12 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AbstractPropertyHolder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AbstractPropertyHolder.java @@ -285,7 +285,7 @@ public abstract class AbstractPropertyHolder implements PropertyHolder { Map currentJoinTableOverride = buildJoinTableOverride( current, getPath() ); currentOverride.putAll( columnOverride ); //subclasses have precedence over superclasses currentJoinOverride.putAll( joinColumnOverride ); //subclasses have precedence over superclasses - currentJoinOverride.putAll( joinColumnOverride ); //subclasses have precedence over superclasses + currentJoinTableOverride.putAll( joinTableOverride ); //subclasses have precedence over superclasses columnOverride = currentOverride; joinColumnOverride = currentJoinOverride; joinTableOverride = currentJoinTableOverride; diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java index 6ec257ce8e..a84adf7b8a 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AnnotationBinder.java @@ -2496,6 +2496,7 @@ public final class AnnotationBinder { value.setPersistentClassName( persistentClassName ); value.setMappings( mappings ); value.setType( inferredData.getProperty(), inferredData.getClassOrElement() ); + value.setAccessType( propertyAccessor ); id = value.make(); } rootClass.setIdentifier( id ); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java index c33817137f..90ca1d159c 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/AvailableSettings.java @@ -642,4 +642,5 @@ public interface AvailableSettings { public static final String SCHEMA_MANAGEMENT_TOOL = "hibernate.schema_management_tool"; // todo : add to Environment String SCHEMA_NAME_RESOLVER = "hibernate.schema_name_resolver"; + public static final String ENABLE_LAZY_LOAD_NO_TRANS = "hibernate.enable_lazy_load_no_trans"; } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java index d6e1cbf59a..03aafe41db 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/BinderHelper.java @@ -115,6 +115,136 @@ public class BinderHelper { return clone; } +// This is sooooooooo close in terms of not generating a synthetic property if we do not have to (where property ref +// refers to a single property). The sticking point is cases where the `referencedPropertyName` come from subclasses +// or secondary tables. Part of the problem is in PersistentClass itself during attempts to resolve the referenced +// property; currently it only considers non-subclass and non-joined properties. Part of the problem is in terms +// of SQL generation. +// public static void createSyntheticPropertyReference( +// Ejb3JoinColumn[] columns, +// PersistentClass ownerEntity, +// PersistentClass associatedEntity, +// Value value, +// boolean inverse, +// Mappings mappings) { +// //associated entity only used for more precise exception, yuk! +// if ( columns[0].isImplicit() || StringHelper.isNotEmpty( columns[0].getMappedBy() ) ) return; +// int fkEnum = Ejb3JoinColumn.checkReferencedColumnsType( columns, ownerEntity, mappings ); +// PersistentClass associatedClass = columns[0].getPropertyHolder() != null ? +// columns[0].getPropertyHolder().getPersistentClass() : +// null; +// if ( Ejb3JoinColumn.NON_PK_REFERENCE == fkEnum ) { +// //find properties associated to a certain column +// Object columnOwner = findColumnOwner( ownerEntity, columns[0].getReferencedColumn(), mappings ); +// List properties = findPropertiesByColumns( columnOwner, columns, mappings ); +// +// if ( properties == null ) { +// //TODO use a ToOne type doing a second select +// StringBuilder columnsList = new StringBuilder(); +// columnsList.append( "referencedColumnNames(" ); +// for (Ejb3JoinColumn column : columns) { +// columnsList.append( column.getReferencedColumn() ).append( ", " ); +// } +// columnsList.setLength( columnsList.length() - 2 ); +// columnsList.append( ") " ); +// +// if ( associatedEntity != null ) { +// //overidden destination +// columnsList.append( "of " ) +// .append( associatedEntity.getEntityName() ) +// .append( "." ) +// .append( columns[0].getPropertyName() ) +// .append( " " ); +// } +// else { +// if ( columns[0].getPropertyHolder() != null ) { +// columnsList.append( "of " ) +// .append( columns[0].getPropertyHolder().getEntityName() ) +// .append( "." ) +// .append( columns[0].getPropertyName() ) +// .append( " " ); +// } +// } +// columnsList.append( "referencing " ) +// .append( ownerEntity.getEntityName() ) +// .append( " not mapped to a single property" ); +// throw new AnnotationException( columnsList.toString() ); +// } +// +// final String referencedPropertyName; +// +// if ( properties.size() == 1 ) { +// referencedPropertyName = properties.get(0).getName(); +// } +// else { +// // Create a synthetic (embedded composite) property to use as the referenced property which +// // contains all the properties mapped to the referenced columns. We need to make a shallow copy +// // of the properties to mark them as non-insertable/updatable. +// +// // todo : what if the columns all match with an existing component? +// +// StringBuilder propertyNameBuffer = new StringBuilder( "_" ); +// propertyNameBuffer.append( associatedClass.getEntityName().replace( '.', '_' ) ); +// propertyNameBuffer.append( "_" ).append( columns[0].getPropertyName() ); +// String syntheticPropertyName = propertyNameBuffer.toString(); +// //create an embeddable component +// +// //todo how about properties.size() == 1, this should be much simpler +// Component embeddedComp = columnOwner instanceof PersistentClass ? +// new Component( mappings, (PersistentClass) columnOwner ) : +// new Component( mappings, (Join) columnOwner ); +// embeddedComp.setEmbedded( true ); +// embeddedComp.setNodeName( syntheticPropertyName ); +// embeddedComp.setComponentClassName( embeddedComp.getOwner().getClassName() ); +// for (Property property : properties) { +// Property clone = BinderHelper.shallowCopy( property ); +// clone.setInsertable( false ); +// clone.setUpdateable( false ); +// clone.setNaturalIdentifier( false ); +// clone.setGeneration( property.getGeneration() ); +// embeddedComp.addProperty( clone ); +// } +// SyntheticProperty synthProp = new SyntheticProperty(); +// synthProp.setName( syntheticPropertyName ); +// synthProp.setNodeName( syntheticPropertyName ); +// synthProp.setPersistentClass( ownerEntity ); +// synthProp.setUpdateable( false ); +// synthProp.setInsertable( false ); +// synthProp.setValue( embeddedComp ); +// synthProp.setPropertyAccessorName( "embedded" ); +// ownerEntity.addProperty( synthProp ); +// //make it unique +// TableBinder.createUniqueConstraint( embeddedComp ); +// +// referencedPropertyName = syntheticPropertyName; +// } +// +// /** +// * creating the property ref to the new synthetic property +// */ +// if ( value instanceof ToOne ) { +// ( (ToOne) value ).setReferencedPropertyName( referencedPropertyName ); +// mappings.addUniquePropertyReference( ownerEntity.getEntityName(), referencedPropertyName ); +// } +// else if ( value instanceof Collection ) { +// ( (Collection) value ).setReferencedPropertyName( referencedPropertyName ); +// //not unique because we could create a mtm wo association table +// mappings.addPropertyReference( ownerEntity.getEntityName(), referencedPropertyName ); +// } +// else { +// throw new AssertionFailure( +// "Do a property ref on an unexpected Value type: " +// + value.getClass().getName() +// ); +// } +// mappings.addPropertyReferencedAssociation( +// ( inverse ? "inverse__" : "" ) + associatedClass.getEntityName(), +// columns[0].getPropertyName(), +// referencedPropertyName +// ); +// } +// } + public static void createSyntheticPropertyReference( Ejb3JoinColumn[] columns, PersistentClass ownerEntity, @@ -138,15 +268,15 @@ public class BinderHelper { */ StringBuilder propertyNameBuffer = new StringBuilder( "_" ); propertyNameBuffer.append( associatedClass.getEntityName().replace( '.', '_' ) ); - propertyNameBuffer.append( "_" ).append( columns[0].getPropertyName() ); + propertyNameBuffer.append( "_" ).append( columns[0].getPropertyName().replace( '.', '_' ) ); String syntheticPropertyName = propertyNameBuffer.toString(); //find properties associated to a certain column Object columnOwner = findColumnOwner( ownerEntity, columns[0].getReferencedColumn(), mappings ); List properties = findPropertiesByColumns( columnOwner, columns, mappings ); //create an embeddable component - Property synthProp = null; + Property synthProp = null; if ( properties != null ) { - //todo how about properties.size() == 1, this should be much simpler + //todo how about properties.size() == 1, this should be much simpler Component embeddedComp = columnOwner instanceof PersistentClass ? new Component( mappings, (PersistentClass) columnOwner ) : new Component( mappings, (Join) columnOwner ); @@ -160,8 +290,8 @@ public class BinderHelper { clone.setNaturalIdentifier( false ); clone.setGeneration( property.getGeneration() ); embeddedComp.addProperty( clone ); - } - synthProp = new SyntheticProperty(); + } + synthProp = new SyntheticProperty(); synthProp.setName( syntheticPropertyName ); synthProp.setNodeName( syntheticPropertyName ); synthProp.setPersistentClass( ownerEntity ); @@ -170,9 +300,9 @@ public class BinderHelper { synthProp.setValue( embeddedComp ); synthProp.setPropertyAccessorName( "embedded" ); ownerEntity.addProperty( synthProp ); - //make it unique + //make it unique TableBinder.createUniqueConstraint( embeddedComp ); - } + } else { //TODO use a ToOne type doing a second select StringBuilder columnsList = new StringBuilder(); @@ -700,7 +830,7 @@ public class BinderHelper { for (int i = 0; i < aliases.length; i++){ if (StringHelper.isNotEmpty(aliases[i].table())){ ret.put(aliases[i].alias(), aliases[i].table()); - } +} } return ret; } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java index c3da353492..c51da15230 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/Configuration.java @@ -1345,18 +1345,7 @@ public class Configuration implements Serializable { metadataSourceQueue.processMetadata( determineMetadataSourcePrecedence() ); } - // process cache queue - { - for ( CacheHolder holder : caches ) { - if ( holder.isClass ) { - applyCacheConcurrencyStrategy( holder ); - } - else { - applyCollectionCacheConcurrencyStrategy( holder ); - } - } - caches.clear(); - } + try { inSecondPass = true; @@ -1376,6 +1365,19 @@ public class Configuration implements Serializable { throw ( RuntimeException ) e.getCause(); } + // process cache queue + { + for ( CacheHolder holder : caches ) { + if ( holder.isClass ) { + applyCacheConcurrencyStrategy( holder ); + } + else { + applyCollectionCacheConcurrencyStrategy( holder ); + } + } + caches.clear(); + } + for ( Map.Entry> tableListEntry : uniqueConstraintHoldersByTable.entrySet() ) { final Table table = tableListEntry.getKey(); final List uniqueConstraints = tableListEntry.getValue(); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java b/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java index 89cc5357a0..8b8a5eaafe 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/SettingsFactory.java @@ -47,6 +47,8 @@ import org.hibernate.internal.util.StringHelper; import org.hibernate.internal.util.config.ConfigurationHelper; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.classloading.spi.ClassLoaderService; +import org.hibernate.service.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider; import org.hibernate.service.jta.platform.spi.JtaPlatform; import org.hibernate.tuple.entity.EntityTuplizerFactory; @@ -152,6 +154,12 @@ public class SettingsFactory implements Serializable { } settings.setJdbcFetchSize(statementFetchSize); + MultiTenancyStrategy multiTenancyStrategy = MultiTenancyStrategy.determineMultiTenancyStrategy( properties ); + if ( debugEnabled ) { + LOG.debugf( "multi-tenancy strategy : %s", multiTenancyStrategy ); + } + settings.setMultiTenancyStrategy( multiTenancyStrategy ); + String releaseModeName = ConfigurationHelper.getString( Environment.RELEASE_CONNECTIONS, properties, "auto" ); if ( debugEnabled ) { LOG.debugf( "Connection release mode: %s", releaseModeName ); @@ -162,10 +170,15 @@ public class SettingsFactory implements Serializable { } else { releaseMode = ConnectionReleaseMode.parse( releaseModeName ); - if ( releaseMode == ConnectionReleaseMode.AFTER_STATEMENT && - ! jdbcServices.getConnectionProvider().supportsAggressiveRelease() ) { - LOG.unsupportedAfterStatement(); - releaseMode = ConnectionReleaseMode.AFTER_TRANSACTION; + if ( releaseMode == ConnectionReleaseMode.AFTER_STATEMENT ) { + // we need to make sure the underlying JDBC connection access supports aggressive release... + boolean supportsAgrressiveRelease = multiTenancyStrategy.requiresMultiTenantConnectionProvider() + ? serviceRegistry.getService( MultiTenantConnectionProvider.class ).supportsAggressiveRelease() + : serviceRegistry.getService( ConnectionProvider.class ).supportsAggressiveRelease(); + if ( ! supportsAgrressiveRelease ) { + LOG.unsupportedAfterStatement(); + releaseMode = ConnectionReleaseMode.AFTER_TRANSACTION; + } } } settings.setConnectionReleaseMode( releaseMode ); @@ -303,12 +316,6 @@ public class SettingsFactory implements Serializable { } settings.setCheckNullability(checkNullability); - MultiTenancyStrategy multiTenancyStrategy = MultiTenancyStrategy.determineMultiTenancyStrategy( properties ); - if ( debugEnabled ) { - LOG.debugf( "multi-tenancy strategy : %s", multiTenancyStrategy ); - } - settings.setMultiTenancyStrategy( multiTenancyStrategy ); - // TODO: Does EntityTuplizerFactory really need to be configurable? revisit for HHH-6383 settings.setEntityTuplizerFactory( new EntityTuplizerFactory() ); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java index 556f48429d..41834a8819 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/CollectionBinder.java @@ -1036,7 +1036,7 @@ public abstract class CollectionBinder { } else { keyVal = (KeyValue) collValue.getOwner() - .getRecursiveProperty( propRef ) + .getReferencedProperty( propRef ) .getValue(); } DependantValue key = new DependantValue( mappings, collValue.getCollectionTable(), keyVal ); @@ -1317,6 +1317,8 @@ public abstract class CollectionBinder { } elementBinder.setColumns( elementColumns ); elementBinder.setType( property, elementClass ); + elementBinder.setPersistentClassName( propertyHolder.getEntityName() ); + elementBinder.setAccessType( accessType ); collValue.setElement( elementBinder.make() ); String orderBy = adjustUserSuppliedValueCollectionOrderingFragment( hqlOrderBy ); if ( orderBy != null ) { @@ -1467,4 +1469,4 @@ public abstract class CollectionBinder { public void setLocalGenerators(HashMap localGenerators) { this.localGenerators = localGenerators; } -} \ No newline at end of file +} diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java index a21074c3be..d62ddc3fb0 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/MapBinder.java @@ -210,26 +210,25 @@ public class MapBinder extends CollectionBinder { } } + PersistentClass owner = mapValue.getOwner(); + AccessType accessType; + // FIXME support @Access for collection of elements + // String accessType = access != null ? access.value() : null; + if ( owner.getIdentifierProperty() != null ) { + accessType = owner.getIdentifierProperty().getPropertyAccessorName().equals( "property" ) ? AccessType.PROPERTY + : AccessType.FIELD; + } + else if ( owner.getIdentifierMapper() != null && owner.getIdentifierMapper().getPropertySpan() > 0 ) { + Property prop = (Property) owner.getIdentifierMapper().getPropertyIterator().next(); + accessType = prop.getPropertyAccessorName().equals( "property" ) ? AccessType.PROPERTY + : AccessType.FIELD; + } + else { + throw new AssertionFailure( "Unable to guess collection property accessor name" ); + } + if ( AnnotatedClassType.EMBEDDABLE.equals( classType ) ) { EntityBinder entityBinder = new EntityBinder(); - PersistentClass owner = mapValue.getOwner(); - boolean isPropertyAnnotated; - //FIXME support @Access for collection of elements - //String accessType = access != null ? access.value() : null; - if ( owner.getIdentifierProperty() != null ) { - isPropertyAnnotated = owner.getIdentifierProperty() - .getPropertyAccessorName() - .equals( "property" ); - } - else - if ( owner.getIdentifierMapper() != null && owner.getIdentifierMapper().getPropertySpan() > 0 ) { - Property prop = (Property) owner.getIdentifierMapper().getPropertyIterator().next(); - isPropertyAnnotated = prop.getPropertyAccessorName().equals( "property" ); - } - else { - throw new AssertionFailure( "Unable to guess collection property accessor name" ); - } - PropertyData inferredData; if ( isHibernateExtensionMapping() ) { @@ -242,7 +241,7 @@ public class MapBinder extends CollectionBinder { //TODO be smart with isNullable Component component = AnnotationBinder.fillComponent( - holder, inferredData, isPropertyAnnotated ? AccessType.PROPERTY : AccessType.FIELD, true, + holder, inferredData, accessType, true, entityBinder, false, false, true, mappings, inheritanceStatePerClass ); @@ -285,6 +284,8 @@ public class MapBinder extends CollectionBinder { else { elementBinder.setType( property, elementClass ); } + elementBinder.setPersistentClassName( propertyHolder.getEntityName() ); + elementBinder.setAccessType( accessType ); mapValue.setIndex( elementBinder.make() ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java index 84d30b332c..b4251816b7 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/PropertyBinder.java @@ -188,6 +188,7 @@ public class PropertyBinder { simpleValueBinder.setType( property, returnedClass ); simpleValueBinder.setMappings( mappings ); simpleValueBinder.setReferencedEntityName( referencedEntityName ); + simpleValueBinder.setAccessType( accessType ); SimpleValue propertyValue = simpleValueBinder.make(); setValue( propertyValue ); return makeProperty(); diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/SimpleValueBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/SimpleValueBinder.java index 24f7219df7..59e5d54f7c 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/SimpleValueBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/SimpleValueBinder.java @@ -24,6 +24,7 @@ package org.hibernate.cfg.annotations; import java.io.Serializable; +import java.lang.annotation.Annotation; import java.lang.reflect.TypeVariable; import java.sql.Types; import java.util.Calendar; @@ -40,15 +41,15 @@ import javax.persistence.MapKeyTemporal; import javax.persistence.Temporal; import javax.persistence.TemporalType; -import org.jboss.logging.Logger; - import org.hibernate.AnnotationException; import org.hibernate.AssertionFailure; +import org.hibernate.MappingException; import org.hibernate.annotations.Parameter; import org.hibernate.annotations.Type; import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.annotations.common.reflection.XProperty; import org.hibernate.annotations.common.util.ReflectHelper; +import org.hibernate.cfg.AccessType; import org.hibernate.cfg.AttributeConverterDefinition; import org.hibernate.cfg.BinderHelper; import org.hibernate.cfg.Ejb3Column; @@ -67,18 +68,25 @@ import org.hibernate.type.PrimitiveCharacterArrayClobType; import org.hibernate.type.SerializableToBlobType; import org.hibernate.type.StandardBasicTypes; import org.hibernate.type.WrappedMaterializedBlobType; +import org.hibernate.usertype.DynamicParameterizedType; + +import org.jboss.logging.Logger; /** * @author Emmanuel Bernard */ public class SimpleValueBinder { - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, SimpleValueBinder.class.getName()); + private static final CoreMessageLogger LOG = Logger.getMessageLogger( + CoreMessageLogger.class, + SimpleValueBinder.class.getName() + ); private String propertyName; private String returnedClassName; private Ejb3Column[] columns; private String persistentClassName; private String explicitType = ""; + private String defaultType = ""; private Properties typeParameters = new Properties(); private Mappings mappings; private Table table; @@ -88,6 +96,8 @@ public class SimpleValueBinder { //is a Map key private boolean key; private String referencedEntityName; + private XProperty xproperty; + private AccessType accessType; private AttributeConverterDefinition attributeConverterDefinition; @@ -113,6 +123,10 @@ public class SimpleValueBinder { public void setReturnedClassName(String returnedClassName) { this.returnedClassName = returnedClassName; + + if ( defaultType.length() == 0 ) { + defaultType = returnedClassName; + } } public void setTable(Table table) { @@ -140,10 +154,17 @@ public class SimpleValueBinder { returnedClassOrElement = property.getElementClass(); isArray = true; } + this.xproperty = property; Properties typeParameters = this.typeParameters; typeParameters.clear(); String type = BinderHelper.ANNOTATION_STRING_DEFAULT; - if ( ( !key && property.isAnnotationPresent( Temporal.class ) ) + + Type annType = property.getAnnotation( Type.class ); + if ( annType != null ) { + setExplicitType( annType ); + type = explicitType; + } + else if ( ( !key && property.isAnnotationPresent( Temporal.class ) ) || ( key && property.isAnnotationPresent( MapKeyTemporal.class ) ) ) { boolean isDate; @@ -179,6 +200,7 @@ public class SimpleValueBinder { default: throw new AssertionFailure( "Unknown temporal type: " + temporalType ); } + explicitType = type; } else if ( property.isAnnotationPresent( Lob.class ) ) { if ( mappings.getReflectionManager().equals( returnedClassOrElement, java.sql.Clob.class ) ) { @@ -215,58 +237,43 @@ public class SimpleValueBinder { else { type = "blob"; } + explicitType = type; } - //implicit type will check basic types and Serializable classes + else if ( ( !key && property.isAnnotationPresent( Enumerated.class ) ) + || ( key && property.isAnnotationPresent( MapKeyEnumerated.class ) ) ) { + type = EnumType.class.getName(); + explicitType = type; + } + + // implicit type will check basic types and Serializable classes if ( columns == null ) { throw new AssertionFailure( "SimpleValueBinder.setColumns should be set before SimpleValueBinder.setType" ); } + if ( BinderHelper.ANNOTATION_STRING_DEFAULT.equals( type ) ) { if ( returnedClassOrElement.isEnum() ) { type = EnumType.class.getName(); - typeParameters = new Properties(); - typeParameters.setProperty( EnumType.ENUM, returnedClassOrElement.getName() ); - String schema = columns[0].getTable().getSchema(); - schema = schema == null ? "" : schema; - String catalog = columns[0].getTable().getCatalog(); - catalog = catalog == null ? "" : catalog; - typeParameters.setProperty( EnumType.SCHEMA, schema ); - typeParameters.setProperty( EnumType.CATALOG, catalog ); - typeParameters.setProperty( EnumType.TABLE, columns[0].getTable().getName() ); - typeParameters.setProperty( EnumType.COLUMN, columns[0].getName() ); - javax.persistence.EnumType enumType = getEnumType( property ); - if ( enumType != null ) { - if ( javax.persistence.EnumType.ORDINAL.equals( enumType ) ) { - typeParameters.setProperty( EnumType.TYPE, String.valueOf( Types.INTEGER ) ); - } - else if ( javax.persistence.EnumType.STRING.equals( enumType ) ) { - typeParameters.setProperty( EnumType.TYPE, String.valueOf( Types.VARCHAR ) ); - } - else { - throw new AssertionFailure( "Unknown EnumType: " + enumType ); - } - } } } - explicitType = type; + + defaultType = BinderHelper.isEmptyAnnotationValue( type ) ? returnedClassName : type; this.typeParameters = typeParameters; - Type annType = property.getAnnotation( Type.class ); - setExplicitType( annType ); applyAttributeConverter( property ); } private void applyAttributeConverter(XProperty property) { - final boolean canBeConverted = ! property.isAnnotationPresent( Id.class ) - && ! isVersion - && ! isAssociation() - && ! property.isAnnotationPresent( Temporal.class ) - && ! property.isAnnotationPresent( Enumerated.class ); + final boolean canBeConverted = !property.isAnnotationPresent( Id.class ) + && !isVersion + && !isAssociation() + && !property.isAnnotationPresent( Temporal.class ) + && !property.isAnnotationPresent( Enumerated.class ); if ( canBeConverted ) { // @Convert annotations take precedence final Convert convertAnnotation = locateConvertAnnotation( property ); if ( convertAnnotation != null ) { - if ( ! convertAnnotation.disableConversion() ) { + if ( !convertAnnotation.disableConversion() ) { attributeConverterDefinition = mappings.locateAttributeConverter( convertAnnotation.converter() ); } } @@ -308,10 +315,12 @@ public class SimpleValueBinder { final XClass owner; try { final Class ownerClass = ReflectHelper.classForName( persistentClassName ); - owner = mappings.getReflectionManager().classForName( persistentClassName, ownerClass ); + owner = mappings.getReflectionManager().classForName( persistentClassName, ownerClass ); } - catch (ClassNotFoundException e) { - throw new AnnotationException( "Unable to resolve Class reference during attempt to locate @Convert annotations" ); + catch ( ClassNotFoundException e ) { + throw new AnnotationException( + "Unable to resolve Class reference during attempt to locate @Convert annotations" + ); } return lookForEntityDefinedConvertAnnotation( property, owner ); @@ -416,7 +425,7 @@ public class SimpleValueBinder { } private static Class getWrapperEquivalent(Class primitive) { - if ( ! primitive.isPrimitive() ) { + if ( !primitive.isPrimitive() ) { throw new AssertionFailure( "Passed type for which to locate wrapper equivalent was not a primitive" ); } @@ -448,23 +457,6 @@ public class SimpleValueBinder { throw new AssertionFailure( "Unexpected primitive type (VOID most likely) passed to getWrapperEquivalent" ); } - private javax.persistence.EnumType getEnumType(XProperty property) { - javax.persistence.EnumType enumType = null; - if ( key ) { - MapKeyEnumerated enumAnn = property.getAnnotation( MapKeyEnumerated.class ); - if ( enumAnn != null ) { - enumType = enumAnn.value(); - } - } - else { - Enumerated enumAnn = property.getAnnotation( Enumerated.class ); - if ( enumAnn != null ) { - enumType = enumAnn.value(); - } - } - return enumType; - } - private TemporalType getTemporalType(XProperty property) { if ( key ) { MapKeyTemporal ann = property.getAnnotation( MapKeyTemporal.class ); @@ -529,7 +521,7 @@ public class SimpleValueBinder { if ( columns[0].isNameDeferred() && !mappings.isInSecondPass() && referencedEntityName != null ) { mappings.addSecondPass( new PkDrivenByDefaultMapsIdSecondPass( - referencedEntityName, ( Ejb3JoinColumn[] ) columns, simpleValue + referencedEntityName, (Ejb3JoinColumn[]) columns, simpleValue ) ); } @@ -544,7 +536,7 @@ public class SimpleValueBinder { LOG.debugf( "Setting SimpleValue typeName for %s", propertyName ); if ( attributeConverterDefinition != null ) { - if ( ! BinderHelper.isEmptyAnnotationValue( explicitType ) ) { + if ( !BinderHelper.isEmptyAnnotationValue( explicitType ) ) { throw new AnnotationException( String.format( "AttributeConverter and explicit Type cannot be applied to same attribute [%s.%s];" + @@ -557,8 +549,26 @@ public class SimpleValueBinder { simpleValue.setJpaAttributeConverterDefinition( attributeConverterDefinition ); } else { - String type = BinderHelper.isEmptyAnnotationValue( explicitType ) ? returnedClassName : explicitType; - org.hibernate.mapping.TypeDef typeDef = mappings.getTypeDef( type ); + String type; + org.hibernate.mapping.TypeDef typeDef; + + if ( !BinderHelper.isEmptyAnnotationValue( explicitType ) ) { + type = explicitType; + typeDef = mappings.getTypeDef( type ); + } + else { + // try implicit type + org.hibernate.mapping.TypeDef implicitTypeDef = mappings.getTypeDef( returnedClassName ); + if ( implicitTypeDef != null ) { + typeDef = implicitTypeDef; + type = returnedClassName; + } + else { + typeDef = mappings.getTypeDef( defaultType ); + type = defaultType; + } + } + if ( typeDef != null ) { type = typeDef.getTypeClass(); simpleValue.setTypeParameters( typeDef.getParameters() ); @@ -582,10 +592,43 @@ public class SimpleValueBinder { if ( timeStampVersionType != null ) { simpleValue.setTypeName( timeStampVersionType ); } + + if ( simpleValue.getTypeName() != null && simpleValue.getTypeName().length() > 0 + && simpleValue.getMappings().getTypeResolver().basic( simpleValue.getTypeName() ) == null ) { + try { + Class typeClass = ReflectHelper.classForName( simpleValue.getTypeName() ); + + if ( typeClass != null && DynamicParameterizedType.class.isAssignableFrom( typeClass ) ) { + Properties parameters = simpleValue.getTypeParameters(); + if ( parameters == null ) { + parameters = new Properties(); + } + parameters.put( DynamicParameterizedType.IS_DYNAMIC, Boolean.toString( true ) ); + parameters.put( DynamicParameterizedType.RETURNED_CLASS, returnedClassName ); + parameters.put( DynamicParameterizedType.IS_PRIMARY_KEY, Boolean.toString( key ) ); + + parameters.put( DynamicParameterizedType.ENTITY, persistentClassName ); + parameters.put( DynamicParameterizedType.PROPERTY, xproperty.getName() ); + parameters.put( DynamicParameterizedType.ACCESS_TYPE, accessType.getType() ); + simpleValue.setTypeParameters( parameters ); + } + } + catch ( ClassNotFoundException cnfe ) { + throw new MappingException( "Could not determine type for: " + simpleValue.getTypeName(), cnfe ); + } + } + } public void setKey(boolean key) { this.key = key; } -} + public AccessType getAccessType() { + return accessType; + } + + public void setAccessType(AccessType accessType) { + this.accessType = accessType; + } +} \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java index afb8d40b81..64dfb89c30 100644 --- a/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java +++ b/hibernate-core/src/main/java/org/hibernate/cfg/annotations/TableBinder.java @@ -402,7 +402,7 @@ public class TableBinder { "No property ref found while expected" ); } - Property synthProp = referencedEntity.getRecursiveProperty( referencedPropertyName ); + Property synthProp = referencedEntity.getReferencedProperty( referencedPropertyName ); if ( synthProp == null ) { throw new AssertionFailure( "Cannot find synthProp: " + referencedEntity.getEntityName() + "." + referencedPropertyName diff --git a/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java b/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java index cce4eaf948..80db62ed21 100644 --- a/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java +++ b/hibernate-core/src/main/java/org/hibernate/collection/internal/AbstractPersistentCollection.java @@ -23,6 +23,7 @@ */ package org.hibernate.collection.internal; +import javax.naming.NamingException; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; @@ -32,18 +33,23 @@ import java.util.Iterator; import java.util.List; import java.util.ListIterator; +import org.jboss.logging.Logger; + import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.LazyInitializationException; +import org.hibernate.Session; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.collection.spi.PersistentCollection; import org.hibernate.engine.internal.ForeignKeys; import org.hibernate.engine.spi.CollectionEntry; import org.hibernate.engine.spi.EntityEntry; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.Status; import org.hibernate.engine.spi.TypedValue; +import org.hibernate.internal.SessionFactoryRegistry; import org.hibernate.internal.util.MarkerObject; -import org.hibernate.internal.util.collections.CollectionHelper; import org.hibernate.internal.util.collections.EmptyIterator; import org.hibernate.internal.util.collections.IdentitySet; import org.hibernate.persister.collection.CollectionPersister; @@ -57,6 +63,7 @@ import org.hibernate.type.Type; * @author Gavin King */ public abstract class AbstractPersistentCollection implements Serializable, PersistentCollection { + private static final Logger log = Logger.getLogger( AbstractPersistentCollection.class ); private transient SessionImplementor session; private boolean initialized; @@ -65,7 +72,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers private transient boolean initializing; private Object owner; private int cachedSize = -1; - + private String role; private Serializable key; // collections detect changes made via their public interface and mark @@ -73,140 +80,269 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers private boolean dirty; private Serializable storedSnapshot; + private String sessionFactoryUuid; + private boolean specjLazyLoad = false; + public final String getRole() { return role; } - + public final Serializable getKey() { return key; } - + public final boolean isUnreferenced() { - return role==null; + return role == null; } - + public final boolean isDirty() { return dirty; } - + public final void clearDirty() { dirty = false; } - + public final void dirty() { dirty = true; } - + public final Serializable getStoredSnapshot() { return storedSnapshot; } - + //Careful: these methods do not initialize the collection. + /** * Is the initialized collection empty? */ public abstract boolean empty(); + /** * Called by any read-only method of the collection interface */ protected final void read() { - initialize(false); + initialize( false ); } /** * Called by the {@link Collection#size} method */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) protected boolean readSize() { - if (!initialized) { - if ( cachedSize!=-1 && !hasQueuedOperations() ) { + if ( !initialized ) { + if ( cachedSize != -1 && !hasQueuedOperations() ) { return true; } else { - throwLazyInitializationExceptionIfNotConnected(); - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); - CollectionPersister persister = entry.getLoadedPersister(); - if ( persister.isExtraLazy() ) { - if ( hasQueuedOperations() ) { - session.flush(); - } - cachedSize = persister.getSize( entry.getLoadedKey(), session ); + boolean isExtraLazy = withTemporarySessionIfNeeded( + new LazyInitializationWork() { + @Override + public Boolean doWork() { + CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); + CollectionPersister persister = entry.getLoadedPersister(); + if ( persister.isExtraLazy() ) { + if ( hasQueuedOperations() ) { + session.flush(); + } + cachedSize = persister.getSize( entry.getLoadedKey(), session ); + return true; + } + else { + read(); + } + return false; + } + } + ); + if ( isExtraLazy ) { return true; } } } - read(); return false; } - - protected Boolean readIndexExistence(Object index) { - if (!initialized) { - throwLazyInitializationExceptionIfNotConnected(); - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); - CollectionPersister persister = entry.getLoadedPersister(); - if ( persister.isExtraLazy() ) { - if ( hasQueuedOperations() ) { - session.flush(); - } - return persister.indexExists( entry.getLoadedKey(), index, session ); - } - } - read(); - return null; - + + public static interface LazyInitializationWork { + public T doWork(); } - - protected Boolean readElementExistence(Object element) { - if (!initialized) { - throwLazyInitializationExceptionIfNotConnected(); - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); - CollectionPersister persister = entry.getLoadedPersister(); - if ( persister.isExtraLazy() ) { - if ( hasQueuedOperations() ) { - session.flush(); - } - return persister.elementExists( entry.getLoadedKey(), element, session ); + + private T withTemporarySessionIfNeeded(LazyInitializationWork lazyInitializationWork) { + SessionImplementor originalSession = null; + boolean isTempSession = false; + + if ( session == null ) { + if ( specjLazyLoad ) { + session = openTemporarySessionForLoading(); + isTempSession = true; + } + else { + throw new LazyInitializationException( "could not initialize proxy - no Session" ); + } + } + else if ( !session.isOpen() ) { + if ( specjLazyLoad ) { + originalSession = session; + session = openTemporarySessionForLoading(); + isTempSession = true; + } + else { + throw new LazyInitializationException( "could not initialize proxy - the owning Session was closed" ); + } + } + else if ( !session.isConnected() ) { + if ( specjLazyLoad ) { + originalSession = session; + session = openTemporarySessionForLoading(); + isTempSession = true; + } + else { + throw new LazyInitializationException( "could not initialize proxy - the owning Session is disconnected" ); + } + } + + if ( isTempSession ) { + session.getPersistenceContext().addUninitializedDetachedCollection( + session.getFactory().getCollectionPersister( getRole() ), + this + ); + } + + try { + return lazyInitializationWork.doWork(); + } + finally { + if ( isTempSession ) { + // make sure the just opened temp session gets closed! + try { + ( (Session) session ).close(); + } + catch (Exception e) { + log.warn( "Unable to close temporary session used to load lazy collection associated to no session" ); + } + session = originalSession; } } - read(); - return null; - } - - protected static final Object UNKNOWN = new MarkerObject("UNKNOWN"); - - protected Object readElementByIndex(Object index) { - if (!initialized) { - throwLazyInitializationExceptionIfNotConnected(); - CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(this); - CollectionPersister persister = entry.getLoadedPersister(); - if ( persister.isExtraLazy() ) { - if ( hasQueuedOperations() ) { - session.flush(); - } - return persister.getElementByIndex( entry.getLoadedKey(), index, session, owner ); + + private SessionImplementor openTemporarySessionForLoading() { + if ( sessionFactoryUuid == null ) { + throwLazyInitializationException( "SessionFactory UUID not known to create temporary Session for loading" ); + } + + SessionFactoryImplementor sf = (SessionFactoryImplementor) + SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid ); + return (SessionImplementor) sf.openSession(); + } + + protected Boolean readIndexExistence(final Object index) { + if ( !initialized ) { + Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded( + new LazyInitializationWork() { + @Override + public Boolean doWork() { + CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); + CollectionPersister persister = entry.getLoadedPersister(); + if ( persister.isExtraLazy() ) { + if ( hasQueuedOperations() ) { + session.flush(); + } + return persister.indexExists( entry.getLoadedKey(), index, session ); + } + else { + read(); + } + return null; + } + } + ); + if ( extraLazyExistenceCheck != null ) { + return extraLazyExistenceCheck; + } + } + return null; + } + + protected Boolean readElementExistence(final Object element) { + if ( !initialized ) { + Boolean extraLazyExistenceCheck = withTemporarySessionIfNeeded( + new LazyInitializationWork() { + @Override + public Boolean doWork() { + CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); + CollectionPersister persister = entry.getLoadedPersister(); + if ( persister.isExtraLazy() ) { + if ( hasQueuedOperations() ) { + session.flush(); + } + return persister.elementExists( entry.getLoadedKey(), element, session ); + } + else { + read(); + } + return null; + } + } + ); + if ( extraLazyExistenceCheck != null ) { + return extraLazyExistenceCheck; + } + } + return null; + } + + protected static final Object UNKNOWN = new MarkerObject( "UNKNOWN" ); + + protected Object readElementByIndex(final Object index) { + if ( !initialized ) { + class ExtraLazyElementByIndexReader implements LazyInitializationWork { + private boolean isExtraLazy; + private Object element; + + @Override + public Object doWork() { + CollectionEntry entry = session.getPersistenceContext().getCollectionEntry( AbstractPersistentCollection.this ); + CollectionPersister persister = entry.getLoadedPersister(); + isExtraLazy = persister.isExtraLazy(); + if ( isExtraLazy ) { + if ( hasQueuedOperations() ) { + session.flush(); + } + element = persister.getElementByIndex( entry.getLoadedKey(), index, session, owner ); + } + else { + read(); + } + return null; + } + } + + ExtraLazyElementByIndexReader reader = new ExtraLazyElementByIndexReader(); + //noinspection unchecked + withTemporarySessionIfNeeded( reader ); + if ( reader.isExtraLazy ) { + return reader.element; } } - read(); return UNKNOWN; - + } - + protected int getCachedSize() { return cachedSize; } - + private boolean isConnectedToSession() { - return session!=null && + return session != null && session.isOpen() && - session.getPersistenceContext().containsCollection(this); + session.getPersistenceContext().containsCollection( this ); } /** * Called by any writer method of the collection interface */ protected final void write() { - initialize(true); + initialize( true ); dirty(); } @@ -214,29 +350,31 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers * Is this collection in a state that would allow us to * "queue" operations? */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) protected boolean isOperationQueueEnabled() { return !initialized && isConnectedToSession() && isInverseCollection(); } + /** * Is this collection in a state that would allow us to * "queue" puts? This is a special case, because of orphan * delete. */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) protected boolean isPutQueueEnabled() { return !initialized && isConnectedToSession() && isInverseOneToManyOrNoOrphanDelete(); } + /** * Is this collection in a state that would allow us to * "queue" clear? This is a special case, because of orphan * delete. */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) protected boolean isClearQueueEnabled() { return !initialized && isConnectedToSession() && @@ -246,9 +384,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers /** * Is this the "inverse" end of a bidirectional association? */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) private boolean isInverseCollection() { - CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this); + CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this ); return ce != null && ce.getLoadedPersister().isInverse(); } @@ -256,34 +394,34 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers * Is this the "inverse" end of a bidirectional association with * no orphan delete enabled? */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) private boolean isInverseCollectionNoOrphanDelete() { - CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this); - return ce != null && + CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this ); + return ce != null && ce.getLoadedPersister().isInverse() && !ce.getLoadedPersister().hasOrphanDelete(); } /** - * Is this the "inverse" end of a bidirectional one-to-many, or + * Is this the "inverse" end of a bidirectional one-to-many, or * of a collection with no orphan delete? */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) private boolean isInverseOneToManyOrNoOrphanDelete() { - CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this); + CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this ); return ce != null && ce.getLoadedPersister().isInverse() && ( - ce.getLoadedPersister().isOneToMany() || - !ce.getLoadedPersister().hasOrphanDelete() - ); + ce.getLoadedPersister().isOneToMany() || + !ce.getLoadedPersister().hasOrphanDelete() + ); } /** * Queue an addition */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) protected final void queueOperation(DelayedOperation operation) { - if (operationQueue==null) { - operationQueue = new ArrayList(10); + if ( operationQueue == null ) { + operationQueue = new ArrayList( 10 ); } operationQueue.add( operation ); dirty = true; //needed so that we remove this collection from the second-level cache @@ -313,16 +451,17 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers * database state is now synchronized with the memory state. */ public void postAction() { - operationQueue=null; + operationQueue = null; cachedSize = -1; clearDirty(); } - + /** * Not called by Hibernate, but used by non-JDK serialization, * eg. SOAP libraries. */ - public AbstractPersistentCollection() {} + public AbstractPersistentCollection() { + } protected AbstractPersistentCollection(SessionImplementor session) { this.session = session; @@ -350,13 +489,13 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers //override on some subclasses return afterInitialize(); } - + public boolean afterInitialize() { setInitialized(); //do this bit after setting initialized to true or it will recurse - if (operationQueue!=null) { + if ( operationQueue != null ) { performQueuedOperations(); - operationQueue=null; + operationQueue = null; cachedSize = -1; return false; } @@ -368,34 +507,42 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers /** * Initialize the collection, if possible, wrapping any exceptions * in a runtime exception + * * @param writing currently obsolete + * * @throws LazyInitializationException if we cannot initialize */ - protected final void initialize(boolean writing) { - if (!initialized) { - if (initializing) { - throw new LazyInitializationException("illegal access to loading collection"); - } - throwLazyInitializationExceptionIfNotConnected(); - session.initializeCollection(this, writing); + protected final void initialize(final boolean writing) { + if ( initialized ) { + return; } + + withTemporarySessionIfNeeded( + new LazyInitializationWork() { + @Override + public Object doWork() { + session.initializeCollection( AbstractPersistentCollection.this, writing ); + return null; + } + } + ); } - + private void throwLazyInitializationExceptionIfNotConnected() { - if ( !isConnectedToSession() ) { - throwLazyInitializationException("no session or session was closed"); + if ( !isConnectedToSession() ) { + throwLazyInitializationException( "no session or session was closed" ); } if ( !session.isConnected() ) { - throwLazyInitializationException("session is disconnected"); - } + throwLazyInitializationException( "session is disconnected" ); + } } - + private void throwLazyInitializationException(String message) { throw new LazyInitializationException( - "failed to lazily initialize a collection" + - ( role==null ? "" : " of role: " + role ) + - ", " + message - ); + "failed to lazily initialize a collection" + + (role == null ? "" : " of role: " + role) + + ", " + message + ); } protected final void setInitialized() { @@ -417,11 +564,13 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers /** * Disassociate this collection from the given session. + * * @return true if this was currently associated with the given session */ public final boolean unsetSession(SessionImplementor currentSession) { - if (currentSession==this.session) { - this.session=null; + prepareForPossibleSpecialSpecjInitialization(); + if ( currentSession == this.session ) { + this.session = null; return true; } else { @@ -429,33 +578,55 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers } } + protected void prepareForPossibleSpecialSpecjInitialization() { + if ( session != null ) { + specjLazyLoad = Boolean.parseBoolean( + session.getFactory() + .getProperties() + .getProperty( AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS ) + ); + + if ( specjLazyLoad && sessionFactoryUuid == null ) { + try { + sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent(); + } + catch (NamingException e) { + //not much we can do if this fails... + } + } + } + } + + /** * Associate the collection with the given session. + * * @return false if the collection was already associated with the session + * * @throws HibernateException if the collection was already associated * with another open session */ public final boolean setCurrentSession(SessionImplementor session) throws HibernateException { - if (session==this.session) { + if ( session == this.session ) { return false; } else { if ( isConnectedToSession() ) { - CollectionEntry ce = session.getPersistenceContext().getCollectionEntry(this); - if (ce==null) { + CollectionEntry ce = session.getPersistenceContext().getCollectionEntry( this ); + if ( ce == null ) { throw new HibernateException( "Illegal attempt to associate a collection with two open sessions" - ); + ); } else { throw new HibernateException( "Illegal attempt to associate a collection with two open sessions: " + - MessageHelper.collectionInfoString( - ce.getLoadedPersister(), - ce.getLoadedKey(), - session.getFactory() - ) - ); + MessageHelper.collectionInfoString( + ce.getLoadedPersister(), + ce.getLoadedKey(), + session.getFactory() + ) + ); } } else { @@ -471,23 +642,23 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers public boolean needsRecreate(CollectionPersister persister) { return false; } - + /** * To be called internally by the session, forcing * immediate initialization. */ public final void forceInitialization() throws HibernateException { - if (!initialized) { - if (initializing) { - throw new AssertionFailure("force initialize loading collection"); + if ( !initialized ) { + if ( initializing ) { + throw new AssertionFailure( "force initialize loading collection" ); } - if (session==null) { - throw new HibernateException("collection is not associated with any session"); + if ( session == null ) { + throw new HibernateException( "collection is not associated with any session" ); } if ( !session.isConnected() ) { - throw new HibernateException("disconnected session"); + throw new HibernateException( "disconnected session" ); } - session.initializeCollection(this, false); + session.initializeCollection( this, false ); } } @@ -495,9 +666,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers /** * Get the current snapshot from the session */ - @SuppressWarnings( {"JavaDoc"}) + @SuppressWarnings({"JavaDoc"}) protected final Serializable getSnapshot() { - return session.getPersistenceContext().getSnapshot(this); + return session.getPersistenceContext().getSnapshot( this ); } /** @@ -506,7 +677,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers public final boolean wasInitialized() { return initialized; } - + public boolean isRowUpdatePossible() { return true; } @@ -515,8 +686,9 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers * Does this instance have any "queued" additions? */ public final boolean hasQueuedOperations() { - return operationQueue!=null; + return operationQueue != null; } + /** * Iterate the "queued" additions */ @@ -524,12 +696,15 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers if ( hasQueuedOperations() ) { return new Iterator() { int i = 0; + public Object next() { - return operationQueue.get(i++).getAddedInstance(); + return operationQueue.get( i++ ).getAddedInstance(); } + public boolean hasNext() { - return istateful implementation of the {@link PersistenceContext} contract meaning that we maintain this @@ -775,6 +777,64 @@ public class StatefulPersistenceContext implements PersistenceContext { */ @Override public Object getCollectionOwner(Serializable key, CollectionPersister collectionPersister) throws MappingException { + // todo : we really just need to add a split in the notions of: + // 1) collection key + // 2) collection owner key + // these 2 are not always the same. Same is true in the case of ToOne associations with property-ref... + final EntityPersister ownerPersister = collectionPersister.getOwnerEntityPersister(); + if ( ownerPersister.getIdentifierType().getReturnedClass().isInstance( key ) ) { + return getEntity( session.generateEntityKey( key, collectionPersister.getOwnerEntityPersister() ) ); + } + + // we have a property-ref type mapping for the collection key. But that could show up a few ways here... + // + // 1) The incoming key could be the entity itself... + if ( ownerPersister.isInstance( key ) ) { + final Serializable owenerId = ownerPersister.getIdentifier( key, session ); + if ( owenerId == null ) { + return null; + } + return getEntity( session.generateEntityKey( owenerId, ownerPersister ) ); + } + + final CollectionType collectionType = collectionPersister.getCollectionType(); + + // 2) The incoming key is most likely the collection key which we need to resolve to the owner key + // find the corresponding owner instance + // a) try by EntityUniqueKey + if ( collectionType.getLHSPropertyName() != null ) { + Object owner = getEntity( + new EntityUniqueKey( + ownerPersister.getEntityName(), + collectionType.getLHSPropertyName(), + key, + collectionPersister.getKeyType(), + ownerPersister.getEntityMode(), + session.getFactory() + ) + ); + if ( owner != null ) { + return owner; + } + + // b) try by EntityKey, which means we need to resolve owner-key -> collection-key + // IMPL NOTE : yes if we get here this impl is very non-performant, but PersistenceContext + // was never designed to handle this case; adding that capability for real means splitting + // the notions of: + // 1) collection key + // 2) collection owner key + // these 2 are not always the same (same is true in the case of ToOne associations with + // property-ref). That would require changes to (at least) CollectionEntry and quite + // probably changes to how the sql for collection initializers are generated + // + // We could also possibly see if the referenced property is a natural id since we already have caching + // in place of natural id snapshots. BUt really its better to just do it the right way ^^ if we start + // going that route + final Serializable ownerId = ownerPersister.getIdByUniqueKey( key, collectionType.getLHSPropertyName(), session ); + return getEntity( session.generateEntityKey( ownerId, ownerPersister ) ); + } + + // as a last resort this is what the old code did... return getEntity( session.generateEntityKey( key, collectionPersister.getOwnerEntityPersister() ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/LogicalConnectionImpl.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/LogicalConnectionImpl.java index e9f92071c8..c0364397ff 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/LogicalConnectionImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/internal/LogicalConnectionImpl.java @@ -98,7 +98,7 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor { boolean isClosed, List observers) { this.connectionReleaseMode = determineConnectionReleaseMode( - jdbcServices, isUserSuppliedConnection, connectionReleaseMode + jdbcConnectionAccess, isUserSuppliedConnection, connectionReleaseMode ); this.jdbcServices = jdbcServices; this.jdbcConnectionAccess = jdbcConnectionAccess; @@ -110,14 +110,14 @@ public class LogicalConnectionImpl implements LogicalConnectionImplementor { } private static ConnectionReleaseMode determineConnectionReleaseMode( - JdbcServices jdbcServices, + JdbcConnectionAccess jdbcConnectionAccess, boolean isUserSuppliedConnection, ConnectionReleaseMode connectionReleaseMode) { if ( isUserSuppliedConnection ) { return ConnectionReleaseMode.ON_CLOSE; } else if ( connectionReleaseMode == ConnectionReleaseMode.AFTER_STATEMENT && - ! jdbcServices.getConnectionProvider().supportsAggressiveRelease() ) { + ! jdbcConnectionAccess.supportsAggressiveRelease() ) { LOG.debug( "Connection provider reports to not support aggressive release; overriding" ); return ConnectionReleaseMode.AFTER_TRANSACTION; } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java index 2c278e0c2a..fc51fea5fd 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/jdbc/spi/JdbcServices.java @@ -23,7 +23,6 @@ */ package org.hibernate.engine.jdbc.spi; -import java.sql.Connection; import java.sql.ResultSet; import org.hibernate.dialect.Dialect; @@ -44,13 +43,15 @@ public interface JdbcServices extends Service { * Obtain service for providing JDBC connections. * * @return The connection provider. + * + * @deprecated See deprecation notice on {@link org.hibernate.engine.spi.SessionFactoryImplementor#getConnectionProvider()} + * for details */ @Deprecated public ConnectionProvider getConnectionProvider(); /** - * Obtain the dialect of the database to which {@link Connection connections} from - * {@link #getConnectionProvider()} point. + * Obtain the dialect of the database. * * @return The database dialect. */ diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/CascadeStyle.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/CascadeStyle.java index c39f0f6840..3daccaeb89 100755 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/CascadeStyle.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/CascadeStyle.java @@ -24,20 +24,16 @@ package org.hibernate.engine.spi; import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -import org.hibernate.MappingException; -import org.hibernate.internal.util.collections.ArrayHelper; /** * A contract for defining the aspects of cascading various persistence actions. * * @author Gavin King + * @author Steve Ebersole + * * @see CascadingAction */ -public abstract class CascadeStyle implements Serializable { - +public interface CascadeStyle extends Serializable { /** * For this style, should the given action be cascaded? * @@ -45,7 +41,7 @@ public abstract class CascadeStyle implements Serializable { * * @return True if the action should be cascaded under this style; false otherwise. */ - public abstract boolean doCascade(CascadingAction action); + public boolean doCascade(CascadingAction action); /** * Probably more aptly named something like doCascadeToCollectionElements(); it is @@ -61,9 +57,7 @@ public abstract class CascadeStyle implements Serializable { * @return True if the action should be really cascaded under this style; * false otherwise. */ - public boolean reallyDoCascade(CascadingAction action) { - return doCascade( action ); - } + public boolean reallyDoCascade(CascadingAction action); /** * Do we need to delete orphaned collection elements? @@ -71,252 +65,5 @@ public abstract class CascadeStyle implements Serializable { * @return True if this style need to account for orphan delete * operations; false otherwise. */ - public boolean hasOrphanDelete() { - return false; - } - - public static final class MultipleCascadeStyle extends CascadeStyle { - private final CascadeStyle[] styles; - - public MultipleCascadeStyle(CascadeStyle[] styles) { - this.styles = styles; - } - - public boolean doCascade(CascadingAction action) { - for ( CascadeStyle style : styles ) { - if ( style.doCascade( action ) ) { - return true; - } - } - return false; - } - - public boolean reallyDoCascade(CascadingAction action) { - for ( CascadeStyle style : styles ) { - if ( style.reallyDoCascade( action ) ) { - return true; - } - } - return false; - } - - public boolean hasOrphanDelete() { - for ( CascadeStyle style : styles ) { - if ( style.hasOrphanDelete() ) { - return true; - } - } - return false; - } - - public String toString() { - return ArrayHelper.toString( styles ); - } - } - - /** - * save / delete / update / evict / lock / replicate / merge / persist + delete orphans - */ - public static final CascadeStyle ALL_DELETE_ORPHAN = new CascadeStyle() { - public boolean doCascade(CascadingAction action) { - return true; - } - - public boolean hasOrphanDelete() { - return true; - } - - public String toString() { - return "STYLE_ALL_DELETE_ORPHAN"; - } - }; - - /** - * save / delete / update / evict / lock / replicate / merge / persist - */ - public static final CascadeStyle ALL = new CascadeStyle() { - public boolean doCascade(CascadingAction action) { - return true; - } - - public String toString() { - return "STYLE_ALL"; - } - }; - - /** - * save / update - */ - public static final CascadeStyle UPDATE = new CascadeStyle() { - public boolean doCascade(CascadingAction action) { - return action == CascadingAction.SAVE_UPDATE; - } - - public String toString() { - return "STYLE_SAVE_UPDATE"; - } - }; - - /** - * lock - */ - public static final CascadeStyle LOCK = new CascadeStyle() { - public boolean doCascade(CascadingAction action) { - return action == CascadingAction.LOCK; - } - - public String toString() { - return "STYLE_LOCK"; - } - }; - - /** - * refresh - */ - public static final CascadeStyle REFRESH = new CascadeStyle() { - public boolean doCascade(CascadingAction action) { - return action == CascadingAction.REFRESH; - } - - public String toString() { - return "STYLE_REFRESH"; - } - }; - - /** - * evict - */ - public static final CascadeStyle EVICT = new CascadeStyle() { - public boolean doCascade(CascadingAction action) { - return action == CascadingAction.EVICT; - } - - public String toString() { - return "STYLE_EVICT"; - } - }; - - /** - * replicate - */ - public static final CascadeStyle REPLICATE = new CascadeStyle() { - public boolean doCascade(CascadingAction action) { - return action == CascadingAction.REPLICATE; - } - - public String toString() { - return "STYLE_REPLICATE"; - } - }; - /** - * merge - */ - public static final CascadeStyle MERGE = new CascadeStyle() { - public boolean doCascade(CascadingAction action) { - return action == CascadingAction.MERGE; - } - - public String toString() { - return "STYLE_MERGE"; - } - }; - - /** - * create - */ - public static final CascadeStyle PERSIST = new CascadeStyle() { - public boolean doCascade(CascadingAction action) { - return action == CascadingAction.PERSIST - || action == CascadingAction.PERSIST_ON_FLUSH; - } - - public String toString() { - return "STYLE_PERSIST"; - } - }; - - /** - * delete - */ - public static final CascadeStyle DELETE = new CascadeStyle() { - public boolean doCascade(CascadingAction action) { - return action == CascadingAction.DELETE; - } - - public String toString() { - return "STYLE_DELETE"; - } - }; - - /** - * delete + delete orphans - */ - public static final CascadeStyle DELETE_ORPHAN = new CascadeStyle() { - public boolean doCascade(CascadingAction action) { - return action == CascadingAction.DELETE || action == CascadingAction.SAVE_UPDATE; - } - - public boolean reallyDoCascade(CascadingAction action) { - return action == CascadingAction.DELETE; - } - - public boolean hasOrphanDelete() { - return true; - } - - public String toString() { - return "STYLE_DELETE_ORPHAN"; - } - }; - - /** - * no cascades - */ - public static final CascadeStyle NONE = new CascadeStyle() { - public boolean doCascade(CascadingAction action) { - return false; - } - - public String toString() { - return "STYLE_NONE"; - } - }; - - public CascadeStyle() { - } - - static final Map STYLES = new HashMap(); - - static { - STYLES.put( "all", ALL ); - STYLES.put( "all-delete-orphan", ALL_DELETE_ORPHAN ); - STYLES.put( "save-update", UPDATE ); - STYLES.put( "persist", PERSIST ); - STYLES.put( "merge", MERGE ); - STYLES.put( "lock", LOCK ); - STYLES.put( "refresh", REFRESH ); - STYLES.put( "replicate", REPLICATE ); - STYLES.put( "evict", EVICT ); - STYLES.put( "delete", DELETE ); - STYLES.put( "remove", DELETE ); // adds remove as a sort-of alias for delete... - STYLES.put( "delete-orphan", DELETE_ORPHAN ); - STYLES.put( "none", NONE ); - } - - /** - * Factory method for obtaining named cascade styles - * - * @param cascade The named cascade style name. - * - * @return The appropriate CascadeStyle - */ - public static CascadeStyle getCascadeStyle(String cascade) { - CascadeStyle style = STYLES.get( cascade ); - if ( style == null ) { - throw new MappingException( "Unsupported cascade style: " + cascade ); - } - else { - return style; - } - } + public boolean hasOrphanDelete(); } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/CascadeStyles.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/CascadeStyles.java new file mode 100644 index 0000000000..10ae5eefcd --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/CascadeStyles.java @@ -0,0 +1,348 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.engine.spi; + +import java.util.HashMap; +import java.util.Map; + +import org.jboss.logging.Logger; + +import org.hibernate.MappingException; +import org.hibernate.internal.util.collections.ArrayHelper; + +/** + * @author Steve Ebersole + */ +public class CascadeStyles { + private static final Logger log = Logger.getLogger( CascadeStyles.class ); + + /** + * Disallow instantiation + */ + private CascadeStyles() { + } + + /** + * save / delete / update / evict / lock / replicate / merge / persist + delete orphans + */ + public static final CascadeStyle ALL_DELETE_ORPHAN = new BaseCascadeStyle() { + @Override + public boolean doCascade(CascadingAction action) { + return true; + } + + @Override + public boolean hasOrphanDelete() { + return true; + } + + @Override + public String toString() { + return "STYLE_ALL_DELETE_ORPHAN"; + } + }; + + /** + * save / delete / update / evict / lock / replicate / merge / persist + */ + public static final CascadeStyle ALL = new BaseCascadeStyle() { + @Override + public boolean doCascade(CascadingAction action) { + return true; + } + + @Override + public String toString() { + return "STYLE_ALL"; + } + }; + + /** + * save / update + */ + public static final CascadeStyle UPDATE = new BaseCascadeStyle() { + @Override + public boolean doCascade(CascadingAction action) { + return action == CascadingActions.SAVE_UPDATE; + } + + @Override + public String toString() { + return "STYLE_SAVE_UPDATE"; + } + }; + + /** + * lock + */ + public static final CascadeStyle LOCK = new BaseCascadeStyle() { + @Override + public boolean doCascade(CascadingAction action) { + return action == CascadingActions.LOCK; + } + + @Override + public String toString() { + return "STYLE_LOCK"; + } + }; + + /** + * refresh + */ + public static final CascadeStyle REFRESH = new BaseCascadeStyle() { + @Override + public boolean doCascade(CascadingAction action) { + return action == CascadingActions.REFRESH; + } + + @Override + public String toString() { + return "STYLE_REFRESH"; + } + }; + + /** + * evict + */ + public static final CascadeStyle EVICT = new BaseCascadeStyle() { + @Override + public boolean doCascade(CascadingAction action) { + return action == CascadingActions.EVICT; + } + + @Override + public String toString() { + return "STYLE_EVICT"; + } + }; + + /** + * replicate + */ + public static final CascadeStyle REPLICATE = new BaseCascadeStyle() { + @Override + public boolean doCascade(CascadingAction action) { + return action == CascadingActions.REPLICATE; + } + + @Override + public String toString() { + return "STYLE_REPLICATE"; + } + }; + + /** + * merge + */ + public static final CascadeStyle MERGE = new BaseCascadeStyle() { + @Override + public boolean doCascade(CascadingAction action) { + return action == CascadingActions.MERGE; + } + + @Override + public String toString() { + return "STYLE_MERGE"; + } + }; + + /** + * create + */ + public static final CascadeStyle PERSIST = new BaseCascadeStyle() { + @Override + public boolean doCascade(CascadingAction action) { + return action == CascadingActions.PERSIST + || action == CascadingActions.PERSIST_ON_FLUSH; + } + + @Override + public String toString() { + return "STYLE_PERSIST"; + } + }; + + /** + * delete + */ + public static final CascadeStyle DELETE = new BaseCascadeStyle() { + @Override + public boolean doCascade(CascadingAction action) { + return action == CascadingActions.DELETE; + } + + @Override + public String toString() { + return "STYLE_DELETE"; + } + }; + + /** + * delete + delete orphans + */ + public static final CascadeStyle DELETE_ORPHAN = new BaseCascadeStyle() { + @Override + public boolean doCascade(CascadingAction action) { + return action == CascadingActions.DELETE || action == CascadingActions.SAVE_UPDATE; + } + + @Override + public boolean reallyDoCascade(CascadingAction action) { + return action == CascadingActions.DELETE; + } + + @Override + public boolean hasOrphanDelete() { + return true; + } + + @Override + public String toString() { + return "STYLE_DELETE_ORPHAN"; + } + }; + + /** + * no cascades + */ + public static final CascadeStyle NONE = new BaseCascadeStyle() { + @Override + public boolean doCascade(CascadingAction action) { + return false; + } + + @Override + public String toString() { + return "STYLE_NONE"; + } + }; + + private static final Map STYLES = buildBaseCascadeStyleMap(); + + private static Map buildBaseCascadeStyleMap() { + final HashMap base = new HashMap(); + + base.put( "all", ALL ); + base.put( "all-delete-orphan", ALL_DELETE_ORPHAN ); + base.put( "save-update", UPDATE ); + base.put( "persist", PERSIST ); + base.put( "merge", MERGE ); + base.put( "lock", LOCK ); + base.put( "refresh", REFRESH ); + base.put( "replicate", REPLICATE ); + base.put( "evict", EVICT ); + base.put( "delete", DELETE ); + base.put( "remove", DELETE ); // adds remove as a sort-of alias for delete... + base.put( "delete-orphan", DELETE_ORPHAN ); + base.put( "none", NONE ); + + return base; + } + + /** + * Factory method for obtaining named cascade styles + * + * @param cascade The named cascade style name. + * + * @return The appropriate CascadeStyle + */ + public static CascadeStyle getCascadeStyle(String cascade) { + CascadeStyle style = STYLES.get( cascade ); + if ( style == null ) { + throw new MappingException( "Unsupported cascade style: " + cascade ); + } + else { + return style; + } + } + + public static void registerCascadeStyle(String name, BaseCascadeStyle cascadeStyle) { + log.tracef( "Registering external cascade style [%s : %s]", name, cascadeStyle ); + final CascadeStyle old = STYLES.put( name, cascadeStyle ); + if ( old != null ) { + log.debugf( + "External cascade style regsitration [%s : %s] overrode base registration [%s]", + name, + cascadeStyle, + old + ); + } + } + + public static abstract class BaseCascadeStyle implements CascadeStyle { + @Override + public boolean reallyDoCascade(CascadingAction action) { + return doCascade( action ); + } + + @Override + public boolean hasOrphanDelete() { + return false; + } + } + + public static final class MultipleCascadeStyle extends BaseCascadeStyle { + private final CascadeStyle[] styles; + + public MultipleCascadeStyle(CascadeStyle[] styles) { + this.styles = styles; + } + + @Override + public boolean doCascade(CascadingAction action) { + for ( CascadeStyle style : styles ) { + if ( style.doCascade( action ) ) { + return true; + } + } + return false; + } + + @Override + public boolean reallyDoCascade(CascadingAction action) { + for ( CascadeStyle style : styles ) { + if ( style.reallyDoCascade( action ) ) { + return true; + } + } + return false; + } + + @Override + public boolean hasOrphanDelete() { + for ( CascadeStyle style : styles ) { + if ( style.hasOrphanDelete() ) { + return true; + } + } + return false; + } + + @Override + public String toString() { + return ArrayHelper.toString( styles ); + } + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/CascadingAction.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/CascadingAction.java index 5ed0c4012c..3a467f5691 100755 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/CascadingAction.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/CascadingAction.java @@ -24,40 +24,19 @@ package org.hibernate.engine.spi; import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import org.jboss.logging.Logger; import org.hibernate.HibernateException; -import org.hibernate.LockMode; -import org.hibernate.LockOptions; -import org.hibernate.ReplicationMode; -import org.hibernate.TransientPropertyValueException; -import org.hibernate.collection.spi.PersistentCollection; -import org.hibernate.engine.internal.ForeignKeys; import org.hibernate.event.spi.EventSource; -import org.hibernate.internal.CoreMessageLogger; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.proxy.HibernateProxy; import org.hibernate.type.CollectionType; -import org.hibernate.type.EntityType; -import org.hibernate.type.Type; /** * A session action that may be cascaded from parent entity to its children * * @author Gavin King + * @author Steve Ebersole */ -public abstract class CascadingAction { - - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, CascadingAction.class.getName()); - - - // the CascadingAction contract ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - public CascadingAction() { - } +public interface CascadingAction { /** * Cascade the action to the child object. @@ -70,7 +49,7 @@ public abstract class CascadingAction { * @param isCascadeDeleteEnabled Are cascading deletes enabled. * @throws HibernateException */ - public abstract void cascade( + public void cascade( EventSource session, Object child, String entityName, @@ -86,7 +65,7 @@ public abstract class CascadingAction { * @param collection The collection instance. * @return The children iterator. */ - public abstract Iterator getCascadableChildrenIterator( + public Iterator getCascadableChildrenIterator( EventSource session, CollectionType collectionType, Object collection); @@ -96,7 +75,7 @@ public abstract class CascadingAction { * * @return True if this action can lead to deletions of orphans. */ - public abstract boolean deleteOrphans(); + public boolean deleteOrphans(); /** @@ -104,9 +83,7 @@ public abstract class CascadingAction { * * @return True if this action requires no-cascade verification; false otherwise. */ - public boolean requiresNoCascadeChecking() { - return false; - } + public boolean requiresNoCascadeChecking(); /** * Called (in the case of {@link #requiresNoCascadeChecking} returning true) to validate @@ -118,357 +95,10 @@ public abstract class CascadingAction { * @param persister The entity persister for the owner * @param propertyIndex The index of the property within the owner. */ - public void noCascade(EventSource session, Object child, Object parent, EntityPersister persister, int propertyIndex) { - } + public void noCascade(EventSource session, Object child, Object parent, EntityPersister persister, int propertyIndex); /** * Should this action be performed (or noCascade consulted) in the case of lazy properties. */ - public boolean performOnLazyProperty() { - return true; - } - - - // the CascadingAction implementations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - /** - * @see org.hibernate.Session#delete(Object) - */ - public static final CascadingAction DELETE = new CascadingAction() { - @Override - public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled) - throws HibernateException { - LOG.tracev( "Cascading to delete: {0}", entityName ); - session.delete( entityName, child, isCascadeDeleteEnabled, ( Set ) anything ); - } - @Override - public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) { - // delete does cascade to uninitialized collections - return CascadingAction.getAllElementsIterator(session, collectionType, collection); - } - @Override - public boolean deleteOrphans() { - // orphans should be deleted during delete - return true; - } - @Override - public String toString() { - return "ACTION_DELETE"; - } - }; - - /** - * @see org.hibernate.Session#lock(Object, LockMode) - */ - public static final CascadingAction LOCK = new CascadingAction() { - @Override - public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled) - throws HibernateException { - LOG.tracev( "Cascading to lock: {0}", entityName ); - LockMode lockMode = LockMode.NONE; - LockOptions lr = new LockOptions(); - if ( anything instanceof LockOptions) { - LockOptions lockOptions = (LockOptions)anything; - lr.setTimeOut(lockOptions.getTimeOut()); - lr.setScope( lockOptions.getScope()); - if ( lockOptions.getScope() == true ) // cascade specified lockMode - lockMode = lockOptions.getLockMode(); - } - lr.setLockMode(lockMode); - session.buildLockRequest(lr).lock(entityName, child); - } - @Override - public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) { - // lock doesn't cascade to uninitialized collections - return getLoadedElementsIterator(session, collectionType, collection); - } - @Override - public boolean deleteOrphans() { - //TODO: should orphans really be deleted during lock??? - return false; - } - @Override - public String toString() { - return "ACTION_LOCK"; - } - }; - - /** - * @see org.hibernate.Session#refresh(Object) - */ - public static final CascadingAction REFRESH = new CascadingAction() { - @Override - public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled) - throws HibernateException { - LOG.tracev( "Cascading to refresh: {0}", entityName ); - session.refresh( child, (Map) anything ); - } - @Override - public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) { - // refresh doesn't cascade to uninitialized collections - return getLoadedElementsIterator(session, collectionType, collection); - } - @Override - public boolean deleteOrphans() { - return false; - } - @Override - public String toString() { - return "ACTION_REFRESH"; - } - }; - - /** - * @see org.hibernate.Session#evict(Object) - */ - public static final CascadingAction EVICT = new CascadingAction() { - @Override - public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled) - throws HibernateException { - LOG.tracev( "Cascading to evict: {0}", entityName ); - session.evict(child); - } - @Override - public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) { - // evicts don't cascade to uninitialized collections - return getLoadedElementsIterator(session, collectionType, collection); - } - @Override - public boolean deleteOrphans() { - return false; - } - @Override - public boolean performOnLazyProperty() { - return false; - } - @Override - public String toString() { - return "ACTION_EVICT"; - } - }; - - /** - * @see org.hibernate.Session#saveOrUpdate(Object) - */ - public static final CascadingAction SAVE_UPDATE = new CascadingAction() { - @Override - public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled) - throws HibernateException { - LOG.tracev( "Cascading to save or update: {0}", entityName ); - session.saveOrUpdate(entityName, child); - } - @Override - public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) { - // saves / updates don't cascade to uninitialized collections - return getLoadedElementsIterator(session, collectionType, collection); - } - @Override - public boolean deleteOrphans() { - // orphans should be deleted during save/update - return true; - } - @Override - public boolean performOnLazyProperty() { - return false; - } - @Override - public String toString() { - return "ACTION_SAVE_UPDATE"; - } - }; - - /** - * @see org.hibernate.Session#merge(Object) - */ - public static final CascadingAction MERGE = new CascadingAction() { - @Override - public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled) - throws HibernateException { - LOG.tracev( "Cascading to merge: {0}", entityName ); - session.merge( entityName, child, (Map) anything ); - } - @Override - public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) { - // merges don't cascade to uninitialized collections -// //TODO: perhaps this does need to cascade after all.... - return getLoadedElementsIterator(session, collectionType, collection); - } - @Override - public boolean deleteOrphans() { - // orphans should not be deleted during merge?? - return false; - } - @Override - public String toString() { - return "ACTION_MERGE"; - } - }; - - /** - * @see org.hibernate.Session#persist(Object) - */ - public static final CascadingAction PERSIST = new CascadingAction() { - @Override - public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled) - throws HibernateException { - LOG.tracev( "Cascading to persist: {0}" + entityName ); - session.persist( entityName, child, (Map) anything ); - } - @Override - public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) { - // persists don't cascade to uninitialized collections - return CascadingAction.getAllElementsIterator(session, collectionType, collection); - } - @Override - public boolean deleteOrphans() { - return false; - } - @Override - public boolean performOnLazyProperty() { - return false; - } - @Override - public String toString() { - return "ACTION_PERSIST"; - } - }; - - /** - * Execute persist during flush time - * - * @see org.hibernate.Session#persist(Object) - */ - public static final CascadingAction PERSIST_ON_FLUSH = new CascadingAction() { - @Override - public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled) - throws HibernateException { - LOG.tracev( "Cascading to persist on flush: {0}", entityName ); - session.persistOnFlush( entityName, child, (Map) anything ); - } - @Override - public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) { - // persists don't cascade to uninitialized collections - return CascadingAction.getLoadedElementsIterator(session, collectionType, collection); - } - @Override - public boolean deleteOrphans() { - return true; - } - @Override - public boolean requiresNoCascadeChecking() { - return true; - } - @Override - public void noCascade( - EventSource session, - Object child, - Object parent, - EntityPersister persister, - int propertyIndex) { - if ( child == null ) { - return; - } - Type type = persister.getPropertyTypes()[propertyIndex]; - if ( type.isEntityType() ) { - String childEntityName = ( ( EntityType ) type ).getAssociatedEntityName( session.getFactory() ); - - if ( ! isInManagedState( child, session ) - && ! ( child instanceof HibernateProxy ) //a proxy cannot be transient and it breaks ForeignKeys.isTransient - && ForeignKeys.isTransient( childEntityName, child, null, session ) ) { - String parentEntiytName = persister.getEntityName(); - String propertyName = persister.getPropertyNames()[propertyIndex]; - throw new TransientPropertyValueException( - "object references an unsaved transient instance - save the transient instance before flushing", - childEntityName, - parentEntiytName, - propertyName - ); - - } - } - } - @Override - public boolean performOnLazyProperty() { - return false; - } - - private boolean isInManagedState(Object child, EventSource session) { - EntityEntry entry = session.getPersistenceContext().getEntry( child ); - return entry != null && - ( - entry.getStatus() == Status.MANAGED || - entry.getStatus() == Status.READ_ONLY || - entry.getStatus() == Status.SAVING - ); - } - - @Override - public String toString() { - return "ACTION_PERSIST_ON_FLUSH"; - } - }; - - /** - * @see org.hibernate.Session#replicate(Object, org.hibernate.ReplicationMode) - */ - public static final CascadingAction REPLICATE = new CascadingAction() { - @Override - public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled) - throws HibernateException { - LOG.tracev( "Cascading to replicate: {0}", entityName ); - session.replicate( entityName, child, (ReplicationMode) anything ); - } - @Override - public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) { - // replicate does cascade to uninitialized collections - return getLoadedElementsIterator(session, collectionType, collection); - } - @Override - public boolean deleteOrphans() { - return false; //I suppose? - } - @Override - public String toString() { - return "ACTION_REPLICATE"; - } - }; - - - // static helper methods ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - /** - * Given a collection, get an iterator of all its children, loading them - * from the database if necessary. - * - * @param session The session within which the cascade is occuring. - * @param collectionType The mapping type of the collection. - * @param collection The collection instance. - * @return The children iterator. - */ - private static Iterator getAllElementsIterator( - EventSource session, - CollectionType collectionType, - Object collection) { - return collectionType.getElementsIterator( collection, session ); - } - - /** - * Iterate just the elements of the collection that are already there. Don't load - * any new elements from the database. - */ - public static Iterator getLoadedElementsIterator(SessionImplementor session, CollectionType collectionType, Object collection) { - if ( collectionIsInitialized(collection) ) { - // handles arrays and newly instantiated collections - return collectionType.getElementsIterator(collection, session); - } - else { - // does not handle arrays (thats ok, cos they can't be lazy) - // or newly instantiated collections, so we can do the cast - return ( (PersistentCollection) collection ).queuedAdditionIterator(); - } - } - - private static boolean collectionIsInitialized(Object collection) { - return !(collection instanceof PersistentCollection) || ( (PersistentCollection) collection ).wasInitialized(); - } + public boolean performOnLazyProperty(); } \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/CascadingActions.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/CascadingActions.java new file mode 100644 index 0000000000..a39014bb3d --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/CascadingActions.java @@ -0,0 +1,521 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.engine.spi; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.jboss.logging.Logger; + +import org.hibernate.HibernateException; +import org.hibernate.LockMode; +import org.hibernate.LockOptions; +import org.hibernate.ReplicationMode; +import org.hibernate.TransientPropertyValueException; +import org.hibernate.collection.spi.PersistentCollection; +import org.hibernate.engine.internal.ForeignKeys; +import org.hibernate.event.spi.EventSource; +import org.hibernate.internal.CoreMessageLogger; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.proxy.HibernateProxy; +import org.hibernate.type.CollectionType; +import org.hibernate.type.EntityType; +import org.hibernate.type.Type; + +/** + * @author Steve Ebersole + */ +public class CascadingActions { + private static final CoreMessageLogger LOG = Logger.getMessageLogger( + CoreMessageLogger.class, + CascadingAction.class.getName() + ); + + /** + * Disallow instantiation + */ + private CascadingActions() { + } + + /** + * @see org.hibernate.Session#delete(Object) + */ + public static final CascadingAction DELETE = new BaseCascadingAction() { + @Override + public void cascade( + EventSource session, + Object child, + String entityName, + Object anything, + boolean isCascadeDeleteEnabled) { + LOG.tracev( "Cascading to delete: {0}", entityName ); + session.delete( entityName, child, isCascadeDeleteEnabled, (Set) anything ); + } + + @Override + public Iterator getCascadableChildrenIterator( + EventSource session, + CollectionType collectionType, + Object collection) { + // delete does cascade to uninitialized collections + return getAllElementsIterator( session, collectionType, collection ); + } + + @Override + public boolean deleteOrphans() { + // orphans should be deleted during delete + return true; + } + + @Override + public String toString() { + return "ACTION_DELETE"; + } + }; + + /** + * @see org.hibernate.Session#lock(Object, org.hibernate.LockMode) + */ + public static final CascadingAction LOCK = new BaseCascadingAction() { + @Override + public void cascade( + EventSource session, + Object child, + String entityName, + Object anything, + boolean isCascadeDeleteEnabled) { + LOG.tracev( "Cascading to lock: {0}", entityName ); + LockMode lockMode = LockMode.NONE; + LockOptions lr = new LockOptions(); + if ( anything instanceof LockOptions ) { + LockOptions lockOptions = (LockOptions) anything; + lr.setTimeOut( lockOptions.getTimeOut() ); + lr.setScope( lockOptions.getScope() ); + if ( lockOptions.getScope() ) { + lockMode = lockOptions.getLockMode(); + } + } + lr.setLockMode( lockMode ); + session.buildLockRequest( lr ).lock( entityName, child ); + } + + @Override + public Iterator getCascadableChildrenIterator( + EventSource session, + CollectionType collectionType, + Object collection) { + // lock doesn't cascade to uninitialized collections + return getLoadedElementsIterator( session, collectionType, collection ); + } + + @Override + public boolean deleteOrphans() { + //TODO: should orphans really be deleted during lock??? + return false; + } + + @Override + public String toString() { + return "ACTION_LOCK"; + } + }; + + /** + * @see org.hibernate.Session#refresh(Object) + */ + public static final CascadingAction REFRESH = new BaseCascadingAction() { + @Override + public void cascade( + EventSource session, + Object child, + String entityName, + Object anything, + boolean isCascadeDeleteEnabled) + throws HibernateException { + LOG.tracev( "Cascading to refresh: {0}", entityName ); + session.refresh( child, (Map) anything ); + } + + @Override + public Iterator getCascadableChildrenIterator( + EventSource session, + CollectionType collectionType, + Object collection) { + // refresh doesn't cascade to uninitialized collections + return getLoadedElementsIterator( session, collectionType, collection ); + } + + @Override + public boolean deleteOrphans() { + return false; + } + + @Override + public String toString() { + return "ACTION_REFRESH"; + } + }; + + /** + * @see org.hibernate.Session#evict(Object) + */ + public static final CascadingAction EVICT = new BaseCascadingAction() { + @Override + public void cascade( + EventSource session, + Object child, + String entityName, + Object anything, + boolean isCascadeDeleteEnabled) + throws HibernateException { + LOG.tracev( "Cascading to evict: {0}", entityName ); + session.evict( child ); + } + + @Override + public Iterator getCascadableChildrenIterator( + EventSource session, + CollectionType collectionType, + Object collection) { + // evicts don't cascade to uninitialized collections + return getLoadedElementsIterator( session, collectionType, collection ); + } + + @Override + public boolean deleteOrphans() { + return false; + } + + @Override + public boolean performOnLazyProperty() { + return false; + } + + @Override + public String toString() { + return "ACTION_EVICT"; + } + }; + + /** + * @see org.hibernate.Session#saveOrUpdate(Object) + */ + public static final CascadingAction SAVE_UPDATE = new BaseCascadingAction() { + @Override + public void cascade( + EventSource session, + Object child, + String entityName, + Object anything, + boolean isCascadeDeleteEnabled) + throws HibernateException { + LOG.tracev( "Cascading to save or update: {0}", entityName ); + session.saveOrUpdate( entityName, child ); + } + + @Override + public Iterator getCascadableChildrenIterator( + EventSource session, + CollectionType collectionType, + Object collection) { + // saves / updates don't cascade to uninitialized collections + return getLoadedElementsIterator( session, collectionType, collection ); + } + + @Override + public boolean deleteOrphans() { + // orphans should be deleted during save/update + return true; + } + + @Override + public boolean performOnLazyProperty() { + return false; + } + + @Override + public String toString() { + return "ACTION_SAVE_UPDATE"; + } + }; + + /** + * @see org.hibernate.Session#merge(Object) + */ + public static final CascadingAction MERGE = new BaseCascadingAction() { + @Override + public void cascade( + EventSource session, + Object child, + String entityName, + Object anything, + boolean isCascadeDeleteEnabled) + throws HibernateException { + LOG.tracev( "Cascading to merge: {0}", entityName ); + session.merge( entityName, child, (Map) anything ); + } + + @Override + public Iterator getCascadableChildrenIterator( + EventSource session, + CollectionType collectionType, + Object collection) { + // merges don't cascade to uninitialized collections + return getLoadedElementsIterator( session, collectionType, collection ); + } + + @Override + public boolean deleteOrphans() { + // orphans should not be deleted during merge?? + return false; + } + + @Override + public String toString() { + return "ACTION_MERGE"; + } + }; + + /** + * @see org.hibernate.Session#persist(Object) + */ + public static final CascadingAction PERSIST = new BaseCascadingAction() { + @Override + public void cascade( + EventSource session, + Object child, + String entityName, + Object anything, + boolean isCascadeDeleteEnabled) + throws HibernateException { + LOG.tracev( "Cascading to persist: {0}" + entityName ); + session.persist( entityName, child, (Map) anything ); + } + + @Override + public Iterator getCascadableChildrenIterator( + EventSource session, + CollectionType collectionType, + Object collection) { + // persists don't cascade to uninitialized collections + return getAllElementsIterator( session, collectionType, collection ); + } + + @Override + public boolean deleteOrphans() { + return false; + } + + @Override + public boolean performOnLazyProperty() { + return false; + } + + @Override + public String toString() { + return "ACTION_PERSIST"; + } + }; + + /** + * Execute persist during flush time + * + * @see org.hibernate.Session#persist(Object) + */ + public static final CascadingAction PERSIST_ON_FLUSH = new BaseCascadingAction() { + @Override + public void cascade( + EventSource session, + Object child, + String entityName, + Object anything, + boolean isCascadeDeleteEnabled) + throws HibernateException { + LOG.tracev( "Cascading to persist on flush: {0}", entityName ); + session.persistOnFlush( entityName, child, (Map) anything ); + } + + @Override + public Iterator getCascadableChildrenIterator( + EventSource session, + CollectionType collectionType, + Object collection) { + // persists don't cascade to uninitialized collections + return getLoadedElementsIterator( session, collectionType, collection ); + } + + @Override + public boolean deleteOrphans() { + return true; + } + + @Override + public boolean requiresNoCascadeChecking() { + return true; + } + + @Override + public void noCascade( + EventSource session, + Object child, + Object parent, + EntityPersister persister, + int propertyIndex) { + if ( child == null ) { + return; + } + Type type = persister.getPropertyTypes()[propertyIndex]; + if ( type.isEntityType() ) { + String childEntityName = ((EntityType) type).getAssociatedEntityName( session.getFactory() ); + + if ( !isInManagedState( child, session ) + && !(child instanceof HibernateProxy) //a proxy cannot be transient and it breaks ForeignKeys.isTransient + && ForeignKeys.isTransient( childEntityName, child, null, session ) ) { + String parentEntiytName = persister.getEntityName(); + String propertyName = persister.getPropertyNames()[propertyIndex]; + throw new TransientPropertyValueException( + "object references an unsaved transient instance - save the transient instance before flushing", + childEntityName, + parentEntiytName, + propertyName + ); + + } + } + } + + @Override + public boolean performOnLazyProperty() { + return false; + } + + private boolean isInManagedState(Object child, EventSource session) { + EntityEntry entry = session.getPersistenceContext().getEntry( child ); + return entry != null && + ( + entry.getStatus() == Status.MANAGED || + entry.getStatus() == Status.READ_ONLY || + entry.getStatus() == Status.SAVING + ); + } + + @Override + public String toString() { + return "ACTION_PERSIST_ON_FLUSH"; + } + }; + + /** + * @see org.hibernate.Session#replicate + */ + public static final CascadingAction REPLICATE = new BaseCascadingAction() { + @Override + public void cascade( + EventSource session, + Object child, + String entityName, + Object anything, + boolean isCascadeDeleteEnabled) + throws HibernateException { + LOG.tracev( "Cascading to replicate: {0}", entityName ); + session.replicate( entityName, child, (ReplicationMode) anything ); + } + + @Override + public Iterator getCascadableChildrenIterator( + EventSource session, + CollectionType collectionType, + Object collection) { + // replicate does cascade to uninitialized collections + return getLoadedElementsIterator( session, collectionType, collection ); + } + + @Override + public boolean deleteOrphans() { + return false; //I suppose? + } + + @Override + public String toString() { + return "ACTION_REPLICATE"; + } + }; + + public abstract static class BaseCascadingAction implements CascadingAction { + @Override + public boolean requiresNoCascadeChecking() { + return false; + } + + @Override + public void noCascade(EventSource session, Object child, Object parent, EntityPersister persister, int propertyIndex) { + } + + @Override + public boolean performOnLazyProperty() { + return true; + } + } + + /** + * Given a collection, get an iterator of all its children, loading them + * from the database if necessary. + * + * @param session The session within which the cascade is occuring. + * @param collectionType The mapping type of the collection. + * @param collection The collection instance. + * + * @return The children iterator. + */ + private static Iterator getAllElementsIterator( + EventSource session, + CollectionType collectionType, + Object collection) { + return collectionType.getElementsIterator( collection, session ); + } + + /** + * Iterate just the elements of the collection that are already there. Don't load + * any new elements from the database. + */ + public static Iterator getLoadedElementsIterator( + SessionImplementor session, + CollectionType collectionType, + Object collection) { + if ( collectionIsInitialized( collection ) ) { + // handles arrays and newly instantiated collections + return collectionType.getElementsIterator( collection, session ); + } + else { + // does not handle arrays (thats ok, cos they can't be lazy) + // or newly instantiated collections, so we can do the cast + return ((PersistentCollection) collection).queuedAdditionIterator(); + } + } + + private static boolean collectionIsInitialized(Object collection) { + return !(collection instanceof PersistentCollection) || ((PersistentCollection) collection).wasInitialized(); + } +} diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionEntry.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionEntry.java index 96ae842322..1edb372459 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionEntry.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/CollectionEntry.java @@ -182,6 +182,9 @@ public final class CollectionEntry implements Serializable { } public void preFlush(PersistentCollection collection) throws HibernateException { + if ( loadedKey == null && collection.getKey() != null ) { + loadedKey = collection.getKey(); + } boolean nonMutableChange = collection.isDirty() && getLoadedPersister()!=null && diff --git a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java index cfe9ac6240..eb3e242f62 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/spi/SessionFactoryImplementor.java @@ -153,7 +153,13 @@ public interface SessionFactoryImplementor extends Mapping, SessionFactory { /** * Get the connection provider + * + * @deprecated Access to connections via {@link org.hibernate.engine.jdbc.spi.JdbcConnectionAccess} should + * be preferred over access via {@link ConnectionProvider}, whenever possible. + * {@link org.hibernate.engine.jdbc.spi.JdbcConnectionAccess} is tied to the Hibernate Session to + * properly account for contextual information. See {@link SessionImplementor#getJdbcConnectionAccess()} */ + @Deprecated public ConnectionProvider getConnectionProvider(); /** * Get the names of all persistent classes that implement/extend the given interface/class diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/TransactionFactoryInitiator.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/TransactionFactoryInitiator.java index 4e08b061bf..b38fbf2564 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/TransactionFactoryInitiator.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/TransactionFactoryInitiator.java @@ -41,13 +41,17 @@ import org.hibernate.service.spi.BasicServiceInitiator; import org.hibernate.service.spi.ServiceRegistryImplementor; /** - * Standard instantiator for the standard {@link TransactionFactory} service. + * Standard initiator for {@link TransactionFactory} service. * * @author Steve Ebersole */ -public class TransactionFactoryInitiator implements BasicServiceInitiator { - private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, - TransactionFactoryInitiator.class.getName()); +public class TransactionFactoryInitiator + implements BasicServiceInitiator { + + private static final CoreMessageLogger LOG = Logger.getMessageLogger( + CoreMessageLogger.class, + TransactionFactoryInitiator.class.getName() + ); public static final TransactionFactoryInitiator INSTANCE = new TransactionFactoryInitiator(); @@ -87,7 +91,7 @@ public class TransactionFactoryInitiator imple } } else { - final String strategyClassName = mapLegacyNames( strategy.toString() ); + final String strategyClassName = mapName( strategy.toString() ); LOG.transactionStrategy( strategyClassName ); try { @@ -111,7 +115,8 @@ public class TransactionFactoryInitiator imple } } - private String mapLegacyNames(String name) { + private String mapName(String name) { + // check legacy names ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ if ( "org.hibernate.transaction.JDBCTransactionFactory".equals( name ) ) { return JdbcTransactionFactory.class.getName(); } @@ -124,6 +129,20 @@ public class TransactionFactoryInitiator imple return CMTTransactionFactory.class.getName(); } + // check short names ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if ( JdbcTransactionFactory.SHORT_NAME.endsWith( name ) ) { + return JdbcTransactionFactory.class.getName(); + } + + if ( JtaTransactionFactory.SHORT_NAME.equals( name ) ) { + return JtaTransactionFactory.class.getName(); + } + + if ( CMTTransactionFactory.SHORT_NAME.equals( name ) ) { + return CMTTransactionFactory.class.getName(); + } + + return name; } } diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jdbc/JdbcIsolationDelegate.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jdbc/JdbcIsolationDelegate.java index 16cd23fe72..748cd87aae 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jdbc/JdbcIsolationDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jdbc/JdbcIsolationDelegate.java @@ -35,7 +35,7 @@ import org.hibernate.engine.transaction.spi.TransactionCoordinator; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.jdbc.WorkExecutor; import org.hibernate.jdbc.WorkExecutorVisitable; -import org.hibernate.service.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.service.jdbc.connections.spi.JdbcConnectionAccess; /** * The isolation delegate for JDBC {@link Connection} based transactions @@ -52,8 +52,8 @@ public class JdbcIsolationDelegate implements IsolationDelegate { this.transactionCoordinator = transactionCoordinator; } - protected ConnectionProvider connectionProvider() { - return transactionCoordinator.getJdbcCoordinator().getLogicalConnection().getJdbcServices().getConnectionProvider(); + protected JdbcConnectionAccess jdbcConnectionAccess() { + return transactionCoordinator.getTransactionContext().getJdbcConnectionAccess(); } protected SqlExceptionHelper sqlExceptionHelper() { @@ -65,7 +65,7 @@ public class JdbcIsolationDelegate implements IsolationDelegate { boolean wasAutoCommit = false; try { // todo : should we use a connection proxy here? - Connection connection = connectionProvider().getConnection(); + Connection connection = jdbcConnectionAccess().obtainConnection(); try { if ( transacted ) { if ( connection.getAutoCommit() ) { @@ -112,7 +112,7 @@ public class JdbcIsolationDelegate implements IsolationDelegate { } } try { - connectionProvider().closeConnection( connection ); + jdbcConnectionAccess().releaseConnection( connection ); } catch ( Exception ignore ) { LOG.unableToReleaseIsolatedConnection( ignore ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jdbc/JdbcTransactionFactory.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jdbc/JdbcTransactionFactory.java index 7e787d104a..833c238821 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jdbc/JdbcTransactionFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jdbc/JdbcTransactionFactory.java @@ -34,6 +34,8 @@ import org.hibernate.engine.transaction.spi.TransactionFactory; * @author Steve Ebersole */ public final class JdbcTransactionFactory implements TransactionFactory { + public static final String SHORT_NAME = "jdbc"; + @Override public JdbcTransaction createTransaction(TransactionCoordinator transactionCoordinator) { return new JdbcTransaction( transactionCoordinator ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/CMTTransactionFactory.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/CMTTransactionFactory.java index ea2a3466cf..c211192d51 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/CMTTransactionFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/CMTTransactionFactory.java @@ -37,6 +37,8 @@ import org.hibernate.engine.transaction.spi.TransactionFactory; * @author Gavin King */ public class CMTTransactionFactory implements TransactionFactory { + public static final String SHORT_NAME = "cmt"; + @Override public CMTTransaction createTransaction(TransactionCoordinator transactionCoordinator) { return new CMTTransaction( transactionCoordinator ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/JtaIsolationDelegate.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/JtaIsolationDelegate.java index fdf32c5f60..2748d29104 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/JtaIsolationDelegate.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/JtaIsolationDelegate.java @@ -23,12 +23,12 @@ */ package org.hibernate.engine.transaction.internal.jta; -import java.sql.Connection; -import java.sql.SQLException; import javax.transaction.NotSupportedException; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; +import java.sql.Connection; +import java.sql.SQLException; import org.jboss.logging.Logger; @@ -39,7 +39,7 @@ import org.hibernate.engine.transaction.spi.TransactionCoordinator; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.jdbc.WorkExecutor; import org.hibernate.jdbc.WorkExecutorVisitable; -import org.hibernate.service.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.service.jdbc.connections.spi.JdbcConnectionAccess; /** * An isolation delegate for JTA environments. @@ -63,11 +63,8 @@ public class JtaIsolationDelegate implements IsolationDelegate { .retrieveTransactionManager(); } - protected ConnectionProvider connectionProvider() { - return transactionCoordinator.getTransactionContext() - .getTransactionEnvironment() - .getJdbcServices() - .getConnectionProvider(); + protected JdbcConnectionAccess jdbcConnectionAccess() { + return transactionCoordinator.getTransactionContext().getJdbcConnectionAccess(); } protected SqlExceptionHelper sqlExceptionHelper() { @@ -120,15 +117,15 @@ public class JtaIsolationDelegate implements IsolationDelegate { } private T doTheWorkInNewTransaction(WorkExecutorVisitable work, TransactionManager transactionManager) { - T result = null; try { // start the new isolated transaction transactionManager.begin(); try { - result = doTheWork( work ); + T result = doTheWork( work ); // if everything went ok, commit the isolated transaction transactionManager.commit(); + return result; } catch ( Exception e ) { try { @@ -146,7 +143,6 @@ public class JtaIsolationDelegate implements IsolationDelegate { catch ( NotSupportedException e ) { throw new HibernateException( "Unable to start isolated transaction", e ); } - return result; } private T doTheWorkInNoTransaction(WorkExecutorVisitable work) { @@ -156,7 +152,7 @@ public class JtaIsolationDelegate implements IsolationDelegate { private T doTheWork(WorkExecutorVisitable work) { try { // obtain our isolated connection - Connection connection = connectionProvider().getConnection(); + Connection connection = jdbcConnectionAccess().obtainConnection(); try { // do the actual work return work.accept( new WorkExecutor(), connection ); @@ -170,7 +166,7 @@ public class JtaIsolationDelegate implements IsolationDelegate { finally { try { // no matter what, release the connection (handle) - connectionProvider().closeConnection( connection ); + jdbcConnectionAccess().releaseConnection( connection ); } catch ( Throwable ignore ) { LOG.unableToReleaseIsolatedConnection( ignore ); diff --git a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/JtaTransactionFactory.java b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/JtaTransactionFactory.java index f7e7cf3945..539a800040 100644 --- a/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/JtaTransactionFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/engine/transaction/internal/jta/JtaTransactionFactory.java @@ -40,6 +40,8 @@ import org.hibernate.service.jta.platform.spi.JtaPlatform; * @author Les Hazlewood */ public class JtaTransactionFactory implements TransactionFactory { + public static final String SHORT_NAME = "jta"; + @Override public JtaTransaction createTransaction(TransactionCoordinator transactionCoordinator) { return new JtaTransaction( transactionCoordinator ); diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java index cd4155f5b7..b64cd9a7ae 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/AbstractFlushingEventListener.java @@ -37,6 +37,7 @@ import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.internal.Collections; import org.hibernate.engine.spi.ActionQueue; import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.CollectionEntry; import org.hibernate.engine.spi.CollectionKey; import org.hibernate.engine.spi.EntityEntry; @@ -168,7 +169,7 @@ public abstract class AbstractFlushingEventListener implements Serializable { protected Object getAnything() { return null; } protected CascadingAction getCascadingAction() { - return CascadingAction.SAVE_UPDATE; + return CascadingActions.SAVE_UPDATE; } /** diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java index 491b0820e0..e993d6b1cf 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultDeleteEventListener.java @@ -37,7 +37,7 @@ import org.hibernate.classic.Lifecycle; import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.internal.ForeignKeys; import org.hibernate.engine.internal.Nullability; -import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.PersistenceContext; @@ -321,7 +321,7 @@ public class DefaultDeleteEventListener implements DeleteEventListener { session.getPersistenceContext().incrementCascadeLevel(); try { // cascade-delete to collections BEFORE the collection owner is deleted - new Cascade( CascadingAction.DELETE, Cascade.AFTER_INSERT_BEFORE_DELETE, session ) + new Cascade( CascadingActions.DELETE, Cascade.AFTER_INSERT_BEFORE_DELETE, session ) .cascade( persister, entity, transientEntities ); } finally { @@ -341,7 +341,7 @@ public class DefaultDeleteEventListener implements DeleteEventListener { session.getPersistenceContext().incrementCascadeLevel(); try { // cascade-delete to many-to-one AFTER the parent was deleted - new Cascade( CascadingAction.DELETE, Cascade.BEFORE_INSERT_AFTER_DELETE, session ) + new Cascade( CascadingActions.DELETE, Cascade.BEFORE_INSERT_AFTER_DELETE, session ) .cascade( persister, entity, transientEntities ); } finally { diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultEvictEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultEvictEventListener.java index d6970ad9b3..8bf320502b 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultEvictEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultEvictEventListener.java @@ -29,7 +29,7 @@ import org.jboss.logging.Logger; import org.hibernate.HibernateException; import org.hibernate.engine.internal.Cascade; -import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.PersistenceContext; @@ -117,7 +117,7 @@ public class DefaultEvictEventListener implements EvictEventListener { // This is now handled by removeEntity() //session.getPersistenceContext().removeDatabaseSnapshot(key); - new Cascade( CascadingAction.EVICT, Cascade.AFTER_EVICT, session ) + new Cascade( CascadingActions.EVICT, Cascade.AFTER_EVICT, session ) .cascade( persister, object ); } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLockEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLockEventListener.java index 5e42c38973..3c2ca3e280 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLockEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultLockEventListener.java @@ -30,7 +30,7 @@ import org.hibernate.LockMode; import org.hibernate.TransientObjectException; import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.internal.ForeignKeys; -import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.event.spi.EventSource; @@ -89,7 +89,7 @@ public class DefaultLockEventListener extends AbstractLockUpgradeEventListener i EventSource source = event.getSession(); source.getPersistenceContext().incrementCascadeLevel(); try { - new Cascade(CascadingAction.LOCK, Cascade.AFTER_LOCK, source) + new Cascade( CascadingActions.LOCK, Cascade.AFTER_LOCK, source) .cascade( persister, entity, event.getLockOptions() ); } finally { diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java index e9ee3a13fe..5d00d6afad 100755 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultMergeEventListener.java @@ -36,6 +36,7 @@ import org.hibernate.WrongClassException; import org.hibernate.bytecode.instrumentation.spi.FieldInterceptor; import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionImplementor; @@ -447,7 +448,7 @@ public class DefaultMergeEventListener extends AbstractSaveEventListener impleme @Override protected CascadingAction getCascadeAction() { - return CascadingAction.MERGE; + return CascadingActions.MERGE; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPersistEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPersistEventListener.java index b68b69c105..69216478fc 100755 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPersistEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPersistEventListener.java @@ -32,6 +32,7 @@ import org.hibernate.HibernateException; import org.hibernate.ObjectDeletedException; import org.hibernate.PersistentObjectException; import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.Status; @@ -60,7 +61,7 @@ public class DefaultPersistEventListener extends AbstractSaveEventListener imple @Override protected CascadingAction getCascadeAction() { - return CascadingAction.PERSIST; + return CascadingActions.PERSIST; } @Override diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPersistOnFlushEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPersistOnFlushEventListener.java index 3f8095688e..58f7375b6d 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPersistOnFlushEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultPersistOnFlushEventListener.java @@ -24,6 +24,7 @@ package org.hibernate.event.internal; import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; /** * When persist is used as the cascade action, persistOnFlush should be used @@ -31,6 +32,6 @@ import org.hibernate.engine.spi.CascadingAction; */ public class DefaultPersistOnFlushEventListener extends DefaultPersistEventListener { protected CascadingAction getCascadeAction() { - return CascadingAction.PERSIST_ON_FLUSH; + return CascadingActions.PERSIST_ON_FLUSH; } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java index 3f6c2c524a..d37818046d 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultRefreshEventListener.java @@ -34,7 +34,7 @@ import org.hibernate.PersistentObjectException; import org.hibernate.UnresolvableObjectException; import org.hibernate.cache.spi.CacheKey; import org.hibernate.engine.internal.Cascade; -import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -119,7 +119,7 @@ public class DefaultRefreshEventListener implements RefreshEventListener { // cascade the refresh prior to refreshing this entity refreshedAlready.put(object, object); - new Cascade( CascadingAction.REFRESH, Cascade.BEFORE_REFRESH, source) + new Cascade( CascadingActions.REFRESH, Cascade.BEFORE_REFRESH, source) .cascade( persister, object, refreshedAlready ); if ( e != null ) { diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultReplicateEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultReplicateEventListener.java index e319792cf6..7885ea6c4c 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultReplicateEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultReplicateEventListener.java @@ -33,6 +33,7 @@ import org.hibernate.ReplicationMode; import org.hibernate.TransientObjectException; import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.Status; @@ -207,7 +208,7 @@ public class DefaultReplicateEventListener extends AbstractSaveEventListener imp EventSource source) { source.getPersistenceContext().incrementCascadeLevel(); try { - new Cascade( CascadingAction.REPLICATE, Cascade.AFTER_UPDATE, source ) + new Cascade( CascadingActions.REPLICATE, Cascade.AFTER_UPDATE, source ) .cascade( persister, entity, replicationMode ); } finally { @@ -217,6 +218,6 @@ public class DefaultReplicateEventListener extends AbstractSaveEventListener imp @Override protected CascadingAction getCascadeAction() { - return CascadingAction.REPLICATE; + return CascadingActions.REPLICATE; } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultSaveOrUpdateEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultSaveOrUpdateEventListener.java index 1e8276f7c8..8fe131bc3d 100755 --- a/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultSaveOrUpdateEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/internal/DefaultSaveOrUpdateEventListener.java @@ -35,6 +35,7 @@ import org.hibernate.TransientObjectException; import org.hibernate.classic.Lifecycle; import org.hibernate.engine.internal.Cascade; import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.SessionFactoryImplementor; @@ -358,7 +359,7 @@ public class DefaultSaveOrUpdateEventListener extends AbstractSaveEventListener EventSource source = event.getSession(); source.getPersistenceContext().incrementCascadeLevel(); try { - new Cascade( CascadingAction.SAVE_UPDATE, Cascade.AFTER_UPDATE, source ) + new Cascade( CascadingActions.SAVE_UPDATE, Cascade.AFTER_UPDATE, source ) .cascade( persister, entity ); } finally { @@ -368,6 +369,6 @@ public class DefaultSaveOrUpdateEventListener extends AbstractSaveEventListener @Override protected CascadingAction getCascadeAction() { - return CascadingAction.SAVE_UPDATE; + return CascadingActions.SAVE_UPDATE; } } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PostDeleteEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PostDeleteEventListener.java index c0a63f8a03..86049d5b29 100644 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PostDeleteEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PostDeleteEventListener.java @@ -25,6 +25,8 @@ package org.hibernate.event.spi; import java.io.Serializable; +import org.hibernate.persister.entity.EntityPersister; + /** * Called after deleting an item from the datastore * @@ -32,4 +34,6 @@ import java.io.Serializable; */ public interface PostDeleteEventListener extends Serializable { public void onPostDelete(PostDeleteEvent event); + + public boolean requiresPostCommitHanding(EntityPersister persister); } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PostInsertEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PostInsertEventListener.java index fb41d46b2f..ea01a01228 100755 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PostInsertEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PostInsertEventListener.java @@ -25,11 +25,25 @@ package org.hibernate.event.spi; import java.io.Serializable; +import org.hibernate.persister.entity.EntityPersister; + /** * Called after insterting an item in the datastore * * @author Gavin King + * @author Steve Ebersole */ public interface PostInsertEventListener extends Serializable { public void onPostInsert(PostInsertEvent event); + + /** + * Does this listener require that after transaction hooks be registered? Typically this is {@code true} + * for post-insert event listeners, but may not be, for example, in JPA cases where there are no callbacks defined + * for the particular entity. + * + * @param persister The persister for the entity in question. + * + * @return {@code true} if after transaction callbacks should be added. + */ + public boolean requiresPostCommitHanding(EntityPersister persister); } diff --git a/hibernate-core/src/main/java/org/hibernate/event/spi/PostUpdateEventListener.java b/hibernate-core/src/main/java/org/hibernate/event/spi/PostUpdateEventListener.java index 5151611d8c..1f1869d7cb 100755 --- a/hibernate-core/src/main/java/org/hibernate/event/spi/PostUpdateEventListener.java +++ b/hibernate-core/src/main/java/org/hibernate/event/spi/PostUpdateEventListener.java @@ -25,6 +25,8 @@ package org.hibernate.event.spi; import java.io.Serializable; +import org.hibernate.persister.entity.EntityPersister; + /** * Called after updating the datastore * @@ -32,4 +34,6 @@ import java.io.Serializable; */ public interface PostUpdateEventListener extends Serializable { public void onPostUpdate(PostUpdateEvent event); + + public boolean requiresPostCommitHanding(EntityPersister persister); } diff --git a/hibernate-core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java b/hibernate-core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java index 4d618dc556..5e64589894 100644 --- a/hibernate-core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/id/enhanced/OptimizerFactory.java @@ -99,7 +99,7 @@ public class OptimizerFactory { return POOLED_LO; } else { - LOG.debugf( "Unknown optimizer key [%s]; returning null assuming Optimizer impl class name" ); + LOG.debugf( "Unknown optimizer key [%s]; returning null assuming Optimizer impl class name", externalName ); return null; } } diff --git a/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java b/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java index fd7dc8cefc..00d6ea820b 100644 --- a/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java +++ b/hibernate-core/src/main/java/org/hibernate/loader/criteria/CriteriaLoader.java @@ -237,5 +237,9 @@ public class CriteriaLoader extends OuterJoinLoader { protected List getResultList(List results, ResultTransformer resultTransformer) { return resolveResultTransformer( resultTransformer ).transformList( results ); } + + protected String getQueryIdentifier() { + return "[CRITERIA] " + getSQLString(); + } } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/JoinedSubclass.java b/hibernate-core/src/main/java/org/hibernate/mapping/JoinedSubclass.java index af837dd4bf..fee5532504 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/JoinedSubclass.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/JoinedSubclass.java @@ -72,7 +72,7 @@ public class JoinedSubclass extends Subclass implements TableOwner { public Iterator getReferenceablePropertyIterator() { return getPropertyIterator(); } - + public Object accept(PersistentClassVisitor mv) { return mv.accept(this); } diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Property.java b/hibernate-core/src/main/java/org/hibernate/mapping/Property.java index 8b206dfc38..6cb91d0f4e 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Property.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Property.java @@ -31,6 +31,7 @@ import org.hibernate.EntityMode; import org.hibernate.MappingException; import org.hibernate.PropertyNotFoundException; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; import org.hibernate.engine.spi.Mapping; import org.hibernate.internal.util.collections.ArrayHelper; import org.hibernate.property.Getter; @@ -128,8 +129,8 @@ public class Property implements Serializable, MetaAttributable { } int length = compositeType.getSubtypes().length; for ( int i=0; i0 ) { + if ( columns != null && columns.size() > 0 ) { msg += ", for columns: " + columns; } throw new MappingException( msg ); @@ -510,4 +519,97 @@ public class SimpleValue implements KeyValue { }; } } -} + + private void createParameterImpl() { + try { + String[] columnsNames = new String[columns.size()]; + for ( int i = 0; i < columns.size(); i++ ) { + columnsNames[i] = ( (Column) columns.get( i ) ).getName(); + } + + AccessType accessType = AccessType.getAccessStrategy( typeParameters + .getProperty( DynamicParameterizedType.ACCESS_TYPE ) ); + final Class classEntity = ReflectHelper.classForName( typeParameters + .getProperty( DynamicParameterizedType.ENTITY ) ); + final String propertyName = typeParameters.getProperty( DynamicParameterizedType.PROPERTY ); + + Annotation[] annotations; + if ( accessType == AccessType.FIELD ) { + annotations = ( (Field) new DirectPropertyAccessor().getGetter( classEntity, propertyName ).getMember() ) + .getAnnotations(); + + } + else { + annotations = ReflectHelper.getGetter( classEntity, propertyName ).getMethod().getAnnotations(); + } + + typeParameters.put( + DynamicParameterizedType.PARAMETER_TYPE, + new ParameterTypeImpl( ReflectHelper.classForName( typeParameters + .getProperty( DynamicParameterizedType.RETURNED_CLASS ) ), annotations, table.getCatalog(), + table.getSchema(), table.getName(), Boolean.valueOf( typeParameters + .getProperty( DynamicParameterizedType.IS_PRIMARY_KEY ) ), columnsNames ) ); + + } + catch ( ClassNotFoundException cnfe ) { + throw new MappingException( "Could not create DynamicParameterizedType for type: " + typeName, cnfe ); + } + } + + private final class ParameterTypeImpl implements DynamicParameterizedType.ParameterType { + + private final Class returnedClass; + private final Annotation[] annotationsMethod; + private final String catalog; + private final String schema; + private final String table; + private final boolean primaryKey; + private final String[] columns; + + private ParameterTypeImpl(Class returnedClass, Annotation[] annotationsMethod, String catalog, String schema, + String table, boolean primaryKey, String[] columns) { + this.returnedClass = returnedClass; + this.annotationsMethod = annotationsMethod; + this.catalog = catalog; + this.schema = schema; + this.table = table; + this.primaryKey = primaryKey; + this.columns = columns; + } + + @Override + public Class getReturnedClass() { + return returnedClass; + } + + @Override + public Annotation[] getAnnotationsMethod() { + return annotationsMethod; + } + + @Override + public String getCatalog() { + return catalog; + } + + @Override + public String getSchema() { + return schema; + } + + @Override + public String getTable() { + return table; + } + + @Override + public boolean isPrimaryKey() { + return primaryKey; + } + + @Override + public String[] getColumns() { + return columns; + } + } +} \ No newline at end of file diff --git a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java index ca9b021b5a..4c2e96903d 100644 --- a/hibernate-core/src/main/java/org/hibernate/mapping/Table.java +++ b/hibernate-core/src/main/java/org/hibernate/mapping/Table.java @@ -311,7 +311,8 @@ public class Table implements RelationalModel, Serializable { } if ( removeIt ) { - uniqueKeys.remove( uniqueKeyEntry.getKey() ); + //uniqueKeys.remove( uniqueKeyEntry.getKey() ); + uniqueKeyEntries.remove(); } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataBuilderImpl.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataBuilderImpl.java index 8313a3cbe2..35b1419607 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataBuilderImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/MetadataBuilderImpl.java @@ -161,16 +161,7 @@ public class MetadataBuilderImpl implements MetadataBuilder { false ); - multiTenancyStrategy = configService.getSetting( - AvailableSettings.MULTI_TENANT, - new ConfigurationService.Converter() { - @Override - public MultiTenancyStrategy convert(Object value) { - return MultiTenancyStrategy.fromConfigValue( value ); - } - }, - MultiTenancyStrategy.NONE - ); + multiTenancyStrategy = MultiTenancyStrategy.determineMultiTenancyStrategy( configService.getSettings() ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/util/EnumConversionHelper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/util/EnumConversionHelper.java index 8e70194738..1b4400a1d5 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/util/EnumConversionHelper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/annotations/util/EnumConversionHelper.java @@ -33,6 +33,7 @@ import org.hibernate.AssertionFailure; import org.hibernate.FetchMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; import org.hibernate.id.MultipleHiLoPerTableGenerator; import org.hibernate.internal.util.collections.CollectionHelper; @@ -68,22 +69,22 @@ public class EnumConversionHelper { public static CascadeStyle cascadeTypeToCascadeStyle(CascadeType cascadeType) { switch ( cascadeType ) { case ALL: { - return CascadeStyle.ALL; + return CascadeStyles.ALL; } case PERSIST: { - return CascadeStyle.PERSIST; + return CascadeStyles.PERSIST; } case MERGE: { - return CascadeStyle.MERGE; + return CascadeStyles.MERGE; } case REMOVE: { - return CascadeStyle.DELETE; + return CascadeStyles.DELETE; } case REFRESH: { - return CascadeStyle.REFRESH; + return CascadeStyles.REFRESH; } case DETACH: { - return CascadeStyle.EVICT; + return CascadeStyles.EVICT; } default: { throw new AssertionFailure( "Unknown cascade type" ); diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/Helper.java b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/Helper.java index a4752361d9..7f442df946 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/Helper.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/internal/source/hbm/Helper.java @@ -35,6 +35,7 @@ import org.hibernate.LockMode; import org.hibernate.TruthValue; import org.hibernate.cache.spi.access.AccessType; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; import org.hibernate.jaxb.spi.hbm.JaxbCacheElement; import org.hibernate.jaxb.spi.hbm.JaxbColumnElement; @@ -252,7 +253,7 @@ public class Helper { cascades = bindingContext.getMappingDefaults().getCascadeStyle(); } for ( String cascade : StringHelper.split( ",", cascades ) ) { - cascadeStyles.add( CascadeStyle.getCascadeStyle( cascade ) ); + cascadeStyles.add( CascadeStyles.getCascadeStyle( cascade ) ); } return cascadeStyles; } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/MetadataSourcesContributor.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/MetadataSourcesContributor.java index de1e51d5f3..5a08cd7692 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/MetadataSourcesContributor.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/MetadataSourcesContributor.java @@ -23,7 +23,8 @@ */ package org.hibernate.metamodel.spi; -import org.jboss.jandex.IndexResult; + +import org.jboss.jandex.IndexView; import org.hibernate.metamodel.MetadataSources; @@ -39,5 +40,5 @@ public interface MetadataSourcesContributor { * @param metadataSources * @param jandexIndex The Jandex index */ - public void contribute(MetadataSources metadataSources, IndexResult jandexIndex); + public void contribute(MetadataSources metadataSources, IndexView jandexIndex); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/AbstractPluralAttributeAssociationElementBinding.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/AbstractPluralAttributeAssociationElementBinding.java index 9b0d705dde..79f5cc41ba 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/AbstractPluralAttributeAssociationElementBinding.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/AbstractPluralAttributeAssociationElementBinding.java @@ -30,6 +30,7 @@ import org.hibernate.FetchMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; /** * @author Steve Ebersole @@ -54,23 +55,23 @@ public abstract class AbstractPluralAttributeAssociationElementBinding public void setCascadeStyles(Iterable cascadeStyles) { List cascadeStyleList = new ArrayList(); for ( CascadeStyle style : cascadeStyles ) { - if ( style != CascadeStyle.NONE ) { + if ( style != CascadeStyles.NONE ) { cascadeStyleList.add( style ); } - if ( style == CascadeStyle.DELETE_ORPHAN || - style == CascadeStyle.ALL_DELETE_ORPHAN ) { + if ( style == CascadeStyles.DELETE_ORPHAN || + style == CascadeStyles.ALL_DELETE_ORPHAN ) { orphanDelete = true; } } if ( cascadeStyleList.isEmpty() ) { - cascadeStyle = CascadeStyle.NONE; + cascadeStyle = CascadeStyles.NONE; } else if ( cascadeStyleList.size() == 1 ) { cascadeStyle = cascadeStyleList.get( 0 ); } else { - cascadeStyle = new CascadeStyle.MultipleCascadeStyle( + cascadeStyle = new CascadeStyles.MultipleCascadeStyle( cascadeStyleList.toArray( new CascadeStyle[ cascadeStyleList.size() ] ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/AbstractPluralAttributeBinding.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/AbstractPluralAttributeBinding.java index 4526334daf..eb11d84347 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/AbstractPluralAttributeBinding.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/AbstractPluralAttributeBinding.java @@ -169,6 +169,26 @@ public abstract class AbstractPluralAttributeBinding extends AbstractAttributeBi return pluralAttributeElementBinding; } + @Override + public FetchTiming getFetchTiming() { + return fetchTiming; + } + + @Override + public void setFetchTiming(FetchTiming fetchTiming) { + this.fetchTiming = fetchTiming; + } + + @Override + public FetchStyle getFetchStyle() { + return fetchStyle; + } + + @Override + public void setFetchStyle(FetchStyle fetchStyle) { + this.fetchStyle = fetchStyle; + } + @Override public String getCustomLoaderName() { return customLoaderName; @@ -267,16 +287,6 @@ public abstract class AbstractPluralAttributeBinding extends AbstractAttributeBi this.batchSize = batchSize; } - - - - - - - - - - @Override public String getReferencedPropertyName() { return referencedPropertyName; @@ -324,23 +334,4 @@ public abstract class AbstractPluralAttributeBinding extends AbstractAttributeBi return fetchTiming != FetchTiming.IMMEDIATE; } - @Override - public FetchTiming getFetchTiming() { - return fetchTiming; - } - - @Override - public void setFetchTiming(FetchTiming fetchTiming) { - this.fetchTiming = fetchTiming; - } - - @Override - public FetchStyle getFetchStyle() { - return fetchStyle; - } - - @Override - public void setFetchStyle(FetchStyle fetchStyle) { - this.fetchStyle = fetchStyle; - } } diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/CascadeType.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/CascadeType.java index 1ca1b5c6ae..226b8568f0 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/CascadeType.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/CascadeType.java @@ -28,6 +28,7 @@ import java.util.Map; import org.hibernate.MappingException; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; /** @@ -125,18 +126,18 @@ public enum CascadeType { private static final Map cascadeTypeToCascadeStyle = new HashMap(); static { - cascadeTypeToCascadeStyle.put( ALL, CascadeStyle.ALL ); - cascadeTypeToCascadeStyle.put( ALL_DELETE_ORPHAN, CascadeStyle.ALL_DELETE_ORPHAN ); - cascadeTypeToCascadeStyle.put( UPDATE, CascadeStyle.UPDATE ); - cascadeTypeToCascadeStyle.put( PERSIST, CascadeStyle.PERSIST ); - cascadeTypeToCascadeStyle.put( MERGE, CascadeStyle.MERGE ); - cascadeTypeToCascadeStyle.put( LOCK, CascadeStyle.LOCK ); - cascadeTypeToCascadeStyle.put( REFRESH, CascadeStyle.REFRESH ); - cascadeTypeToCascadeStyle.put( REPLICATE, CascadeStyle.REPLICATE ); - cascadeTypeToCascadeStyle.put( EVICT, CascadeStyle.EVICT ); - cascadeTypeToCascadeStyle.put( DELETE, CascadeStyle.DELETE ); - cascadeTypeToCascadeStyle.put( DELETE_ORPHAN, CascadeStyle.DELETE_ORPHAN ); - cascadeTypeToCascadeStyle.put( NONE, CascadeStyle.NONE ); + cascadeTypeToCascadeStyle.put( ALL, CascadeStyles.ALL ); + cascadeTypeToCascadeStyle.put( ALL_DELETE_ORPHAN, CascadeStyles.ALL_DELETE_ORPHAN ); + cascadeTypeToCascadeStyle.put( UPDATE, CascadeStyles.UPDATE ); + cascadeTypeToCascadeStyle.put( PERSIST, CascadeStyles.PERSIST ); + cascadeTypeToCascadeStyle.put( MERGE, CascadeStyles.MERGE ); + cascadeTypeToCascadeStyle.put( LOCK, CascadeStyles.LOCK ); + cascadeTypeToCascadeStyle.put( REFRESH, CascadeStyles.REFRESH ); + cascadeTypeToCascadeStyle.put( REPLICATE, CascadeStyles.REPLICATE ); + cascadeTypeToCascadeStyle.put( EVICT, CascadeStyles.EVICT ); + cascadeTypeToCascadeStyle.put( DELETE, CascadeStyles.DELETE ); + cascadeTypeToCascadeStyle.put( DELETE_ORPHAN, CascadeStyles.DELETE_ORPHAN ); + cascadeTypeToCascadeStyle.put( NONE, CascadeStyles.NONE ); } /** diff --git a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/ManyToOneAttributeBinding.java b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/ManyToOneAttributeBinding.java index fab1b00f57..4af0566bd3 100644 --- a/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/ManyToOneAttributeBinding.java +++ b/hibernate-core/src/main/java/org/hibernate/metamodel/spi/binding/ManyToOneAttributeBinding.java @@ -32,6 +32,7 @@ import org.hibernate.FetchMode; import org.hibernate.engine.FetchStyle; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; import org.hibernate.metamodel.spi.domain.SingularAttribute; import org.hibernate.metamodel.spi.source.MetaAttributeContext; @@ -124,18 +125,18 @@ public class ManyToOneAttributeBinding public void setCascadeStyles(Iterable cascadeStyles) { List cascadeStyleList = new ArrayList(); for ( CascadeStyle style : cascadeStyles ) { - if ( style != CascadeStyle.NONE ) { + if ( style != CascadeStyles.NONE ) { cascadeStyleList.add( style ); } } if ( cascadeStyleList.isEmpty() ) { - cascadeStyle = CascadeStyle.NONE; + cascadeStyle = CascadeStyles.NONE; } else if ( cascadeStyleList.size() == 1 ) { cascadeStyle = cascadeStyleList.get( 0 ); } else { - cascadeStyle = new CascadeStyle.MultipleCascadeStyle( + cascadeStyle = new CascadeStyles.MultipleCascadeStyle( cascadeStyleList.toArray( new CascadeStyle[cascadeStyleList.size()] ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java index 3bec8de768..b7f7924435 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/AbstractEntityPersister.java @@ -64,7 +64,8 @@ import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.jdbc.batch.internal.BasicBatchKey; import org.hibernate.engine.spi.CachedNaturalIdValueSource; import org.hibernate.engine.spi.CascadeStyle; -import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadeStyles; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.EntityKey; import org.hibernate.engine.spi.ExecuteUpdateResultCheckStyle; @@ -1083,7 +1084,7 @@ public abstract class AbstractEntityPersister } } if ( cascadeStyle == null ) { - cascadeStyle = CascadeStyle.NONE; + cascadeStyle = CascadeStyles.NONE; } cascades.add( cascadeStyle ); @@ -1542,6 +1543,107 @@ public abstract class AbstractEntityPersister } + @Override + public Serializable getIdByUniqueKey(Serializable key, String uniquePropertyName, SessionImplementor session) throws HibernateException { + if ( LOG.isTraceEnabled() ) { + LOG.tracef( + "resolving unique key [%s] to identifier for entity [%s]", + key, + getEntityName() + ); + } + + int propertyIndex = getSubclassPropertyIndex( uniquePropertyName ); + if ( propertyIndex < 0 ) { + throw new HibernateException( + "Could not determine Type for property [" + uniquePropertyName + "] on entity [" + getEntityName() + "]" + ); + } + Type propertyType = getSubclassPropertyType( propertyIndex ); + + try { + PreparedStatement ps = session.getTransactionCoordinator() + .getJdbcCoordinator() + .getStatementPreparer() + .prepareStatement( generateIdByUniqueKeySelectString( uniquePropertyName ) ); + try { + propertyType.nullSafeSet( ps, key, 1, session ); + ResultSet rs = ps.executeQuery(); + try { + //if there is no resulting row, return null + if ( !rs.next() ) { + return null; + } + return (Serializable) getIdentifierType().nullSafeGet( rs, getIdentifierAliases(), session, null ); + } + finally { + rs.close(); + } + } + finally { + ps.close(); + } + } + catch ( SQLException e ) { + throw getFactory().getSQLExceptionHelper().convert( + e, + String.format( + "could not resolve unique property [%s] to identifier for entity [%s]", + uniquePropertyName, + getEntityName() + ), + getSQLSnapshotSelectString() + ); + } + + } + + protected String generateIdByUniqueKeySelectString(String uniquePropertyName) { + Select select = new Select( getFactory().getDialect() ); + + if ( getFactory().getSettings().isCommentsEnabled() ) { + select.setComment( "resolve id by unique property [" + getEntityName() + "." + uniquePropertyName + "]" ); + } + + final String rooAlias = getRootAlias(); + + select.setFromClause( fromTableFragment( rooAlias ) + fromJoinFragment( rooAlias, true, false ) ); + + SelectFragment selectFragment = new SelectFragment(); + selectFragment.addColumns( rooAlias, getIdentifierColumnNames(), getIdentifierAliases() ); + select.setSelectClause( selectFragment ); + + StringBuilder whereClauseBuffer = new StringBuilder(); + final int uniquePropertyIndex = getSubclassPropertyIndex( uniquePropertyName ); + final String uniquePropertyTableAlias = generateTableAlias( + rooAlias, + getSubclassPropertyTableNumber( uniquePropertyIndex ) + ); + String sep = ""; + for ( String columnTemplate : getSubclassPropertyColumnReaderTemplateClosure()[uniquePropertyIndex] ) { + if ( columnTemplate == null ) { + continue; + } + final String columnReference = StringHelper.replace( columnTemplate, Template.TEMPLATE, uniquePropertyTableAlias ); + whereClauseBuffer.append( sep ).append( columnReference ).append( "=?" ); + sep = " and "; + } + for ( String formulaTemplate : getSubclassPropertyFormulaTemplateClosure()[uniquePropertyIndex] ) { + if ( formulaTemplate == null ) { + continue; + } + final String formulaReference = StringHelper.replace( formulaTemplate, Template.TEMPLATE, uniquePropertyTableAlias ); + whereClauseBuffer.append( sep ).append( formulaReference ).append( "=?" ); + sep = " and "; + } + whereClauseBuffer.append( whereJoinFragment( rooAlias, true, false ) ); + + select.setWhereClause( whereClauseBuffer.toString() ); + + return select.setOuterJoins( "", "" ).toStatementString(); + } + + /** * Generate the SQL that selects the version number by id */ @@ -3781,11 +3883,11 @@ public abstract class AbstractEntityPersister loaders.put( "merge", - new CascadeEntityLoader( this, CascadingAction.MERGE, getFactory() ) + new CascadeEntityLoader( this, CascadingActions.MERGE, getFactory() ) ); loaders.put( "refresh", - new CascadeEntityLoader( this, CascadingAction.REFRESH, getFactory() ) + new CascadeEntityLoader( this, CascadingActions.REFRESH, getFactory() ) ); } diff --git a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java index 43c62c9656..29b4faa7ac 100644 --- a/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java +++ b/hibernate-core/src/main/java/org/hibernate/persister/entity/EntityPersister.java @@ -509,6 +509,8 @@ public interface EntityPersister extends OptimisticCacheSource { public Object[] getDatabaseSnapshot(Serializable id, SessionImplementor session) throws HibernateException; + public Serializable getIdByUniqueKey(Serializable key, String uniquePropertyName, SessionImplementor session); + /** * Get the current version of the object, or return null if there is no row for * the given identifier. In the case of unversioned data, return any object diff --git a/hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java b/hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java index 908305dfe1..be044a96d7 100755 --- a/hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java +++ b/hibernate-core/src/main/java/org/hibernate/proxy/AbstractLazyInitializer.java @@ -23,14 +23,21 @@ */ package org.hibernate.proxy; +import javax.naming.NamingException; import java.io.Serializable; +import org.jboss.logging.Logger; + import org.hibernate.HibernateException; import org.hibernate.LazyInitializationException; +import org.hibernate.Session; import org.hibernate.SessionException; import org.hibernate.TransientObjectException; +import org.hibernate.cfg.AvailableSettings; import org.hibernate.engine.spi.EntityKey; +import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.internal.SessionFactoryRegistry; import org.hibernate.persister.entity.EntityPersister; /** @@ -41,6 +48,8 @@ import org.hibernate.persister.entity.EntityPersister; * @author Gavin King */ public abstract class AbstractLazyInitializer implements LazyInitializer { + private static final Logger log = Logger.getLogger( AbstractLazyInitializer.class ); + private String entityName; private Serializable id; private Object target; @@ -50,6 +59,9 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { private transient SessionImplementor session; private Boolean readOnlyBeforeAttachedToSession; + private String sessionFactoryUuid; + private boolean specjLazyLoad = false; + /** * For serialization from the non-pojo initializers (HHH-3309) */ @@ -104,12 +116,12 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { public final void setSession(SessionImplementor s) throws HibernateException { if ( s != session ) { // check for s == null first, since it is least expensive - if ( s == null ){ + if ( s == null ) { unsetSession(); } else if ( isConnectedToSession() ) { //TODO: perhaps this should be some other RuntimeException... - throw new HibernateException("illegally attempted to associate a proxy with two open Sessions"); + throw new HibernateException( "illegally attempted to associate a proxy with two open Sessions" ); } else { // s != null @@ -117,7 +129,7 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { if ( readOnlyBeforeAttachedToSession == null ) { // use the default read-only/modifiable setting final EntityPersister persister = s.getFactory().getEntityPersister( entityName ); - setReadOnly( s.getPersistenceContext().isDefaultReadOnly() || ! persister.isMutable() ); + setReadOnly( s.getPersistenceContext().isDefaultReadOnly() || !persister.isMutable() ); } else { // use the read-only/modifiable setting indicated during deserialization @@ -137,6 +149,7 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { @Override public final void unsetSession() { + prepareForPossibleSpecialSpecjInitialization(); session = null; readOnly = false; readOnlyBeforeAttachedToSession = null; @@ -144,18 +157,21 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { @Override public final void initialize() throws HibernateException { - if (!initialized) { - if ( session==null ) { - throw new LazyInitializationException("could not initialize proxy - no Session"); + if ( !initialized ) { + if ( specjLazyLoad ) { + specialSpecjInitialization(); + } + else if ( session == null ) { + throw new LazyInitializationException( "could not initialize proxy - no Session" ); } else if ( !session.isOpen() ) { - throw new LazyInitializationException("could not initialize proxy - the owning Session was closed"); + throw new LazyInitializationException( "could not initialize proxy - the owning Session was closed" ); } else if ( !session.isConnected() ) { - throw new LazyInitializationException("could not initialize proxy - the owning Session is disconnected"); + throw new LazyInitializationException( "could not initialize proxy - the owning Session is disconnected" ); } else { - target = session.immediateLoad(entityName, id); + target = session.immediateLoad( entityName, id ); initialized = true; checkTargetState(); } @@ -165,6 +181,67 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { } } + protected void specialSpecjInitialization() { + if ( session == null ) { + //we have a detached collection thats set to null, reattach + if ( sessionFactoryUuid == null ) { + throw new LazyInitializationException( "could not initialize proxy - no Session" ); + } + try { + SessionFactoryImplementor sf = (SessionFactoryImplementor) + SessionFactoryRegistry.INSTANCE.getSessionFactory( sessionFactoryUuid ); + SessionImplementor session = (SessionImplementor) sf.openSession(); + + try { + target = session.immediateLoad( entityName, id ); + } + finally { + // make sure the just opened temp session gets closed! + try { + ( (Session) session ).close(); + } + catch (Exception e) { + log.warn( "Unable to close temporary session used to load lazy proxy associated to no session" ); + } + } + initialized = true; + checkTargetState(); + } + catch (Exception e) { + e.printStackTrace(); + throw new LazyInitializationException( e.getMessage() ); + } + } + else if ( session.isOpen() && session.isConnected() ) { + target = session.immediateLoad( entityName, id ); + initialized = true; + checkTargetState(); + } + else { + throw new LazyInitializationException( "could not initialize proxy - Session was closed or disced" ); + } + } + + protected void prepareForPossibleSpecialSpecjInitialization() { + if ( session != null ) { + specjLazyLoad = + Boolean.parseBoolean( + session.getFactory() + .getProperties() + .getProperty( AvailableSettings.ENABLE_LAZY_LOAD_NO_TRANS ) + ); + + if ( specjLazyLoad && sessionFactoryUuid == null ) { + try { + sessionFactoryUuid = (String) session.getFactory().getReference().get( "uuid" ).getContent(); + } + catch (NamingException e) { + //not much we can do if this fails... + } + } + } + } + private void checkTargetState() { if ( !unwrap ) { if ( target == null ) { @@ -205,7 +282,7 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { @Override public final Object getImplementation(SessionImplementor s) throws HibernateException { final EntityKey entityKey = generateEntityKeyOrNull( getIdentifier(), s, getEntityName() ); - return ( entityKey == null ? null : s.getPersistenceContext().getEntity( entityKey ) ); + return (entityKey == null ? null : s.getPersistenceContext().getEntity( entityKey )); } /** @@ -221,17 +298,19 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { @Override public final boolean isReadOnlySettingAvailable() { - return ( session != null && ! session.isClosed() ); + return (session != null && !session.isClosed()); } private void errorIfReadOnlySettingNotAvailable() { if ( session == null ) { throw new TransientObjectException( - "Proxy is detached (i.e, session is null). The read-only/modifiable setting is only accessible when the proxy is associated with an open session." ); + "Proxy is detached (i.e, session is null). The read-only/modifiable setting is only accessible when the proxy is associated with an open session." + ); } if ( session.isClosed() ) { throw new SessionException( - "Session is closed. The read-only/modifiable setting is only accessible when the proxy is associated with an open session." ); + "Session is closed. The read-only/modifiable setting is only accessible when the proxy is associated with an open session." + ); } } @@ -247,8 +326,8 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { // only update if readOnly is different from current setting if ( this.readOnly != readOnly ) { final EntityPersister persister = session.getFactory().getEntityPersister( entityName ); - if ( ! persister.isMutable() && ! readOnly ) { - throw new IllegalStateException( "cannot make proxies for immutable entities modifiable"); + if ( !persister.isMutable() && !readOnly ) { + throw new IllegalStateException( "cannot make proxies for immutable entities modifiable" ); } this.readOnly = readOnly; if ( initialized ) { @@ -263,13 +342,14 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { /** * Get the read-only/modifiable setting that should be put in affect when it is * attached to a session. - * + *

* This method should only be called during serialization when read-only/modifiable setting * is not available (i.e., isReadOnlySettingAvailable() == false) * * @return null, if the default setting should be used; - * true, for read-only; - * false, for modifiable + * true, for read-only; + * false, for modifiable + * * @throws IllegalStateException if isReadOnlySettingAvailable() == true */ protected final Boolean isReadOnlyBeforeAttachedToSession() { @@ -284,12 +364,13 @@ public abstract class AbstractLazyInitializer implements LazyInitializer { /** * Set the read-only/modifiable setting that should be put in affect when it is * attached to a session. - * + *

* This method should only be called during deserialization, before associating * the proxy with a session. * * @param readOnlyBeforeAttachedToSession, the read-only/modifiable setting to use when * associated with a session; null indicates that the default should be used. + * * @throws IllegalStateException if isReadOnlySettingAvailable() == true */ /* package-private */ diff --git a/hibernate-core/src/main/java/org/hibernate/service/classloading/internal/ClassLoaderServiceImpl.java b/hibernate-core/src/main/java/org/hibernate/service/classloading/internal/ClassLoaderServiceImpl.java index 2329822c53..3588970f17 100644 --- a/hibernate-core/src/main/java/org/hibernate/service/classloading/internal/ClassLoaderServiceImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/service/classloading/internal/ClassLoaderServiceImpl.java @@ -52,6 +52,7 @@ public class ClassLoaderServiceImpl implements ClassLoaderService { private final ClassLoader classClassLoader; private final ClassLoader resourcesClassLoader; + private final ClassLoader serviceLoaderClassLoader; private final StrategyInstanceResolverImpl strategyInstanceResolver = new StrategyInstanceResolverImpl( this ); @@ -108,6 +109,8 @@ public class ClassLoaderServiceImpl implements ClassLoaderService { }; this.resourcesClassLoader = resourcesClassLoader; + + this.serviceLoaderClassLoader = buildServiceLoaderClassLoader(); } @SuppressWarnings( {"UnusedDeclaration"}) @@ -138,6 +141,68 @@ public class ClassLoaderServiceImpl implements ClassLoaderService { } } + private ClassLoader buildServiceLoaderClassLoader() { + return new ClassLoader(null) { + final ClassLoader[] classLoaderArray = new ClassLoader[] { + // first look on the hibernate class loader + getClass().getClassLoader(), + // next look on the resource class loader + resourcesClassLoader, + // finally look on the combined class class loader + classClassLoader + }; + + @Override + public Enumeration getResources(String name) throws IOException { + final HashSet resourceUrls = new HashSet(); + + for ( ClassLoader classLoader : classLoaderArray ) { + final Enumeration urls = classLoader.getResources( name ); + while ( urls.hasMoreElements() ) { + resourceUrls.add( urls.nextElement() ); + } + } + + return new Enumeration() { + final Iterator resourceUrlIterator = resourceUrls.iterator(); + @Override + public boolean hasMoreElements() { + return resourceUrlIterator.hasNext(); + } + + @Override + public URL nextElement() { + return resourceUrlIterator.next(); + } + }; + } + + @Override + protected URL findResource(String name) { + for ( ClassLoader classLoader : classLoaderArray ) { + final URL resource = classLoader.getResource( name ); + if ( resource != null ) { + return resource; + } + } + return super.findResource( name ); + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + for ( ClassLoader classLoader : classLoaderArray ) { + try { + return classLoader.loadClass( name ); + } + catch (Exception ignore) { + } + } + + throw new ClassNotFoundException( "Could not load requested class : " + name ); + } + }; + } + @Override @SuppressWarnings( {"unchecked"}) public Class classForName(String className) { @@ -230,55 +295,6 @@ public class ClassLoaderServiceImpl implements ClassLoaderService { @Override public LinkedHashSet loadJavaServices(Class serviceContract) { - final ClassLoader serviceLoaderClassLoader = new ClassLoader(null) { - final ClassLoader[] classLoaderArray = new ClassLoader[] { - // first look on the hibernate class loader - getClass().getClassLoader(), - // next look on the resource class loader - resourcesClassLoader, - // finally look on the combined class class loader - classClassLoader - }; - - @Override - public Enumeration getResources(String name) throws IOException { - final HashSet resourceUrls = new HashSet(); - - for ( ClassLoader classLoader : classLoaderArray ) { - final Enumeration urls = classLoader.getResources( name ); - while ( urls.hasMoreElements() ) { - resourceUrls.add( urls.nextElement() ); - } - } - - return new Enumeration() { - final Iterator resourceUrlIterator = resourceUrls.iterator(); - @Override - public boolean hasMoreElements() { - return resourceUrlIterator.hasNext(); - } - - @Override - public URL nextElement() { - return resourceUrlIterator.next(); - } - }; - } - - @Override - protected Class findClass(String name) throws ClassNotFoundException { - for ( ClassLoader classLoader : classLoaderArray ) { - try { - return classLoader.loadClass( name ); - } - catch (Exception ignore) { - } - } - - throw new ClassNotFoundException( "Could not load requested class : " + name ); - } - }; - final ServiceLoader loader = ServiceLoader.load( serviceContract, serviceLoaderClassLoader ); final LinkedHashSet services = new LinkedHashSet(); for ( S service : loader ) { @@ -288,6 +304,37 @@ public class ClassLoaderServiceImpl implements ClassLoaderService { return services; } + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // completely temporary !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + + public static interface Work { + public T perform(); + } + + public T withTccl(Work work) { + final ClassLoader tccl = Thread.currentThread().getContextClassLoader(); + + boolean set = false; + + try { + Thread.currentThread().setContextClassLoader( serviceLoaderClassLoader); + set = true; + } + catch (Exception ignore) { + } + + try { + return work.perform(); + } + finally { + if ( set ) { + Thread.currentThread().setContextClassLoader( tccl ); + } + } + + } + @Override public StrategyInstanceResolver getStrategyInstanceResolver() { return strategyInstanceResolver; diff --git a/hibernate-core/src/main/java/org/hibernate/service/jdbc/connections/spi/JdbcConnectionAccess.java b/hibernate-core/src/main/java/org/hibernate/service/jdbc/connections/spi/JdbcConnectionAccess.java index ee17af66a6..7ea1c3fb85 100644 --- a/hibernate-core/src/main/java/org/hibernate/service/jdbc/connections/spi/JdbcConnectionAccess.java +++ b/hibernate-core/src/main/java/org/hibernate/service/jdbc/connections/spi/JdbcConnectionAccess.java @@ -1,7 +1,7 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2011, Red Hat Inc. or third-party contributors as + * Copyright (c) 2012, 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. @@ -56,8 +56,8 @@ public interface JdbcConnectionAccess extends Serializable { * Does the underlying provider of connections support aggressive releasing of connections (and re-acquisition * of those connections later, if need be) in JTA environments? * - * @see ConnectionProvider#supportsAggressiveRelease() - * @see MultiTenantConnectionProvider#supportsAggressiveRelease() + * @see org.hibernate.service.jdbc.connections.spi.ConnectionProvider#supportsAggressiveRelease() + * @see org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider#supportsAggressiveRelease() */ public boolean supportsAggressiveRelease(); } diff --git a/hibernate-core/src/main/java/org/hibernate/service/jdbc/env/internal/JdbcEnvironment.java b/hibernate-core/src/main/java/org/hibernate/service/jdbc/env/internal/JdbcEnvironment.java deleted file mode 100644 index c7a1532442..0000000000 --- a/hibernate-core/src/main/java/org/hibernate/service/jdbc/env/internal/JdbcEnvironment.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2012, 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.service.jdbc.env.internal; - -import java.util.Set; - -import org.hibernate.dialect.Dialect; -import org.hibernate.engine.jdbc.internal.TypeInfo; -import org.hibernate.engine.jdbc.spi.SqlExceptionHelper; -import org.hibernate.metamodel.spi.relational.Identifier; -import org.hibernate.service.Service; -import org.hibernate.service.jdbc.env.spi.ExtractedDatabaseMetaData; -import org.hibernate.service.jdbc.env.spi.IdentifierHelper; -import org.hibernate.service.jdbc.env.spi.QualifiedObjectNameSupport; - -/** - * Initial look at this concept we keep talking about with merging information from {@link java.sql.DatabaseMetaData} - * and {@link org.hibernate.dialect.Dialect} - * - * @author Steve Ebersole - */ -public interface JdbcEnvironment extends Service { - /** - * Get the dialect for this environment. - * - * @return The dialect. - */ - public Dialect getDialect(); - - /** - * Access to the bits of information we pulled off the JDBC {@link java.sql.DatabaseMetaData} (that did not get - * "interpreted" into the helpers/delegates available here). - * - * @return The values extracted from JDBC DatabaseMetaData - */ - public ExtractedDatabaseMetaData getExtractedDatabaseMetaData(); - - /** - * Get the current database catalog. Typically will come from either {@link java.sql.Connection#getCatalog()} - * or {@link org.hibernate.cfg.AvailableSettings#DEFAULT_CATALOG}. - * - * @return The current catalog. - */ - public Identifier getCurrentCatalog(); - - /** - * Get the current database catalog. Typically will come from either - * {@link org.hibernate.service.jdbc.env.spi.SchemaNameResolver#resolveSchemaName(java.sql.Connection)} or - * {@link org.hibernate.cfg.AvailableSettings#DEFAULT_CATALOG}. - * - * @return The current schema - */ - public Identifier getCurrentSchema(); - - /** - * Obtain support for reading and writing qualified object names. - * - * @return Qualified name support. - */ - public QualifiedObjectNameSupport getQualifiedObjectNameSupport(); - - /** - * Obtain the helper for dealing with identifiers in this environment. - * - * @return The identifier helper. - */ - public IdentifierHelper getIdentifierHelper(); - - /** - * Get the complete set of reserved words for this environment. These are significant because they represent - * the complete set of terms that MUST BE quoted if used as identifiers. This allows us to apply auto-quoting - * in the metamodel based on these terms. - * - * Note that the standard IdentifierHelper returned by {@link #getIdentifierHelper()} already accounts for - * auto-quoting :) yaay! - * - * @return Reserved words - */ - public Set getReservedWords(); - - /** - * Obtain the helper for dealing with JDBC {@link java.sql.SQLException} faults. - * - * @return This environment's helper. - */ - public SqlExceptionHelper getSqlExceptionHelper(); - - /** - * Find type information for the type identified by the given "JDBC type code". - * - * @param jdbcTypeCode The JDBC type code. - * - * @return The corresponding type info. - */ - public TypeInfo getTypeInfoForJdbcCode(int jdbcTypeCode); -} diff --git a/hibernate-core/src/main/java/org/hibernate/sql/Select.java b/hibernate-core/src/main/java/org/hibernate/sql/Select.java index 795562bb68..2b67c9b471 100644 --- a/hibernate-core/src/main/java/org/hibernate/sql/Select.java +++ b/hibernate-core/src/main/java/org/hibernate/sql/Select.java @@ -151,6 +151,11 @@ public class Select { return this; } + public Select setSelectClause(SelectFragment selectFragment) { + setSelectClause( selectFragment.toFragmentString().substring( 2 ) ); + return this; + } + /** * Sets the whereClause. * @param whereClause The whereClause to set @@ -204,5 +209,4 @@ public class Select { LockOptions.copy(lockOptions, this.lockOptions); return this; } - } diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java b/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java index 3025b75a2f..52a8cec6c8 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/PropertyFactory.java @@ -30,6 +30,7 @@ import org.hibernate.FetchMode; import org.hibernate.PropertyNotFoundException; import org.hibernate.engine.internal.UnsavedValueFactory; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; import org.hibernate.engine.spi.IdentifierValue; import org.hibernate.engine.spi.VersionValue; import org.hibernate.id.IdentifierGenerator; @@ -212,7 +213,7 @@ public class PropertyFactory { final CascadeStyle cascadeStyle = property.isAssociation() ? ( (SingularAssociationAttributeBinding) property ).getCascadeStyle() - : CascadeStyle.NONE; + : CascadeStyles.NONE; return new VersionProperty( property.getAttribute().getName(), @@ -296,7 +297,7 @@ public class PropertyFactory { final SingularAttributeBinding singularAttributeBinding = ( SingularAttributeBinding ) property; final CascadeStyle cascadeStyle = singularAttributeBinding.isAssociation() ? ( (SingularAssociationAttributeBinding) singularAttributeBinding ).getCascadeStyle() - : CascadeStyle.NONE; + : CascadeStyles.NONE; final FetchMode fetchMode = singularAttributeBinding.isAssociation() ? ( (SingularAssociationAttributeBinding) singularAttributeBinding ).getFetchMode() : FetchMode.DEFAULT; @@ -325,7 +326,7 @@ public class PropertyFactory { final AbstractPluralAttributeBinding pluralAttributeBinding = (AbstractPluralAttributeBinding) property; final CascadeStyle cascadeStyle = pluralAttributeBinding.isAssociation() ? ( (PluralAttributeAssociationElementBinding) pluralAttributeBinding.getPluralAttributeElementBinding() ).getCascadeStyle() - : CascadeStyle.NONE; + : CascadeStyles.NONE; final FetchMode fetchMode = pluralAttributeBinding.isAssociation() ? pluralAttributeBinding.getFetchMode() : FetchMode.DEFAULT; diff --git a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java index 851aa1161b..7748bc1c5b 100644 --- a/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java +++ b/hibernate-core/src/main/java/org/hibernate/tuple/entity/EntityMetamodel.java @@ -42,6 +42,7 @@ import org.hibernate.cfg.Environment; import org.hibernate.engine.OptimisticLockStyle; import org.hibernate.engine.internal.Versioning; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.ValueInclusion; import org.hibernate.internal.CoreMessageLogger; @@ -234,7 +235,7 @@ public class EntityMetamodel implements Serializable { hasLazy = true; } - if ( properties[i].getCascadeStyle() != CascadeStyle.NONE ) { + if ( properties[i].getCascadeStyle() != CascadeStyles.NONE ) { foundCascade = true; } @@ -484,7 +485,7 @@ public class EntityMetamodel implements Serializable { hasLazy = true; } - if ( properties[i].getCascadeStyle() != CascadeStyle.NONE ) { + if ( properties[i].getCascadeStyle() != CascadeStyles.NONE ) { foundCascade = true; } diff --git a/hibernate-core/src/main/java/org/hibernate/type/AnyType.java b/hibernate-core/src/main/java/org/hibernate/type/AnyType.java index 53574d1fe1..9f5a0da65f 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/AnyType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/AnyType.java @@ -40,6 +40,7 @@ import org.hibernate.MappingException; import org.hibernate.TransientObjectException; import org.hibernate.engine.internal.ForeignKeys; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; import org.hibernate.engine.spi.Mapping; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; @@ -270,7 +271,7 @@ public class AnyType extends AbstractType implements CompositeType, AssociationT } } public CascadeStyle getCascadeStyle(int i) { - return CascadeStyle.NONE; + return CascadeStyles.NONE; } public FetchMode getFetchMode(int i) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/CompositeCustomType.java b/hibernate-core/src/main/java/org/hibernate/type/CompositeCustomType.java index c84b4e77e2..05840121e8 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/CompositeCustomType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/CompositeCustomType.java @@ -38,6 +38,7 @@ import org.hibernate.FetchMode; import org.hibernate.HibernateException; import org.hibernate.MappingException; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; import org.hibernate.engine.spi.Mapping; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; @@ -121,7 +122,7 @@ public class CompositeCustomType extends AbstractType implements CompositeType, } public CascadeStyle getCascadeStyle(int i) { - return CascadeStyle.NONE; + return CascadeStyles.NONE; } public FetchMode getFetchMode(int i) { diff --git a/hibernate-core/src/main/java/org/hibernate/type/EnumType.java b/hibernate-core/src/main/java/org/hibernate/type/EnumType.java index 7c87540def..809948c80f 100644 --- a/hibernate-core/src/main/java/org/hibernate/type/EnumType.java +++ b/hibernate-core/src/main/java/org/hibernate/type/EnumType.java @@ -24,20 +24,22 @@ package org.hibernate.type; import java.io.Serializable; +import java.lang.annotation.Annotation; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Types; import java.util.Properties; - -import org.jboss.logging.Logger; - +import javax.persistence.Enumerated; +import javax.persistence.MapKeyEnumerated; +import org.hibernate.AssertionFailure; import org.hibernate.HibernateException; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.internal.CoreMessageLogger; import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.usertype.DynamicParameterizedType; import org.hibernate.usertype.EnhancedUserType; -import org.hibernate.usertype.ParameterizedType; +import org.jboss.logging.Logger; /** * Enum type mapper @@ -49,7 +51,7 @@ import org.hibernate.usertype.ParameterizedType; * @author Hardy Ferentschik */ @SuppressWarnings("unchecked") -public class EnumType implements EnhancedUserType, ParameterizedType, Serializable { +public class EnumType implements EnhancedUserType, DynamicParameterizedType, Serializable { private static final CoreMessageLogger LOG = Logger.getMessageLogger(CoreMessageLogger.class, EnumType.class.getName()); @@ -168,17 +170,37 @@ public class EnumType implements EnhancedUserType, ParameterizedType, Serializab } public void setParameterValues(Properties parameters) { - String enumClassName = parameters.getProperty( ENUM ); - try { - enumClass = ReflectHelper.classForName( enumClassName, this.getClass() ).asSubclass( Enum.class ); - } - catch ( ClassNotFoundException exception ) { - throw new HibernateException( "Enum class not found", exception ); - } + ParameterType reader = (ParameterType) parameters.get( PARAMETER_TYPE ); - String type = parameters.getProperty( TYPE ); - if ( type != null ) { - sqlType = Integer.decode( type ); + if ( reader != null ) { + enumClass = reader.getReturnedClass().asSubclass( Enum.class ); + + javax.persistence.EnumType enumType = getEnumType( reader ); + if ( enumType != null ) { + if ( javax.persistence.EnumType.ORDINAL.equals( enumType ) ) { + sqlType = Types.INTEGER; + } + else if ( javax.persistence.EnumType.STRING.equals( enumType ) ) { + sqlType = Types.VARCHAR; + } + else { + throw new AssertionFailure( "Unknown EnumType: " + enumType ); + } + } + } + else { + String enumClassName = (String) parameters.get( ENUM ); + try { + enumClass = ReflectHelper.classForName( enumClassName, this.getClass() ).asSubclass( Enum.class ); + } + catch ( ClassNotFoundException exception ) { + throw new HibernateException( "Enum class not found", exception ); + } + + String type = (String) parameters.get( TYPE ); + if ( type != null ) { + sqlType = Integer.decode( type ); + } } } @@ -234,4 +256,30 @@ public class EnumType implements EnhancedUserType, ParameterizedType, Serializab } } } + + private javax.persistence.EnumType getEnumType(ParameterType reader) { + javax.persistence.EnumType enumType = null; + if ( reader.isPrimaryKey() ) { + MapKeyEnumerated enumAnn = getAnnotation( reader.getAnnotationsMethod(), MapKeyEnumerated.class ); + if ( enumAnn != null ) { + enumType = enumAnn.value(); + } + } + else { + Enumerated enumAnn = getAnnotation( reader.getAnnotationsMethod(), Enumerated.class ); + if ( enumAnn != null ) { + enumType = enumAnn.value(); + } + } + return enumType; + } + + private T getAnnotation(Annotation[] annotations, Class anClass) { + for ( Annotation annotation : annotations ) { + if ( anClass.isInstance( annotation ) ) { + return (T) annotation; + } + } + return null; + } } diff --git a/hibernate-core/src/main/java/org/hibernate/usertype/DynamicParameterizedType.java b/hibernate-core/src/main/java/org/hibernate/usertype/DynamicParameterizedType.java new file mode 100644 index 0000000000..6b072c3611 --- /dev/null +++ b/hibernate-core/src/main/java/org/hibernate/usertype/DynamicParameterizedType.java @@ -0,0 +1,45 @@ +package org.hibernate.usertype; + +import java.lang.annotation.Annotation; + +/** + * {@inheritDoc} + * + * Types who implements this interface will have in the setParameterValues an + * instance of the class DynamicParameterizedType$ParameterType instead of + * the key PARAMETER_TYPE = "org.hibernate.type.ParameterType" + * + * The interface ParameterType provides some methods to read information + * dynamically for build the type + * + * @author Janario Oliveira + */ +public interface DynamicParameterizedType extends ParameterizedType { + public static final String PARAMETER_TYPE = "org.hibernate.type.ParameterType"; + + public static final String IS_DYNAMIC = "org.hibernate.type.ParameterType.dynamic"; + + public static final String RETURNED_CLASS = "org.hibernate.type.ParameterType.returnedClass"; + public static final String IS_PRIMARY_KEY = "org.hibernate.type.ParameterType.primaryKey"; + public static final String ENTITY = "org.hibernate.type.ParameterType.entityClass"; + public static final String PROPERTY = "org.hibernate.type.ParameterType.propertyName"; + public static final String ACCESS_TYPE = "org.hibernate.type.ParameterType.accessType"; + + public static interface ParameterType { + + public Class getReturnedClass(); + + public Annotation[] getAnnotationsMethod(); + + public String getCatalog(); + + public String getSchema(); + + public String getTable(); + + public boolean isPrimaryKey(); + + public String[] getColumns(); + + } +} diff --git a/hibernate-core/src/main/java/org/jboss/jandex/CompositeIndex.java b/hibernate-core/src/main/java/org/jboss/jandex/CompositeIndex.java deleted file mode 100644 index d0a7fcac9f..0000000000 --- a/hibernate-core/src/main/java/org/jboss/jandex/CompositeIndex.java +++ /dev/null @@ -1,186 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2012, 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.jboss.jandex; - -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; - -/** - * Aggregates information from multiple {@link Index} instances. - * - * @author John Bailey - * @author Steve Ebersole - */ -public class CompositeIndex implements IndexResult { - private final List indexes; - - public CompositeIndex(Index... indexes) { - this( Arrays.asList( indexes ) ); - } - - public CompositeIndex(List indexes) { - this.indexes = indexes; - } - - @Override - public Collection getAnnotations(DotName annotationName) { - final Set allInstances = new HashSet(); - for (Index index : indexes) { - copy( index.getAnnotations( annotationName ), allInstances ); - } - return Collections.unmodifiableSet( allInstances ); - } - - private void copy(Collection source, Collection target) { - if ( source != null ) { - target.addAll( source ); - } - } - - @Override - public Collection getKnownClasses() { - final List allKnown = new ArrayList(); - for ( Index index : indexes ) { - copy( index.getKnownClasses(), allKnown ); - } - return Collections.unmodifiableCollection( allKnown ); - } - - @Override - public ClassInfo getClassByName(DotName className) { - for ( Index index : indexes ) { - final ClassInfo info = index.getClassByName( className ); - if ( info != null ) { - return info; - } - } - return null; - } - - @Override - public Collection getKnownDirectSubclasses(DotName className) { - final Set allKnown = new HashSet(); - for ( Index index : indexes ) { - copy( index.getKnownDirectSubclasses( className ), allKnown ); - } - return Collections.unmodifiableSet( allKnown ); - } - - @Override - public Set getAllKnownSubclasses(final DotName className) { - final Set allKnown = new HashSet(); - final Set processedClasses = new HashSet(); - getAllKnownSubClasses(className, allKnown, processedClasses); - return allKnown; - } - - private void getAllKnownSubClasses(DotName className, Set allKnown, Set processedClasses) { - final Set subClassesToProcess = new HashSet(); - subClassesToProcess.add(className); - while (!subClassesToProcess.isEmpty()) { - final Iterator toProcess = subClassesToProcess.iterator(); - DotName name = toProcess.next(); - toProcess.remove(); - processedClasses.add(name); - getAllKnownSubClasses(name, allKnown, subClassesToProcess, processedClasses); - } - } - - private void getAllKnownSubClasses( - DotName name, - Set allKnown, - Set subClassesToProcess, - Set processedClasses) { - for ( Index index : indexes ) { - final Collection list = index.getKnownDirectSubclasses( name ); - if ( list != null ) { - for ( final ClassInfo clazz : list ) { - final DotName className = clazz.name(); - if ( !processedClasses.contains( className ) ) { - allKnown.add( clazz ); - subClassesToProcess.add( className ); - } - } - } - } - } - - @Override - public Collection getKnownDirectImplementors(DotName className) { - final Set allKnown = new HashSet(); - for ( Index index : indexes ) { - copy( index.getKnownDirectImplementors( className ), allKnown ); - } - return Collections.unmodifiableSet(allKnown); } - - @Override - public Collection getAllKnownImplementors(DotName interfaceName) { - final Set allKnown = new HashSet(); - final Set subInterfacesToProcess = new HashSet(); - final Set processedClasses = new HashSet(); - subInterfacesToProcess.add( interfaceName ); - while ( !subInterfacesToProcess.isEmpty() ) { - final Iterator toProcess = subInterfacesToProcess.iterator(); - DotName name = toProcess.next(); - toProcess.remove(); - processedClasses.add( name ); - getKnownImplementors( name, allKnown, subInterfacesToProcess, processedClasses ); - } - return allKnown; - } - - private void getKnownImplementors( - DotName name, - Set allKnown, - Set subInterfacesToProcess, - Set processedClasses) { - for (Index index : indexes) { - final List list = index.getKnownDirectImplementors(name); - if (list != null) { - for (final ClassInfo clazz : list) { - final DotName className = clazz.name(); - if (!processedClasses.contains(className)) { - if ( Modifier.isInterface( clazz.flags() )) { - subInterfacesToProcess.add(className); - } - else { - if (!allKnown.contains(clazz)) { - allKnown.add(clazz); - processedClasses.add(className); - getAllKnownSubClasses(className, allKnown, processedClasses); - } - } - } - } - } - } - } -} diff --git a/hibernate-core/src/test/java/org/hibernate/cache/spi/NaturalIdCacheKeyTest.java b/hibernate-core/src/test/java/org/hibernate/cache/spi/NaturalIdCacheKeyTest.java new file mode 100644 index 0000000000..7f32b1ce23 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/cache/spi/NaturalIdCacheKeyTest.java @@ -0,0 +1,96 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.cache.spi; + +import static junit.framework.Assert.assertEquals; +import static org.junit.Assert.assertArrayEquals; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; + +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.engine.spi.SessionImplementor; +import org.hibernate.persister.entity.EntityPersister; +import org.hibernate.type.Type; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +public class NaturalIdCacheKeyTest { + @Test + public void testSerializationRoundTrip() throws Exception { + final EntityPersister entityPersister = mock(EntityPersister.class); + final SessionImplementor sessionImplementor = mock(SessionImplementor.class); + final SessionFactoryImplementor sessionFactoryImplementor = mock(SessionFactoryImplementor.class); + final Type mockType = mock(Type.class); + + when (entityPersister.getRootEntityName()).thenReturn("EntityName"); + + when(sessionImplementor.getFactory()).thenReturn(sessionFactoryImplementor); + + when(entityPersister.getNaturalIdentifierProperties()).thenReturn(new int[] {0, 1, 2}); + when(entityPersister.getPropertyTypes()).thenReturn(new Type[] { + mockType, + mockType, + mockType + }); + + when(mockType.getHashCode(anyObject(), eq(sessionFactoryImplementor))).thenAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return invocation.getArguments()[0].hashCode(); + } + }); + + when(mockType.disassemble(anyObject(), eq(sessionImplementor), eq(null))).thenAnswer(new Answer() { + @Override + public Object answer(InvocationOnMock invocation) throws Throwable { + return invocation.getArguments()[0]; + } + }); + + final NaturalIdCacheKey key = new NaturalIdCacheKey(new Object[] {"a", "b", "c"}, entityPersister, sessionImplementor); + + final ByteArrayOutputStream baos = new ByteArrayOutputStream(); + final ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(key); + + final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); + final NaturalIdCacheKey keyClone = (NaturalIdCacheKey)ois.readObject(); + + assertEquals(key, keyClone); + assertEquals(key.hashCode(), keyClone.hashCode()); + assertEquals(key.toString(), keyClone.toString()); + assertEquals(key.getEntityName(), keyClone.getEntityName()); + assertArrayEquals(key.getNaturalIdValues(), keyClone.getNaturalIdValues()); + assertEquals(key.getTenantId(), keyClone.getTenantId()); + + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/metamodel/spi/binding/AbstractBasicBindingTests.java b/hibernate-core/src/test/java/org/hibernate/metamodel/spi/binding/AbstractBasicBindingTests.java index af4cd898fc..cdfc022c83 100644 --- a/hibernate-core/src/test/java/org/hibernate/metamodel/spi/binding/AbstractBasicBindingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/metamodel/spi/binding/AbstractBasicBindingTests.java @@ -29,6 +29,7 @@ import java.util.List; import org.hibernate.FetchMode; import org.hibernate.engine.FetchTiming; import org.hibernate.engine.spi.CascadeStyle; +import org.hibernate.engine.spi.CascadeStyles; import org.hibernate.metamodel.MetadataSources; import org.hibernate.metamodel.internal.MetadataImpl; import org.hibernate.metamodel.spi.MetadataImplementor; @@ -170,7 +171,7 @@ public abstract class AbstractBasicBindingTests extends BaseUnitTestCase { ManyToOneAttributeBinding manyToOneAttributeBinding = (ManyToOneAttributeBinding) attributeBinding; assertEquals( referencedEntityName, manyToOneAttributeBinding.getReferencedEntityName() ); assertSame( referencedEntityBinding, manyToOneAttributeBinding.getReferencedEntityBinding() ); - assertSame( CascadeStyle.NONE, manyToOneAttributeBinding.getCascadeStyle() ); + assertSame( CascadeStyles.NONE, manyToOneAttributeBinding.getCascadeStyle() ); assertTrue( manyToOneAttributeBinding.isLazy() ); assertSame( FetchMode.SELECT, manyToOneAttributeBinding.getFetchMode() ); assertSame( FetchTiming.DELAYED, manyToOneAttributeBinding.getFetchTiming() ); diff --git a/hibernate-core/src/test/java/org/hibernate/sharedSession/SessionWithSharedConnectionTest.java b/hibernate-core/src/test/java/org/hibernate/sharedSession/SessionWithSharedConnectionTest.java index 84f127880a..72578949b2 100644 --- a/hibernate-core/src/test/java/org/hibernate/sharedSession/SessionWithSharedConnectionTest.java +++ b/hibernate-core/src/test/java/org/hibernate/sharedSession/SessionWithSharedConnectionTest.java @@ -34,6 +34,8 @@ import org.hibernate.IrrelevantEntity; import org.hibernate.Session; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.transaction.spi.TransactionContext; +import org.hibernate.persister.entity.EntityPersister; + import org.hibernate.testing.TestForIssue; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; @@ -241,12 +243,20 @@ public class SessionWithSharedConnectionTest extends BaseCoreFunctionalTestCase EventListenerRegistry eventListenerRegistry = sessionFactory().getServiceRegistry().getService(EventListenerRegistry.class); //register a post commit listener - eventListenerRegistry.appendListeners( EventType.POST_COMMIT_INSERT, new PostInsertEventListener(){ - @Override - public void onPostInsert(PostInsertEvent event) { - ((IrrelevantEntity) event.getEntity()).setName( postCommitMessage ); - } - }); + eventListenerRegistry.appendListeners( + EventType.POST_COMMIT_INSERT, + new PostInsertEventListener() { + @Override + public void onPostInsert(PostInsertEvent event) { + ((IrrelevantEntity) event.getEntity()).setName( postCommitMessage ); + } + + @Override + public boolean requiresPostCommitHanding(EntityPersister persister) { + return true; + } + } + ); session.getTransaction().begin(); diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/entity/HibernateAnnotationMappingTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/entity/HibernateAnnotationMappingTest.java new file mode 100644 index 0000000000..6200ce36e6 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/entity/HibernateAnnotationMappingTest.java @@ -0,0 +1,57 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2009, Red Hat, Inc. and/or its affiliates 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.test.annotations.entity; + +import static org.junit.Assert.fail; + +import java.util.ConcurrentModificationException; + +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.junit.Test; + +/** + * @author Guenther Demetz + */ +public class HibernateAnnotationMappingTest extends BaseUnitTestCase { + + @Test + @TestForIssue( jiraKey = "HHH-7446" ) + public void testUniqueConstraintAnnotationOnNaturalIds() throws Exception { + Configuration configuration = new Configuration(); + configuration.setProperty( Environment.HBM2DDL_AUTO, "create-drop" ); + configuration.addAnnotatedClass(Month.class); + SessionFactory sf = null; + try { + sf = configuration.buildSessionFactory(); + sf.close(); + } + catch (ConcurrentModificationException e) { + fail(e.toString()); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/entity/Month.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/entity/Month.java new file mode 100644 index 0000000000..b9b863e6a9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/entity/Month.java @@ -0,0 +1,63 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2009, Red Hat, Inc. and/or its affiliates 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.test.annotations.entity; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +import org.hibernate.annotations.NaturalId; + +/** + * @author Guenther Demetz + */ +@Entity +@Table(uniqueConstraints = {@UniqueConstraint(columnNames={"year", "month"})}) +// Remark: Without line above, hibernate creates the combined unique index as follows: unique (month, year) +// It seems that hibernate orders the attributes in alphabetic order +// We indeed want to have the inverted sequence: year, month +// In this way queries with only year in the where-clause can take advantage of this index +// N.B.: Usually a user defines a combined index beginning with the most discriminating property +public class Month { + + @Id @GeneratedValue + private int id; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @NaturalId + private int year; + + @NaturalId + private int month; + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/EntityEnum.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/EntityEnum.java new file mode 100644 index 0000000000..8016c10abc --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/EntityEnum.java @@ -0,0 +1,93 @@ +package org.hibernate.test.annotations.enumerated; + +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import org.hibernate.annotations.Type; +import org.hibernate.annotations.TypeDef; +import org.hibernate.annotations.TypeDefs; + +/** + * @author Janario Oliveira + */ +@Entity +@TypeDefs({ @TypeDef(typeClass = LastNumberType.class, defaultForType = EntityEnum.LastNumber.class) }) +public class EntityEnum { + + enum Common { + + A1, A2, B1, B2 + } + + enum FirstLetter { + + A_LETTER, B_LETTER, C_LETTER + } + + enum LastNumber { + + NUMBER_1, NUMBER_2, NUMBER_3 + } + + @Id + @GeneratedValue + private long id; + private Common ordinal; + @Enumerated(EnumType.STRING) + private Common string; + @Type(type = "org.hibernate.test.annotations.enumerated.FirstLetterType") + private FirstLetter firstLetter; + private LastNumber lastNumber; + @Enumerated(EnumType.STRING) + private LastNumber explicitOverridingImplicit; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public Common getOrdinal() { + return ordinal; + } + + public void setOrdinal(Common ordinal) { + this.ordinal = ordinal; + } + + public Common getString() { + return string; + } + + public void setString(Common string) { + this.string = string; + } + + public FirstLetter getFirstLetter() { + return firstLetter; + } + + public void setFirstLetter(FirstLetter firstLetter) { + this.firstLetter = firstLetter; + } + + public LastNumber getLastNumber() { + return lastNumber; + } + + public void setLastNumber(LastNumber lastNumber) { + this.lastNumber = lastNumber; + } + + public LastNumber getExplicitOverridingImplicit() { + return explicitOverridingImplicit; + } + + public void setExplicitOverridingImplicit(LastNumber explicitOverridingImplicit) { + this.explicitOverridingImplicit = explicitOverridingImplicit; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/EnumeratedTypeTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/EnumeratedTypeTest.java new file mode 100644 index 0000000000..4c96a0d3c4 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/EnumeratedTypeTest.java @@ -0,0 +1,337 @@ +package org.hibernate.test.annotations.enumerated; + +import java.io.Serializable; +import org.hibernate.Session; +import org.hibernate.cfg.Configuration; +import org.hibernate.criterion.Restrictions; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.testing.FailureExpectedWithNewMetamodel; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; +import org.hibernate.type.EnumType; +import org.hibernate.type.Type; +import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.hibernate.test.annotations.enumerated.EntityEnum.*; + +/** + * Test type definition for enum + * + * @author Janario Oliveira + */ +@FailureExpectedWithNewMetamodel +public class EnumeratedTypeTest extends BaseCoreFunctionalTestCase { + + @Test + public void testTypeDefinition() { + Configuration cfg = configuration(); + PersistentClass pc = cfg.getClassMapping( EntityEnum.class.getName() ); + + // ordinal default of EnumType + Type ordinalEnum = pc.getProperty( "ordinal" ).getType(); + assertEquals( Common.class, ordinalEnum.getReturnedClass() ); + assertEquals( EnumType.class.getName(), ordinalEnum.getName() ); + + // string defined by Enumerated(STRING) + Type stringEnum = pc.getProperty( "string" ).getType(); + assertEquals( Common.class, stringEnum.getReturnedClass() ); + assertEquals( EnumType.class.getName(), stringEnum.getName() ); + + // explicit defined by @Type + Type first = pc.getProperty( "firstLetter" ).getType(); + assertEquals( FirstLetter.class, first.getReturnedClass() ); + assertEquals( FirstLetterType.class.getName(), first.getName() ); + + // implicit defined by @TypeDef in somewhere + Type last = pc.getProperty( "lastNumber" ).getType(); + assertEquals( LastNumber.class, last.getReturnedClass() ); + assertEquals( LastNumberType.class.getName(), last.getName() ); + + // implicit defined by @TypeDef in anywhere, but overrided by Enumerated(STRING) + Type implicitOverrideExplicit = pc.getProperty( "explicitOverridingImplicit" ).getType(); + assertEquals( LastNumber.class, implicitOverrideExplicit.getReturnedClass() ); + assertEquals( EnumType.class.getName(), implicitOverrideExplicit.getName() ); + } + + @Test + public void testTypeQuery() { + Session session = openSession(); + session.getTransaction().begin(); + + // persist + EntityEnum entityEnum = new EntityEnum(); + entityEnum.setOrdinal( Common.A2 ); + Serializable id = session.save( entityEnum ); + + session.getTransaction().commit(); + session.close(); + session = openSession(); + session.getTransaction().begin(); + + // find + entityEnum = (EntityEnum) session.createQuery( "from EntityEnum ee where ee.ordinal=1" ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( Common.A2, entityEnum.getOrdinal() ); + // find parameter + entityEnum = (EntityEnum) session.createQuery( "from EntityEnum ee where ee.ordinal=:ordinal" ) + .setParameter( "ordinal", Common.A2 ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( Common.A2, entityEnum.getOrdinal() ); + // delete + assertEquals( 1, session.createSQLQuery( "DELETE FROM EntityEnum where ordinal=1" ).executeUpdate() ); + + session.getTransaction().commit(); + session.close(); + + // ************** + session = openSession(); + session.getTransaction().begin(); + + // persist + entityEnum = new EntityEnum(); + entityEnum.setString( Common.B1 ); + id = session.save( entityEnum ); + + session.getTransaction().commit(); + session.close(); + session = openSession(); + session.getTransaction().begin(); + + // find + entityEnum = (EntityEnum) session.createQuery( "from EntityEnum ee where ee.string='B1'" ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( Common.B1, entityEnum.getString() ); + // find parameter + entityEnum = (EntityEnum) session.createQuery( "from EntityEnum ee where ee.string=:string" ) + .setParameter( "string", Common.B1 ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( Common.B1, entityEnum.getString() ); + // delete + assertEquals( 1, session.createSQLQuery( "DELETE FROM EntityEnum where string='B1'" ).executeUpdate() ); + session.getTransaction().commit(); + session.close(); + + // ************** + session = openSession(); + session.getTransaction().begin(); + + // persist + entityEnum = new EntityEnum(); + entityEnum.setFirstLetter( FirstLetter.C_LETTER ); + id = session.save( entityEnum ); + + session.getTransaction().commit(); + session.close(); + session = openSession(); + session.getTransaction().begin(); + + // find + entityEnum = (EntityEnum) session.createQuery( "from EntityEnum ee where ee.firstLetter='C'" ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( FirstLetter.C_LETTER, entityEnum.getFirstLetter() ); + // find parameter + entityEnum = (EntityEnum) session.createQuery( "from EntityEnum ee where ee.firstLetter=:firstLetter" ) + .setParameter( "firstLetter", FirstLetter.C_LETTER ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( FirstLetter.C_LETTER, entityEnum.getFirstLetter() ); + // delete + assertEquals( 1, session.createSQLQuery( "DELETE FROM EntityEnum where firstLetter='C'" ).executeUpdate() ); + + session.getTransaction().commit(); + session.close(); + + // ************** + session = openSession(); + session.getTransaction().begin(); + + // persist + entityEnum = new EntityEnum(); + entityEnum.setLastNumber( LastNumber.NUMBER_1 ); + id = session.save( entityEnum ); + + session.getTransaction().commit(); + session.close(); + session = openSession(); + session.getTransaction().begin(); + + // find + entityEnum = (EntityEnum) session.createQuery( "from EntityEnum ee where ee.lastNumber='1'" ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( LastNumber.NUMBER_1, entityEnum.getLastNumber() ); + // find parameter + entityEnum = (EntityEnum) session.createQuery( "from EntityEnum ee where ee.lastNumber=:lastNumber" ) + .setParameter( "lastNumber", LastNumber.NUMBER_1 ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( LastNumber.NUMBER_1, entityEnum.getLastNumber() ); + // delete + assertEquals( 1, session.createSQLQuery( "DELETE FROM EntityEnum where lastNumber='1'" ).executeUpdate() ); + + session.getTransaction().commit(); + session.close(); + + // ************** + session = openSession(); + session.getTransaction().begin(); + + // persist + entityEnum = new EntityEnum(); + entityEnum.setExplicitOverridingImplicit( LastNumber.NUMBER_2 ); + id = session.save( entityEnum ); + + session.getTransaction().commit(); + session.close(); + session = openSession(); + session.getTransaction().begin(); + + // find + entityEnum = (EntityEnum) session.createQuery( + "from EntityEnum ee where ee.explicitOverridingImplicit='NUMBER_2'" ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( LastNumber.NUMBER_2, entityEnum.getExplicitOverridingImplicit() ); + // find parameter + entityEnum = (EntityEnum) session + .createQuery( "from EntityEnum ee where ee.explicitOverridingImplicit=:override" ) + .setParameter( "override", LastNumber.NUMBER_2 ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( LastNumber.NUMBER_2, entityEnum.getExplicitOverridingImplicit() ); + // delete + assertEquals( 1, session.createSQLQuery( "DELETE FROM EntityEnum where explicitOverridingImplicit='NUMBER_2'" ) + .executeUpdate() ); + + session.getTransaction().commit(); + session.close(); + } + + @Test + public void testTypeCriteria() { + Session session = openSession(); + session.getTransaction().begin(); + + // persist + EntityEnum entityEnum = new EntityEnum(); + entityEnum.setOrdinal( Common.A1 ); + Serializable id = session.save( entityEnum ); + + session.getTransaction().commit(); + session.close(); + session = openSession(); + session.getTransaction().begin(); + + // find + entityEnum = (EntityEnum) session.createCriteria( EntityEnum.class ) + .add( Restrictions.eq( "ordinal", Common.A1 ) ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( Common.A1, entityEnum.getOrdinal() ); + // delete + assertEquals( 1, session.createSQLQuery( "DELETE FROM EntityEnum where ordinal=0" ).executeUpdate() ); + + session.getTransaction().commit(); + session.close(); + + // ************** + session = openSession(); + session.getTransaction().begin(); + + // persist + entityEnum = new EntityEnum(); + entityEnum.setString( Common.B2 ); + id = session.save( entityEnum ); + + session.getTransaction().commit(); + session.close(); + session = openSession(); + session.getTransaction().begin(); + + // find + entityEnum = (EntityEnum) session.createCriteria( EntityEnum.class ) + .add( Restrictions.eq( "string", Common.B2 ) ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( Common.B2, entityEnum.getString() ); + // delete + assertEquals( 1, session.createSQLQuery( "DELETE FROM EntityEnum where string='B2'" ).executeUpdate() ); + + session.getTransaction().commit(); + session.close(); + + // ************** + session = openSession(); + session.getTransaction().begin(); + + // persist + entityEnum = new EntityEnum(); + entityEnum.setFirstLetter( FirstLetter.A_LETTER ); + id = session.save( entityEnum ); + + session.getTransaction().commit(); + session.close(); + session = openSession(); + session.getTransaction().begin(); + + // find + entityEnum = (EntityEnum) session.createCriteria( EntityEnum.class ) + .add( Restrictions.eq( "firstLetter", FirstLetter.A_LETTER ) ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( FirstLetter.A_LETTER, entityEnum.getFirstLetter() ); + // delete + assertEquals( 1, session.createSQLQuery( "DELETE FROM EntityEnum where firstLetter='A'" ).executeUpdate() ); + + session.getTransaction().commit(); + session.close(); + + // ************** + session = openSession(); + session.getTransaction().begin(); + + // persist + entityEnum = new EntityEnum(); + entityEnum.setLastNumber( LastNumber.NUMBER_3 ); + id = session.save( entityEnum ); + + session.getTransaction().commit(); + session.close(); + session = openSession(); + session.getTransaction().begin(); + + // find + entityEnum = (EntityEnum) session.createCriteria( EntityEnum.class ) + .add( Restrictions.eq( "lastNumber", LastNumber.NUMBER_3 ) ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( LastNumber.NUMBER_3, entityEnum.getLastNumber() ); + // delete + assertEquals( 1, session.createSQLQuery( "DELETE FROM EntityEnum where lastNumber='3'" ).executeUpdate() ); + + session.getTransaction().commit(); + session.close(); + + // ************** + session = openSession(); + session.getTransaction().begin(); + + // persist + entityEnum = new EntityEnum(); + entityEnum.setExplicitOverridingImplicit( LastNumber.NUMBER_2 ); + id = session.save( entityEnum ); + + session.getTransaction().commit(); + session.close(); + session = openSession(); + session.getTransaction().begin(); + + // find + entityEnum = (EntityEnum) session.createCriteria( EntityEnum.class ) + .add( Restrictions.eq( "explicitOverridingImplicit", LastNumber.NUMBER_2 ) ).uniqueResult(); + assertEquals( id, entityEnum.getId() ); + assertEquals( LastNumber.NUMBER_2, entityEnum.getExplicitOverridingImplicit() ); + // delete + assertEquals( 1, session.createSQLQuery( "DELETE FROM EntityEnum where explicitOverridingImplicit='NUMBER_2'" ) + .executeUpdate() ); + + session.getTransaction().commit(); + session.close(); + + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { EntityEnum.class }; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/FirstLetterType.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/FirstLetterType.java new file mode 100644 index 0000000000..83ab61f2ee --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/FirstLetterType.java @@ -0,0 +1,41 @@ +package org.hibernate.test.annotations.enumerated; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SessionImplementor; + +/** + * @author Janario Oliveira + */ +public class FirstLetterType extends org.hibernate.type.EnumType { + + @Override + public int[] sqlTypes() { + return new int[] { Types.VARCHAR }; + } + + @Override + public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) + throws HibernateException, SQLException { + String persistValue = (String) rs.getObject( names[0] ); + if ( rs.wasNull() ) { + return null; + } + return Enum.valueOf( returnedClass(), persistValue + "_LETTER" ); + } + + @Override + public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) + throws HibernateException, SQLException { + if ( value == null ) { + st.setNull( index, sqlTypes()[0] ); + } + else { + String enumString = ( (Enum) value ).name(); + st.setObject( index, enumString.charAt( 0 ), sqlTypes()[0] ); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/LastNumberType.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/LastNumberType.java new file mode 100644 index 0000000000..65e38525f9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/enumerated/LastNumberType.java @@ -0,0 +1,42 @@ +package org.hibernate.test.annotations.enumerated; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Types; +import org.hibernate.HibernateException; +import org.hibernate.engine.spi.SessionImplementor; + +/** + * @author Janario Oliveira + */ +public class LastNumberType extends org.hibernate.type.EnumType { + + @Override + public int[] sqlTypes() { + return new int[] { Types.VARCHAR }; + } + + @Override + public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) + throws HibernateException, SQLException { + String persistValue = (String) rs.getObject( names[0] ); + if ( rs.wasNull() ) { + return null; + } + return Enum.valueOf( returnedClass(), "NUMBER_" + persistValue ); + } + + @Override + public void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) + throws HibernateException, SQLException { + if ( value == null ) { + st.setNull( index, sqlTypes()[0] ); + } + else { + + String enumString = ( (Enum) value ).name(); + st.setObject( index, enumString.charAt( enumString.length() - 1 ), sqlTypes()[0] ); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/id/sequences/HibernateSequenceTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/id/sequences/HibernateSequenceTest.java index ce627162dd..b4c21e73bd 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/id/sequences/HibernateSequenceTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/id/sequences/HibernateSequenceTest.java @@ -35,9 +35,11 @@ public class HibernateSequenceTest extends BaseCoreFunctionalTestCase { protected void configure(Configuration cfg) { super.configure( cfg ); cfg.addResource( "org/hibernate/test/annotations/id/sequences/orm.xml" ); - cfg.setProperty( - Environment.URL, cfg.getProperty( Environment.URL ) + ";INIT=CREATE SCHEMA IF NOT EXISTS " + SCHEMA_NAME - ); + } + + @Override + protected String createSecondSchema() { + return SCHEMA_NAME; } @Test diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/override/AssociationOverrideSchemaTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/override/AssociationOverrideSchemaTest.java new file mode 100644 index 0000000000..99d1c78d5c --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/override/AssociationOverrideSchemaTest.java @@ -0,0 +1,60 @@ +package org.hibernate.test.annotations.override; + +import java.util.Iterator; + +import org.junit.Assert; +import org.junit.Test; + +import org.hibernate.dialect.H2Dialect; +import org.hibernate.mapping.Table; +import org.hibernate.test.util.SchemaUtil; +import org.hibernate.testing.FailureExpectedWithNewMetamodel; +import org.hibernate.testing.RequiresDialect; +import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@RequiresDialect({ H2Dialect.class }) +@TestForIssue(jiraKey = "HHH-6662") +@FailureExpectedWithNewMetamodel +public class AssociationOverrideSchemaTest extends BaseCoreFunctionalTestCase { + public static final String SCHEMA_NAME = "OTHER_SCHEMA"; + public static final String TABLE_NAME = "BLOG_TAGS"; + public static final String ID_COLUMN_NAME = "BLOG_ID"; + public static final String VALUE_COLUMN_NAME = "BLOG_TAG"; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { Entry.class, BlogEntry.class }; + } + + @Override + protected String createSecondSchema() { + return SCHEMA_NAME; + } + + @Test + public void testJoinTableSchemaName() { + Iterator

tableIterator = configuration().getTableMappings(); + while ( tableIterator.hasNext() ) { + Table table = tableIterator.next(); + if ( TABLE_NAME.equals( table.getName() ) ) { + Assert.assertEquals( SCHEMA_NAME, table.getSchema() ); + return; + } + } + Assert.fail(); + } + + @Test + public void testJoinTableJoinColumnName() { + Assert.assertTrue( SchemaUtil.isColumnPresent( TABLE_NAME, ID_COLUMN_NAME, configuration() ) ); + } + + @Test + public void testJoinTableColumnName() { + Assert.assertTrue( SchemaUtil.isColumnPresent( TABLE_NAME, VALUE_COLUMN_NAME, configuration() ) ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/override/BlogEntry.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/override/BlogEntry.java new file mode 100644 index 0000000000..2378f5374d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/override/BlogEntry.java @@ -0,0 +1,56 @@ +package org.hibernate.test.annotations.override; + +import javax.persistence.AssociationOverride; +import javax.persistence.AttributeOverride; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.Table; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +@Table(schema = AssociationOverrideSchemaTest.SCHEMA_NAME) +@AssociationOverride(name = "tags", + joinTable = @JoinTable(name = AssociationOverrideSchemaTest.TABLE_NAME, + joinColumns = @JoinColumn(name = AssociationOverrideSchemaTest.ID_COLUMN_NAME), + schema = AssociationOverrideSchemaTest.SCHEMA_NAME)) +@AttributeOverride(name = "tags", column = @Column(name = AssociationOverrideSchemaTest.VALUE_COLUMN_NAME)) +public class BlogEntry extends Entry { + private String text; + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof BlogEntry ) ) return false; + if ( !super.equals( o ) ) return false; + + BlogEntry blogEntry = (BlogEntry) o; + + if ( text != null ? !text.equals( blogEntry.text ) : blogEntry.text != null ) return false; + + return true; + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + ( text != null ? text.hashCode() : 0 ); + return result; + } + + @Override + public String toString() { + return "BlogEntry(" + super.toString() + ", text = " + text + ")"; + } + + public String getText() { + return text; + } + + public void setText(String text) { + this.text = text; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/override/Entry.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/override/Entry.java new file mode 100644 index 0000000000..5dc80580fe --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/override/Entry.java @@ -0,0 +1,73 @@ +package org.hibernate.test.annotations.override; + +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.MappedSuperclass; + +import org.hibernate.annotations.Fetch; +import org.hibernate.annotations.FetchMode; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@MappedSuperclass +public abstract class Entry implements Serializable { + @Id + @GeneratedValue + private Long id; + + @ElementCollection(fetch = FetchType.EAGER) + @JoinTable(name = "TAGS", joinColumns = @JoinColumn(name = "ID")) + @Column(name = "KEYWORD") + @Fetch(FetchMode.JOIN) + private Set tags = new HashSet(); + + @Override + public boolean equals(Object o) { + if ( this == o ) return true; + if ( !( o instanceof Entry ) ) return false; + + Entry entry = (Entry) o; + + if ( id != null ? !id.equals( entry.id ) : entry.id != null ) return false; + if ( tags != null ? !tags.equals( entry.tags ) : entry.tags != null ) return false; + + return true; + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + ( tags != null ? tags.hashCode() : 0 ); + return result; + } + + @Override + public String toString() { + return "Entry(id = " + id + ", tags = " + tags + ")"; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Set getTags() { + return tags; + } + + public void setTags(Set tags) { + this.tags = tags; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/referencedcolumnname/HousePlaces.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/referencedcolumnname/HousePlaces.java new file mode 100644 index 0000000000..125c5b56b9 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/referencedcolumnname/HousePlaces.java @@ -0,0 +1,29 @@ +package org.hibernate.test.annotations.referencedcolumnname; + +import javax.persistence.AssociationOverride; +import javax.persistence.AssociationOverrides; +import javax.persistence.Embedded; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; + +/** + * @author Janario Oliveira + */ +@Entity +public class HousePlaces { + + @Id + @GeneratedValue + int id; + @Embedded + Places places; + @Embedded + @AssociationOverrides({ + @AssociationOverride(name = "livingRoom", joinColumns = { + @JoinColumn(name = "NEIGHBOUR_LIVINGROOM", referencedColumnName = "NAME"), + @JoinColumn(name = "NEIGHBOUR_LIVINGROOM_OWNER", referencedColumnName = "OWNER") }), + @AssociationOverride(name = "kitchen", joinColumns = @JoinColumn(name = "NEIGHBOUR_KITCHEN", referencedColumnName = "NAME")) }) + Places neighbourPlaces; +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/referencedcolumnname/Place.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/referencedcolumnname/Place.java new file mode 100644 index 0000000000..ac9977773f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/referencedcolumnname/Place.java @@ -0,0 +1,21 @@ +package org.hibernate.test.annotations.referencedcolumnname; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; + +/** + * @author Janario Oliveira + */ +@Entity +public class Place { + + @Id + @GeneratedValue + int id; + @Column(name = "NAME") + String name; + @Column(name = "OWNER") + String owner; +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/referencedcolumnname/Places.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/referencedcolumnname/Places.java new file mode 100644 index 0000000000..ccd1dd074b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/referencedcolumnname/Places.java @@ -0,0 +1,22 @@ +package org.hibernate.test.annotations.referencedcolumnname; + +import javax.persistence.CascadeType; +import javax.persistence.Embeddable; +import javax.persistence.JoinColumn; +import javax.persistence.JoinColumns; +import javax.persistence.ManyToOne; + +/** + * @author Janario Oliveira + */ +@Embeddable +public class Places { + + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumns({ @JoinColumn(name = "LIVING_ROOM", referencedColumnName = "NAME"), + @JoinColumn(name = "LIVING_ROOM_OWNER", referencedColumnName = "OWNER") }) + Place livingRoom; + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "KITCHEN", referencedColumnName = "NAME") + Place kitchen; +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/annotations/referencedcolumnname/ReferencedColumnNameTest.java b/hibernate-core/src/test/java/org/hibernate/test/annotations/referencedcolumnname/ReferencedColumnNameTest.java index a69c643da8..0b3b8edcd2 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/annotations/referencedcolumnname/ReferencedColumnNameTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/annotations/referencedcolumnname/ReferencedColumnNameTest.java @@ -31,6 +31,7 @@ import org.junit.Test; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.testing.FailureExpectedWithNewMetamodel; +import org.hibernate.criterion.Restrictions; import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; import static org.junit.Assert.assertEquals; @@ -208,6 +209,68 @@ public class ReferencedColumnNameTest extends BaseCoreFunctionalTestCase { s.close(); } + @Test + public void testManyToOneInsideComponentReferencedColumn() { + HousePlaces house = new HousePlaces(); + house.places = new Places(); + + house.places.livingRoom = new Place(); + house.places.livingRoom.name = "First"; + house.places.livingRoom.owner = "mine"; + + house.places.kitchen = new Place(); + house.places.kitchen.name = "Kitchen 1"; + + house.neighbourPlaces = new Places(); + house.neighbourPlaces.livingRoom = new Place(); + house.neighbourPlaces.livingRoom.name = "Neighbour"; + house.neighbourPlaces.livingRoom.owner = "his"; + + house.neighbourPlaces.kitchen = new Place(); + house.neighbourPlaces.kitchen.name = "His Kitchen"; + + Session s = openSession(); + Transaction tx = s.beginTransaction(); + s.save( house ); + s.flush(); + + HousePlaces get = (HousePlaces) s.get( HousePlaces.class, house.id ); + assertEquals( house.id, get.id ); + + HousePlaces uniqueResult = (HousePlaces) s.createQuery( "from HousePlaces h where h.places.livingRoom.name='First'" ) + .uniqueResult(); + assertNotNull( uniqueResult ); + assertEquals( uniqueResult.places.livingRoom.name, "First" ); + assertEquals( uniqueResult.places.livingRoom.owner, "mine" ); + + uniqueResult = (HousePlaces) s.createQuery( "from HousePlaces h where h.places.livingRoom.owner=:owner" ) + .setParameter( "owner", "mine" ).uniqueResult(); + assertNotNull( uniqueResult ); + assertEquals( uniqueResult.places.livingRoom.name, "First" ); + assertEquals( uniqueResult.places.livingRoom.owner, "mine" ); + + assertNotNull( s.createCriteria( HousePlaces.class ).add( Restrictions.eq( "places.livingRoom.owner", "mine" ) ) + .uniqueResult() ); + + // override + uniqueResult = (HousePlaces) s.createQuery( "from HousePlaces h where h.neighbourPlaces.livingRoom.owner='his'" ) + .uniqueResult(); + assertNotNull( uniqueResult ); + assertEquals( uniqueResult.neighbourPlaces.livingRoom.name, "Neighbour" ); + assertEquals( uniqueResult.neighbourPlaces.livingRoom.owner, "his" ); + + uniqueResult = (HousePlaces) s.createQuery( "from HousePlaces h where h.neighbourPlaces.livingRoom.name=:name" ) + .setParameter( "name", "Neighbour" ).uniqueResult(); + assertNotNull( uniqueResult ); + assertEquals( uniqueResult.neighbourPlaces.livingRoom.name, "Neighbour" ); + assertEquals( uniqueResult.neighbourPlaces.livingRoom.owner, "his" ); + + assertNotNull( s.createCriteria( HousePlaces.class ) + .add( Restrictions.eq( "neighbourPlaces.livingRoom.owner", "his" ) ).uniqueResult() ); + + tx.rollback(); + } + @Override protected Class[] getAnnotatedClasses() { return new Class[]{ @@ -221,7 +284,9 @@ public class ReferencedColumnNameTest extends BaseCoreFunctionalTestCase { Item.class, ItemCost.class, Vendor.class, - WarehouseItem.class + WarehouseItem.class, + Place.class, + HousePlaces.class }; } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/BaseClass.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/BaseClass.hbm.xml new file mode 100644 index 0000000000..f002a86bca --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/BaseClass.hbm.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/BaseClass.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/BaseClass.java new file mode 100644 index 0000000000..61ff20ff94 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/BaseClass.java @@ -0,0 +1,23 @@ +package org.hibernate.test.cfg.cache; + +public class BaseClass { + Long id; + String value; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/CacheConfigurationTest.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/CacheConfigurationTest.java new file mode 100644 index 0000000000..ece105aacc --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/CacheConfigurationTest.java @@ -0,0 +1,44 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2009-2011, 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.test.cfg.cache; + +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; +import org.hibernate.testing.junit4.BaseUnitTestCase; +import org.junit.Test; + +/** + * Tests using of cacheable configuration files. + * + * @author Tair Sabirgaliev + */ +public class CacheConfigurationTest extends BaseUnitTestCase { + public static final String CFG_XML = "org/hibernate/test/cfg/cache/hibernate.cfg.xml"; + + @Test + public void testCacheConfiguration() throws Exception { + Configuration cfg = new Configuration().configure(CFG_XML); + SessionFactory sessionFactory = cfg.buildSessionFactory(); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/Item.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/Item.hbm.xml new file mode 100644 index 0000000000..808831f23d --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/Item.hbm.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/Item.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/Item.java new file mode 100644 index 0000000000..97ffa83b4e --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/Item.java @@ -0,0 +1,24 @@ +package org.hibernate.test.cfg.cache; + +public class Item { + Long id; + + String name; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/SubClass.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/SubClass.hbm.xml new file mode 100644 index 0000000000..52fa3c2e7a --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/SubClass.hbm.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/SubClass.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/SubClass.java new file mode 100644 index 0000000000..a53641874b --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/SubClass.java @@ -0,0 +1,17 @@ +package org.hibernate.test.cfg.cache; + +import java.util.HashSet; +import java.util.Set; + +public class SubClass extends BaseClass { + Set items = new HashSet(); + + public Set getItems() { + return items; + } + + public void setItems(Set items) { + this.items = items; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/hibernate.cfg.xml b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/hibernate.cfg.xml new file mode 100644 index 0000000000..b1cb10071f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/cache/hibernate.cfg.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java index d3b84dd235..b61c01f9a0 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java +++ b/hibernate-core/src/test/java/org/hibernate/test/cfg/persister/GoofyPersisterClassProvider.java @@ -420,6 +420,11 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver { return new Object[0]; } + @Override + public Serializable getIdByUniqueKey(Serializable key, String uniquePropertyName, SessionImplementor session) { + throw new UnsupportedOperationException( "not supported" ); + } + @Override public Object getCurrentVersion(Serializable id, SessionImplementor session) throws HibernateException { return null; diff --git a/hibernate-core/src/test/java/org/hibernate/test/jpa/AbstractJPATest.java b/hibernate-core/src/test/java/org/hibernate/test/jpa/AbstractJPATest.java index 1421a49352..df44a13605 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/jpa/AbstractJPATest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/jpa/AbstractJPATest.java @@ -30,6 +30,7 @@ import javax.persistence.EntityNotFoundException; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.event.internal.DefaultAutoFlushEventListener; import org.hibernate.event.internal.DefaultFlushEntityEventListener; @@ -151,7 +152,7 @@ public abstract class AbstractJPATest extends BaseCoreFunctionalTestCase { public static class JPAPersistOnFlushEventListener extends JPAPersistEventListener { @Override protected CascadingAction getCascadeAction() { - return CascadingAction.PERSIST_ON_FLUSH; + return CascadingActions.PERSIST_ON_FLUSH; } } @@ -161,7 +162,7 @@ public abstract class AbstractJPATest extends BaseCoreFunctionalTestCase { @Override protected CascadingAction getCascadingAction() { - return CascadingAction.PERSIST_ON_FLUSH; + return CascadingActions.PERSIST_ON_FLUSH; } @Override @@ -176,7 +177,7 @@ public abstract class AbstractJPATest extends BaseCoreFunctionalTestCase { @Override protected CascadingAction getCascadingAction() { - return CascadingAction.PERSIST_ON_FLUSH; + return CascadingActions.PERSIST_ON_FLUSH; } @Override diff --git a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java index 0d36c2dcbe..3289c7a75d 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java +++ b/hibernate-core/src/test/java/org/hibernate/test/legacy/CustomPersister.java @@ -551,6 +551,11 @@ public class CustomPersister implements EntityPersister { return null; } + @Override + public Serializable getIdByUniqueKey(Serializable key, String uniquePropertyName, SessionImplementor session) { + throw new UnsupportedOperationException( "not supported" ); + } + @Override public boolean[] getPropertyVersionability() { return MUTABILITY; diff --git a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/ConfigurationValidationTest.java b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/ConfigurationValidationTest.java index 2003685ba2..f258c62f57 100644 --- a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/ConfigurationValidationTest.java +++ b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/ConfigurationValidationTest.java @@ -2,13 +2,16 @@ package org.hibernate.test.multitenancy; import org.junit.Test; +import org.hibernate.ConnectionReleaseMode; import org.hibernate.MultiTenancyStrategy; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.service.ServiceRegistryBuilder; +import org.hibernate.service.jdbc.connections.spi.MultiTenantConnectionProvider; import org.hibernate.service.spi.ServiceException; import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.testing.TestForIssue; +import org.hibernate.testing.env.ConnectionProviderBuilder; import org.hibernate.testing.junit4.BaseUnitTestCase; /** @@ -26,4 +29,27 @@ public class ConfigurationValidationTest extends BaseUnitTestCase { .applySettings( cfg.getProperties() ).buildServiceRegistry(); cfg.buildSessionFactory( serviceRegistry ); } + + @Test + public void testReleaseMode() { + Configuration cfg = new Configuration(); + cfg.getProperties().put( Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA ); + cfg.getProperties().put( Environment.RELEASE_CONNECTIONS, ConnectionReleaseMode.AFTER_STATEMENT.name() ); + cfg.buildMappings(); + + ServiceRegistryImplementor serviceRegistry = (ServiceRegistryImplementor) new ServiceRegistryBuilder() + .applySettings( cfg.getProperties() ) + .addService( + MultiTenantConnectionProvider.class, + new TestingConnectionProvider( + new TestingConnectionProvider.NamedConnectionProviderPair( + "acme", + ConnectionProviderBuilder.buildConnectionProvider( "acme" ) + ) + ) + ) + .buildServiceRegistry(); + + cfg.buildSessionFactory( serviceRegistry ); + } } diff --git a/hibernate-core/src/test/java/org/hibernate/test/multitenancy/TestingConnectionProvider.java b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/TestingConnectionProvider.java new file mode 100644 index 0000000000..64d78abae8 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/multitenancy/TestingConnectionProvider.java @@ -0,0 +1,69 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.test.multitenancy; + +import java.util.HashMap; +import java.util.Map; + +import org.hibernate.service.jdbc.connections.spi.AbstractMultiTenantConnectionProvider; +import org.hibernate.service.jdbc.connections.spi.ConnectionProvider; + +/** + * @author Steve Ebersole + */ +public class TestingConnectionProvider extends AbstractMultiTenantConnectionProvider { + private Map connectionProviderMap; + + public TestingConnectionProvider(Map connectionProviderMap) { + this.connectionProviderMap = connectionProviderMap; + } + + public TestingConnectionProvider(NamedConnectionProviderPair... pairs) { + Map map = new HashMap(); + for ( NamedConnectionProviderPair pair : pairs ) { + map.put( pair.name, pair.connectionProvider ); + } + this.connectionProviderMap = map; + } + + @Override + protected ConnectionProvider getAnyConnectionProvider() { + return connectionProviderMap.values().iterator().next(); + } + + @Override + protected ConnectionProvider selectConnectionProvider(String tenantIdentifier) { + return connectionProviderMap.get( tenantIdentifier ); + } + + public static class NamedConnectionProviderPair { + private final String name; + private final ConnectionProvider connectionProvider; + + public NamedConnectionProviderPair(String name, ConnectionProvider connectionProvider) { + this.name = name; + this.connectionProvider = connectionProvider; + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/ondemandload/Inventory.java b/hibernate-core/src/test/java/org/hibernate/test/ondemandload/Inventory.java new file mode 100644 index 0000000000..494df1381f --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/ondemandload/Inventory.java @@ -0,0 +1,102 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.test.ondemandload; + +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +import java.math.BigDecimal; + +import org.hibernate.annotations.GenericGenerator; + +@Entity +public class Inventory { + private int id = -1; + private Store store; + private Product product; + private Long quantity; + private BigDecimal storePrice; + + public Inventory() { + } + + public Inventory(Store store, Product product) { + this.store = store; + this.product = product; + } + + @Id + @GeneratedValue + @GenericGenerator( name = "increment", strategy = "increment" ) + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @ManyToOne + @JoinColumn( name = "store_id" ) + public Store getStore() { + return store; + } + + public Inventory setStore(Store store) { + this.store = store; + return this; + } + + @ManyToOne + @JoinColumn( name = "prod_id" ) + public Product getProduct() { + return product; + } + + public Inventory setProduct(Product product) { + this.product = product; + return this; + } + + public Long getQuantity() { + return quantity; + } + + public Inventory setQuantity(Long quantity) { + this.quantity = quantity; + return this; + } + + public BigDecimal getStorePrice() { + return storePrice; + } + + public Inventory setStorePrice(BigDecimal storePrice) { + this.storePrice = storePrice; + return this; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/ondemandload/LazyLoadingTest.java b/hibernate-core/src/test/java/org/hibernate/test/ondemandload/LazyLoadingTest.java new file mode 100644 index 0000000000..b1e79310fa --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/ondemandload/LazyLoadingTest.java @@ -0,0 +1,191 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2012, JBoss Inc., and individual contributors as indicated + * by the @authors tag. See the copyright.txt in the distribution for a + * full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software 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 software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.hibernate.test.ondemandload; + +import java.math.BigDecimal; + +import org.hibernate.Hibernate; +import org.hibernate.Session; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import org.hibernate.testing.FailureExpectedWithNewMetamodel; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +@FailureExpectedWithNewMetamodel +public class LazyLoadingTest extends BaseCoreFunctionalTestCase { + + @Before + public void setUpData() { + Session s = openSession(); + s.beginTransaction(); + Store store = new Store( 1 ) + .setName( "Acme Super Outlet" ); + s.persist( store ); + + Product product = new Product( "007" ) + .setName( "widget" ) + .setDescription( "FooBar" ); + s.persist( product ); + + store.addInventoryProduct( product ) + .setQuantity( 10L ) + .setStorePrice( new BigDecimal( 500 ) ); + + s.getTransaction().commit(); + s.close(); + } + + @After + public void cleanUpData() { + Session s = openSession(); + s.beginTransaction(); + s.delete( s.get( Store.class, 1 ) ); + s.delete( s.get( Product.class, "007" ) ); + s.getTransaction().commit(); + s.close(); + } + + @Test + public void testLazyCollectionLoadingWithClearedSession() { + sessionFactory().getStatistics().clear(); + + Session s = openSession(); + s.beginTransaction(); + // first load the store, making sure collection is not initialized + Store store = (Store) s.get( Store.class, 1 ); + assertNotNull( store ); + assertFalse( Hibernate.isInitialized( store.getInventories() ) ); + + assertEquals( 1, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 0, sessionFactory().getStatistics().getSessionCloseCount() ); + + // then clear session and try to initialize collection + s.clear(); + store.getInventories().size(); + assertTrue( Hibernate.isInitialized( store.getInventories() ) ); + + assertEquals( 2, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 1, sessionFactory().getStatistics().getSessionCloseCount() ); + + s.clear(); + store = (Store) s.get( Store.class, 1 ); + assertNotNull( store ); + assertFalse( Hibernate.isInitialized( store.getInventories() ) ); + + assertEquals( 2, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 1, sessionFactory().getStatistics().getSessionCloseCount() ); + + s.clear(); + store.getInventories().iterator(); + assertTrue( Hibernate.isInitialized( store.getInventories() ) ); + + assertEquals( 3, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 2, sessionFactory().getStatistics().getSessionCloseCount() ); + + s.getTransaction().commit(); + s.close(); + } + + @Test + public void testLazyCollectionLoadingWithClosedSession() { + sessionFactory().getStatistics().clear(); + + Session s = openSession(); + s.beginTransaction(); + // first load the store, making sure collection is not initialized + Store store = (Store) s.get( Store.class, 1 ); + assertNotNull( store ); + assertFalse( Hibernate.isInitialized( store.getInventories() ) ); + + assertEquals( 1, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 0, sessionFactory().getStatistics().getSessionCloseCount() ); + + // close the session and try to initialize collection + s.getTransaction().commit(); + s.close(); + + assertEquals( 1, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 1, sessionFactory().getStatistics().getSessionCloseCount() ); + + store.getInventories().size(); + assertTrue( Hibernate.isInitialized( store.getInventories() ) ); + + assertEquals( 2, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 2, sessionFactory().getStatistics().getSessionCloseCount() ); + } + + @Test + public void testLazyEntityLoadingWithClosedSession() { + sessionFactory().getStatistics().clear(); + + Session s = openSession(); + s.beginTransaction(); + // first load the store, making sure it is not initialized + Store store = (Store) s.load( Store.class, 1 ); + assertNotNull( store ); + assertFalse( Hibernate.isInitialized( store ) ); + + assertEquals( 1, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 0, sessionFactory().getStatistics().getSessionCloseCount() ); + + // close the session and try to initialize store + s.getTransaction().commit(); + s.close(); + + assertEquals( 1, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 1, sessionFactory().getStatistics().getSessionCloseCount() ); + + store.getName(); + assertTrue( Hibernate.isInitialized( store ) ); + + assertEquals( 2, sessionFactory().getStatistics().getSessionOpenCount() ); + assertEquals( 2, sessionFactory().getStatistics().getSessionCloseCount() ); + } + + @Override + protected void configure(Configuration cfg) { + super.configure( cfg ); + cfg.setProperty( Environment.ENABLE_LAZY_LOAD_NO_TRANS, "true" ); + cfg.setProperty( Environment.GENERATE_STATISTICS, "true" ); + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + Store.class, + Inventory.class, + Product.class + }; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/ondemandload/Product.java b/hibernate-core/src/test/java/org/hibernate/test/ondemandload/Product.java new file mode 100644 index 0000000000..aedff6ae12 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/ondemandload/Product.java @@ -0,0 +1,91 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates 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.test.ondemandload; + +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Version; +import java.io.Serializable; +import java.math.BigDecimal; + +@Entity +public class Product implements Serializable { + private String id; + private String name; + private String description; + private BigDecimal msrp; + private int version; + + private Product() { + } + + public Product(String id) { + this.id = id; + } + + @Id + public String getId() { + return id; + } + + private void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public Product setName(String name) { + this.name = name; + return this; + } + + public String getDescription() { + return description; + } + + public Product setDescription(String description) { + this.description = description; + return this; + } + + public BigDecimal getMsrp() { + return msrp; + } + + public Product setMsrp(BigDecimal msrp) { + this.msrp = msrp; + return this; + } + + @Version + public int getVersion() { + return version; + } + + private void setVersion(int version) { + this.version = version; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/ondemandload/Store.java b/hibernate-core/src/test/java/org/hibernate/test/ondemandload/Store.java new file mode 100644 index 0000000000..d2c1eb1236 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/ondemandload/Store.java @@ -0,0 +1,94 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2010, Red Hat, Inc. and/or its affiliates 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.test.ondemandload; + +import javax.persistence.CascadeType; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.Id; +import javax.persistence.OneToMany; +import javax.persistence.Version; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +@Entity +public class Store implements Serializable { + private int id; + private String name; + private List inventories = new ArrayList(); + private int version; + + protected Store() { + } + + public Store(int id) { + this.id = id; + } + + @Id + @Column(name = "ID") + public Integer getId() { + return id; + } + + private void setId(int id) { + this.id = id; + } + + @Column(name = "NAME") + public String getName() { + return name; + } + + public Store setName(String name) { + this.name = name; + return this; + } + + @OneToMany(mappedBy = "store", cascade = CascadeType.ALL, fetch = FetchType.LAZY) + public List getInventories() { + return inventories; + } + + public void setInventories(List inventories) { + this.inventories = inventories; + } + + public Inventory addInventoryProduct(Product product) { + final Inventory inventory = new Inventory( this, product ); + this.inventories.add( inventory ); + return inventory; + } + + @Version + public int getVersion() { + return version; + } + + private void setVersion(int version) { + this.version = version; + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/propertyref/DoesNotWork.java b/hibernate-core/src/test/java/org/hibernate/test/propertyref/DoesNotWork.java new file mode 100644 index 0000000000..1cb7292cc6 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/propertyref/DoesNotWork.java @@ -0,0 +1,126 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.test.propertyref; + +import javax.persistence.CollectionTable; +import javax.persistence.Column; +import javax.persistence.ElementCollection; +import javax.persistence.EmbeddedId; +import javax.persistence.Entity; +import javax.persistence.JoinColumn; +import javax.persistence.JoinTable; +import javax.persistence.Table; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +import org.hibernate.annotations.IndexColumn; + +/** + * @author Steve Ebersole + */ +@Entity +@Table(name = "vgras007_v031") +public class DoesNotWork implements Serializable { + + private static final long serialVersionUID = 1L; + + @EmbeddedId + private DoesNotWorkPk doesNotWorkPk; + + @Column(name = "production_credits_tid", insertable = false, updatable = false) + private Long globAdditInfoTid; + + @ElementCollection + @CollectionTable( + name = "vgras029_v031", + joinColumns = @JoinColumn(name = "text_id", referencedColumnName = "production_credits_tid") + ) + @Column(name = "text_part", insertable = false, updatable = false) + @IndexColumn(name = "seq_no", base = 1) + private List globalNotes = new ArrayList(); + + public DoesNotWork() { + } + + public DoesNotWork(DoesNotWorkPk doesNotWorkPk) { + this.doesNotWorkPk = doesNotWorkPk; + } + + public DoesNotWorkPk getDoesNotWorkPk() { + return doesNotWorkPk; + } + + public void setDoesNotWorkPk(DoesNotWorkPk doesNotWorkPk) { + this.doesNotWorkPk = doesNotWorkPk; + } + + public List getGlobalNotes() { + return globalNotes; + } + + public void setGlobalNotes(List globalNotes) { + this.globalNotes = globalNotes; + } + + public Long getGlobAdditInfoTid() { + return globAdditInfoTid; + } + + public void setGlobAdditInfoTid(Long globAdditInfoTid) { + this.globAdditInfoTid = globAdditInfoTid; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((doesNotWorkPk == null) ? 0 : doesNotWorkPk.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) { + return true; + } + if ( obj == null ) { + return false; + } + if ( !(obj instanceof DoesNotWork) ) { + return false; + } + DoesNotWork other = (DoesNotWork) obj; + if ( doesNotWorkPk == null ) { + if ( other.doesNotWorkPk != null ) { + return false; + } + } + else if ( !doesNotWorkPk.equals( other.doesNotWorkPk ) ) { + return false; + } + return true; + } + +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/propertyref/DoesNotWorkPk.java b/hibernate-core/src/test/java/org/hibernate/test/propertyref/DoesNotWorkPk.java new file mode 100644 index 0000000000..f753f19313 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/propertyref/DoesNotWorkPk.java @@ -0,0 +1,100 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.test.propertyref; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import java.io.Serializable; + +/** + * @author Steve Ebersole + */ +@Embeddable +public class DoesNotWorkPk implements Serializable { + + private static final long serialVersionUID = 1L; + + @Column(name = "track_no") + private String id1; + + @Column(name = "track_ext") + private String id2; + + public String getId1() { + return id1; + } + + public void setId1(String id1) { + this.id1 = id1; + } + + public String getId2() { + return id2; + } + + public void setId2(String id2) { + this.id2 = id2; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id1 == null) ? 0 : id1.hashCode()); + result = prime * result + ((id2 == null) ? 0 : id2.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if ( this == obj ) { + return true; + } + if ( obj == null ) { + return false; + } + if ( !(obj instanceof DoesNotWorkPk) ) { + return false; + } + DoesNotWorkPk other = (DoesNotWorkPk) obj; + if ( id1 == null ) { + if ( other.id1 != null ) { + return false; + } + } + else if ( !id1.equals( other.id1 ) ) { + return false; + } + if ( id2 == null ) { + if ( other.id2 != null ) { + return false; + } + } + else if ( !id2.equals( other.id2 ) ) { + return false; + } + return true; + } + +} \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/propertyref/DoesNotWorkTest.java b/hibernate-core/src/test/java/org/hibernate/test/propertyref/DoesNotWorkTest.java new file mode 100644 index 0000000000..cd17d0aac0 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/propertyref/DoesNotWorkTest.java @@ -0,0 +1,85 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.test.propertyref; + +import java.util.List; + +import org.hibernate.Session; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; + +import org.junit.Test; + +import org.hibernate.testing.FailureExpectedWithNewMetamodel; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +/** + * @author Steve Ebersole + */ +@FailureExpectedWithNewMetamodel +public class DoesNotWorkTest extends BaseCoreFunctionalTestCase { + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] {DoesNotWork.class}; + } + + @Override + protected void configure(Configuration configuration) { + super.configure( configuration ); + configuration.setProperty( AvailableSettings.USE_SECOND_LEVEL_CACHE, "false" ); + configuration.setProperty( AvailableSettings.HBM2DDL_IMPORT_FILES, "/org/hibernate/test/propertyref/import.sql" ); + } + + @Test + public void testIt() { + DoesNotWorkPk pk = new DoesNotWorkPk(); + pk.setId1( "ZZZ" ); + pk.setId2( "00" ); + +// { +// Session session = openSession(); +// session.beginTransaction(); +// DoesNotWork entity = new DoesNotWork( pk ); +// entity.setGlobalNotes( Arrays.asList( "My first note!" ) ); +// session.save( entity ); +// session.getTransaction().commit(); +// session.close(); +// } + + { + Session session = openSession(); + session.beginTransaction(); + DoesNotWork entity = (DoesNotWork) session.get( DoesNotWork.class, pk ); + List notes = entity.getGlobalNotes(); + if ( notes != null && notes.size() > 0 ) { + for ( String s : notes ) { + System.out.println( s ); + } + } + session.delete( entity ); + session.getTransaction().commit(); + session.close(); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/propertyref/DoesNotWorkWithHbmTest.java b/hibernate-core/src/test/java/org/hibernate/test/propertyref/DoesNotWorkWithHbmTest.java new file mode 100644 index 0000000000..8f18850a66 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/propertyref/DoesNotWorkWithHbmTest.java @@ -0,0 +1,90 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.test.propertyref; + +import java.util.List; + +import org.hibernate.Session; +import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.Configuration; + +import org.junit.Test; + +import org.hibernate.testing.FailureExpectedWithNewMetamodel; +import org.hibernate.testing.junit4.BaseCoreFunctionalTestCase; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +/** + * @author Steve Ebersole + */ +@FailureExpectedWithNewMetamodel +public class DoesNotWorkWithHbmTest extends BaseCoreFunctionalTestCase { + + @Override + protected String[] getMappings() { + return new String[] { "propertyref/Mapping.hbm.xml" }; + } + + @Override + protected void configure(Configuration configuration) { + super.configure( configuration ); + configuration.setProperty( AvailableSettings.USE_SECOND_LEVEL_CACHE, "false" ); + configuration.setProperty( AvailableSettings.HBM2DDL_IMPORT_FILES, "/org/hibernate/test/propertyref/import.sql" ); + } + + @Test + public void testIt() { + DoesNotWorkPk pk = new DoesNotWorkPk(); + pk.setId1( "ZZZ" ); + pk.setId2( "00" ); + +// { +// Session session = openSession(); +// session.beginTransaction(); +// DoesNotWork entity = new DoesNotWork( pk ); +// entity.setGlobalNotes( Arrays.asList( "My first note!" ) ); +// session.save( entity ); +// session.getTransaction().commit(); +// session.close(); +// } + + { + Session session = openSession(); + session.beginTransaction(); + DoesNotWork entity = (DoesNotWork) session.get( DoesNotWork.class, pk ); + assertNotNull( entity ); + List notes = entity.getGlobalNotes(); + assertNotNull( notes ); + assertEquals( 2, notes.size() ); + for ( String s : notes ) { + System.out.println( s ); + } + session.delete( entity ); + session.getTransaction().commit(); + session.close(); + } + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/test/propertyref/Mapping.hbm.xml b/hibernate-core/src/test/java/org/hibernate/test/propertyref/Mapping.hbm.xml new file mode 100644 index 0000000000..c1fecca5e1 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/propertyref/Mapping.hbm.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hibernate-core/src/test/java/org/hibernate/test/propertyref/import.sql b/hibernate-core/src/test/java/org/hibernate/test/propertyref/import.sql new file mode 100644 index 0000000000..b9d3a08744 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/test/propertyref/import.sql @@ -0,0 +1,3 @@ +INSERT INTO `vgras007_v031` VALUES ('ZZZ','00',1); + +INSERT INTO `vgras029_v031` VALUES (1,'Foo Foo Foo',1), (1,'Bar Bar Bar',2); \ No newline at end of file diff --git a/hibernate-ehcache/hibernate-ehcache.gradle b/hibernate-ehcache/hibernate-ehcache.gradle index b2192abfb3..24b887f9d7 100644 --- a/hibernate-ehcache/hibernate-ehcache.gradle +++ b/hibernate-ehcache/hibernate-ehcache.gradle @@ -1,9 +1,6 @@ -apply plugin: 'java' - dependencies { compile project( ':hibernate-core' ) - compile "net.sf.ehcache:ehcache-core:2.4.3" + compile( libraries.ehcache ) testCompile project( ':hibernate-testing' ) - testCompile libraries.h2 } diff --git a/hibernate-entitymanager/hibernate-entitymanager.gradle b/hibernate-entitymanager/hibernate-entitymanager.gradle index 37f2c03e2b..fb219fe5a3 100644 --- a/hibernate-entitymanager/hibernate-entitymanager.gradle +++ b/hibernate-entitymanager/hibernate-entitymanager.gradle @@ -1,6 +1,5 @@ import org.apache.tools.ant.filters.ReplaceTokens -apply plugin: 'java' apply plugin: org.hibernate.build.gradle.testing.matrix.MatrixTestingPlugin dependencies { @@ -10,12 +9,16 @@ dependencies { compile( libraries.jpa ) compile( libraries.jta ) compile( libraries.javassist ) + provided( "javax.enterprise:cdi-api:1.0-SP4" ) testCompile( project(':hibernate-testing') ) - testCompile( libraries.junit ) testCompile( libraries.shrinkwrap_api ) testCompile( libraries.shrinkwrap ) testCompile( libraries.validation ) testRuntime( libraries.validator ) + testCompile( "org.jboss.weld.arquillian.container:arquillian-weld-ee-embedded-1.1:1.1.2.Final" ) + testCompile( "org.jboss.weld:weld-core:1.1.9.Final" ) + testRuntime( "org.glassfish.web:el-impl:2.1.2-b04" ) + testRuntime( "org.jboss.ejb3:jboss-ejb3-api:3.1.0" ) } //////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -25,7 +28,7 @@ aptDumpDir = file( "${buildDir}/tmp/apt" ) sourceSets.test { originalJavaSrcDirs = java.srcDirs - generatedJpaMetamodelSrcDir = file( "${buildDir}/generated-src/jpamodelgen/${name}" ) + ext.generatedJpaMetamodelSrcDir = file( "${buildDir}/generated-src/jpamodelgen/${name}" ) java.srcDir generatedJpaMetamodelSrcDir } task generateTestJpaMetamodelClasses(type: Compile) { @@ -53,8 +56,8 @@ compileTestJava.options.define(compilerArgs: ["-proc:none"]) //////////////////////////////////////////////////////////////////////////////////////////////////////// // Process 'bundle resources' for the packaging tests //////////////////////////////////////////////////////////////////////////////////////////////////////// -bundlesTargetDir = file( "${buildDir}/bundles" ) task copyBundleResources (type: Copy) { + ext.bundlesTargetDir = file( "${buildDir}/bundles" ) from file('src/test/bundles') into bundlesTargetDir filter(ReplaceTokens, tokens: [ @@ -74,7 +77,7 @@ processTestResources.dependsOn copyBundleResources // create an artifact configuration composed of the test classes so that envers can access hem test classes task testJar(type: Jar, dependsOn: testClasses) { classifier = 'test' - from sourceSets.test.classes + from sourceSets.test.output } configurations { @@ -83,4 +86,4 @@ configurations { artifacts { tests testJar -} \ No newline at end of file +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/engine/spi/JpaCascadingAction.java b/hibernate-entitymanager/src/main/java/org/hibernate/engine/spi/JpaCascadingAction.java deleted file mode 100644 index 99da8ad2ba..0000000000 --- a/hibernate-entitymanager/src/main/java/org/hibernate/engine/spi/JpaCascadingAction.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2009, Red Hat Middleware LLC 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 Middleware LLC. - * - * 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.engine.spi; - -import java.util.Iterator; -import java.util.Map; - -import org.jboss.logging.Logger; - -import org.hibernate.HibernateException; -import org.hibernate.event.spi.EventSource; -import org.hibernate.type.CollectionType; - -/** - * Because of CascadingAction constructor visibility - * I need a packaged friendly subclass - * TODO Get rid of it for 3.3 - * @author Emmanuel Bernard - */ -public abstract class JpaCascadingAction extends CascadingAction { - private static final Logger LOG = Logger.getLogger( JpaCascadingAction.class.getName() ); - - /** - * @see org.hibernate.Session#persist(Object) - */ - public static final CascadingAction PERSIST_SKIPLAZY = new CascadingAction() { - @Override - public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled) - throws HibernateException { - LOG.trace("Cascading to persist: " + entityName); - session.persist( entityName, child, (Map) anything ); - } - @Override - public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) { - // persists don't cascade to uninitialized collections - return CascadingAction.getLoadedElementsIterator( session, collectionType, collection ); - } - @Override - public boolean deleteOrphans() { - return false; - } - @Override - public boolean performOnLazyProperty() { - return false; - } - @Override - public String toString() { - return "ACTION_PERSIST_SKIPLAZY"; - } - }; - -} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/AvailableSettings.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/AvailableSettings.java index 83790b6aa8..b027e11be7 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/AvailableSettings.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/AvailableSettings.java @@ -187,6 +187,11 @@ public interface AvailableSettings extends org.hibernate.cfg.AvailableSettings { */ public static final String REMOVE_VALIDATION_GROUP = "javax.persistence.validation.group.pre-remove"; + /** + * Used to pass along the CDI BeanManager, if any, to be used. + */ + public static final String CDI_BEAN_MANAGER = "javax.persistence.bean.manager"; + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Hibernate specific settings diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java index 7e920b33e0..548790aeee 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/internal/EntityManagerFactoryBuilderImpl.java @@ -54,7 +54,7 @@ import org.jboss.jandex.ClassInfo; import org.jboss.jandex.CompositeIndex; import org.jboss.jandex.DotName; import org.jboss.jandex.Index; -import org.jboss.jandex.IndexResult; +import org.jboss.jandex.IndexView; import org.jboss.jandex.Indexer; import org.jboss.logging.Logger; @@ -82,6 +82,7 @@ import org.hibernate.jpa.boot.spi.JpaUnifiedSettingsBuilder; import org.hibernate.jpa.boot.spi.PersistenceUnitDescriptor; import org.hibernate.jpa.internal.EntityManagerFactoryImpl; import org.hibernate.jpa.internal.EntityManagerMessageLogger; +import org.hibernate.jpa.event.spi.JpaIntegrator; import org.hibernate.jpa.internal.util.LogHelper; import org.hibernate.jpa.internal.util.PersistenceUnitTransactionTypeHelper; import org.hibernate.jpa.packaging.internal.NativeScanner; @@ -95,6 +96,7 @@ import org.hibernate.secure.internal.JACCConfiguration; import org.hibernate.service.BootstrapServiceRegistry; import org.hibernate.service.ServiceRegistry; import org.hibernate.service.ServiceRegistryBuilder; +import org.hibernate.service.classloading.internal.ClassLoaderServiceImpl; import org.hibernate.service.classloading.spi.ClassLoaderService; import org.hibernate.service.spi.ServiceRegistryImplementor; @@ -180,7 +182,7 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil ScanResult scanResult = scan( bootstrapServiceRegistry ); // 2) building a Jandex index Set collectedManagedClassNames = collectManagedClassNames( scanResult ); - IndexResult jandexIndex = locateOrBuildJandexIndex( collectedManagedClassNames, scanResult.getPackageNames(), bootstrapServiceRegistry ); + IndexView jandexIndex = locateOrBuildJandexIndex( collectedManagedClassNames, scanResult.getPackageNames(), bootstrapServiceRegistry ); // 3) building "metadata sources" to keep for later to use in building the SessionFactory metadataSources = prepareMetadataSources( jandexIndex, collectedManagedClassNames, scanResult, bootstrapServiceRegistry ); @@ -226,7 +228,7 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil @SuppressWarnings("unchecked") private MetadataSources prepareMetadataSources( - IndexResult jandexIndex, + IndexView jandexIndex, Set collectedManagedClassNames, ScanResult scanResult, BootstrapServiceRegistry bootstrapServiceRegistry) { @@ -286,7 +288,7 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil return collectedNames; } - private IndexResult locateOrBuildJandexIndex( + private IndexView locateOrBuildJandexIndex( Set collectedManagedClassNames, List packageNames, BootstrapServiceRegistry bootstrapServiceRegistry) { @@ -295,14 +297,14 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil // 2) pass that Index along to the metamodel code... // // (1) is mocked up here, but JBoss AS does not currently pass in any Index to use... - IndexResult jandexIndex = (IndexResult) configurationValues.get( JANDEX_INDEX ); + IndexView jandexIndex = (IndexView) configurationValues.get( JANDEX_INDEX ); if ( jandexIndex == null ) { jandexIndex = buildJandexIndex( collectedManagedClassNames, packageNames, bootstrapServiceRegistry ); } return jandexIndex; } - private IndexResult buildJandexIndex(Set classNamesSource, List packageNames, BootstrapServiceRegistry bootstrapServiceRegistry) { + private IndexView buildJandexIndex(Set classNamesSource, List packageNames, BootstrapServiceRegistry bootstrapServiceRegistry) { Indexer indexer = new Indexer(); for ( String className : classNamesSource ) { @@ -319,7 +321,7 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil // for now, we also need to wrap this in a CompositeIndex until Jandex is updated to use a common interface // between the 2... - return new CompositeIndex( indexer.complete() ); + return CompositeIndex.create( indexer.complete() ); } private void indexResource(String resourceName, Indexer indexer, BootstrapServiceRegistry bootstrapServiceRegistry) { @@ -772,25 +774,37 @@ public class EntityManagerFactoryBuilderImpl implements EntityManagerFactoryBuil @SuppressWarnings("unchecked") public EntityManagerFactory buildEntityManagerFactory() { + // IMPL NOTE : TCCL handling here is temporary. + // It is needed because this code still uses Hibernate Configuration and Hibernate commons-annotations + // in turn which relies on TCCL being set. + final ServiceRegistry serviceRegistry = buildServiceRegistry(); + final ClassLoaderService classLoaderService = serviceRegistry.getService( ClassLoaderService.class ); - hibernateConfiguration = buildHibernateConfiguration( serviceRegistry ); + return ( (ClassLoaderServiceImpl) classLoaderService ).withTccl( + new ClassLoaderServiceImpl.Work() { + @Override + public EntityManagerFactoryImpl perform() { + hibernateConfiguration = buildHibernateConfiguration( serviceRegistry ); - SessionFactoryImplementor sessionFactory; - try { - sessionFactory = (SessionFactoryImplementor) hibernateConfiguration.buildSessionFactory( serviceRegistry ); - } - catch (MappingException e) { - throw persistenceException( "Unable to build Hibernate SessionFactory", e ); - } + SessionFactoryImplementor sessionFactory; + try { + sessionFactory = (SessionFactoryImplementor) hibernateConfiguration.buildSessionFactory( serviceRegistry ); + } + catch (MappingException e) { + throw persistenceException( "Unable to build Hibernate SessionFactory", e ); + } - if ( suppliedSessionFactoryObserver != null ) { - sessionFactory.addObserver( suppliedSessionFactoryObserver ); - } - sessionFactory.addObserver( new ServiceRegistryCloser() ); + if ( suppliedSessionFactoryObserver != null ) { + sessionFactory.addObserver( suppliedSessionFactoryObserver ); + } + sessionFactory.addObserver( new ServiceRegistryCloser() ); - // NOTE : passing cfg is temporary until - return new EntityManagerFactoryImpl( persistenceUnit.getName(), sessionFactory, settings, configurationValues, hibernateConfiguration ); + // NOTE : passing cfg is temporary until + return new EntityManagerFactoryImpl( persistenceUnit.getName(), sessionFactory, settings, configurationValues, hibernateConfiguration ); + } + } + ); } public ServiceRegistry buildServiceRegistry() { diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/JpaBootstrapServiceRegistryBuilder.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/JpaBootstrapServiceRegistryBuilder.java index bbb23f5ccf..b4a710bd6f 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/JpaBootstrapServiceRegistryBuilder.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/boot/spi/JpaBootstrapServiceRegistryBuilder.java @@ -26,7 +26,7 @@ package org.hibernate.jpa.boot.spi; import java.util.Map; import org.hibernate.integrator.spi.Integrator; -import org.hibernate.jpa.internal.event.JpaIntegrator; +import org.hibernate.jpa.event.spi.JpaIntegrator; import org.hibernate.service.BootstrapServiceRegistry; import org.hibernate.service.BootstrapServiceRegistryBuilder; diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/HibernateEntityManagerEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/HibernateEntityManagerEventListener.java similarity index 96% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/HibernateEntityManagerEventListener.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/HibernateEntityManagerEventListener.java index eb42886b10..4533abb167 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/HibernateEntityManagerEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/HibernateEntityManagerEventListener.java @@ -21,7 +21,7 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.core; /** * Marker interface for handling listener duplication checking (to avoid multiple registrations). diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaAutoFlushEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaAutoFlushEventListener.java similarity index 92% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaAutoFlushEventListener.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaAutoFlushEventListener.java index b55ca1a5e8..3fbcfadeca 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaAutoFlushEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaAutoFlushEventListener.java @@ -21,11 +21,12 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.core; import java.util.IdentityHashMap; import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.event.internal.DefaultAutoFlushEventListener; import org.hibernate.event.spi.AutoFlushEventListener; @@ -43,7 +44,7 @@ public class JpaAutoFlushEventListener @Override protected CascadingAction getCascadingAction() { - return CascadingAction.PERSIST_ON_FLUSH; + return CascadingActions.PERSIST_ON_FLUSH; } @Override diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaDeleteEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaDeleteEventListener.java similarity index 80% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaDeleteEventListener.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaDeleteEventListener.java index 8c98271cc9..185b37f0ff 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaDeleteEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaDeleteEventListener.java @@ -21,13 +21,15 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.core; import java.io.Serializable; import org.hibernate.event.internal.DefaultDeleteEventListener; import org.hibernate.event.spi.DeleteEvent; import org.hibernate.event.spi.EventSource; +import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; import org.hibernate.persister.entity.EntityPersister; /** @@ -35,25 +37,25 @@ import org.hibernate.persister.entity.EntityPersister; * * @author Emmanuel Bernard */ -public class JpaDeleteEventListener extends DefaultDeleteEventListener implements CallbackHandlerConsumer { - private EntityCallbackHandler callbackHandler; +public class JpaDeleteEventListener extends DefaultDeleteEventListener implements CallbackRegistryConsumer { + private CallbackRegistry callbackRegistry; - public void setCallbackHandler(EntityCallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; + public void injectCallbackRegistry(CallbackRegistry callbackRegistry) { + this.callbackRegistry = callbackRegistry; } public JpaDeleteEventListener() { super(); } - public JpaDeleteEventListener(EntityCallbackHandler callbackHandler) { + public JpaDeleteEventListener(CallbackRegistry callbackRegistry) { this(); - this.callbackHandler = callbackHandler; + this.callbackRegistry = callbackRegistry; } @Override protected boolean invokeDeleteLifecycle(EventSource session, Object entity, EntityPersister persister) { - callbackHandler.preRemove( entity ); + callbackRegistry.preRemove( entity ); return super.invokeDeleteLifecycle( session, entity, persister ); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaFlushEntityEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaFlushEntityEventListener.java similarity index 82% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaFlushEntityEventListener.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaFlushEntityEventListener.java index e7f162684e..12fab24418 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaFlushEntityEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaFlushEntityEventListener.java @@ -21,13 +21,15 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.core; import org.hibernate.SessionFactory; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.engine.spi.Status; import org.hibernate.event.internal.DefaultFlushEntityEventListener; +import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; import org.hibernate.metadata.ClassMetadata; import org.hibernate.persister.entity.EntityPersister; import org.hibernate.type.Type; @@ -37,20 +39,20 @@ import org.hibernate.type.Type; * * @author Emmanuel Bernard */ -public class JpaFlushEntityEventListener extends DefaultFlushEntityEventListener implements CallbackHandlerConsumer { - private EntityCallbackHandler callbackHandler; +public class JpaFlushEntityEventListener extends DefaultFlushEntityEventListener implements CallbackRegistryConsumer { + private CallbackRegistry callbackRegistry; - public void setCallbackHandler(EntityCallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; + public void injectCallbackRegistry(CallbackRegistry callbackRegistry) { + this.callbackRegistry = callbackRegistry; } public JpaFlushEntityEventListener() { super(); } - public JpaFlushEntityEventListener(EntityCallbackHandler callbackHandler) { + public JpaFlushEntityEventListener(CallbackRegistry callbackRegistry) { super(); - this.callbackHandler = callbackHandler; + this.callbackRegistry = callbackRegistry; } @Override @@ -62,7 +64,7 @@ public class JpaFlushEntityEventListener extends DefaultFlushEntityEventListener EntityPersister persister) { boolean isDirty = false; if ( entry.getStatus() != Status.DELETED ) { - if ( callbackHandler.preUpdate( entity ) ) { + if ( callbackRegistry.preUpdate( entity ) ) { isDirty = copyState( entity, persister.getPropertyTypes(), values, session.getFactory() ); } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaFlushEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaFlushEventListener.java similarity index 92% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaFlushEventListener.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaFlushEventListener.java index 836489a259..a61eab6ef1 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaFlushEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaFlushEventListener.java @@ -21,11 +21,12 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.core; import java.util.IdentityHashMap; import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.event.internal.DefaultFlushEventListener; import org.hibernate.event.spi.FlushEventListener; @@ -40,7 +41,7 @@ public class JpaFlushEventListener extends DefaultFlushEventListener implements @Override protected CascadingAction getCascadingAction() { - return CascadingAction.PERSIST_ON_FLUSH; + return CascadingActions.PERSIST_ON_FLUSH; } @Override diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaMergeEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaMergeEventListener.java similarity index 77% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaMergeEventListener.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaMergeEventListener.java index ec7bea6bcd..26fc3d16dc 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaMergeEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaMergeEventListener.java @@ -21,32 +21,34 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.core; import java.io.Serializable; import org.hibernate.event.internal.DefaultMergeEventListener; import org.hibernate.event.spi.EventSource; +import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; /** * Overrides the LifeCycle OnSave call to call the PrePersist operation * * @author Emmanuel Bernard */ -public class JpaMergeEventListener extends DefaultMergeEventListener implements CallbackHandlerConsumer { - private EntityCallbackHandler callbackHandler; +public class JpaMergeEventListener extends DefaultMergeEventListener implements CallbackRegistryConsumer { + private CallbackRegistry callbackRegistry; - public void setCallbackHandler(EntityCallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; + public void injectCallbackRegistry(CallbackRegistry callbackRegistry) { + this.callbackRegistry = callbackRegistry; } public JpaMergeEventListener() { super(); } - public JpaMergeEventListener(EntityCallbackHandler callbackHandler) { + public JpaMergeEventListener(CallbackRegistry callbackRegistry) { super(); - this.callbackHandler = callbackHandler; + this.callbackRegistry = callbackRegistry; } @Override @@ -56,7 +58,7 @@ public class JpaMergeEventListener extends DefaultMergeEventListener implements String entityName, Object anything, EventSource source) { - callbackHandler.preCreate( entity ); + callbackRegistry.preCreate( entity ); return super.saveWithRequestedId( entity, requestedId, entityName, anything, source ); } @@ -67,7 +69,7 @@ public class JpaMergeEventListener extends DefaultMergeEventListener implements Object anything, EventSource source, boolean requiresImmediateIdAccess) { - callbackHandler.preCreate( entity ); + callbackRegistry.preCreate( entity ); return super.saveWithGeneratedId( entity, entityName, anything, source, requiresImmediateIdAccess ); } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPersistEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPersistEventListener.java similarity index 53% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPersistEventListener.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPersistEventListener.java index 4f584d4a1b..1815324ef0 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPersistEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPersistEventListener.java @@ -21,40 +21,45 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.core; import java.io.Serializable; +import java.util.Iterator; +import java.util.Map; +import org.jboss.logging.Logger; + +import org.hibernate.HibernateException; import org.hibernate.engine.spi.CascadingAction; -import org.hibernate.engine.spi.JpaCascadeStyle; -import org.hibernate.engine.spi.JpaCascadingAction; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.event.internal.DefaultPersistEventListener; import org.hibernate.event.spi.EventSource; +import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; +import org.hibernate.type.CollectionType; /** * Overrides the LifeCycle OnSave call to call the PrePersist operation * * @author Emmanuel Bernard */ -public class JpaPersistEventListener extends DefaultPersistEventListener implements CallbackHandlerConsumer { - static { - JpaCascadeStyle.PERSIST_JPA.hasOrphanDelete(); //triggers class loading to override persist with PERSIST_JPA - } +public class JpaPersistEventListener extends DefaultPersistEventListener implements CallbackRegistryConsumer { + private static final Logger log = Logger.getLogger( JpaPersistEventListener.class ); - private EntityCallbackHandler callbackHandler; + private CallbackRegistry callbackRegistry; @Override - public void setCallbackHandler(EntityCallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; + public void injectCallbackRegistry(CallbackRegistry callbackRegistry) { + this.callbackRegistry = callbackRegistry; } public JpaPersistEventListener() { super(); } - public JpaPersistEventListener(EntityCallbackHandler callbackHandler) { + public JpaPersistEventListener(CallbackRegistry callbackRegistry) { super(); - this.callbackHandler = callbackHandler; + this.callbackRegistry = callbackRegistry; } @Override @@ -64,7 +69,7 @@ public class JpaPersistEventListener extends DefaultPersistEventListener impleme String entityName, Object anything, EventSource source) { - callbackHandler.preCreate( entity ); + callbackRegistry.preCreate( entity ); return super.saveWithRequestedId( entity, requestedId, entityName, anything, source ); } @@ -75,12 +80,38 @@ public class JpaPersistEventListener extends DefaultPersistEventListener impleme Object anything, EventSource source, boolean requiresImmediateIdAccess) { - callbackHandler.preCreate( entity ); + callbackRegistry.preCreate( entity ); return super.saveWithGeneratedId( entity, entityName, anything, source, requiresImmediateIdAccess ); } @Override protected CascadingAction getCascadeAction() { - return JpaCascadingAction.PERSIST_SKIPLAZY; + return PERSIST_SKIPLAZY; } + + public static final CascadingAction PERSIST_SKIPLAZY = new CascadingActions.BaseCascadingAction() { + @Override + public void cascade(EventSource session, Object child, String entityName, Object anything, boolean isCascadeDeleteEnabled) + throws HibernateException { + log.trace( "Cascading persist to : " + entityName ); + session.persist( entityName, child, (Map) anything ); + } + @Override + public Iterator getCascadableChildrenIterator(EventSource session, CollectionType collectionType, Object collection) { + // persists don't cascade to uninitialized collections + return CascadingActions.getLoadedElementsIterator( session, collectionType, collection ); + } + @Override + public boolean deleteOrphans() { + return false; + } + @Override + public boolean performOnLazyProperty() { + return false; + } + @Override + public String toString() { + return "ACTION_PERSIST_SKIPLAZY"; + } + }; } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPersistOnFlushEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPersistOnFlushEventListener.java similarity index 90% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPersistOnFlushEventListener.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPersistOnFlushEventListener.java index 88a91ed4ce..2702e1c685 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPersistOnFlushEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPersistOnFlushEventListener.java @@ -21,9 +21,10 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.core; import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; /** * @author Emmanuel Bernard @@ -31,6 +32,6 @@ import org.hibernate.engine.spi.CascadingAction; public class JpaPersistOnFlushEventListener extends JpaPersistEventListener { @Override protected CascadingAction getCascadeAction() { - return CascadingAction.PERSIST_ON_FLUSH; + return CascadingActions.PERSIST_ON_FLUSH; } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPostDeleteEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostDeleteEventListener.java similarity index 65% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPostDeleteEventListener.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostDeleteEventListener.java index b5e13fd965..c09ed5e02d 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPostDeleteEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostDeleteEventListener.java @@ -21,32 +21,39 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.core; import org.hibernate.event.spi.PostDeleteEvent; import org.hibernate.event.spi.PostDeleteEventListener; +import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; +import org.hibernate.persister.entity.EntityPersister; /** * @author Kabir Khan */ -public class JpaPostDeleteEventListener implements PostDeleteEventListener, CallbackHandlerConsumer { - EntityCallbackHandler callbackHandler; +public class JpaPostDeleteEventListener implements PostDeleteEventListener, CallbackRegistryConsumer { + private CallbackRegistry callbackRegistry; - public void setCallbackHandler(EntityCallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; + public void injectCallbackRegistry(CallbackRegistry callbackRegistry) { + this.callbackRegistry = callbackRegistry; } public JpaPostDeleteEventListener() { super(); } - public JpaPostDeleteEventListener(EntityCallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; + public JpaPostDeleteEventListener(CallbackRegistry callbackRegistry) { + this.callbackRegistry = callbackRegistry; } public void onPostDelete(PostDeleteEvent event) { Object entity = event.getEntity(); - callbackHandler.postRemove( entity ); + callbackRegistry.postRemove( entity ); } + @Override + public boolean requiresPostCommitHanding(EntityPersister persister) { + return callbackRegistry.hasPostRemoveCallbacks( persister.getMappedClass() ); + } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPostInsertEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostInsertEventListener.java similarity index 65% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPostInsertEventListener.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostInsertEventListener.java index 8aa7ad373c..ea07eef5c4 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPostInsertEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostInsertEventListener.java @@ -21,33 +21,42 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.core; import org.hibernate.event.spi.PostInsertEvent; import org.hibernate.event.spi.PostInsertEventListener; +import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; +import org.hibernate.persister.entity.EntityPersister; /** * @author Kabir Khan + * @author Steve Ebersole */ -public class JpaPostInsertEventListener implements PostInsertEventListener, CallbackHandlerConsumer { - EntityCallbackHandler callbackHandler; +public class JpaPostInsertEventListener implements PostInsertEventListener, CallbackRegistryConsumer { + private CallbackRegistry callbackRegistry; @Override - public void setCallbackHandler(EntityCallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; + public void injectCallbackRegistry(CallbackRegistry callbackRegistry) { + this.callbackRegistry = callbackRegistry; } public JpaPostInsertEventListener() { super(); } - public JpaPostInsertEventListener(EntityCallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; + public JpaPostInsertEventListener(CallbackRegistry callbackRegistry) { + this.callbackRegistry = callbackRegistry; } @Override public void onPostInsert(PostInsertEvent event) { Object entity = event.getEntity(); - callbackHandler.postCreate( entity ); + callbackRegistry.postCreate( entity ); + } + + @Override + public boolean requiresPostCommitHanding(EntityPersister persister) { + return callbackRegistry.hasPostCreateCallbacks( persister.getMappedClass() ); } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPostLoadEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostLoadEventListener.java similarity index 73% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPostLoadEventListener.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostLoadEventListener.java index 6f10739b80..01fe7f7962 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPostLoadEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostLoadEventListener.java @@ -21,34 +21,36 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.core; import org.hibernate.event.spi.PostLoadEvent; import org.hibernate.event.spi.PostLoadEventListener; +import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; /** * @author Kabir Khan */ -public class JpaPostLoadEventListener implements PostLoadEventListener, CallbackHandlerConsumer { - EntityCallbackHandler callbackHandler; +public class JpaPostLoadEventListener implements PostLoadEventListener, CallbackRegistryConsumer { + CallbackRegistry callbackRegistry; @Override - public void setCallbackHandler(EntityCallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; + public void injectCallbackRegistry(CallbackRegistry callbackRegistry) { + this.callbackRegistry = callbackRegistry; } public JpaPostLoadEventListener() { super(); } - public JpaPostLoadEventListener(EntityCallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; + public JpaPostLoadEventListener(CallbackRegistry callbackRegistry) { + this.callbackRegistry = callbackRegistry; } @Override public void onPostLoad(PostLoadEvent event) { Object entity = event.getEntity(); - callbackHandler.postLoad( entity ); + callbackRegistry.postLoad( entity ); } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPostUpdateEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostUpdateEventListener.java similarity index 81% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPostUpdateEventListener.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostUpdateEventListener.java index 83d88675c7..42fd9d8249 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaPostUpdateEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaPostUpdateEventListener.java @@ -21,7 +21,7 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.core; import org.hibernate.engine.spi.EntityEntry; import org.hibernate.engine.spi.Status; @@ -34,6 +34,9 @@ import org.hibernate.event.spi.PostCollectionUpdateEvent; import org.hibernate.event.spi.PostCollectionUpdateEventListener; import org.hibernate.event.spi.PostUpdateEvent; import org.hibernate.event.spi.PostUpdateEventListener; +import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; +import org.hibernate.persister.entity.EntityPersister; /** * Implementation of the post update listeners. @@ -43,23 +46,23 @@ import org.hibernate.event.spi.PostUpdateEventListener; @SuppressWarnings("serial") public class JpaPostUpdateEventListener implements PostUpdateEventListener, - CallbackHandlerConsumer, + CallbackRegistryConsumer, PostCollectionRecreateEventListener, PostCollectionRemoveEventListener, PostCollectionUpdateEventListener { - EntityCallbackHandler callbackHandler; + private CallbackRegistry callbackRegistry; @Override - public void setCallbackHandler(EntityCallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; + public void injectCallbackRegistry(CallbackRegistry callbackRegistry) { + this.callbackRegistry = callbackRegistry; } public JpaPostUpdateEventListener() { super(); } - public JpaPostUpdateEventListener(EntityCallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; + public JpaPostUpdateEventListener(CallbackRegistry callbackRegistry) { + this.callbackRegistry = callbackRegistry; } @Override @@ -74,10 +77,15 @@ public class JpaPostUpdateEventListener .getEntityEntries().get(entity); // mimic the preUpdate filter if ( Status.DELETED != entry.getStatus()) { - callbackHandler.postUpdate(entity); + callbackRegistry.postUpdate(entity); } } + @Override + public boolean requiresPostCommitHanding(EntityPersister persister) { + return callbackRegistry.hasPostUpdateCallbacks( persister.getMappedClass() ); + } + @Override public void onPostRecreateCollection(PostCollectionRecreateEvent event) { Object entity = event.getCollection().getOwner(); diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaSaveEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaSaveEventListener.java similarity index 77% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaSaveEventListener.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaSaveEventListener.java index 8f9c7b145c..36ab60cb1e 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaSaveEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaSaveEventListener.java @@ -21,32 +21,34 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.core; import java.io.Serializable; import org.hibernate.event.internal.DefaultSaveEventListener; import org.hibernate.event.spi.EventSource; +import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; /** * Overrides the LifeCycle OnSave call to call the PrePersist operation * * @author Emmanuel Bernard */ -public class JpaSaveEventListener extends DefaultSaveEventListener implements CallbackHandlerConsumer { - private EntityCallbackHandler callbackHandler; +public class JpaSaveEventListener extends DefaultSaveEventListener implements CallbackRegistryConsumer { + private CallbackRegistry callbackRegistry; - public void setCallbackHandler(EntityCallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; + public void injectCallbackRegistry(CallbackRegistry callbackRegistry) { + this.callbackRegistry = callbackRegistry; } public JpaSaveEventListener() { super(); } - public JpaSaveEventListener(EntityCallbackHandler callbackHandler) { + public JpaSaveEventListener(CallbackRegistry callbackRegistry) { super(); - this.callbackHandler = callbackHandler; + this.callbackRegistry = callbackRegistry; } @Override @@ -56,7 +58,7 @@ public class JpaSaveEventListener extends DefaultSaveEventListener implements Ca String entityName, Object anything, EventSource source) { - callbackHandler.preCreate( entity ); + callbackRegistry.preCreate( entity ); return super.saveWithRequestedId( entity, requestedId, entityName, anything, source ); } @@ -67,7 +69,7 @@ public class JpaSaveEventListener extends DefaultSaveEventListener implements Ca Object anything, EventSource source, boolean requiresImmediateIdAccess) { - callbackHandler.preCreate( entity ); + callbackRegistry.preCreate( entity ); return super.saveWithGeneratedId( entity, entityName, anything, source, requiresImmediateIdAccess ); } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaSaveOrUpdateEventListener.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaSaveOrUpdateEventListener.java similarity index 77% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaSaveOrUpdateEventListener.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaSaveOrUpdateEventListener.java index 6675764c4c..a2bb3353b5 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaSaveOrUpdateEventListener.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/JpaSaveOrUpdateEventListener.java @@ -21,32 +21,34 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.core; import java.io.Serializable; import org.hibernate.event.internal.DefaultSaveOrUpdateEventListener; import org.hibernate.event.spi.EventSource; +import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; /** * Overrides the LifeCycle OnSave call to call the PrePersist operation * * @author Emmanuel Bernard */ -public class JpaSaveOrUpdateEventListener extends DefaultSaveOrUpdateEventListener implements CallbackHandlerConsumer { - private EntityCallbackHandler callbackHandler; +public class JpaSaveOrUpdateEventListener extends DefaultSaveOrUpdateEventListener implements CallbackRegistryConsumer { + private CallbackRegistry callbackRegistry; - public void setCallbackHandler(EntityCallbackHandler callbackHandler) { - this.callbackHandler = callbackHandler; + public void injectCallbackRegistry(CallbackRegistry callbackRegistry) { + this.callbackRegistry = callbackRegistry; } public JpaSaveOrUpdateEventListener() { super(); } - public JpaSaveOrUpdateEventListener(EntityCallbackHandler callbackHandler) { + public JpaSaveOrUpdateEventListener(CallbackRegistry callbackRegistry) { super(); - this.callbackHandler = callbackHandler; + this.callbackRegistry = callbackRegistry; } @Override @@ -56,7 +58,7 @@ public class JpaSaveOrUpdateEventListener extends DefaultSaveOrUpdateEventListen String entityName, Object anything, EventSource source) { - callbackHandler.preCreate( entity ); + callbackRegistry.preCreate( entity ); return super.saveWithRequestedId( entity, requestedId, entityName, anything, source ); } @@ -67,7 +69,7 @@ public class JpaSaveOrUpdateEventListener extends DefaultSaveOrUpdateEventListen Object anything, EventSource source, boolean requiresImmediateIdAccess) { - callbackHandler.preCreate( entity ); + callbackRegistry.preCreate( entity ); return super.saveWithGeneratedId( entity, entityName, anything, source, requiresImmediateIdAccess ); } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/package-info.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/package-info.java new file mode 100644 index 0000000000..981dcb0e78 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/core/package-info.java @@ -0,0 +1,32 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.event.internal.core; + +/** + * Hibernate EntityManager specific implementations of Hibernate event listeners. Generally the listeners + * here either:
    + *
  • provide tweaks to internal processing to conform with JPA spec
  • + *
  • bridge to JPA event callbacks
  • + *
+ */ \ No newline at end of file diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/BeanManagerListenerFactory.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/BeanManagerListenerFactory.java new file mode 100644 index 0000000000..2e55804b10 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/BeanManagerListenerFactory.java @@ -0,0 +1,89 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.event.internal.jpa; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.InjectionTarget; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.hibernate.jpa.event.spi.jpa.ListenerFactory; + +/** + * CID-based implementation of the ListenerFactory contract. Listener instances are kept in a map keyed by Class + * to achieve singleton-ness. + * + * @author Steve Ebersole + */ +public class BeanManagerListenerFactory implements ListenerFactory { + private final BeanManager beanManager; + private final Map listeners = new ConcurrentHashMap(); + + public BeanManagerListenerFactory(BeanManager beanManager) { + this.beanManager = beanManager; + } + + @Override + public T buildListener(Class listenerClass) { + BeanMetaData beanMetaData = listeners.get( listenerClass ); + if ( beanMetaData == null ) { + beanMetaData = new BeanMetaData( listenerClass ); + listeners.put( listenerClass, beanMetaData ); + } + return beanMetaData.instance; + } + + @Override + public void release() { + for ( BeanMetaData beanMetaData : listeners.values() ) { + beanMetaData.release(); + } + listeners.clear(); + } + + private class BeanMetaData { + private final InjectionTarget injectionTarget; + private final CreationalContext creationalContext; + private final T instance; + + private BeanMetaData(Class listenerClass) { + AnnotatedType annotatedType = beanManager.createAnnotatedType( listenerClass ); + this.injectionTarget = beanManager.createInjectionTarget( annotatedType ); + this.creationalContext = beanManager.createCreationalContext( null ); + + this.instance = injectionTarget.produce( creationalContext ); + injectionTarget.inject( this.instance, creationalContext ); + + injectionTarget.postConstruct( this.instance ); + } + + private void release() { + injectionTarget.preDestroy( instance ); + injectionTarget.dispose( instance ); + creationalContext.release(); + } + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackProcessor.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackProcessor.java new file mode 100644 index 0000000000..37f1d2786a --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackProcessor.java @@ -0,0 +1,58 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.event.internal.jpa; + +import javax.persistence.PostLoad; +import javax.persistence.PostPersist; +import javax.persistence.PostRemove; +import javax.persistence.PostUpdate; +import javax.persistence.PrePersist; +import javax.persistence.PreRemove; +import javax.persistence.PreUpdate; + +/** + * Delegate for interpreting, parsing and processing callbacks + * + * @author Steve Ebersole + */ +public interface CallbackProcessor { + public static final Class[] CALLBACK_ANNOTATION_CLASSES = new Class[] { + PreUpdate.class, PostUpdate.class, + PrePersist.class, PostPersist.class, + PreRemove.class, PostRemove.class, + PostLoad.class + }; + + /** + * Ugh, Object to account for Configuration/Metamodel split. Should eventually be EntityBinding from + * metamodel code base. Currently each Integrator method passes in different type and each impl + * interprets differently. + * + * @param entityObject + * @param registry + */ + public void processCallbacksForEntity(Object entityObject, CallbackRegistryImpl registry); + + public void release(); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackProcessorImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackProcessorImpl.java new file mode 100644 index 0000000000..b9e2a5904d --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackProcessorImpl.java @@ -0,0 +1,164 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.event.internal.jpa; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + +import org.jboss.logging.Logger; + +import org.hibernate.MappingException; +import org.hibernate.jpa.event.spi.jpa.Callback; +import org.hibernate.jpa.event.spi.jpa.ListenerFactory; +import org.hibernate.metamodel.spi.MetadataImplementor; +import org.hibernate.metamodel.spi.binding.EntityBinding; +import org.hibernate.metamodel.spi.source.JpaCallbackSource; +import org.hibernate.service.classloading.spi.ClassLoaderService; +import org.hibernate.service.classloading.spi.ClassLoadingException; +import org.hibernate.service.spi.SessionFactoryServiceRegistry; + +/** + * @author Steve Ebersole + */ +public class CallbackProcessorImpl implements CallbackProcessor { + private static final Logger log = Logger.getLogger( CallbackProcessorImpl.class ); + + private final ListenerFactory jpaListenerFactory; + private final MetadataImplementor metadata; + + private final ClassLoaderService classLoaderService; + + public CallbackProcessorImpl( + ListenerFactory jpaListenerFactory, + MetadataImplementor metadata, + SessionFactoryServiceRegistry serviceRegistry) { + this.jpaListenerFactory = jpaListenerFactory; + this.metadata = metadata; + this.classLoaderService = serviceRegistry.getService( ClassLoaderService.class ); + } + + @Override + public void processCallbacksForEntity(Object entityObject, CallbackRegistryImpl callbackRegistry) { + final EntityBinding entityBinding = (EntityBinding) entityObject; + final String entityClassName = entityBinding.getEntity().getClassName(); + if ( entityClassName == null ) { + return; + } + + try { + final Class entityClass = classLoaderService.classForName( entityClassName ); + for ( Class annotationClass : CALLBACK_ANNOTATION_CLASSES ) { + callbackRegistry.addEntityCallbacks( + entityClass, + annotationClass, + collectCallbacks( entityBinding, entityClass, annotationClass ) + ); + } + } + catch (ClassLoadingException e) { + throw new MappingException( "entity class not found: " + entityClassName, e ); + } + } + + private Callback[] collectCallbacks(EntityBinding entityBinding, Class entityClass, Class annotationClass) { + final List callbacks = new ArrayList(); + for ( JpaCallbackSource jpaCallbackClass : entityBinding.getJpaCallbackClasses() ) { + final Class listenerClass = classLoaderService.classForName( jpaCallbackClass.getName() ); + final String methodName = jpaCallbackClass.getCallbackMethod( annotationClass ); + + log.debugf( + "Adding $s.%s as %s callback for entity %s", + listenerClass.getName(), + methodName, + annotationClass.getName(), + entityClass.getName() + ); + + final Callback callback = jpaCallbackClass.isListener() + ? createListenerCallback( listenerClass, entityClass, methodName ) + : createBeanCallback( listenerClass, methodName ); + assert callback != null; + callbacks.add(callback); + } + return callbacks.toArray(new Callback[callbacks.size()]); + } + + private Callback createListenerCallback( + Class listenerClass, + Class entityClass, + String methodName ) { + final Class callbackSuperclass = listenerClass.getSuperclass(); + if ( callbackSuperclass != null ) { + Callback callback = createListenerCallback( entityClass, callbackSuperclass, methodName ); + if ( callback != null ) { + return callback; + } + } + + final Object listenerInstance = jpaListenerFactory.buildListener( listenerClass ); + for ( Method method : listenerClass.getDeclaredMethods() ) { + if ( !method.getName().equals(methodName) ) { + continue; + } + + final Class[] argTypes = method.getParameterTypes(); + if (argTypes.length != 1) { + continue; + } + + final Class argType = argTypes[0]; + if (argType != Object.class && argType != entityClass) { + continue; + } + if (!method.isAccessible()) { + method.setAccessible( true ); + } + + return new ListenerCallback( listenerInstance, method ); + } + return null; + } + + private Callback createBeanCallback( Class callbackClass, + String methodName ) { + Class callbackSuperclass = callbackClass.getSuperclass(); + if (callbackSuperclass != null) { + Callback callback = createBeanCallback(callbackSuperclass, methodName); + if (callback != null) return callback; + } + for (Method method : callbackClass.getDeclaredMethods()) { + if (!method.getName().equals(methodName)) continue; + if (method.getParameterTypes().length != 0) continue; + if (!method.isAccessible()) method.setAccessible(true); + return new EntityCallback(method); + } + return null; + } + + @Override + public void release() { + //To change body of implemented methods use File | Settings | File Templates. + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryConsumer.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryConsumer.java new file mode 100644 index 0000000000..804da6dc98 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryConsumer.java @@ -0,0 +1,42 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2009-2011, 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.event.internal.jpa; + +import org.hibernate.jpa.event.internal.core.HibernateEntityManagerEventListener; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; + +/** + * Contract for injecting the registry of Callbacks into event listeners. + * + * @author Emmanuel Bernard + * @author Steve Ebersole + */ +public interface CallbackRegistryConsumer extends HibernateEntityManagerEventListener { + /** + * Injection of the CallbackRegistry + * + * @param callbackRegistry The CallbackRegistry + */ + public void injectCallbackRegistry(CallbackRegistry callbackRegistry); +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryImpl.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryImpl.java new file mode 100644 index 0000000000..10e40f33e7 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/CallbackRegistryImpl.java @@ -0,0 +1,180 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2009-2011, 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.event.internal.jpa; + +import javax.persistence.PersistenceException; +import javax.persistence.PostLoad; +import javax.persistence.PostPersist; +import javax.persistence.PostRemove; +import javax.persistence.PostUpdate; +import javax.persistence.PrePersist; +import javax.persistence.PreRemove; +import javax.persistence.PreUpdate; +import java.util.HashMap; + +import org.hibernate.jpa.event.spi.jpa.Callback; +import org.hibernate.jpa.event.spi.jpa.CallbackRegistry; + +/** + * Keep track of all lifecycle callbacks and listeners for a given persistence unit + * + * @author Kabir Khan + * @author Steve Ebersole + */ +@SuppressWarnings({"unchecked", "serial"}) +public class CallbackRegistryImpl implements CallbackRegistry { + private HashMap preCreates = new HashMap(); + private HashMap postCreates = new HashMap(); + private HashMap preRemoves = new HashMap(); + private HashMap postRemoves = new HashMap(); + private HashMap preUpdates = new HashMap(); + private HashMap postUpdates = new HashMap(); + private HashMap postLoads = new HashMap(); + + @Override + public void preCreate(Object bean) { + callback( preCreates.get( bean.getClass() ), bean ); + } + + @Override + public boolean hasPostCreateCallbacks(Class entityClass) { + return notEmpty( preCreates.get( entityClass ) ); + } + + private boolean notEmpty(Callback[] callbacks) { + return callbacks != null && callbacks.length > 0; + } + + @Override + public void postCreate(Object bean) { + callback( postCreates.get( bean.getClass() ), bean ); + } + + @Override + public boolean preUpdate(Object bean) { + return callback( preUpdates.get( bean.getClass() ), bean ); + } + + @Override + public boolean hasPostUpdateCallbacks(Class entityClass) { + return notEmpty( postUpdates.get( entityClass ) ); + } + + @Override + public void postUpdate(Object bean) { + callback( postUpdates.get( bean.getClass() ), bean ); + } + + @Override + public void preRemove(Object bean) { + callback( preRemoves.get( bean.getClass() ), bean ); + } + + @Override + public boolean hasPostRemoveCallbacks(Class entityClass) { + return notEmpty( postRemoves.get( entityClass ) ); + } + + @Override + public void postRemove(Object bean) { + callback( postRemoves.get( bean.getClass() ), bean ); + } + + @Override + public boolean postLoad(Object bean) { + return callback( postLoads.get( bean.getClass() ), bean ); + } + + private boolean callback(Callback[] callbacks, Object bean) { + if ( callbacks != null && callbacks.length != 0 ) { + for ( Callback callback : callbacks ) { + callback.performCallback( bean ); + } + return true; + } + else { + return false; + } + } + + + /** + * Great care should be taken calling this. Not a fan of it being public, but that is needed because of + * @param entityClass + * @param annotationClass + * @param callbacks + */ + public void addEntityCallbacks(Class entityClass, Class annotationClass, Callback[] callbacks) { + final HashMap map = determineAppropriateCallbackMap( annotationClass ); + if ( map.containsKey( entityClass ) ) { + throw new PersistenceException( "Error build callback listeners; entity [" + entityClass.getName() + " was already processed" ); + } + map.put( entityClass, callbacks ); + } + + private HashMap determineAppropriateCallbackMap(Class annotationClass) { + if ( PrePersist.class.equals( annotationClass ) ) { + return preCreates; + } + + if ( PostPersist.class.equals( annotationClass ) ) { + return postCreates; + } + + if ( PreRemove.class.equals( annotationClass ) ) { + return preRemoves; + } + + if ( PostRemove.class.equals( annotationClass ) ) { + return postRemoves; + } + + if ( PreUpdate.class.equals( annotationClass ) ) { + return preUpdates; + } + + if ( PostUpdate.class.equals( annotationClass ) ) { + return postUpdates; + } + + if ( PostLoad.class.equals( annotationClass ) ) { + return postLoads; + } + + throw new PersistenceException( "Unrecognized JPA callback annotation [" + annotationClass.getName() + "]" ); + } + + public void release() { + preCreates.clear(); + postCreates.clear(); + + preRemoves.clear(); + postRemoves.clear(); + + preUpdates.clear(); + postUpdates.clear(); + + postLoads.clear(); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/BeanCallback.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/EntityCallback.java similarity index 75% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/BeanCallback.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/EntityCallback.java index 445aadcb37..80c27758c9 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/BeanCallback.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/EntityCallback.java @@ -21,22 +21,31 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.jpa; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import org.hibernate.jpa.event.spi.jpa.Callback; + /** + * Represents a JPA callback on the entity itself + * * @author Kabir Khan + * @author Steve Ebersole */ -public class BeanCallback extends Callback { - public BeanCallback(Method callbackMethod) { - super( callbackMethod ); +public class EntityCallback implements Callback { + private Method callbackMethod; + + public EntityCallback(Method callbackMethod) { + this.callbackMethod = callbackMethod; } - public void invoke(Object bean) { + @Override + public boolean performCallback(Object entity) { try { - callbackMethod.invoke( bean, new Object[0] ); + callbackMethod.invoke( entity ); + return true; } catch (InvocationTargetException e) { //keep runtime exceptions as is @@ -52,5 +61,8 @@ public class BeanCallback extends Callback { } } - + @Override + public boolean isActive() { + return true; + } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/CallbackResolver.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/LegacyCallbackProcessor.java similarity index 58% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/CallbackResolver.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/LegacyCallbackProcessor.java index afa3a3cfc7..1109da2684 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/CallbackResolver.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/LegacyCallbackProcessor.java @@ -1,7 +1,7 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2009-2011, Red Hat Inc. or third-party contributors as + * Copyright (c) 2012, 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. @@ -21,14 +21,8 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.jpa; -import java.lang.annotation.Annotation; -import java.lang.annotation.ElementType; -import java.lang.annotation.Target; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; import javax.persistence.Entity; import javax.persistence.EntityListeners; import javax.persistence.ExcludeDefaultListeners; @@ -36,41 +30,54 @@ import javax.persistence.ExcludeSuperclassListeners; import javax.persistence.MappedSuperclass; import javax.persistence.PersistenceException; +import java.lang.annotation.Annotation; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; + import org.jboss.logging.Logger; +import org.hibernate.MappingException; import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.annotations.common.reflection.XClass; import org.hibernate.annotations.common.reflection.XMethod; -import org.hibernate.jpa.internal.EntityManagerMessageLogger; -import org.hibernate.metamodel.spi.binding.EntityBinding; -import org.hibernate.metamodel.spi.source.JpaCallbackSource; -import org.hibernate.service.classloading.spi.ClassLoaderService; +import org.hibernate.jpa.event.spi.jpa.Callback; +import org.hibernate.jpa.event.spi.jpa.ListenerFactory; /** * @author Kabir Khan + * @author Steve Ebersole */ -public final class CallbackResolver { +public class LegacyCallbackProcessor implements CallbackProcessor { + private static final Logger log = Logger.getLogger( LegacyCallbackProcessor.class ); - private static final EntityManagerMessageLogger LOG = Logger.getMessageLogger(EntityManagerMessageLogger.class, - CallbackResolver.class.getName()); + private final ListenerFactory jpaListenerFactory; + private final ReflectionManager reflectionManager; - private static boolean useAnnotationAnnotatedByListener; + public LegacyCallbackProcessor(ListenerFactory jpaListenerFactory, ReflectionManager reflectionManager) { + this.jpaListenerFactory = jpaListenerFactory; + this.reflectionManager = reflectionManager; + } - static { - //check whether reading annotations of annotations is useful or not - useAnnotationAnnotatedByListener = false; - Target target = EntityListeners.class.getAnnotation( Target.class ); - if ( target != null ) { - for ( ElementType type : target.value() ) { - if ( type.equals( ElementType.ANNOTATION_TYPE ) ) useAnnotationAnnotatedByListener = true; + @Override + public void processCallbacksForEntity(Object entityObject, CallbackRegistryImpl callbackRegistry) { + final String entityClassName = (String) entityObject; + try { + final XClass entityXClass = reflectionManager.classForName( entityClassName, this.getClass() ); + final Class entityClass = reflectionManager.toClass( entityXClass ); + for ( Class annotationClass : CALLBACK_ANNOTATION_CLASSES ) { + final Callback[] callbacks = resolveCallbacks( entityXClass, annotationClass, reflectionManager ); + callbackRegistry.addEntityCallbacks( entityClass, annotationClass, callbacks ); } } + catch (ClassNotFoundException e) { + throw new MappingException( "entity class not found: " + entityClassName, e ); + } } - private CallbackResolver() { - } - - public static Callback[] resolveCallback(XClass beanClass, Class annotation, ReflectionManager reflectionManager) { + public Callback[] resolveCallbacks(XClass beanClass, Class annotation, ReflectionManager reflectionManager) { List callbacks = new ArrayList(); List callbacksMethodNames = new ArrayList(); //used to track overridden methods List orderedListeners = new ArrayList(); @@ -89,7 +96,7 @@ public final class CallbackResolver { if ( ! callbacksMethodNames.contains( methodName ) ) { //overridden method, remove the superclass overridden method if ( callback == null ) { - callback = new BeanCallback( method ); + callback = new EntityCallback( method ); Class returnType = method.getReturnType(); Class[] args = method.getParameterTypes(); if ( returnType != Void.TYPE || args.length != 0 ) { @@ -98,11 +105,11 @@ public final class CallbackResolver { .getName() + " - " + xMethod ); } - if (!method.isAccessible()) method.setAccessible(true); - LOG.debugf("Adding %s as %s callback for entity %s", - methodName, - annotation.getSimpleName(), - beanClass.getName()); + if (!method.isAccessible()) method.setAccessible(true); + log.debugf("Adding %s as %s callback for entity %s", + methodName, + annotation.getSimpleName(), + beanClass.getName()); callbacks.add( 0, callback ); //superclass first callbacksMethodNames.add( 0, methodName ); } @@ -158,21 +165,8 @@ public final class CallbackResolver { if ( ! callbacksMethodNames.contains( methodName ) ) { //overridden method, remove the superclass overridden method if ( callback == null ) { - try { - callback = new ListenerCallback( method, listener.newInstance() ); - } - catch (IllegalAccessException e) { - throw new PersistenceException( - "Unable to create instance of " + listener.getName() - + " as a listener of beanClass", e - ); - } - catch (InstantiationException e) { - throw new PersistenceException( - "Unable to create instance of " + listener.getName() - + " as a listener of beanClass", e - ); - } + callback = new ListenerCallback( jpaListenerFactory.buildListener( listener ), method ); + Class returnType = method.getReturnType(); Class[] args = method.getParameterTypes(); if ( returnType != Void.TYPE || args.length != 1 ) { @@ -181,11 +175,11 @@ public final class CallbackResolver { .getName() + " - " + method ); } - if (!method.isAccessible()) method.setAccessible(true); - LOG.debugf("Adding %s as %s callback for entity %s", - methodName, - annotation.getSimpleName(), - beanClass.getName()); + if (!method.isAccessible()) method.setAccessible(true); + log.debugf("Adding %s as %s callback for entity %s", + methodName, + annotation.getSimpleName(), + beanClass.getName()); callbacks.add( 0, callback ); // listeners first } else { @@ -203,61 +197,18 @@ public final class CallbackResolver { return callbacks.toArray( new Callback[ callbacks.size() ] ); } - public static Callback[] resolveCallbacks( Class entityClass, - Class callbackClass, - ClassLoaderService classLoaderService, - EntityBinding binding ) { - List callbacks = new ArrayList(); - for (JpaCallbackSource jpaCallbackClass : binding.getJpaCallbackClasses()) { - Object listener = classLoaderService.classForName(jpaCallbackClass.getName()); - String methodName = jpaCallbackClass.getCallbackMethod( callbackClass ); - Callback callback = jpaCallbackClass.isListener() ? - createListenerCallback(entityClass, callbackClass, listener, methodName) : - createBeanCallback(callbackClass, methodName); - LOG.debugf("Adding %s as %s callback for entity %s", methodName, callbackClass.getName(), - entityClass.getName()); - assert callback != null; - callbacks.add(callback); - } - return callbacks.toArray(new Callback[callbacks.size()]); - } + private static boolean useAnnotationAnnotatedByListener; - private static Callback createListenerCallback( Class entityClass, - Class callbackClass, - Object listener, - String methodName ) { - Class callbackSuperclass = callbackClass.getSuperclass(); - if (callbackSuperclass != null) { - Callback callback = createListenerCallback(entityClass, callbackSuperclass, listener, methodName); - if (callback != null) return callback; - } - for (Method method : callbackClass.getDeclaredMethods()) { - if (!method.getName().equals(methodName)) continue; - Class[] argTypes = method.getParameterTypes(); - if (argTypes.length != 1) continue; - Class argType = argTypes[0]; - if (argType != Object.class && argType != entityClass) continue; - if (!method.isAccessible()) method.setAccessible(true); - return new ListenerCallback(method, listener); - } - return null; - } - - private static Callback createBeanCallback( Class callbackClass, - String methodName ) { - Class callbackSuperclass = callbackClass.getSuperclass(); - if (callbackSuperclass != null) { - Callback callback = createBeanCallback(callbackSuperclass, methodName); - if (callback != null) return callback; - } - for (Method method : callbackClass.getDeclaredMethods()) { - if (!method.getName().equals(methodName)) continue; - if (method.getParameterTypes().length != 0) continue; - if (!method.isAccessible()) method.setAccessible(true); - return new BeanCallback(method); - } - return null; - } + static { + //check whether reading annotations of annotations is useful or not + useAnnotationAnnotatedByListener = false; + Target target = EntityListeners.class.getAnnotation( Target.class ); + if ( target != null ) { + for ( ElementType type : target.value() ) { + if ( type.equals( ElementType.ANNOTATION_TYPE ) ) useAnnotationAnnotatedByListener = true; + } + } + } private static void getListeners(XClass currentClazz, List orderedListeners) { EntityListeners entityListeners = currentClazz.getAnnotation( EntityListeners.class ); @@ -282,4 +233,9 @@ public final class CallbackResolver { } } } + + @Override + public void release() { + // nothing to do here + } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/ListenerCallback.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerCallback.java similarity index 57% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/ListenerCallback.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerCallback.java index 0796d3f269..ebabc25a42 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/ListenerCallback.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/ListenerCallback.java @@ -21,31 +21,33 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.jpa; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.jpa.event.spi.jpa.Callback; /** + * Represents a JPA callback using a dedicated listener + * * @author Kabir Khan + * @author Steve Ebersole */ -public class ListenerCallback extends Callback { - protected transient Object listener; +public class ListenerCallback implements Callback { + private final Method callbackMethod; + private final Object listenerInstance; - public ListenerCallback(Method callbackMethod, Object listener) { - super( callbackMethod ); - this.listener = listener; + public ListenerCallback(Object listenerInstance, Method callbackMethod) { + this.listenerInstance = listenerInstance; + this.callbackMethod = callbackMethod; } @Override - public void invoke(Object bean) { + public boolean performCallback(Object entity) { try { - callbackMethod.invoke( listener, new Object[]{bean} ); + callbackMethod.invoke( listenerInstance, entity ); + return true; } catch (InvocationTargetException e) { //keep runtime exceptions as is @@ -61,22 +63,8 @@ public class ListenerCallback extends Callback { } } - private void writeObject(ObjectOutputStream oos) throws IOException { - oos.defaultWriteObject(); - oos.writeObject( listener.getClass().getName() ); - } - - private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { - ois.defaultReadObject(); - String listenerClass = (String) ois.readObject(); - try { - listener = ReflectHelper.classForName( listenerClass, this.getClass() ).newInstance(); - } - catch (InstantiationException e) { - throw new ClassNotFoundException( "Unable to load class:" + listenerClass, e ); - } - catch (IllegalAccessException e) { - throw new ClassNotFoundException( "Unable to load class:" + listenerClass, e ); - } + @Override + public boolean isActive() { + return true; } } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/StandardListenerFactory.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/StandardListenerFactory.java new file mode 100644 index 0000000000..feb2d02dcb --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/StandardListenerFactory.java @@ -0,0 +1,64 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.event.internal.jpa; + +import javax.persistence.PersistenceException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.hibernate.jpa.event.spi.jpa.ListenerFactory; + +/** + * Standard implementation of the ListenerFactory contract using simple instantiation. Listener instances + * are kept in a map keyed by Class to achieve singleton-ness. + * + * @author Steve Ebersole + */ +public class StandardListenerFactory implements ListenerFactory { + private Map listenerInstances = new ConcurrentHashMap(); + + @Override + @SuppressWarnings("unchecked") + public T buildListener(Class listenerClass) { + Object listenerInstance = listenerInstances.get( listenerClass ); + if ( listenerInstance == null ) { + try { + listenerInstance = listenerClass.newInstance(); + } + catch (Exception e) { + throw new PersistenceException( + "Unable to create instance of " + listenerClass.getName() + " as a JPA callback listener", + e + ); + } + listenerInstances.put( listenerClass, listenerInstance ); + } + return (T) listenerInstance; + } + + @Override + public void release() { + listenerInstances.clear(); + } +} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/CallbackHandlerConsumer.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/package-info.java similarity index 76% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/CallbackHandlerConsumer.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/package-info.java index 420435bea4..978b9da95c 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/CallbackHandlerConsumer.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/internal/jpa/package-info.java @@ -1,7 +1,7 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2009-2011, Red Hat Inc. or third-party contributors as + * Copyright (c) 2012, 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. @@ -21,11 +21,8 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.internal.jpa; /** - * @author Emmanuel Bernard - */ -public interface CallbackHandlerConsumer extends HibernateEntityManagerEventListener { - void setCallbackHandler(EntityCallbackHandler callbackHandler); -} + * Classes for integrating with JPA event callbacks + */ \ No newline at end of file diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaIntegrator.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java similarity index 67% rename from hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaIntegrator.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java index c368e4e4d7..b7754ad78b 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/JpaIntegrator.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/JpaIntegrator.java @@ -21,23 +21,47 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.jpa.internal.event; +package org.hibernate.jpa.event.spi; +import javax.enterprise.inject.spi.BeanManager; import java.util.Iterator; import java.util.Map; import org.hibernate.HibernateException; -import org.hibernate.MappingException; -import org.hibernate.annotations.common.reflection.ReflectionManager; import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; -import org.hibernate.ejb.AvailableSettings; +import org.hibernate.engine.spi.CascadeStyles; +import org.hibernate.engine.spi.CascadingAction; +import org.hibernate.engine.spi.CascadingActions; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.event.service.spi.DuplicationStrategy; import org.hibernate.event.service.spi.EventListenerGroup; import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.EventType; import org.hibernate.integrator.spi.Integrator; +import org.hibernate.jpa.AvailableSettings; +import org.hibernate.jpa.event.internal.core.HibernateEntityManagerEventListener; +import org.hibernate.jpa.event.internal.core.JpaAutoFlushEventListener; +import org.hibernate.jpa.event.internal.core.JpaDeleteEventListener; +import org.hibernate.jpa.event.internal.core.JpaFlushEntityEventListener; +import org.hibernate.jpa.event.internal.core.JpaFlushEventListener; +import org.hibernate.jpa.event.internal.core.JpaMergeEventListener; +import org.hibernate.jpa.event.internal.core.JpaPersistEventListener; +import org.hibernate.jpa.event.internal.core.JpaPersistOnFlushEventListener; +import org.hibernate.jpa.event.internal.core.JpaPostDeleteEventListener; +import org.hibernate.jpa.event.internal.core.JpaPostInsertEventListener; +import org.hibernate.jpa.event.internal.core.JpaPostLoadEventListener; +import org.hibernate.jpa.event.internal.core.JpaPostUpdateEventListener; +import org.hibernate.jpa.event.internal.core.JpaSaveEventListener; +import org.hibernate.jpa.event.internal.core.JpaSaveOrUpdateEventListener; +import org.hibernate.jpa.event.internal.jpa.BeanManagerListenerFactory; +import org.hibernate.jpa.event.internal.jpa.CallbackProcessor; +import org.hibernate.jpa.event.internal.jpa.CallbackProcessorImpl; +import org.hibernate.jpa.event.internal.jpa.CallbackRegistryConsumer; +import org.hibernate.jpa.event.internal.jpa.CallbackRegistryImpl; +import org.hibernate.jpa.event.internal.jpa.LegacyCallbackProcessor; +import org.hibernate.jpa.event.spi.jpa.ListenerFactory; +import org.hibernate.jpa.event.internal.jpa.StandardListenerFactory; import org.hibernate.mapping.PersistentClass; import org.hibernate.metamodel.spi.binding.EntityBinding; import org.hibernate.metamodel.spi.MetadataImplementor; @@ -47,16 +71,19 @@ import org.hibernate.secure.internal.JACCPreLoadEventListener; import org.hibernate.secure.internal.JACCPreUpdateEventListener; import org.hibernate.secure.internal.JACCSecurityListener; import org.hibernate.service.classloading.spi.ClassLoaderService; -import org.hibernate.service.classloading.spi.ClassLoadingException; import org.hibernate.service.spi.ServiceRegistryImplementor; import org.hibernate.service.spi.SessionFactoryServiceRegistry; /** - * Prepare the HEM-specific event listeners. + * Hibernate EntityManager specific Integrator, performing JPA setup. * * @author Steve Ebersole */ public class JpaIntegrator implements Integrator { + private ListenerFactory jpaListenerFactory; + private CallbackProcessor callbackProcessor; + private CallbackRegistryImpl callbackRegistry; + private static final DuplicationStrategy JPA_DUPLICATION_STRATEGY = new DuplicationStrategy() { @Override public boolean areMatch(Object listener, Object original) { @@ -89,6 +116,25 @@ public class JpaIntegrator implements Integrator { Configuration configuration, SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { + // first, register the JPA-specific persist cascade style + CascadeStyles.registerCascadeStyle( + "persist", + new CascadeStyles.BaseCascadeStyle() { + @Override + public boolean doCascade(CascadingAction action) { + return action == JpaPersistEventListener.PERSIST_SKIPLAZY + || action == CascadingActions.PERSIST_ON_FLUSH; + } + + @Override + public String toString() { + return "STYLE_PERSIST_SKIPLAZY"; + } + } + ); + + + // then prepare listeners final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class ); boolean isSecurityEnabled = configuration.getProperties().containsKey( AvailableSettings.JACC_ENABLED ); @@ -138,28 +184,30 @@ public class JpaIntegrator implements Integrator { } } - final EntityCallbackHandler callbackHandler = new EntityCallbackHandler(); + // handle JPA "entity listener classes"... + + this.callbackRegistry = new CallbackRegistryImpl(); + final BeanManager beanManager = (BeanManager) configuration.getProperties().get( AvailableSettings.CDI_BEAN_MANAGER ); + this.jpaListenerFactory = beanManager == null + ? new StandardListenerFactory() + : new BeanManagerListenerFactory( beanManager ); + this.callbackProcessor = new LegacyCallbackProcessor( jpaListenerFactory, configuration.getReflectionManager() ); + Iterator classes = configuration.getClassMappings(); - ReflectionManager reflectionManager = configuration.getReflectionManager(); while ( classes.hasNext() ) { - PersistentClass clazz = (PersistentClass) classes.next(); + final PersistentClass clazz = (PersistentClass) classes.next(); if ( clazz.getClassName() == null ) { - //we can have non java class persisted by hibernate + // we can have non java class persisted by hibernate continue; } - try { - callbackHandler.add( reflectionManager.classForName( clazz.getClassName(), this.getClass() ), reflectionManager ); - } - catch (ClassNotFoundException e) { - throw new MappingException( "entity class not found: " + clazz.getNodeName(), e ); - } + callbackProcessor.processCallbacksForEntity( clazz.getClassName(), callbackRegistry ); } for ( EventType eventType : EventType.values() ) { final EventListenerGroup eventListenerGroup = eventListenerRegistry.getEventListenerGroup( eventType ); for ( Object listener : eventListenerGroup.listeners() ) { - if ( CallbackHandlerConsumer.class.isInstance( listener ) ) { - ( (CallbackHandlerConsumer) listener ).setCallbackHandler( callbackHandler ); + if ( CallbackRegistryConsumer.class.isInstance( listener ) ) { + ( (CallbackRegistryConsumer) listener ).injectCallbackRegistry( callbackRegistry ); } } } @@ -171,9 +219,28 @@ public class JpaIntegrator implements Integrator { * @see org.hibernate.integrator.spi.Integrator#integrate(org.hibernate.metamodel.spi.MetadataImplementor, org.hibernate.engine.spi.SessionFactoryImplementor, org.hibernate.service.spi.SessionFactoryServiceRegistry) */ @Override - public void integrate( MetadataImplementor metadata, - SessionFactoryImplementor sessionFactory, - SessionFactoryServiceRegistry serviceRegistry ) { + public void integrate( + MetadataImplementor metadata, + SessionFactoryImplementor sessionFactory, + SessionFactoryServiceRegistry serviceRegistry ) { + // first, register the JPA-specific persist cascade style + CascadeStyles.registerCascadeStyle( + "persist", + new CascadeStyles.BaseCascadeStyle() { + @Override + public boolean doCascade(CascadingAction action) { + return action == JpaPersistEventListener.PERSIST_SKIPLAZY + || action == CascadingActions.PERSIST_ON_FLUSH; + } + + @Override + public String toString() { + return "STYLE_PERSIST_SKIPLAZY"; + } + } + ); + + // then prepare listeners final EventListenerRegistry eventListenerRegistry = serviceRegistry.getService( EventListenerRegistry.class ); boolean isSecurityEnabled = sessionFactory.getProperties().containsKey( AvailableSettings.JACC_ENABLED ); @@ -223,33 +290,40 @@ public class JpaIntegrator implements Integrator { } } - final EntityCallbackHandler callbackHandler = new EntityCallbackHandler(); - ClassLoaderService classLoaderSvc = serviceRegistry.getService(ClassLoaderService.class); - for (EntityBinding binding : metadata.getEntityBindings()) { - String name = binding.getEntity().getName(); // Should this be getClassName()? - if (name == null) { - //we can have non java class persisted by hibernate - continue; - } - try { - callbackHandler.add(classLoaderSvc.classForName(name), classLoaderSvc, binding); - } catch (ClassLoadingException error) { - throw new MappingException( "entity class not found: " + name, error ); + // handle JPA "entity listener classes"... + + this.callbackRegistry = new CallbackRegistryImpl(); + final BeanManager beanManager = (BeanManager) sessionFactory.getProperties().get( AvailableSettings.CDI_BEAN_MANAGER ); + this.jpaListenerFactory = beanManager == null + ? new StandardListenerFactory() + : new BeanManagerListenerFactory( beanManager ); + this.callbackProcessor = new CallbackProcessorImpl( jpaListenerFactory, metadata, serviceRegistry ); + + for ( EntityBinding binding : metadata.getEntityBindings() ) { + callbackProcessor.processCallbacksForEntity( binding, callbackRegistry ); + } + + for ( EventType eventType : EventType.values() ) { + final EventListenerGroup eventListenerGroup = eventListenerRegistry.getEventListenerGroup( eventType ); + for ( Object listener : eventListenerGroup.listeners() ) { + if ( CallbackRegistryConsumer.class.isInstance( listener ) ) { + ( (CallbackRegistryConsumer) listener ).injectCallbackRegistry( callbackRegistry ); + } } } -// -// for ( EventType eventType : EventType.values() ) { -// final EventListenerGroup eventListenerGroup = eventListenerRegistry.getEventListenerGroup( eventType ); -// for ( Object listener : eventListenerGroup.listeners() ) { -// if ( CallbackHandlerConsumer.class.isInstance( listener ) ) { -// ( (CallbackHandlerConsumer) listener ).setCallbackHandler( callbackHandler ); -// } -// } -// } } @Override public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { + if ( callbackRegistry != null ) { + callbackRegistry.release(); + } + if ( callbackProcessor != null ) { + callbackProcessor.release(); + } + if ( jpaListenerFactory != null ) { + jpaListenerFactory.release(); + } } private Object instantiate(String listenerImpl, ServiceRegistryImplementor serviceRegistry) { diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/Callback.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/Callback.java new file mode 100644 index 0000000000..daf7860d86 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/Callback.java @@ -0,0 +1,50 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2009-2011, 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.event.spi.jpa; + +import java.io.Serializable; + +/** + * Represents a JPA event callback. + * + * @author Kabir Khan + * @author Steve Ebersole + */ +public interface Callback extends Serializable { + /** + * Contract for performing the callback + * + * @param entity Reference to the entity for which the callback is triggered. + * + * @return Did a callback actually happen? + */ + public boolean performCallback(Object entity); + + /** + * Is this callback active (will it do anything)? + * + * @return + */ + public boolean isActive(); +} diff --git a/hibernate-core/src/main/java/org/jboss/jandex/IndexResult.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackRegistry.java similarity index 54% rename from hibernate-core/src/main/java/org/jboss/jandex/IndexResult.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackRegistry.java index a85fc392bc..365ed203d2 100644 --- a/hibernate-core/src/main/java/org/jboss/jandex/IndexResult.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/CallbackRegistry.java @@ -21,36 +21,25 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.jboss.jandex; +package org.hibernate.jpa.event.spi.jpa; -import java.util.Collection; +import java.io.Serializable; /** - * The basic contract for accessing Jandex indexed information. - * - * @author Jason Greene * @author Steve Ebersole */ -public interface IndexResult { - public Collection getKnownClasses(); +public interface CallbackRegistry extends Serializable { + public void preCreate(Object entity); + public boolean hasPostCreateCallbacks(Class entityClass); + public void postCreate(Object entity); - public ClassInfo getClassByName(DotName className); + public boolean preUpdate(Object entity); + public boolean hasPostUpdateCallbacks(Class entityClass); + public void postUpdate(Object entity); - public Collection getKnownDirectSubclasses(DotName className); + public void preRemove(Object entity); + public boolean hasPostRemoveCallbacks(Class entityClass); + public void postRemove(Object entity); - /** - * Returns all known (including non-direct) sub classes of the given class. I.e., returns all known classes - * that are assignable to the given class. - * - * @param className The class - * - * @return All known subclasses - */ - public Collection getAllKnownSubclasses(final DotName className); - - public Collection getKnownDirectImplementors(DotName className); - - public Collection getAllKnownImplementors(final DotName interfaceName); - - public Collection getAnnotations(DotName annotationName); + public boolean postLoad(Object entity); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/engine/spi/JpaCascadeStyle.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/ListenerFactory.java similarity index 54% rename from hibernate-entitymanager/src/main/java/org/hibernate/engine/spi/JpaCascadeStyle.java rename to hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/ListenerFactory.java index 2b1b515161..de227b5a5d 100644 --- a/hibernate-entitymanager/src/main/java/org/hibernate/engine/spi/JpaCascadeStyle.java +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/ListenerFactory.java @@ -1,7 +1,7 @@ /* * Hibernate, Relational Persistence for Idiomatic Java * - * Copyright (c) 2009-2012, Red Hat Inc. or third-party contributors as + * Copyright (c) 2012, 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. @@ -21,34 +21,15 @@ * 51 Franklin Street, Fifth Floor * Boston, MA 02110-1301 USA */ -package org.hibernate.engine.spi; +package org.hibernate.jpa.event.spi.jpa; /** - * Because CascadeStyle is not opened and package protected, - * I need to subclass and override the persist alias + * Factory for building instances user-specified event listeners. * - * Note that This class has to be triggered by JpaPersistEventListener at class loading time - * - * TODO get rid of it for 3.3 - * - * @author Emmanuel Bernard + * @author Steve Ebersole */ -public abstract class JpaCascadeStyle extends CascadeStyle { +public interface ListenerFactory { + public T buildListener(Class listenerClass); - /** - * cascade using JpaCascadingAction - */ - public static final CascadeStyle PERSIST_JPA = new CascadeStyle() { - public boolean doCascade(CascadingAction action) { - return action== JpaCascadingAction.PERSIST_SKIPLAZY - || action==CascadingAction.PERSIST_ON_FLUSH; - } - public String toString() { - return "STYLE_PERSIST_SKIPLAZY"; - } - }; - - static { - STYLES.put( "persist", PERSIST_JPA ); - } + public void release(); } diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/package-info.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/package-info.java new file mode 100644 index 0000000000..2ff6682a86 --- /dev/null +++ b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/event/spi/jpa/package-info.java @@ -0,0 +1,28 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.event.spi.jpa; + +/** + * SPI classes for integrating with JPA event callbacks + */ \ No newline at end of file diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/Callback.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/Callback.java deleted file mode 100644 index 6c6a697968..0000000000 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/Callback.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2009-2011, 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.internal.event; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; -import java.util.StringTokenizer; - -import org.hibernate.internal.util.ReflectHelper; - -/** - * @author Kabir Khan - */ -public abstract class Callback implements Serializable { - transient protected Method callbackMethod; - - public Callback(Method callbackMethod) { - this.callbackMethod = callbackMethod; - } - - public Method getCallbackMethod() { - return callbackMethod; - } - - public abstract void invoke(Object bean); - - private void writeObject(ObjectOutputStream oos) throws IOException { - oos.defaultWriteObject(); - oos.writeObject( callbackMethod.toGenericString() ); - } - - private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { - ois.defaultReadObject(); - String signature = (String) ois.readObject(); - StringTokenizer st = new StringTokenizer( signature, " ", false ); - String usefulSignature = null; - while ( st.hasMoreElements() ) usefulSignature = (String) st.nextElement(); - int parenthesis = usefulSignature.indexOf( "(" ); - String methodAndClass = usefulSignature.substring( 0, parenthesis ); - int lastDot = methodAndClass.lastIndexOf( "." ); - String clazzName = methodAndClass.substring( 0, lastDot ); - Class callbackClass = ReflectHelper.classForName( clazzName, this.getClass() ); - String parametersString = usefulSignature.substring( parenthesis + 1, usefulSignature.length() - 1 ); - st = new StringTokenizer( parametersString, ", ", false ); - List parameters = new ArrayList(); - while ( st.hasMoreElements() ) { - String parameter = (String) st.nextElement(); - parameters.add( ReflectHelper.classForName( parameter, this.getClass() ) ); - } - String methodName = methodAndClass.substring( lastDot + 1, methodAndClass.length() ); - try { - callbackMethod = callbackClass.getDeclaredMethod( - methodName, - parameters.toArray( new Class[ parameters.size() ] ) - ); - if ( ! callbackMethod.isAccessible() ) { - callbackMethod.setAccessible( true ); - } - } - catch (NoSuchMethodException e) { - throw new IOException( "Unable to get EJB3 callback method: " + signature + ", cause: " + e ); - } - } -} diff --git a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/EntityCallbackHandler.java b/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/EntityCallbackHandler.java deleted file mode 100644 index ff92ed637a..0000000000 --- a/hibernate-entitymanager/src/main/java/org/hibernate/jpa/internal/event/EntityCallbackHandler.java +++ /dev/null @@ -1,134 +0,0 @@ -/* - * Hibernate, Relational Persistence for Idiomatic Java - * - * Copyright (c) 2009-2011, 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.internal.event; - -import java.io.Serializable; -import java.util.HashMap; -import javax.persistence.PostLoad; -import javax.persistence.PostPersist; -import javax.persistence.PostRemove; -import javax.persistence.PostUpdate; -import javax.persistence.PrePersist; -import javax.persistence.PreRemove; -import javax.persistence.PreUpdate; - -import org.hibernate.annotations.common.reflection.ReflectionManager; -import org.hibernate.annotations.common.reflection.XClass; -import org.hibernate.metamodel.spi.binding.EntityBinding; -import org.hibernate.service.classloading.spi.ClassLoaderService; - -/** - * Keep track of all lifecycle callbacks and listeners for a given persistence unit - * - * @author Kabir Khan - */ -@SuppressWarnings({"unchecked", "serial"}) -public class EntityCallbackHandler implements Serializable { - private HashMap preCreates = new HashMap(); - private HashMap postCreates = new HashMap(); - private HashMap preRemoves = new HashMap(); - private HashMap postRemoves = new HashMap(); - private HashMap preUpdates = new HashMap(); - private HashMap postUpdates = new HashMap(); - private HashMap postLoads = new HashMap(); - - public void add(XClass entity, ReflectionManager reflectionManager) { - addCallback( entity, preCreates, PrePersist.class, reflectionManager ); - addCallback( entity, postCreates, PostPersist.class, reflectionManager ); - addCallback( entity, preRemoves, PreRemove.class, reflectionManager ); - addCallback( entity, postRemoves, PostRemove.class, reflectionManager ); - addCallback( entity, preUpdates, PreUpdate.class, reflectionManager ); - addCallback( entity, postUpdates, PostUpdate.class, reflectionManager ); - addCallback( entity, postLoads, PostLoad.class, reflectionManager ); - } - - public void add( Class entity, - ClassLoaderService classLoaderService, - EntityBinding binding ) { - addCallback( entity, preCreates, PrePersist.class, classLoaderService, binding ); - addCallback( entity, postCreates, PostPersist.class, classLoaderService, binding ); - addCallback( entity, preRemoves, PreRemove.class, classLoaderService, binding ); - addCallback( entity, postRemoves, PostRemove.class, classLoaderService, binding ); - addCallback( entity, preUpdates, PreUpdate.class, classLoaderService, binding ); - addCallback( entity, postUpdates, PostUpdate.class, classLoaderService, binding ); - addCallback( entity, postLoads, PostLoad.class, classLoaderService, binding ); - } - - public boolean preCreate(Object bean) { - return callback( preCreates.get( bean.getClass() ), bean ); - } - - public boolean postCreate(Object bean) { - return callback( postCreates.get( bean.getClass() ), bean ); - } - - public boolean preRemove(Object bean) { - return callback( preRemoves.get( bean.getClass() ), bean ); - } - - public boolean postRemove(Object bean) { - return callback( postRemoves.get( bean.getClass() ), bean ); - } - - public boolean preUpdate(Object bean) { - return callback( preUpdates.get( bean.getClass() ), bean ); - } - - public boolean postUpdate(Object bean) { - return callback( postUpdates.get( bean.getClass() ), bean ); - } - - public boolean postLoad(Object bean) { - return callback( postLoads.get( bean.getClass() ), bean ); - } - - - private boolean callback(Callback[] callbacks, Object bean) { - if ( callbacks != null && callbacks.length != 0 ) { - for ( Callback callback : callbacks ) { - callback.invoke( bean ); - } - return true; - } - else { - return false; - } - } - - private void addCallback( - XClass entity, HashMap map, Class annotation, ReflectionManager reflectionManager - ) { - Callback[] callbacks = null; - callbacks = CallbackResolver.resolveCallback( entity, annotation, reflectionManager ); - map.put( reflectionManager.toClass( entity ), callbacks ); - } - - private void addCallback( Class entity, - HashMap map, - Class annotation, - ClassLoaderService classLoaderService, - EntityBinding binding ) { - map.put(entity, CallbackResolver.resolveCallbacks(entity, annotation, classLoaderService, binding)); - } -} diff --git a/hibernate-entitymanager/src/test/bundles/cfgxmlpar/org/hibernate/jpa/test/pack/cfgxmlpar/hibernate.cfg.xml b/hibernate-entitymanager/src/test/bundles/cfgxmlpar/org/hibernate/jpa/test/pack/cfgxmlpar/hibernate.cfg.xml index 1c400efa7f..77c89c3cfc 100644 --- a/hibernate-entitymanager/src/test/bundles/cfgxmlpar/org/hibernate/jpa/test/pack/cfgxmlpar/hibernate.cfg.xml +++ b/hibernate-entitymanager/src/test/bundles/cfgxmlpar/org/hibernate/jpa/test/pack/cfgxmlpar/hibernate.cfg.xml @@ -16,6 +16,7 @@ 3 create-drop true + value org.hibernate.testing.cache.CachingRegionFactory diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/BaseEntityManagerFunctionalTestCase.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/BaseEntityManagerFunctionalTestCase.java index 62d3a0ef60..27df78bada 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/BaseEntityManagerFunctionalTestCase.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/BaseEntityManagerFunctionalTestCase.java @@ -40,7 +40,7 @@ import org.jboss.logging.Logger; import org.hibernate.cfg.Environment; import org.hibernate.dialect.Dialect; -import org.hibernate.ejb.AvailableSettings; +import org.hibernate.jpa.AvailableSettings; import org.hibernate.jpa.internal.EntityManagerFactoryImpl; import org.hibernate.internal.util.StringHelper; import org.hibernate.jpa.HibernatePersistenceProvider; diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BaseCDIIntegrationTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BaseCDIIntegrationTest.java new file mode 100644 index 0000000000..6d134fe9ce --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BaseCDIIntegrationTest.java @@ -0,0 +1,66 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.cdi; + +import javax.enterprise.inject.spi.BeanManager; +import java.util.Map; + +import org.jboss.arquillian.container.weld.ee.embedded_1_1.mock.TestContainer; +import org.jboss.weld.bootstrap.api.Environments; + +import org.hibernate.jpa.AvailableSettings; +import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase; + +/** + * @author Steve Ebersole + */ +public abstract class BaseCDIIntegrationTest extends BaseEntityManagerFunctionalTestCase { + private TestContainer testContainer; + + @Override + @SuppressWarnings("unchecked") + protected void addConfigOptions(Map options) { + super.addConfigOptions( options ); + + testContainer = new TestContainer( getCdiBeans() ); + testContainer.getBootstrap().startContainer( Environments.SE, testContainer.getDeployment() ); + testContainer.getBootstrap().startInitialization(); + testContainer.getBootstrap().deployBeans(); + testContainer.getBootstrap().validateBeans().endInitialization(); + options.put( AvailableSettings.CDI_BEAN_MANAGER, getBeanManager() ); + } + + protected BeanManager getBeanManager() { + return testContainer.getBeanManager( testContainer.getDeployment().getBeanDeploymentArchives().iterator().next() ); + } + + public abstract Class[] getCdiBeans(); + + @Override + public void releaseResources() { + super.releaseResources(); // closes the EMF + + testContainer.stopContainer(); + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BasicCDITest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BasicCDITest.java new file mode 100644 index 0000000000..a15615f668 --- /dev/null +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/cdi/BasicCDITest.java @@ -0,0 +1,166 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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.cdi; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.InjectionTarget; +import javax.inject.Inject; +import javax.persistence.Entity; +import javax.persistence.EntityListeners; +import javax.persistence.EntityManager; +import javax.persistence.Id; +import javax.persistence.PrePersist; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * @author Steve Ebersole + */ +public class BasicCDITest extends BaseCDIIntegrationTest { + private static int count; + + @Override + public Class[] getCdiBeans() { + return new Class[] { EventQueue.class }; + } + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { MyEntity.class }; + } + + @Test + @SuppressWarnings("unchecked") + public void testIt() { + count = 0; + + EntityManager em = getOrCreateEntityManager(); + em.getTransaction().begin(); + em.persist( new MyEntity( 1 ) ); + em.getTransaction().commit(); + em.close(); + + assertEquals( 1, count ); + + em = getOrCreateEntityManager(); + em.getTransaction().begin(); + em.remove( em.getReference( MyEntity.class, 1 ) ); + em.getTransaction().commit(); + em.close(); + } + + @Entity + @EntityListeners( Monitor.class ) + public static class MyEntity { + private Integer id; + private String name; + + public MyEntity() { + } + + public MyEntity(Integer id) { + this.id = id; + } + + @Id + 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; + } + } + + public static class EventQueue { + private List events; + + public void addEvent(Event anEvent) { + if ( events == null ) { + events = new ArrayList(); + } + events.add( anEvent ); + count++; + } + } + + public static class Event { + private final String who; + private final String what; + private final String when; + + public Event(String who, String what, String when) { + this.who = who; + this.what = what; + this.when = when; + } + + public String getWho() { + return who; + } + + public String getWhat() { + return what; + } + + public String getWhen() { + return when; + } + } + + public static class Monitor { + private final EventQueue eventQueue; + + @Inject + public Monitor(EventQueue eventQueue) { + this.eventQueue = eventQueue; + } + + @PrePersist + public void onCreate(Object entity) { + eventQueue.addEvent( + new Event( entity.toString(), "created", now() ) + ); + } + + private String now() { + return new SimpleDateFormat().format( new Date() ); + } + } +} diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java index 3a079adf9a..57311c6fdf 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/ejb3configuration/PersisterClassProviderTest.java @@ -451,6 +451,11 @@ public class PersisterClassProviderTest { return new Object[0]; } + @Override + public Serializable getIdByUniqueKey(Serializable key, String uniquePropertyName, SessionImplementor session) { + throw new UnsupportedOperationException( "Not supported" ); + } + @Override public Object getCurrentVersion(Serializable id, SessionImplementor session) throws HibernateException { return null; diff --git a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagedEntityManagerTest.java b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagedEntityManagerTest.java index d91eaaf97a..2fed86b63a 100644 --- a/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagedEntityManagerTest.java +++ b/hibernate-entitymanager/src/test/java/org/hibernate/jpa/test/packaging/PackagedEntityManagerTest.java @@ -253,6 +253,9 @@ public class PackagedEntityManagerTest extends PackagingTestCase { addPackageToClasspath( testPackage ); EntityManagerFactory emf = Persistence.createEntityManagerFactory( "cfgxmlpar", new HashMap() ); + + assertTrue( emf.getProperties().containsKey( "test-assertable-setting" ) ); + EntityManager em = emf.createEntityManager(); Item i = new Item(); i.setDescr( "Blah" ); diff --git a/hibernate-envers/hibernate-envers.gradle b/hibernate-envers/hibernate-envers.gradle index ea2f2c150c..5178f839de 100644 --- a/hibernate-envers/hibernate-envers.gradle +++ b/hibernate-envers/hibernate-envers.gradle @@ -1,24 +1,18 @@ -apply plugin: 'java' apply plugin: org.hibernate.build.gradle.testing.matrix.MatrixTestingPlugin dependencies { compile( project( ':hibernate-core' ) ) compile( project( ':hibernate-entitymanager' ) ) - compile( libraries.commons_annotations ) - compile( libraries.dom4j ) provided( [group: 'org.hibernate', name: 'hibernate-tools', version: '3.2.0.ga'] ) provided( libraries.ant ) - testCompile( libraries.junit ) testCompile( project(':hibernate-testing') ) testCompile( project(path: ':hibernate-entitymanager', configuration: 'tests') ) - testCompile( libraries.jpa ) - testRuntime( libraries.h2 ) testRuntime( libraries.javassist ) } sourceSets { main { - generatedJpaMetamodelSrcDir = file( "${buildDir}/generated-src/jpamodelgen/${name}" ) + ext.generatedJpaMetamodelSrcDir = file( "${buildDir}/generated-src/jpamodelgen/${name}" ) java { srcDir generatedJpaMetamodelSrcDir } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/BasicMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/BasicMetadataGenerator.java index c575deac75..06a8e72f00 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/BasicMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/BasicMetadataGenerator.java @@ -64,10 +64,16 @@ public final class BasicMetadataGenerator { Element type_mapping = prop_mapping.addElement("type"); type_mapping.addAttribute("name", typeName); - for (java.util.Map.Entry paramKeyValue : typeParameters.entrySet()) { - Element type_param = type_mapping.addElement("param"); - type_param.addAttribute("name", (String) paramKeyValue.getKey()); - type_param.setText((String) paramKeyValue.getValue()); + + for (Object object : typeParameters.keySet()) { + String keyType = (String) object; + String property = typeParameters.getProperty(keyType); + + if (property != null) { + Element type_param = type_mapping.addElement("param"); + type_param.addAttribute("name", keyType); + type_param.setText(property); + } } } } @@ -82,4 +88,21 @@ public final class BasicMetadataGenerator { return true; } + + @SuppressWarnings({"unchecked"}) + boolean addKeyManyToOne(Element parent, PropertyAuditingData propertyAuditingData, Value value, + SimpleMapperBuilder mapper) { + Type type = value.getType(); + + Element manyToOneElement = parent.addElement("key-many-to-one"); + manyToOneElement.addAttribute("name", propertyAuditingData.getName()); + manyToOneElement.addAttribute("class", type.getName()); + MetadataTools.addColumns(manyToOneElement, value.getColumnIterator()); + + // A null mapper means that we only want to add xml mappings + if (mapper != null) { + mapper.add(propertyAuditingData.getPropertyData()); + } + return true; + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java index 3d9f7b156b..f90fa5c96a 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/CollectionMetadataGenerator.java @@ -153,7 +153,7 @@ public final class CollectionMetadataGenerator { boolean oneToManyAttachedType = type instanceof BagType || type instanceof SetType || type instanceof MapType || type instanceof ListType; boolean inverseOneToMany = (value instanceof OneToMany) && (propertyValue.isInverse()); - boolean owningManyToOneWithJoinTableBidirectional = (value instanceof ManyToOne) && (propertyAuditingData.getAuditMappedBy() != null); + boolean owningManyToOneWithJoinTableBidirectional = (value instanceof ManyToOne) && (propertyAuditingData.getRelationMappedBy() != null); boolean fakeOneToManyBidirectional = (value instanceof OneToMany) && (propertyAuditingData.getAuditMappedBy() != null); if (oneToManyAttachedType && (inverseOneToMany || fakeOneToManyBidirectional || owningManyToOneWithJoinTableBidirectional)) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java index d1147e62aa..f1c22a6b39 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/IdMetadataGenerator.java @@ -41,6 +41,7 @@ import org.hibernate.envers.entities.mapper.id.SingleIdMapper; import org.hibernate.mapping.Component; import org.hibernate.mapping.PersistentClass; import org.hibernate.mapping.Property; +import org.hibernate.type.ManyToOneType; import org.hibernate.type.Type; /** @@ -61,11 +62,17 @@ public final class IdMetadataGenerator { Property property = properties.next(); Type propertyType = property.getType(); if (!"_identifierMapper".equals(property.getName())) { - // Last but one parameter: ids are always insertable - boolean added = mainGenerator.getBasicMetadataGenerator().addBasic(parent, - getIdPersistentPropertyAuditingData(property), - property.getValue(), mapper, true, key); - + boolean added = false; + if (propertyType instanceof ManyToOneType) { + added = mainGenerator.getBasicMetadataGenerator().addKeyManyToOne(parent, + getIdPersistentPropertyAuditingData(property), + property.getValue(), mapper); + } else { + // Last but one parameter: ids are always insertable + added = mainGenerator.getBasicMetadataGenerator().addBasic(parent, + getIdPersistentPropertyAuditingData(property), + property.getValue(), mapper, true, key); + } if (!added) { // If the entity is audited, and a non-supported id component is used, throwing an exception. // If the entity is not audited, then we simply don't support this entity, even in diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java index e280663f6f..b9067cec8e 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/AuditedPropertiesReader.java @@ -410,8 +410,9 @@ public class AuditedPropertiesReader { if (!processPropertyAuditingOverrides(property, propertyData)) { return false; // not audited due to AuditOverride annotation } - addPropertyMapKey(property, propertyData); + addPropertyMapKey(property, propertyData); setPropertyAuditMappedBy(property, propertyData); + setPropertyRelationMappedBy(property, propertyData); return true; } @@ -441,11 +442,14 @@ public class AuditedPropertiesReader { globalCfg.isGlobalWithModifiedFlag() : aud.withModifiedFlag(); } - private void setPropertyAuditMappedBy(XProperty property, PropertyAuditingData propertyData) { + private void setPropertyRelationMappedBy(XProperty property, PropertyAuditingData propertyData) { OneToMany oneToMany = property.getAnnotation(OneToMany.class); if (oneToMany != null && !"".equals(oneToMany.mappedBy())) { - propertyData.setAuditMappedBy(oneToMany.mappedBy()); + propertyData.setRelationMappedBy(oneToMany.mappedBy()); } + } + + private void setPropertyAuditMappedBy(XProperty property, PropertyAuditingData propertyData) { AuditMappedBy auditMappedBy = property.getAnnotation(AuditMappedBy.class); if (auditMappedBy != null) { propertyData.setAuditMappedBy(auditMappedBy.mappedBy()); diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/PropertyAuditingData.java b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/PropertyAuditingData.java index 04408be7ed..68ccce4243 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/PropertyAuditingData.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/configuration/metadata/reader/PropertyAuditingData.java @@ -47,6 +47,7 @@ public class PropertyAuditingData { private final List auditJoinTableOverrides = new ArrayList(0); private RelationTargetAuditMode relationTargetAuditMode; private String auditMappedBy; + private String relationMappedBy; private String positionMappedBy; private boolean forceInsertable; private boolean usingModifiedFlag; @@ -134,6 +135,14 @@ public class PropertyAuditingData { this.auditMappedBy = auditMappedBy; } + public String getRelationMappedBy() { + return relationMappedBy; + } + + public void setRelationMappedBy(String relationMappedBy) { + this.relationMappedBy = relationMappedBy; + } + public String getPositionMappedBy() { return positionMappedBy; } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/EntityInstantiator.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/EntityInstantiator.java index 05b0dd7bb8..b4e07432f2 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/EntityInstantiator.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/EntityInstantiator.java @@ -22,16 +22,22 @@ * Boston, MA 02110-1301 USA */ package org.hibernate.envers.entities; -import java.util.Collection; -import java.util.List; -import java.util.Map; import org.hibernate.envers.configuration.AuditConfiguration; import org.hibernate.envers.entities.mapper.id.IdMapper; +import org.hibernate.envers.entities.mapper.id.MultipleIdMapper; +import org.hibernate.envers.entities.mapper.relation.lazy.ToOneDelegateSessionImplementor; import org.hibernate.envers.exception.AuditException; import org.hibernate.envers.reader.AuditReaderImplementor; import org.hibernate.envers.tools.reflection.ReflectionTools; import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.proxy.HibernateProxy; +import org.hibernate.proxy.LazyInitializer; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Map; /** * @author Adam Warski (adam at warski dot org) @@ -70,6 +76,11 @@ public class EntityInstantiator { IdMapper idMapper = verCfg.getEntCfg().get(entityName).getIdMapper(); Map originalId = (Map) versionsEntity.get(verCfg.getAuditEntCfg().getOriginalIdPropName()); + // Fixes HHH-4751 issue (@IdClass with @ManyToOne relation mapping inside) + // Note that identifiers are always audited + // Replace identifier proxies if do not point to audit tables + replaceNonAuditIdProxies(originalId, revision); + Object primaryKey = idMapper.mapToIdFromMap(originalId); // Checking if the entity is in cache @@ -106,6 +117,25 @@ public class EntityInstantiator { return ret; } + @SuppressWarnings({"unchecked"}) + private void replaceNonAuditIdProxies(Map originalId, Number revision) { + for (Object key : originalId.keySet()) { + Object value = originalId.get(key); + if (value instanceof HibernateProxy) { + HibernateProxy hibernateProxy = (HibernateProxy) value; + LazyInitializer initializer = hibernateProxy.getHibernateLazyInitializer(); + final String entityName = initializer.getEntityName(); + final Serializable entityId = initializer.getIdentifier(); + if (verCfg.getEntCfg().isVersioned(entityName)) { + final String entityClassName = verCfg.getEntCfg().get(entityName).getEntityClassName(); + final ToOneDelegateSessionImplementor delegate = new ToOneDelegateSessionImplementor(versionsReader, ReflectionTools.loadClass(entityClassName), entityId, revision, verCfg); + originalId.put(key, + versionsReader.getSessionImplementor().getFactory().getEntityPersister(entityName).createProxy(entityId, delegate)); + } + } + } + } + @SuppressWarnings({"unchecked"}) public void addInstancesFromVersionsEntities(String entityName, Collection addTo, List versionsEntities, Number revision) { for (Map versionsEntity : versionsEntities) { diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/AbstractOneToOneMapper.java b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/AbstractOneToOneMapper.java index 2a96959f09..b1440b7305 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/AbstractOneToOneMapper.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/entities/mapper/relation/AbstractOneToOneMapper.java @@ -53,8 +53,6 @@ public abstract class AbstractOneToOneMapper extends AbstractToOneMapper { protected abstract Object queryForReferencedEntity(AuditReaderImplementor versionsReader, EntityInfo referencedEntity, Serializable primaryKey, Number revision); - - @Override public void mapModifiedFlagsToMapFromEntity(SessionImplementor session, Map data, Object newObj, Object oldObj) { } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/event/EnversPostDeleteEventListenerImpl.java b/hibernate-envers/src/main/java/org/hibernate/envers/event/EnversPostDeleteEventListenerImpl.java index cc048e4570..9a80ae803c 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/event/EnversPostDeleteEventListenerImpl.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/event/EnversPostDeleteEventListenerImpl.java @@ -29,6 +29,7 @@ import org.hibernate.envers.synchronization.work.AuditWorkUnit; import org.hibernate.envers.synchronization.work.DelWorkUnit; import org.hibernate.event.spi.PostDeleteEvent; import org.hibernate.event.spi.PostDeleteEventListener; +import org.hibernate.persister.entity.EntityPersister; /** * @author Adam Warski (adam at warski dot org) @@ -71,4 +72,9 @@ public class EnversPostDeleteEventListenerImpl extends BaseEnversEventListener i } } } + + @Override + public boolean requiresPostCommitHanding(EntityPersister persister) { + return getAuditConfiguration().getEntCfg().isVersioned( persister.getEntityName() ); + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/event/EnversPostInsertEventListenerImpl.java b/hibernate-envers/src/main/java/org/hibernate/envers/event/EnversPostInsertEventListenerImpl.java index 013bf5eaad..7819b5b549 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/event/EnversPostInsertEventListenerImpl.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/event/EnversPostInsertEventListenerImpl.java @@ -29,6 +29,7 @@ import org.hibernate.envers.synchronization.work.AddWorkUnit; import org.hibernate.envers.synchronization.work.AuditWorkUnit; import org.hibernate.event.spi.PostInsertEvent; import org.hibernate.event.spi.PostInsertEventListener; +import org.hibernate.persister.entity.EntityPersister; /** * @author Adam Warski (adam at warski dot org) @@ -70,4 +71,9 @@ public class EnversPostInsertEventListenerImpl extends BaseEnversEventListener i } } } + + @Override + public boolean requiresPostCommitHanding(EntityPersister persister) { + return getAuditConfiguration().getEntCfg().isVersioned( persister.getEntityName() ); + } } diff --git a/hibernate-envers/src/main/java/org/hibernate/envers/event/EnversPostUpdateEventListenerImpl.java b/hibernate-envers/src/main/java/org/hibernate/envers/event/EnversPostUpdateEventListenerImpl.java index 31d90cc8fd..659396ee5b 100644 --- a/hibernate-envers/src/main/java/org/hibernate/envers/event/EnversPostUpdateEventListenerImpl.java +++ b/hibernate-envers/src/main/java/org/hibernate/envers/event/EnversPostUpdateEventListenerImpl.java @@ -88,4 +88,9 @@ public class EnversPostUpdateEventListenerImpl extends BaseEnversEventListener i } return newDbState; } + + @Override + public boolean requiresPostCommitHanding(EntityPersister persister) { + return getAuditConfiguration().getEntCfg().isVersioned( persister.getEntityName() ); + } } diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/BaseEnversJPAFunctionalTestCase.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/BaseEnversJPAFunctionalTestCase.java index 8142395b31..3eea5518fa 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/BaseEnversJPAFunctionalTestCase.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/BaseEnversJPAFunctionalTestCase.java @@ -35,6 +35,7 @@ import org.jboss.logging.Logger; import org.hibernate.cfg.Configuration; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.H2Dialect; import org.hibernate.jpa.test.PersistenceUnitDescriptorAdapter; import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper; import org.hibernate.envers.AuditReader; @@ -53,6 +54,7 @@ import org.junit.After; import org.hibernate.testing.AfterClassOnce; import org.hibernate.testing.BeforeClassOnce; import org.hibernate.testing.jta.TestingJtaPlatformImpl; +import org.hibernate.testing.junit4.Helper; /** * @author Strong Liu (stliu@hibernate.org) @@ -114,6 +116,13 @@ public abstract class BaseEnversJPAFunctionalTestCase extends AbstractEnversTest if ( createSchema() ) { settings.put( org.hibernate.cfg.AvailableSettings.HBM2DDL_AUTO, "create-drop" ); + final String secondSchemaName = createSecondSchema(); + if ( StringHelper.isNotEmpty( secondSchemaName ) ) { + if ( !( getDialect() instanceof H2Dialect ) ) { + throw new UnsupportedOperationException( "Only H2 dialect supports creation of second schema." ); + } + Helper.createH2Schema( secondSchemaName, settings ); + } } if ( StringHelper.isNotEmpty( getAuditStrategy() ) ) { @@ -196,6 +205,15 @@ public abstract class BaseEnversJPAFunctionalTestCase extends AbstractEnversTest protected boolean createSchema() { return true; } + + /** + * Feature supported only by H2 dialect. + * @return Provide not empty name to create second schema. + */ + protected String createSecondSchema() { + return null; + } + protected boolean isAudit() { return true; } diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/idclass/ClassType.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/idclass/ClassType.java new file mode 100644 index 0000000000..b077b2d6e4 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/idclass/ClassType.java @@ -0,0 +1,68 @@ +package org.hibernate.envers.test.integration.ids.idclass; + +import java.io.Serializable; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; + +import org.hibernate.envers.Audited; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Audited +@Entity +public class ClassType implements Serializable { + @Id + @Column(name = "Name") + private String type; + + private String description; + + public ClassType() { + } + + public ClassType(String type, String description) { + this.type = type; + this.description = description; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof ClassType)) return false; + + ClassType classType = (ClassType) o; + + if (type != null ? !type.equals(classType.type) : classType.type != null) return false; + + return true; + } + + @Override + public String toString() { + return "ClassType(type = " + type + ", description = " + description + ")"; + } + + @Override + public int hashCode() { + return type != null ? type.hashCode() : 0; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/idclass/IdClassWithRelationTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/idclass/IdClassWithRelationTest.java new file mode 100644 index 0000000000..7556b990e1 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/idclass/IdClassWithRelationTest.java @@ -0,0 +1,116 @@ +package org.hibernate.envers.test.integration.ids.idclass; + +import junit.framework.Assert; +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; +import org.hibernate.envers.test.Priority; +import org.hibernate.testing.TestForIssue; +import org.junit.Test; + +import javax.persistence.EntityManager; +import java.util.Arrays; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@TestForIssue(jiraKey = "HHH-4751") +public class IdClassWithRelationTest extends BaseEnversJPAFunctionalTestCase { + private RelationalClassId entityId = null; + private String typeId = null; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[]{SampleClass.class, RelationalClassId.class, ClassType.class}; + } + + @Test + @Priority(10) + public void initData() { + EntityManager em = getEntityManager(); + + // Revision 1 + em.getTransaction().begin(); + ClassType type = new ClassType("type", "initial description"); + SampleClass entity = new SampleClass(); + entity.setType(type); + entity.setSampleValue("initial data"); + em.persist(type); + em.persist(entity); + em.getTransaction().commit(); + + typeId = type.getType(); + entityId = new RelationalClassId(entity.getId(), new ClassType("type", "initial description")); + + // Revision 2 + em.getTransaction().begin(); + type = em.find(ClassType.class, type.getType()); + type.setDescription("modified description"); + em.merge(type); + em.getTransaction().commit(); + + // Revision 3 + em.getTransaction().begin(); + entity = em.find(SampleClass.class, entityId); + entity.setSampleValue("modified data"); + em.merge(entity); + em.getTransaction().commit(); + + em.close(); + } + + @Test + public void testRevisionsCounts() { + Assert.assertEquals(Arrays.asList(1, 2), getAuditReader().getRevisions(ClassType.class, typeId)); + Assert.assertEquals(Arrays.asList(1, 3), getAuditReader().getRevisions(SampleClass.class, entityId)); + } + + @Test + public void testHistoryOfEntity() { + // given + SampleClass entity = new SampleClass(entityId.getId(), entityId.getType(), "initial data"); + + // when + SampleClass ver1 = getAuditReader().find(SampleClass.class, entityId, 1); + + // then + Assert.assertEquals(entity.getId(), ver1.getId()); + Assert.assertEquals(entity.getSampleValue(), ver1.getSampleValue()); + Assert.assertEquals(entity.getType().getType(), ver1.getType().getType()); + Assert.assertEquals(entity.getType().getDescription(), ver1.getType().getDescription()); + + // given + entity.setSampleValue("modified data"); + entity.getType().setDescription("modified description"); + + // when + SampleClass ver2 = getAuditReader().find(SampleClass.class, entityId, 3); + + // then + Assert.assertEquals(entity.getId(), ver2.getId()); + Assert.assertEquals(entity.getSampleValue(), ver2.getSampleValue()); + Assert.assertEquals(entity.getType().getType(), ver2.getType().getType()); + Assert.assertEquals(entity.getType().getDescription(), ver2.getType().getDescription()); + } + + @Test + public void testHistoryOfType() { + // given + ClassType type = new ClassType(typeId, "initial description"); + + // when + ClassType ver1 = getAuditReader().find(ClassType.class, typeId, 1); + + // then + Assert.assertEquals(type, ver1); + Assert.assertEquals(type.getDescription(), ver1.getDescription()); + + // given + type.setDescription("modified description"); + + // when + ClassType ver2 = getAuditReader().find(ClassType.class, typeId, 2); + + // then + Assert.assertEquals(type, ver2); + Assert.assertEquals(type.getDescription(), ver2.getDescription()); + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/idclass/RelationalClassId.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/idclass/RelationalClassId.java new file mode 100644 index 0000000000..a158b9b177 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/idclass/RelationalClassId.java @@ -0,0 +1,60 @@ +package org.hibernate.envers.test.integration.ids.idclass; + +import java.io.Serializable; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +public class RelationalClassId implements Serializable { + private Long id; + private ClassType type; + + public RelationalClassId() { + } + + public RelationalClassId(Long id, ClassType type) { + this.id = id; + this.type = type; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof RelationalClassId)) return false; + + RelationalClassId that = (RelationalClassId) o; + + if (id != null ? !id.equals(that.id) : that.id != null) return false; + if (type != null ? !type.equals(that.type) : that.type != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (type != null ? type.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "RelationalClassId(id = " + id + ", type = " + type + ")"; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public ClassType getType() { + return type; + } + + public void setType(ClassType type) { + this.type = type; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/idclass/SampleClass.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/idclass/SampleClass.java new file mode 100644 index 0000000000..a5dd4df63a --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/ids/idclass/SampleClass.java @@ -0,0 +1,96 @@ +package org.hibernate.envers.test.integration.ids.idclass; + +import java.io.Serializable; +import javax.persistence.Entity; +import javax.persistence.FetchType; +import javax.persistence.GeneratedValue; +import javax.persistence.Id; +import javax.persistence.IdClass; +import javax.persistence.JoinColumn; +import javax.persistence.ManyToOne; + +import org.hibernate.envers.Audited; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Audited +@Entity +@IdClass(RelationalClassId.class) +public class SampleClass implements Serializable { + @Id + @GeneratedValue + private Long id; + + @Id + @ManyToOne(fetch = FetchType.LAZY, optional = false) + @JoinColumn(name = "ClassTypeName", referencedColumnName = "Name", + insertable = true, updatable = true, nullable = false) + private ClassType type; + + private String sampleValue; + + public SampleClass() { + } + + public SampleClass(ClassType type) { + this.type = type; + } + + public SampleClass(Long id, ClassType type) { + this.id = id; + this.type = type; + } + + public SampleClass(Long id, ClassType type, String sampleValue) { + this.id = id; + this.type = type; + this.sampleValue = sampleValue; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SampleClass)) return false; + + SampleClass sampleClass = (SampleClass) o; + + if (id != null ? !id.equals(sampleClass.id) : sampleClass.id != null) return false; + if (type != null ? !type.equals(sampleClass.type) : sampleClass.type != null) return false; + if (sampleValue != null ? !sampleValue.equals(sampleClass.sampleValue) : sampleClass.sampleValue != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (type != null ? type.hashCode() : 0); + result = 31 * result + (sampleValue != null ? sampleValue.hashCode() : 0); + return result; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public ClassType getType() { + return type; + } + + public void setType(ClassType type) { + this.type = type; + } + + public String getSampleValue() { + return sampleValue; + } + + public void setSampleValue(String sampleValue) { + this.sampleValue = sampleValue; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/embeddedid/Constant.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/embeddedid/Constant.java new file mode 100644 index 0000000000..14ed0b0665 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/embeddedid/Constant.java @@ -0,0 +1,70 @@ +package org.hibernate.envers.test.integration.onetomany.embeddedid; + +import org.hibernate.envers.Audited; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import java.io.Serializable; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +@Audited +public class Constant implements Serializable { + @Id + @Column(length = 3) + private String id; + + private String name; + + public Constant() { + } + + public Constant(String id, String name) { + this.id = id; + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Constant)) return false; + + Constant constant = (Constant) o; + + if (id != null ? !id.equals(constant.id) : constant.id != null) return false; + if (name != null ? !name.equals(constant.name) : constant.name != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = id != null ? id.hashCode() : 0; + result = 31 * result + (name != null ? name.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "Constant(id = " + id + ", name = " + name + ")"; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/embeddedid/MapsIdTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/embeddedid/MapsIdTest.java new file mode 100644 index 0000000000..b5964d4fd0 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/embeddedid/MapsIdTest.java @@ -0,0 +1,130 @@ +package org.hibernate.envers.test.integration.onetomany.embeddedid; + +import javax.persistence.EntityManager; + +import org.hibernate.envers.test.Priority; +import org.hibernate.testing.TestForIssue; +import org.junit.Assert; +import org.junit.Test; + +import org.hibernate.envers.test.BaseEnversJPAFunctionalTestCase; + +import java.util.Arrays; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@TestForIssue(jiraKey = "HHH-7157") +public class MapsIdTest extends BaseEnversJPAFunctionalTestCase { + private PersonTuple tuple1Ver1 = null; + private PersonTuple tuple2Ver1 = null; + private PersonTuple tuple2Ver2 = null; + private Person personCVer1 = null; + private Person personCVer2 = null; + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[]{Person.class, PersonTuple.class, Constant.class}; + } + + @Test + @Priority(10) + public void initData() { + EntityManager em = getEntityManager(); + + // Revision 1 + em.getTransaction().begin(); + Person personA = new Person("Peter"); + Person personB = new Person("Mary"); + em.persist(personA); + em.persist(personB); + Constant cons = new Constant("USD", "US Dollar"); + em.persist(cons); + PersonTuple tuple1 = new PersonTuple(true, personA, personB, cons); + em.persist(tuple1); + em.getTransaction().commit(); + + tuple1Ver1 = new PersonTuple(tuple1.isHelloWorld(), tuple1.getPersonA(), tuple1.getPersonB(), tuple1.getConstant()); + + // Revision 2 + em.getTransaction().begin(); + cons = em.find(Constant.class, cons.getId()); + Person personC1 = new Person("Lukasz"); + em.persist(personC1); + PersonTuple tuple2 = new PersonTuple(true, personA, personC1, cons); + em.persist(tuple2); + em.getTransaction().commit(); + + tuple2Ver1 = new PersonTuple(tuple2.isHelloWorld(), tuple2.getPersonA(), tuple2.getPersonB(), tuple2.getConstant()); + personCVer1 = new Person(personC1.getId(), personC1.getName()); + personCVer1.getPersonBTuples().add(tuple2Ver1); + + // Revision 3 + em.getTransaction().begin(); + tuple2 = em.find(PersonTuple.class, tuple2.getPersonTupleId()); + tuple2.setHelloWorld(false); + em.merge(tuple2); + em.getTransaction().commit(); + + tuple2Ver2 = new PersonTuple(tuple2.isHelloWorld(), tuple2.getPersonA(), tuple2.getPersonB(), tuple2.getConstant()); + + // Revision 4 + em.getTransaction().begin(); + Person personC2 = em.find(Person.class, personC1.getId()); + personC2.setName("Robert"); + em.merge(personC2); + em.getTransaction().commit(); + + personCVer2 = new Person(personC2.getId(), personC2.getName()); + personCVer2.getPersonBTuples().add(tuple2Ver1); + + em.close(); + } + + @Test + public void testRevisionsCounts() { + Assert.assertEquals(Arrays.asList(1), getAuditReader().getRevisions(PersonTuple.class, tuple1Ver1.getPersonTupleId())); + Assert.assertEquals(Arrays.asList(2, 3), getAuditReader().getRevisions(PersonTuple.class, tuple2Ver1.getPersonTupleId())); + Assert.assertEquals(Arrays.asList(2, 4), getAuditReader().getRevisions(Person.class, personCVer1.getId())); + } + + @Test + public void testHistoryOfTuple1() { + PersonTuple tuple = getAuditReader().find(PersonTuple.class, tuple1Ver1.getPersonTupleId(), 1); + + Assert.assertEquals(tuple1Ver1, tuple); + Assert.assertEquals(tuple1Ver1.isHelloWorld(), tuple.isHelloWorld()); + Assert.assertEquals(tuple1Ver1.getPersonA().getId(), tuple.getPersonA().getId()); + Assert.assertEquals(tuple1Ver1.getPersonB().getId(), tuple.getPersonB().getId()); + } + + @Test + public void testHistoryOfTuple2() { + PersonTuple tuple = getAuditReader().find(PersonTuple.class, tuple2Ver2.getPersonTupleId(), 2); + + Assert.assertEquals(tuple2Ver1, tuple); + Assert.assertEquals(tuple2Ver1.isHelloWorld(), tuple.isHelloWorld()); + Assert.assertEquals(tuple2Ver1.getPersonA().getId(), tuple.getPersonA().getId()); + Assert.assertEquals(tuple2Ver1.getPersonB().getId(), tuple.getPersonB().getId()); + + tuple = getAuditReader().find(PersonTuple.class, tuple2Ver2.getPersonTupleId(), 3); + + Assert.assertEquals(tuple2Ver2, tuple); + Assert.assertEquals(tuple2Ver2.isHelloWorld(), tuple.isHelloWorld()); + Assert.assertEquals(tuple2Ver2.getPersonA().getId(), tuple.getPersonA().getId()); + Assert.assertEquals(tuple2Ver2.getPersonB().getId(), tuple.getPersonB().getId()); + } + + @Test + public void testHistoryOfPersonC() { + Person person = getAuditReader().find(Person.class, personCVer1.getId(), 2); + + Assert.assertEquals(personCVer1, person); + Assert.assertEquals(personCVer1.getPersonATuples(), person.getPersonATuples()); + Assert.assertEquals(personCVer1.getPersonBTuples(), person.getPersonBTuples()); + + person = getAuditReader().find(Person.class, personCVer2.getId(), 4); + + Assert.assertEquals(personCVer2, person); + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/embeddedid/Person.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/embeddedid/Person.java new file mode 100644 index 0000000000..f38fea9ee8 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/embeddedid/Person.java @@ -0,0 +1,96 @@ +package org.hibernate.envers.test.integration.onetomany.embeddedid; + +import org.hibernate.envers.Audited; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +@Audited +public class Person implements Serializable { + @Id + @GeneratedValue + private long id; + + private String name; + + @OneToMany(mappedBy = "personA") + private Set personATuples = new HashSet(); + + @OneToMany(mappedBy = "personB") + private Set personBTuples = new HashSet(); + + public Person() { + } + + public Person(String name) { + this.name = name; + } + + public Person(long id, String name) { + this.id = id; + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Person)) return false; + + Person person = (Person) o; + + if (id != person.id) return false; + if (name != null ? !name.equals(person.name) : person.name != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = (int) (id ^ (id >>> 32)); + result = 31 * result + (name != null ? name.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "Person(id = " + id + ", name = " + name + ")"; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Set getPersonBTuples() { + return personBTuples; + } + + public void setPersonBTuples(Set personBTuples) { + this.personBTuples = personBTuples; + } + + public Set getPersonATuples() { + return personATuples; + } + + public void setPersonATuples(Set personATuples) { + this.personATuples = personATuples; + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/embeddedid/PersonTuple.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/embeddedid/PersonTuple.java new file mode 100644 index 0000000000..47c2d9bbd7 --- /dev/null +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/onetomany/embeddedid/PersonTuple.java @@ -0,0 +1,174 @@ +package org.hibernate.envers.test.integration.onetomany.embeddedid; + +import org.hibernate.envers.Audited; + +import javax.persistence.*; +import java.io.Serializable; + +/** + * @author Lukasz Antoniak (lukasz dot antoniak at gmail dot com) + */ +@Entity +@Audited +public class PersonTuple implements Serializable { + @Embeddable + public static class PersonTupleId implements Serializable { + @Column(nullable = false) + private long personAId; + + @Column(nullable = false) + private long personBId; + + @Column(nullable = false) + private String constantId; + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PersonTupleId)) return false; + + PersonTupleId that = (PersonTupleId) o; + + if (personAId != that.personAId) return false; + if (personBId != that.personBId) return false; + if (constantId != null ? !constantId.equals(that.constantId) : that.constantId != null) return false; + + return true; + } + + @Override + public int hashCode() { + int result = (int) (personAId ^ (personAId >>> 32)); + result = 31 * result + (int) (personBId ^ (personBId >>> 32)); + result = 31 * result + (constantId != null ? constantId.hashCode() : 0); + return result; + } + + @Override + public String toString() { + return "PersonTupleId(personAId = " + personAId + ", personBId = " + personBId + ", constantId = " + constantId + ")"; + } + + public long getPersonAId() { + return personAId; + } + + public void setPersonAId(long personAId) { + this.personAId = personAId; + } + + public long getPersonBId() { + return personBId; + } + + public void setPersonBId(long personBId) { + this.personBId = personBId; + } + + public String getConstantId() { + return constantId; + } + + public void setConstantId(String constantId) { + this.constantId = constantId; + } + } + + @EmbeddedId + private PersonTupleId personTupleId = new PersonTupleId(); + + @MapsId("personAId") + @ManyToOne(optional = false) + @JoinColumn(insertable = false, updatable = false) + private Person personA; + + @MapsId("personBId") + @ManyToOne(optional = false) + @JoinColumn(insertable = false, updatable = false) + private Person personB; + + @MapsId("constantId") + @ManyToOne(optional = false) + @JoinColumn(insertable = false, updatable = false) + private Constant constant; + + @Column(nullable = false) + private boolean helloWorld = false; + + public PersonTuple() { + } + + public PersonTuple(boolean helloWorld, Person personA, Person personB, Constant constant) { + this.helloWorld = helloWorld; + this.personA = personA; + this.personB = personB; + this.constant = constant; + + this.personTupleId.personAId = personA.getId(); + this.personTupleId.personBId = personB.getId(); + this.personTupleId.constantId = constant.getId(); + + personA.getPersonATuples().add(this); + personB.getPersonBTuples().add(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof PersonTuple)) return false; + + PersonTuple that = (PersonTuple) o; + + return personTupleId.equals(that.personTupleId); + } + + @Override + public int hashCode() { + return personTupleId.hashCode(); + } + + @Override + public String toString() { + return "PersonTuple(id = " + personTupleId + ", helloWorld = " + helloWorld + ")"; + } + + public PersonTupleId getPersonTupleId() { + return personTupleId; + } + + public void setPersonTupleId(PersonTupleId personTupleId) { + this.personTupleId = personTupleId; + } + + public Person getPersonA() { + return personA; + } + + public void setPersonA(Person personA) { + this.personA = personA; + } + + public Person getPersonB() { + return personB; + } + + public void setPersonB(Person personB) { + this.personB = personB; + } + + public Constant getConstant() { + return constant; + } + + public void setConstant(Constant constant) { + this.constant = constant; + } + + public boolean isHelloWorld() { + return helloWorld; + } + + public void setHelloWorld(boolean helloWorld) { + this.helloWorld = helloWorld; + } +} \ No newline at end of file diff --git a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/reventity/DifferentDBSchemaTest.java b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/reventity/DifferentDBSchemaTest.java index 0f79df33d9..32a1ea6b88 100644 --- a/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/reventity/DifferentDBSchemaTest.java +++ b/hibernate-envers/src/test/java/org/hibernate/envers/test/integration/reventity/DifferentDBSchemaTest.java @@ -29,10 +29,14 @@ public class DifferentDBSchemaTest extends BaseEnversJPAFunctionalTestCase { super.addConfigOptions(options); // Creates new schema after establishing connection options.putAll(Environment.getProperties()); - options.put(Environment.URL, options.get(Environment.URL) + ";INIT=CREATE SCHEMA IF NOT EXISTS " + SCHEMA_NAME); options.put("org.hibernate.envers.default_schema", SCHEMA_NAME); } + @Override + protected String createSecondSchema() { + return SCHEMA_NAME; + } + @Override protected Class[] getAnnotatedClasses() { return new Class[] { StrTestEntity.class }; diff --git a/hibernate-infinispan/hibernate-infinispan.gradle b/hibernate-infinispan/hibernate-infinispan.gradle index 7b8a44f021..6d108b1720 100644 --- a/hibernate-infinispan/hibernate-infinispan.gradle +++ b/hibernate-infinispan/hibernate-infinispan.gradle @@ -1,23 +1,18 @@ -apply plugin: 'java' - configurations { all*.exclude group: 'org.jboss.logging', module: 'jboss-logging-spi' } dependencies { - infinispanVersion = '5.1.4.FINAL' - jnpVersion = '5.0.3.GA' - compile project( ':hibernate-core' ) - compile "org.infinispan:infinispan-core:${infinispanVersion}" - compile "org.rhq.helpers:rhq-pluginAnnotations:3.0.4" + compile( libraries.infinispan ) + compile( libraries.rhq ) testCompile project( ':hibernate-testing' ) - testCompile "org.infinispan:infinispan-core:${infinispanVersion}:tests@jar" - testCompile "org.jboss:jboss-common-core:2.2.14.GA" - testCompile "org.jboss.naming:jnp-client:${jnpVersion}" - testCompile "org.jboss.naming:jnpserver:${jnpVersion}" - testCompile "org.rhq.helpers:rhq-pluginAnnotations:1.4.0.B01" + testCompile( libraries.infinispan_test ) + testCompile( libraries.jboss_common_core ) + testCompile( libraries.jnp_client ) + testCompile( libraries.jnp_server ) + testCompile( libraries.rhq ) } test { diff --git a/hibernate-proxool/hibernate-proxool.gradle b/hibernate-proxool/hibernate-proxool.gradle index b4b2d3ee35..f57746b053 100644 --- a/hibernate-proxool/hibernate-proxool.gradle +++ b/hibernate-proxool/hibernate-proxool.gradle @@ -1,9 +1,6 @@ -apply plugin: 'java' dependencies { compile project( ':hibernate-core' ) - compile "proxool:proxool:0.8.3" - + compile( libraries.proxool ) testCompile project( ':hibernate-testing' ) - testRuntime libraries.h2 } diff --git a/hibernate-testing/hibernate-testing.gradle b/hibernate-testing/hibernate-testing.gradle index 604d0e1adc..efb8bf39ee 100644 --- a/hibernate-testing/hibernate-testing.gradle +++ b/hibernate-testing/hibernate-testing.gradle @@ -1,5 +1,3 @@ -apply plugin: 'java' - dependencies { compile project( ':hibernate-core' ) compile( libraries.junit ) @@ -8,8 +6,8 @@ dependencies { compile( libraries.byteman_bmunit ) compile( libraries.jandex ) compile( libraries.classmate ) - compile "com.experlog:xapool:1.5.0" - compile ( "org.jboss.jbossts:jbossjta:4.15.1.Final" ) { + compile( libraries.xapool ) + compile ( libraries.jboss_jta ) { transitive=false; } } \ No newline at end of file diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java index 42deca52e6..01c8a5eeba 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/BaseCoreFunctionalTestCase.java @@ -46,6 +46,7 @@ import org.hibernate.cfg.Configuration; import org.hibernate.cfg.Environment; import org.hibernate.cfg.Mappings; import org.hibernate.dialect.Dialect; +import org.hibernate.dialect.H2Dialect; import org.hibernate.engine.spi.SessionFactoryImplementor; import org.hibernate.engine.spi.SessionImplementor; import org.hibernate.internal.util.StringHelper; @@ -200,6 +201,13 @@ public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase { configuration.setProperty( AvailableSettings.USE_NEW_ID_GENERATOR_MAPPINGS, "true" ); if ( createSchema() ) { configuration.setProperty( Environment.HBM2DDL_AUTO, "create-drop" ); + final String secondSchemaName = createSecondSchema(); + if ( StringHelper.isNotEmpty( secondSchemaName ) ) { + if ( !( getDialect() instanceof H2Dialect ) ) { + throw new UnsupportedOperationException( "Only H2 dialect supports creation of second schema." ); + } + Helper.createH2Schema( secondSchemaName, configuration ); + } } configuration.setProperty( Environment.DIALECT, getDialect().getClass().getName() ); return configuration; @@ -416,6 +424,14 @@ public abstract class BaseCoreFunctionalTestCase extends BaseUnitTestCase { return true; } + /** + * Feature supported only by H2 dialect. + * @return Provide not empty name to create second schema. + */ + protected String createSecondSchema() { + return null; + } + protected boolean rebuildSessionFactoryOnError() { return true; } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/Helper.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/Helper.java index 151adff618..a67206a018 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/Helper.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/Helper.java @@ -25,10 +25,13 @@ package org.hibernate.testing.junit4; import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import java.util.Map; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.TestClass; +import org.hibernate.cfg.Configuration; +import org.hibernate.cfg.Environment; import org.hibernate.testing.FailureExpected; /** @@ -98,4 +101,23 @@ public class Helper { .toString(); } + /** + * @see #createH2Schema(String, Map) + */ + public static void createH2Schema(String schemaName, Configuration cfg) { + createH2Schema( schemaName, cfg.getProperties() ); + } + + /** + * Create additional H2 schema. + * + * @param schemaName New schema name. + * @param settings Current settings. + */ + public static void createH2Schema(String schemaName, Map settings) { + settings.put( + Environment.URL, + settings.get( Environment.URL ) + ";INIT=CREATE SCHEMA IF NOT EXISTS " + schemaName + ); + } } diff --git a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/TestClassMetadata.java b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/TestClassMetadata.java index b93ce3f1aa..aea68ca3f7 100644 --- a/hibernate-testing/src/main/java/org/hibernate/testing/junit4/TestClassMetadata.java +++ b/hibernate-testing/src/main/java/org/hibernate/testing/junit4/TestClassMetadata.java @@ -192,6 +192,19 @@ public class TestClassMetadata { } private void invokeCallback(Method callback, Object target) { + try { + performCallbackInvocation( callback, target ); + } + catch (CallbackException e) { + // this is getting eaten, at least when run from IntelliJ. The test fails to start (for start up + // callbacks), but the exception is never shown.. + System.out.println( "Error performing callback invocation : " + e.getLocalizedMessage() ); + e.printStackTrace(); + throw e; + } + } + + private void performCallbackInvocation(Method callback, Object target) { try { callback.invoke( target, NO_ARGS ); } diff --git a/libraries.gradle b/libraries.gradle new file mode 100644 index 0000000000..c204fbc3b7 --- /dev/null +++ b/libraries.gradle @@ -0,0 +1,111 @@ + +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * Copyright (c) 2012, 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 + */ + +// build a map of the dependency artifacts to use. Allows centralized definition of the version of artifacts to +// use. In that respect it serves a role similar to in Maven +ext { + + slf4jVersion = '1.6.1' + junitVersion = '4.10' + h2Version = '1.2.145' + bytemanVersion = '1.5.2' + infinispanVersion = '5.1.6.FINAL' + jnpVersion = '5.0.6.CR1' + + libraries = [ + // Ant + ant: 'org.apache.ant:ant:1.8.2', + + // Antlr + antlr: 'antlr:antlr:2.7.7', + + // Annotations + commons_annotations: + 'org.hibernate.common:hibernate-commons-annotations:4.0.1.Final@jar', + jandex: 'org.jboss:jandex:1.1.0.Alpha1', + classmate: 'com.fasterxml:classmate:0.5.4', + + // Dom4J + dom4j: 'dom4j:dom4j:1.6.1@jar', + + // Javassist + javassist: 'org.javassist:javassist:3.15.0-GA', + + // Connection pool + c3p0: "c3p0:c3p0:0.9.1", + proxool: "proxool:proxool:0.8.3", + + // Encache + ehcache: "net.sf.ehcache:ehcache-core:2.4.3", + // Infinsipan + infinispan: "org.infinispan:infinispan-core:${infinispanVersion}", + + // javax + jpa: 'org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Draft-7plus', + jta: 'org.jboss.spec.javax.transaction:jboss-transaction-api_1.1_spec:1.0.0.Final', + validation: 'javax.validation:validation-api:1.0.0.GA', + jacc: 'org.jboss.spec.javax.security.jacc:jboss-jacc-api_1.4_spec:1.0.0.Final', + + // logging + logging: 'org.jboss.logging:jboss-logging:3.1.0.GA', + logging_processor: 'org.jboss.logging:jboss-logging-processor:1.0.0.Final', + + // jaxb task + jaxb: 'com.sun.xml.bind:jaxb-xjc:2.2.5', + jaxb2_basics: 'org.jvnet.jaxb2_commons:jaxb2-basics:0.6.3', + jaxb2_ant: 'org.jvnet.jaxb2_commons:jaxb2-basics-ant:0.6.3', + jaxb2_jaxb: 'org.jvnet.jaxb2_commons:jaxb2-basics-jaxb:2.2.4-1', + jaxb2_jaxb_xjc: 'org.jvnet.jaxb2_commons:jaxb2-basics-jaxb-xjc:2.2.4-1', + // ~~~~~~~~~~~~~~~~~~~~~~~~~~ testing + + // logging for testing + slf4j_api: "org.slf4j:slf4j-api:${slf4jVersion}", + slf4j_log4j12: "org.slf4j:slf4j-log4j12:${slf4jVersion}", + jcl_slf4j: "org.slf4j:jcl-over-slf4j:${slf4jVersion}", + jcl_api: 'commons-logging:commons-logging-api:99.0-does-not-exist', + jcl: 'commons-logging:commons-logging:99.0-does-not-exist', + + + junit: "junit:junit:${junitVersion}", + byteman: "org.jboss.byteman:byteman:${bytemanVersion}", + byteman_install: "org.jboss.byteman:byteman-install:${bytemanVersion}", + byteman_bmunit: "org.jboss.byteman:byteman-bmunit:${bytemanVersion}", + jpa_modelgen: 'org.hibernate:hibernate-jpamodelgen:1.1.1.Final', + shrinkwrap_api: 'org.jboss.shrinkwrap:shrinkwrap-api:1.0.0-beta-6', + shrinkwrap: 'org.jboss.shrinkwrap:shrinkwrap-impl-base:1.0.0-beta-6', + validator: 'org.hibernate:hibernate-validator:4.2.0.Final', + h2: "com.h2database:h2:${h2Version}", + jboss_jta: "org.jboss.jbossts:jbossjta:4.16.4.Final", + xapool: "com.experlog:xapool:1.5.0", + mockito: 'org.mockito:mockito-core:1.9.0', + + // infinispan test + infinispan_test: "org.infinispan:infinispan-core:${infinispanVersion}:tests@jar", + rhq: "org.rhq.helpers:rhq-pluginAnnotations:3.0.4", + jboss_common_core: "org.jboss:jboss-common-core:2.2.16.GA@jar", + jnp_client: "org.jboss.naming:jnp-client:${jnpVersion}", + jnp_server: "org.jboss.naming:jnpserver:${jnpVersion}", + ] +} diff --git a/release/release.gradle b/release/release.gradle index c843f9a090..cac16e1949 100644 --- a/release/release.gradle +++ b/release/release.gradle @@ -10,7 +10,7 @@ idea.module { // Javadocs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -javadocBuildDir = mkdir( "${buildDir}/documentation/javadocs" ) +ext.javadocBuildDir = mkdir( "${buildDir}/documentation/javadocs" ) def copyRightYear = new java.util.GregorianCalendar().get( java.util.Calendar.YEAR ); @@ -107,11 +107,11 @@ aggregateJavadocs.doLast { // release bundles ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -releaseBuildDir = mkdir( buildDir ) +ext.releaseBuildDir = mkdir( buildDir ) task prepareReleaseBundles( dependsOn: [parent.project( 'documentation' ).tasks.buildDocs,aggregateJavadocs] ) -releaseCopySpec = copySpec { +ext.releaseCopySpec = copySpec { into( "hibernate-release-$project.version" ) { from new File( parent.projectDir, 'lgpl.txt' ) from new File( parent.projectDir, 'changelog.txt' )