diff --git a/gradle/published-java-module.gradle b/gradle/published-java-module.gradle index 1785c814a175..61fcf60861e5 100644 --- a/gradle/published-java-module.gradle +++ b/gradle/published-java-module.gradle @@ -100,39 +100,49 @@ publishing { } } - -// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -// Signing - -def signPublicationsTask = tasks.register('signPublications') { - description "Grouping task which executes all Sign tasks" - - dependsOn tasks.withType( Sign ) +tasks.withType(PublishToMavenLocal).configureEach { + doFirst { + logger.lifecycle("PublishToMavenLocal ({})", publication.name) + logger.lifecycle(" - {} : {} : {} ", publication.groupId, publication.artifactId, publication.pom.packaging) + logger.lifecycle(" - artifacts ({})...", publication.artifacts.size()) + publication.artifacts.forEach { + logger.lifecycle(" - artifact ({}) : {} ({})" , it.classifier, it.file, it.file.size()) + } + } } -tasks.named( "publishPublishedArtifactsPublicationToSonatypeRepository" ) { - // publishing depends on signing - dependsOn signPublicationsTask +tasks.withType(PublishToMavenRepository).configureEach { + doFirst { + logger.lifecycle("PublishToMavenRepository ({} : {})", publication.name, repository.name) + logger.lifecycle(" - {} : {} : {} ", publication.groupId, publication.artifactId, publication.pom.packaging) + logger.lifecycle(" - artifacts ({})...", publication.artifacts.size()) + publication.artifacts.forEach { + logger.lifecycle(" - artifact ({}) : {} ({})" , it.classifier, it.file, it.file.size()) + } + } } -tasks.register('sign') { - description "Pseudonym for :signPublications" - dependsOn signPublicationsTask -} + +// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// Signing + +def signingKey = resolveSigningKey() +def signingPassphrase = resolveSigningPassphrase() var signingExtension = project.getExtensions().getByType(SigningExtension) as SigningExtension signingExtension.sign publishing.publications.publishedArtifacts +signingExtension.useInMemoryPgpKeys(signingKey, signingPassphrase) + gradle.taskGraph.whenReady { TaskExecutionGraph graph -> - boolean wasSigningRequested = false + // are we publishing to OSSRH? boolean wasPublishingRequested = false graph.allTasks.each {task -> - if ( task instanceof Sign ) { - wasSigningRequested = true - } - else if ( task instanceof PublishToMavenRepository ) { - wasPublishingRequested = true + if ( task instanceof PublishToMavenRepository ) { + if ( "sonatype" == task.repository.name ) { + wasPublishingRequested = true + } } } @@ -143,20 +153,9 @@ gradle.taskGraph.whenReady { TaskExecutionGraph graph -> throw new RuntimeException( "Cannot perform publishing to OSSRH without credentials." ) } logger.lifecycle "Publishing {} : {} : {}", project.group, project.name, project.version - } - - if ( wasSigningRequested || wasPublishingRequested ) { - // signing was explicitly requested and/or we are publishing to Sonatype OSSRH - // - we need the signing to happen signingExtension.required = true - - var signingKey = resolveSigningKey() - var signingPassword = resolveSigningPassphrase() - signingExtension.useInMemoryPgpKeys( signingKey, signingPassword ) } - else { - // signing was not explicitly requested and we are not publishing to OSSRH, - // - disable all Sign tasks + else if ( signingKey == null || signingPassphrase == null ) { tasks.withType( Sign ).each { t-> t.enabled = false } } } @@ -172,15 +171,11 @@ static String resolveSigningKey() { return new File( keyFile ).text } - throw new RuntimeException( "Cannot perform signing without GPG details." ) + return null } static String resolveSigningPassphrase() { - var passphrase = System.getenv().get( "SIGNING_GPG_PASSPHRASE" ) - if ( passphrase == null ) { - throw new RuntimeException( "Cannot perform signing without GPG details." ) - } - return passphrase + return System.getenv().get( "SIGNING_GPG_PASSPHRASE" ) } @@ -201,16 +196,17 @@ tasks.preVerifyRelease.dependsOn generatePomFileForRelocationPomPublication tasks.publishToSonatype.mustRunAfter test + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Ancillary tasks tasks.register('showPublications') { doFirst { - project.publishing.publications.each { publication -> + publishing.publications.each { publication -> println "Publication (${publication.name}): ${publication.groupId}:${publication.artifactId}:${publication.version}" publication.artifacts.each { artifact -> println " > ${artifact}" } } } -} \ No newline at end of file +} diff --git a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java index 4e687362419d..f263f9a31b88 100644 --- a/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java +++ b/hibernate-core/src/main/java/org/hibernate/bytecode/enhance/internal/bytebuddy/InlineDirtyCheckingHandler.java @@ -78,8 +78,9 @@ else if ( !persistentField.hasAnnotation( Id.class ) } if ( enhancementContext.isCompositeField( persistentField ) - // Don't do composite owner tracking for records - && !persistentField.getType().isRecord() ) { + && !persistentField.hasAnnotation( EmbeddedId.class ) + // Don't do composite owner tracking for records + && !persistentField.getType().isRecord() ) { // HHH-13759 - Call getter on superclass if field is not visible // An embedded field won't be visible if declared private in a superclass diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/dirty/DirtyTrackingIdTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/dirty/DirtyTrackingIdTest.java new file mode 100644 index 000000000000..230f1d1e1b92 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/bytecode/enhancement/dirty/DirtyTrackingIdTest.java @@ -0,0 +1,117 @@ +/* + * Hibernate, Relational Persistence for Idiomatic Java + * + * License: GNU Lesser General Public License (LGPL), version 2.1 or later + * See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html + */ +package org.hibernate.orm.test.bytecode.enhancement.dirty; + +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; + +import org.hibernate.testing.bytecode.enhancement.BytecodeEnhancerRunner; +import org.hibernate.testing.bytecode.enhancement.EnhancementOptions; +import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase; +import org.hibernate.testing.orm.junit.JiraKey; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.Objects; + +import static org.assertj.core.api.Assertions.assertThat; + +@JiraKey("HHH-19206") +@RunWith(BytecodeEnhancerRunner.class) +@EnhancementOptions(lazyLoading = true, inlineDirtyChecking = true, extendedEnhancement = true) +public class DirtyTrackingIdTest extends BaseNonConfigCoreFunctionalTestCase { + + @Override + protected Class[] getAnnotatedClasses() { + return new Class[] { + MyEntity.class + }; + } + + @Test + public void test() { + inTransaction( session -> { + MyEntity myEntity = new MyEntity(); + myEntity.setAnId( new MyEntityId( 1L ) ); + myEntity.setData( "initial" ); + session.persist( myEntity ); + + // This is unnecessary, but should be harmless... + // Unfortunately it causes dirty checking to misbehave. + // Comment it, and the test will pass. + myEntity.setAnId( new MyEntityId( 1L ) ); + + myEntity.setData( "updated" ); + } ); + inTransaction( session -> { + var entityFromDb = session.find( MyEntity.class, new MyEntityId( 1L ) ); + assertThat( entityFromDb.getData() ).isEqualTo( "updated" ); + } ); + } + + // --- // + + @Entity(name = "MyEntity") + public static class MyEntity { + // The name of this property must be (alphabetically) before the name of "data" to trigger the bug. + // Yes, it's weird. + @EmbeddedId + private MyEntityId anId; + private String data; + + public void setAnId(MyEntityId id) { + this.anId = id; + } + + public MyEntityId getAnId() { + return anId; + } + + public String getData() { + return data; + } + + public void setData(String name) { + this.data = name; + } + } + + @Embeddable + public static class MyEntityId { + private Long id; + + public MyEntityId() { + } + + public MyEntityId(Long id) { + this.id = id; + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + @Override + public final boolean equals(Object o) { + if ( !(o instanceof MyEntityId) ) { + return false; + } + + return Objects.equals( id, ( (MyEntityId) o ).id ); + } + + @Override + public int hashCode() { + return Objects.hashCode( id ); + } + } +}