Skip to content

Commit 87b4532

Browse files
committed
introduce incubating Assignment API
1 parent 4fd814b commit 87b4532

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;
@@ -49,27 +50,44 @@
4950
*/
5051
public class MutationSpecificationImpl<T> implements MutationSpecification<T>, TypedQueryReference<Void> {
5152

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

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

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

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

7593
@Override
@@ -104,6 +122,19 @@ public MutationSpecification<T> restrict(Restriction<? super T> restriction) {
104122
return this;
105123
}
106124

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

0 commit comments

Comments
 (0)