Skip to content

Commit 49aa3bd

Browse files
committed
introduce incubating Assignment API
1 parent 0f41601 commit 49aa3bd

File tree

10 files changed

+395
-1
lines changed

10 files changed

+395
-1
lines changed

hibernate-core/src/main/java/org/hibernate/proxy/pojo/BasicLazyInitializer.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ else if ( method.equals( setIdentifierMethod ) ) {
8686

8787
// otherwise:
8888
return INVOKE_IMPLEMENTATION;
89-
9089
}
9190

9291
private Object getReplacement() {
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.query.assignment;
6+
7+
8+
import jakarta.persistence.metamodel.SingularAttribute;
9+
import org.hibernate.Incubating;
10+
import org.hibernate.query.restriction.Path;
11+
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
12+
13+
/**
14+
* An assignment to a field or property of an entity or embeddable.
15+
*
16+
* @param <T> The target entity type of the assignment
17+
*
18+
* @since 7.2
19+
*
20+
* @author Gavin King
21+
*/
22+
@Incubating
23+
public interface Assignment<T> {
24+
25+
/**
26+
* An assigment of the given literal value to the given attribute
27+
* of the root entity.
28+
*/
29+
static <T,X> Assignment<T> set(SingularAttribute<T,X> attribute, X value) {
30+
return new AttributeAssignment<>( attribute, value );
31+
}
32+
33+
/**
34+
* An assigment of the given literal value to the entity or embeddable
35+
* field or property identified by the given path from the root entity.
36+
*/
37+
static <T,X> Assignment<T> set(Path<T,X> path, X value) {
38+
return new PathAssignment<>( path, value );
39+
}
40+
41+
/**
42+
* An assigment of the entity or embeddable field or property identified
43+
* by the given path from the root entity to the given attribute of the
44+
* root entity.
45+
*/
46+
static <T,X> Assignment<T> set(SingularAttribute<T,X> attribute, Path<T,X> value) {
47+
return new PathToAttributeAssignment<>( attribute, value );
48+
}
49+
50+
/**
51+
* An assigment of one entity or embeddable field or property to another
52+
* entity or embeddable field or property, each identified by a given path
53+
* from the root entity.
54+
*/
55+
static <T,X> Assignment<T> set(Path<T,X> path, Path<T,X> value) {
56+
return new PathToPathAssignment<>( path, value );
57+
}
58+
59+
void apply(SqmUpdateStatement<? extends T> update);
60+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.query.assignment;
6+
7+
import jakarta.persistence.metamodel.SingularAttribute;
8+
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
9+
10+
/**
11+
* Assignment of a value to an attribute.
12+
*
13+
* @author Gavin King
14+
*/
15+
record AttributeAssignment<T, X>(SingularAttribute<T, X> attribute, X value)
16+
implements Assignment<T> {
17+
@Override
18+
public void apply(SqmUpdateStatement<? extends T> update) {
19+
update.set( attribute, value );
20+
}
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.query.assignment;
6+
7+
import org.hibernate.query.restriction.Path;
8+
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
9+
10+
/**
11+
* Assignment of a value to a path.
12+
*
13+
* @author Gavin King
14+
*/
15+
record PathAssignment<T, X>(Path<T, X> path, X value)
16+
implements Assignment<T> {
17+
@Override
18+
public void apply(SqmUpdateStatement<? extends T> update) {
19+
update.set( path.path( update.getRoot() ), value );
20+
}
21+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.query.assignment;
6+
7+
import jakarta.persistence.metamodel.SingularAttribute;
8+
import org.hibernate.query.restriction.Path;
9+
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
10+
11+
/**
12+
* Assignment of a path to an attribute.
13+
*
14+
* @author Gavin King
15+
*/
16+
record PathToAttributeAssignment<T, X>(SingularAttribute<T, X> attribute, Path<T,X> value)
17+
implements Assignment<T> {
18+
@Override
19+
public void apply(SqmUpdateStatement<? extends T> update) {
20+
update.set( attribute, value.path( update.getRoot() ) );
21+
}
22+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
/*
2+
* SPDX-License-Identifier: Apache-2.0
3+
* Copyright Red Hat Inc. and Hibernate Authors
4+
*/
5+
package org.hibernate.query.assignment;
6+
7+
import org.hibernate.query.restriction.Path;
8+
import org.hibernate.query.sqm.tree.update.SqmUpdateStatement;
9+
10+
/**
11+
* * Assignment of a path to a path.
12+
*
13+
* @author Gavin King
14+
*/
15+
record PathToPathAssignment<T, X>(Path<T, X> path, Path<T,X> value)
16+
implements Assignment<T> {
17+
@Override
18+
public void apply(SqmUpdateStatement<? extends T> update) {
19+
update.set( path.path( update.getRoot() ), value.path( update.getRoot() ) );
20+
}
21+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Support for {@linkplain org.hibernate.query.assignment.Assignment assignment}
3+
* with {@link org.hibernate.query.specification.MutationSpecification}.
4+
*
5+
* @since 7.2
6+
*/
7+
@Incubating
8+
package org.hibernate.query.assignment;
9+
10+
import org.hibernate.Incubating;

hibernate-core/src/main/java/org/hibernate/query/specification/MutationSpecification.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
import org.hibernate.StatelessSession;
1717
import org.hibernate.query.IllegalMutationQueryException;
1818
import org.hibernate.query.MutationQuery;
19+
import org.hibernate.query.assignment.Assignment;
1920
import org.hibernate.query.specification.internal.MutationSpecificationImpl;
21+
import org.hibernate.query.specification.internal.MutationSpecificationImpl.MutationType;
2022
import org.hibernate.query.restriction.Restriction;
2123

2224
/**
@@ -51,6 +53,20 @@ public interface MutationSpecification<T> extends QuerySpecification<T> {
5153
@Override
5254
MutationSpecification<T> restrict(Restriction<? super T> restriction);
5355

56+
/**
57+
* If this {@code MutationSpecification} represents an {@code update}
58+
* statement, add an assigment to a field or property of the target
59+
* entity.
60+
*
61+
* @param assignment The assignment to add
62+
*
63+
* @return {@code this} for method chaining.
64+
*
65+
* @since 7.2
66+
*/
67+
@Incubating
68+
MutationSpecification<T> assign(Assignment<? super T> assignment);
69+
5470
/**
5571
* A function capable of modifying or augmenting a criteria query.
5672
*
@@ -131,4 +147,30 @@ static <T> MutationSpecification<T> create(CriteriaUpdate<T> criteriaUpdate) {
131147
static <T> MutationSpecification<T> create(CriteriaDelete<T> criteriaDelete) {
132148
return new MutationSpecificationImpl<>( criteriaDelete );
133149
}
150+
151+
/**
152+
* Returns a specification reference which can be used to programmatically,
153+
* iteratively build a {@linkplain MutationQuery} which updates the given
154+
* entity type..
155+
*
156+
* @param targetEntityClass The target entity type
157+
*
158+
* @param <T> The root entity type for the mutation (the "target").
159+
*/
160+
static <T> MutationSpecification<T> createUpdate(Class<T> targetEntityClass) {
161+
return new MutationSpecificationImpl<>( MutationType.UPDATE, targetEntityClass );
162+
}
163+
164+
/**
165+
* Returns a specification reference which can be used to programmatically,
166+
* iteratively build a {@linkplain MutationQuery} which deletes the given
167+
* entity type.
168+
*
169+
* @param targetEntityClass The target entity type
170+
*
171+
* @param <T> The root entity type for the mutation (the "target").
172+
*/
173+
static <T> MutationSpecification<T> createDelete(Class<T> targetEntityClass) {
174+
return new MutationSpecificationImpl<>( MutationType.DELETE, targetEntityClass );
175+
}
134176
}

hibernate-core/src/main/java/org/hibernate/query/specification/internal/MutationSpecificationImpl.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.hibernate.SharedSessionContract;
1717
import org.hibernate.StatelessSession;
1818
import org.hibernate.engine.spi.SharedSessionContractImplementor;
19+
import org.hibernate.query.assignment.Assignment;
1920
import org.hibernate.query.specification.MutationSpecification;
2021
import org.hibernate.query.IllegalMutationQueryException;
2122
import org.hibernate.query.MutationQuery;
@@ -48,27 +49,44 @@
4849
*/
4950
public class MutationSpecificationImpl<T> implements MutationSpecification<T>, TypedQueryReference<Void> {
5051

52+
public enum MutationType {
53+
// INSERT,
54+
UPDATE,
55+
DELETE
56+
}
57+
5158
private final List<BiConsumer<SqmDeleteOrUpdateStatement<T>, SqmRoot<T>>> specifications = new ArrayList<>();
5259
private final String hql;
5360
private final Class<T> mutationTarget;
5461
private final SqmDeleteOrUpdateStatement<T> deleteOrUpdateStatement;
62+
private final MutationType type;
5563

5664
public MutationSpecificationImpl(String hql, Class<T> mutationTarget) {
5765
this.hql = hql;
5866
this.mutationTarget = mutationTarget;
5967
this.deleteOrUpdateStatement = null;
68+
this.type = null;
6069
}
6170

6271
public MutationSpecificationImpl(CriteriaUpdate<T> criteriaQuery) {
6372
this.deleteOrUpdateStatement = (SqmUpdateStatement<T>) criteriaQuery;
6473
this.mutationTarget = deleteOrUpdateStatement.getTarget().getManagedType().getJavaType();
6574
this.hql = null;
75+
this.type = MutationType.UPDATE;
6676
}
6777

6878
public MutationSpecificationImpl(CriteriaDelete<T> criteriaQuery) {
6979
this.deleteOrUpdateStatement = (SqmDeleteStatement<T>) criteriaQuery;
7080
this.mutationTarget = deleteOrUpdateStatement.getTarget().getManagedType().getJavaType();
7181
this.hql = null;
82+
this.type = MutationType.DELETE;
83+
}
84+
85+
public MutationSpecificationImpl(MutationType type, Class<T> mutationTarget) {
86+
this.deleteOrUpdateStatement = null;
87+
this.mutationTarget = mutationTarget;
88+
this.hql = null;
89+
this.type = type;
7290
}
7391

7492
@Override
@@ -102,6 +120,19 @@ public MutationSpecification<T> restrict(Restriction<? super T> restriction) {
102120
return this;
103121
}
104122

123+
@Override
124+
public MutationSpecification<T> assign(Assignment<? super T> assignment) {
125+
specifications.add( (sqmStatement, mutationTargetRoot) -> {
126+
if ( sqmStatement instanceof SqmUpdateStatement<T> sqmUpdateStatement ) {
127+
assignment.apply( sqmUpdateStatement );
128+
}
129+
else {
130+
throw new IllegalStateException( "Delete query cannot perform assignment" );
131+
}
132+
} );
133+
return this;
134+
}
135+
105136
@Override
106137
public MutationSpecification<T> augment(Augmentation<T> augmentation) {
107138
specifications.add( (sqmStatement, mutationTargetRoot) ->
@@ -137,6 +168,14 @@ else if ( deleteOrUpdateStatement != null ) {
137168
mutationTargetRoot = resolveSqmRoot( sqmStatement,
138169
sqmStatement.getTarget().getManagedType().getJavaType() );
139170
}
171+
else if ( type != null ) {
172+
final var criteriaBuilder = queryEngine.getCriteriaBuilder();
173+
sqmStatement = switch ( type ) {
174+
case UPDATE -> criteriaBuilder.createCriteriaUpdate( mutationTarget );
175+
case DELETE -> criteriaBuilder.createCriteriaDelete( mutationTarget );
176+
};
177+
mutationTargetRoot = sqmStatement.getTarget();
178+
}
140179
else {
141180
throw new AssertionFailure( "No HQL or criteria" );
142181
}

0 commit comments

Comments
 (0)