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 89cbf6e15ef9..862a382a433e 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 @@ -180,7 +180,9 @@ public SqmUpdateStatement set(SingularAttribute attribute, @Override public SqmUpdateStatement set(Path attribute, X value) { - applyAssignment( (SqmPath) attribute, (SqmExpression) nodeBuilder().value( value ) ); + final SqmCriteriaNodeBuilder nodeBuilder = (SqmCriteriaNodeBuilder) nodeBuilder(); + final SqmPath sqmAttribute = (SqmPath) attribute; + applyAssignment( sqmAttribute, nodeBuilder.value( value, sqmAttribute ) ); return this; } @@ -192,15 +194,15 @@ public SqmUpdateStatement set(Path attribute, Expression @Override @SuppressWarnings({"rawtypes", "unchecked"}) public SqmUpdateStatement set(String attributeName, Object value) { - final SqmPath sqmPath = getTarget().get(attributeName); + final SqmPath sqmPath = getTarget().get( attributeName ); final SqmExpression expression; if ( value instanceof SqmExpression ) { expression = (SqmExpression) value; } else { - expression = (SqmExpression) nodeBuilder().value( value ); + final SqmCriteriaNodeBuilder nodeBuilder = (SqmCriteriaNodeBuilder) nodeBuilder(); + expression = nodeBuilder.value( value, sqmPath ); } - assertAssignable( null, sqmPath, expression, nodeBuilder() ); applyAssignment( sqmPath, expression ); return this; } diff --git a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/JsonMappingTests.java b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/JsonMappingTests.java index 66833e1716bf..593ba12eb780 100644 --- a/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/JsonMappingTests.java +++ b/hibernate-core/src/test/java/org/hibernate/orm/test/mapping/basic/JsonMappingTests.java @@ -4,48 +4,51 @@ */ package org.hibernate.orm.test.mapping.basic; -import java.nio.charset.StandardCharsets; -import java.sql.Blob; -import java.sql.Clob; -import java.util.List; -import java.util.Map; - import com.fasterxml.jackson.databind.JsonNode; import jakarta.json.JsonValue; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import jakarta.persistence.criteria.CriteriaUpdate; +import jakarta.persistence.criteria.Root; import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.cfg.AvailableSettings; import org.hibernate.community.dialect.AltibaseDialect; -import org.hibernate.dialect.HANADialect; import org.hibernate.community.dialect.DerbyDialect; +import org.hibernate.dialect.HANADialect; import org.hibernate.dialect.OracleDialect; import org.hibernate.dialect.SybaseDialect; import org.hibernate.metamodel.mapping.internal.BasicAttributeMapping; import org.hibernate.metamodel.spi.MappingMetamodelImplementor; import org.hibernate.persister.entity.EntityPersister; -import org.hibernate.type.SqlTypes; -import org.hibernate.type.descriptor.jdbc.JdbcType; -import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; - +import org.hibernate.query.MutationQuery; +import org.hibernate.query.criteria.HibernateCriteriaBuilder; import org.hibernate.testing.orm.junit.DomainModel; +import org.hibernate.testing.orm.junit.Jira; import org.hibernate.testing.orm.junit.JiraKey; import org.hibernate.testing.orm.junit.ServiceRegistry; import org.hibernate.testing.orm.junit.SessionFactory; import org.hibernate.testing.orm.junit.SessionFactoryScope; import org.hibernate.testing.orm.junit.Setting; import org.hibernate.testing.orm.junit.SkipForDialect; +import org.hibernate.type.SqlTypes; +import org.hibernate.type.descriptor.jdbc.JdbcType; +import org.hibernate.type.descriptor.jdbc.spi.JdbcTypeRegistry; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; +import java.nio.charset.StandardCharsets; +import java.sql.Blob; +import java.sql.Clob; +import java.util.List; +import java.util.Map; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.isOneOf; import static org.hamcrest.Matchers.isA; +import static org.hamcrest.Matchers.isOneOf; import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.nullValue; @@ -120,7 +123,8 @@ public void verifyMappings(SessionFactoryScope scope) { "objectMap" ); final BasicAttributeMapping listAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "list" ); - final BasicAttributeMapping jsonAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( "jsonString" ); + final BasicAttributeMapping jsonAttribute = (BasicAttributeMapping) entityDescriptor.findAttributeMapping( + "jsonString" ); assertThat( stringMapAttribute.getJavaType().getJavaTypeClass(), equalTo( Map.class ) ); assertThat( objectMapAttribute.getJavaType().getJavaTypeClass(), equalTo( Map.class ) ); @@ -142,8 +146,8 @@ public void verifyReadWorks(SessionFactoryScope scope) { assertThat( entityWithJson.stringMap, is( stringMap ) ); assertThat( entityWithJson.objectMap, is( objectMap ) ); assertThat( entityWithJson.list, is( list ) ); - assertThat( entityWithJson.jsonNode, is( nullValue() )); - assertThat( entityWithJson.jsonValue, is( nullValue() )); + assertThat( entityWithJson.jsonNode, is( nullValue() ) ); + assertThat( entityWithJson.jsonValue, is( nullValue() ) ); } ); } @@ -163,14 +167,14 @@ public void verifyMergeWorks(SessionFactoryScope scope) { assertThat( entityWithJson.objectMap, is( nullValue() ) ); assertThat( entityWithJson.list, is( nullValue() ) ); assertThat( entityWithJson.jsonString, is( nullValue() ) ); - assertThat( entityWithJson.jsonNode, is( nullValue() )); - assertThat( entityWithJson.jsonValue, is( nullValue() )); + assertThat( entityWithJson.jsonNode, is( nullValue() ) ); + assertThat( entityWithJson.jsonValue, is( nullValue() ) ); } ); } @Test - @JiraKey( "HHH-16682" ) + @JiraKey("HHH-16682") public void verifyDirtyChecking(SessionFactoryScope scope) { scope.inTransaction( (session) -> { @@ -187,14 +191,19 @@ public void verifyDirtyChecking(SessionFactoryScope scope) { } @Test - @SkipForDialect(dialectClass = DerbyDialect.class, reason = "Derby doesn't support comparing CLOBs with the = operator") - @SkipForDialect(dialectClass = HANADialect.class, matchSubTypes = true, reason = "HANA doesn't support comparing LOBs with the = operator") - @SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, reason = "Sybase doesn't support comparing LOBs with the = operator") - @SkipForDialect(dialectClass = OracleDialect.class, matchSubTypes = true, reason = "Oracle doesn't support comparing JSON with the = operator") - @SkipForDialect(dialectClass = AltibaseDialect.class, reason = "Altibase doesn't support comparing CLOBs with the = operator") + @SkipForDialect(dialectClass = DerbyDialect.class, + reason = "Derby doesn't support comparing CLOBs with the = operator") + @SkipForDialect(dialectClass = HANADialect.class, matchSubTypes = true, + reason = "HANA doesn't support comparing LOBs with the = operator") + @SkipForDialect(dialectClass = SybaseDialect.class, matchSubTypes = true, + reason = "Sybase doesn't support comparing LOBs with the = operator") + @SkipForDialect(dialectClass = OracleDialect.class, matchSubTypes = true, + reason = "Oracle doesn't support comparing JSON with the = operator") + @SkipForDialect(dialectClass = AltibaseDialect.class, + reason = "Altibase doesn't support comparing CLOBs with the = operator") public void verifyComparisonWorks(SessionFactoryScope scope) { scope.inTransaction( - (session) -> { + (session) -> { // PostgreSQL returns the JSON slightly formatted String alternativeJson = "{\"name\": \"abc\"}"; EntityWithJson entityWithJson = session.createQuery( @@ -239,6 +248,32 @@ else if ( nativeJson instanceof Clob ) { ); } + @Test + @Jira("https://hibernate.atlassian.net/browse/HHH-18709") + public void verifyCriteriaUpdateQueryWorks(SessionFactoryScope scope) { + final Map newMap = Map.of( "name", "ABC" ); + final List newList = List.of( new StringNode( "ABC" ) ); + final String newJson = "{\"count\":123}"; + scope.inTransaction( session -> { + final HibernateCriteriaBuilder builder = session.getCriteriaBuilder(); + final CriteriaUpdate criteria = builder.createCriteriaUpdate( EntityWithJson.class ); + final Root root = criteria.from( EntityWithJson.class ); + criteria.set( root.get( "stringMap" ), newMap ); + criteria.set( "list", newList ); + criteria.set( root.get( "jsonString" ), newJson ); + criteria.where( builder.equal( root.get( "id" ), 1 ) ); + final MutationQuery query = session.createMutationQuery( criteria ); + final int count = query.executeUpdate(); + assertThat( count, is( 1 ) ); + } ); + scope.inSession( session -> { + final EntityWithJson entityWithJson = session.find( EntityWithJson.class, 1 ); + assertThat( entityWithJson.stringMap, is( newMap ) ); + assertThat( entityWithJson.list, is( newList ) ); + assertThat( entityWithJson.jsonString.replaceAll( "\\s", "" ), is( newJson ) ); + } ); + } + @Entity(name = "EntityWithJson") @Table(name = "EntityWithJson") public static class EntityWithJson {