From a910a73ea25bfd2168abfdfd4fa56c832fb37a79 Mon Sep 17 00:00:00 2001 From: tcordel Date: Mon, 13 Oct 2025 15:06:03 +0200 Subject: [PATCH] HHH-19862: fix CriteriaUpdate for converted field --- .../sqm/tree/update/SqmUpdateStatement.java | 4 +- .../convert/ConvertedDateAttributeTest.java | 85 +++++++++++++++++++ .../orm/test/jpa/criteria/convert/Log.java | 48 +++++++++++ 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/convert/ConvertedDateAttributeTest.java create mode 100644 hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/convert/Log.java diff --git a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java index 727ff25e00e7..e5064f1cb0c6 100644 --- a/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java +++ b/hibernate-core/src/main/java/org/hibernate/query/sqm/tree/update/SqmUpdateStatement.java @@ -173,7 +173,9 @@ public void setSetClause(SqmSetClause setClause) { @Override public SqmUpdateStatement set(SingularAttribute attribute, X value) { - applyAssignment( getTarget().get( attribute ), (SqmExpression) nodeBuilder().value( value ) ); + final SqmCriteriaNodeBuilder nodeBuilder = (SqmCriteriaNodeBuilder) nodeBuilder(); + SqmPath sqmAttribute = getTarget().get( attribute ); + applyAssignment( sqmAttribute, nodeBuilder.value( value, sqmAttribute) ); return this; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/convert/ConvertedDateAttributeTest.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/convert/ConvertedDateAttributeTest.java new file mode 100644 index 000000000000..268c03df7191 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/convert/ConvertedDateAttributeTest.java @@ -0,0 +1,85 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.jpa.criteria.convert; + +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaUpdate; +import jakarta.persistence.criteria.Root; +import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.JiraKey; +import org.hibernate.testing.orm.junit.SessionFactory; +import org.hibernate.testing.orm.junit.SessionFactoryScope; +import org.hibernate.testing.orm.junit.SessionFactoryScopeAware; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.Date; +import java.util.function.Consumer; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@DomainModel(annotatedClasses = {Log.class}) +@SessionFactory +@JiraKey(value = "HHH-19862") +public class ConvertedDateAttributeTest implements SessionFactoryScopeAware { + private SessionFactoryScope scope; + + @ParameterizedTest + @MethodSource("criteriaUpdateFieldSetters") + public void testConvertedFieldUpdateUsingPath(Consumer criteriaUpdateFieldSetter) { + + scope.inTransaction( (session) -> { + Log log = new Log(); + session.persist( log ); + + final CriteriaBuilder cb = session.getCriteriaBuilder(); + final CriteriaUpdate query = cb.createCriteriaUpdate( Log.class ); + final Root root = query.from( Log.class ); + query.where( cb.equal( root.get( Log_.id ), log.getId() ) ); + Date update = new Date(); + criteriaUpdateFieldSetter.accept( new UpdateContext( query, root, update ) ); + + int updates = session.createMutationQuery( query ).executeUpdate(); + session.refresh( log ); + + assertEquals( 1, updates ); + assertEquals( log.getLastUpdate(), update ); + + } ); + } + + @Override + public void injectSessionFactoryScope(SessionFactoryScope scope) { + this.scope = scope; + } + + static class UpdateContext { + final CriteriaUpdate query; + final Root root; + final Date lastUpdate; + + public UpdateContext(CriteriaUpdate query, Root root, Date lastUpdate) { + this.query = query; + this.root = root; + this.lastUpdate = lastUpdate; + } + } + + static Stream criteriaUpdateFieldSetters() { + Consumer updateUsingPath = context -> + context.query.set( context.root.get( Log_.lastUpdate ), context.lastUpdate ); + Consumer updateUsingSingularAttribute = context -> + context.query.set( Log_.lastUpdate, context.lastUpdate ); + Consumer updateUsingName = context -> + context.query.set( Log_.LAST_UPDATE, context.lastUpdate ); + return Stream.of( + Arguments.of( updateUsingPath ), + Arguments.of( updateUsingSingularAttribute ), + Arguments.of( updateUsingName ) + ); + } +} diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/convert/Log.java b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/convert/Log.java new file mode 100644 index 000000000000..39a968c99c19 --- /dev/null +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/jpa/criteria/convert/Log.java @@ -0,0 +1,48 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * Copyright Red Hat Inc. and Hibernate Authors + */ +package org.hibernate.orm.test.jpa.criteria.convert; + +import jakarta.persistence.AttributeConverter; +import jakarta.persistence.Convert; +import jakarta.persistence.Converter; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +import java.util.Date; +@Entity +public class Log { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Convert(converter = DateConverter.class) + private Date lastUpdate; + @Converter + static class DateConverter implements AttributeConverter { + @Override + public Long convertToDatabaseColumn(Date date) { + if (null == date) { + return null; + } + return date.getTime(); + } + @Override + public Date convertToEntityAttribute(Long dbDate) { + if (null == dbDate) { + return null; + } + return new Date( dbDate ); + } + } + + public Long getId() { + return id; + } + + public Date getLastUpdate() { + return lastUpdate; + } +}