Skip to content

Commit dd9bea2

Browse files
gavinkingDavideD
authored andcommitted
fix for #1207
add reactive version of QueuedOperationCollectionAction
1 parent a67102b commit dd9bea2

File tree

4 files changed

+225
-19
lines changed

4 files changed

+225
-19
lines changed

hibernate-reactive-core/src/main/java/org/hibernate/reactive/engine/ReactiveActionQueue.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
import org.hibernate.action.internal.EntityAction;
2929
import org.hibernate.action.internal.EntityActionVetoException;
3030
import org.hibernate.action.internal.EntityDeleteAction;
31-
import org.hibernate.action.internal.QueuedOperationCollectionAction;
3231
import org.hibernate.action.internal.UnresolvedEntityInsertActions;
3332
import org.hibernate.action.spi.AfterTransactionCompletionProcess;
3433
import org.hibernate.action.spi.BeforeTransactionCompletionProcess;
@@ -43,6 +42,7 @@
4342
import org.hibernate.metadata.ClassMetadata;
4443
import org.hibernate.proxy.HibernateProxy;
4544
import org.hibernate.proxy.LazyInitializer;
45+
import org.hibernate.reactive.engine.impl.QueuedOperationCollectionAction;
4646
import org.hibernate.reactive.engine.impl.ReactiveCollectionRecreateAction;
4747
import org.hibernate.reactive.engine.impl.ReactiveCollectionRemoveAction;
4848
import org.hibernate.reactive.engine.impl.ReactiveCollectionUpdateAction;
@@ -126,19 +126,19 @@ ExecutableList<ReactiveEntityUpdateAction> init(ReactiveActionQueue instance) {
126126
}
127127
}
128128
);
129-
// EXECUTABLE_LISTS_MAP.put(
130-
// QueuedOperationCollectionAction.class,
131-
// new ListProvider<QueuedOperationCollectionAction>() {
132-
// ExecutableList<QueuedOperationCollectionAction> get(ReactiveActionQueue instance) {
133-
// return instance.collectionQueuedOps;
134-
// }
135-
// ExecutableList<QueuedOperationCollectionAction> init(ReactiveActionQueue instance) {
136-
// return instance.collectionQueuedOps = new ExecutableList<>(
137-
// instance.isOrderUpdatesEnabled()
138-
// );
139-
// }
140-
// }
141-
// );
129+
EXECUTABLE_LISTS_MAP.put(
130+
QueuedOperationCollectionAction.class,
131+
new ListProvider<QueuedOperationCollectionAction>() {
132+
ExecutableList<QueuedOperationCollectionAction> get(ReactiveActionQueue instance) {
133+
return instance.collectionQueuedOps;
134+
}
135+
ExecutableList<QueuedOperationCollectionAction> init(ReactiveActionQueue instance) {
136+
return instance.collectionQueuedOps = new ExecutableList<>(
137+
instance.isOrderUpdatesEnabled()
138+
);
139+
}
140+
}
141+
);
142142
EXECUTABLE_LISTS_MAP.put(
143143
ReactiveCollectionRemoveAction.class,
144144
new ListProvider<ReactiveCollectionRemoveAction>() {
@@ -479,8 +479,7 @@ public void addAction(ReactiveCollectionUpdateAction action) {
479479
* @param action The action representing the queued operation
480480
*/
481481
public void addAction(QueuedOperationCollectionAction action) {
482-
// addAction( QueuedOperationCollectionAction.class, action );
483-
throw new UnsupportedOperationException();
482+
addAction( QueuedOperationCollectionAction.class, action );
484483
}
485484

486485
/**
@@ -892,8 +891,8 @@ public boolean hasBeforeTransactionActions() {
892891
public boolean hasAnyQueuedActions() {
893892
return ( updates != null && !updates.isEmpty() ) || ( insertions != null && !insertions.isEmpty() ) || hasUnresolvedEntityInsertActions()
894893
|| ( deletions != null && !deletions.isEmpty() ) || ( collectionUpdates != null && !collectionUpdates.isEmpty() )
895-
|| ( collectionQueuedOps != null && !collectionQueuedOps.isEmpty() ) || ( collectionRemovals != null && !collectionRemovals
896-
.isEmpty() )
894+
|| ( collectionQueuedOps != null && !collectionQueuedOps.isEmpty() )
895+
|| ( collectionRemovals != null && !collectionRemovals.isEmpty() )
897896
|| ( collectionCreations != null && !collectionCreations.isEmpty() );
898897
}
899898

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive.engine.impl;
7+
8+
import java.io.Serializable;
9+
import java.util.concurrent.CompletionStage;
10+
11+
import org.hibernate.HibernateException;
12+
import org.hibernate.action.internal.CollectionAction;
13+
import org.hibernate.collection.internal.AbstractPersistentCollection;
14+
import org.hibernate.collection.spi.PersistentCollection;
15+
import org.hibernate.engine.spi.CollectionEntry;
16+
import org.hibernate.engine.spi.SharedSessionContractImplementor;
17+
import org.hibernate.persister.collection.CollectionPersister;
18+
import org.hibernate.reactive.engine.ReactiveExecutable;
19+
import org.hibernate.reactive.util.impl.CompletionStages;
20+
21+
/**
22+
* Copy of {@link QueuedOperationCollectionAction}, that adapts to {@link ReactiveExecutable}
23+
*
24+
* @author Gavin King
25+
*/
26+
public final class QueuedOperationCollectionAction extends CollectionAction implements ReactiveExecutable {
27+
28+
/**
29+
* Constructs a CollectionUpdateAction
30+
*
31+
* @param collection The collection to update
32+
* @param persister The collection persister
33+
* @param id The collection key
34+
* @param session The session
35+
*/
36+
public QueuedOperationCollectionAction(
37+
final PersistentCollection collection,
38+
final CollectionPersister persister,
39+
final Serializable id,
40+
final SharedSessionContractImplementor session) {
41+
super( persister, collection, id, session );
42+
}
43+
44+
@Override
45+
public CompletionStage<Void> reactiveExecute() {
46+
// this QueuedOperationCollectionAction has to be executed before any other
47+
// CollectionAction involving the same collection.
48+
49+
// this is not needed in HR, unless we someday add support for extra-lazy collections
50+
// getPersister().processQueuedOps( getCollection(), getKey(), getSession() );
51+
52+
// TODO: It would be nice if this could be done safely by CollectionPersister#processQueuedOps;
53+
// Can't change the SPI to do this though.
54+
((AbstractPersistentCollection) getCollection() ).clearOperationQueue();
55+
56+
// The other CollectionAction types call CollectionEntry#afterAction, which
57+
// clears the dirty flag. We don't want to call CollectionEntry#afterAction unless
58+
// there is no other CollectionAction that will be executed on the same collection.
59+
final CollectionEntry ce = getSession().getPersistenceContextInternal().getCollectionEntry( getCollection() );
60+
if ( !ce.isDoremove() && !ce.isDoupdate() && !ce.isDorecreate() ) {
61+
ce.afterAction( getCollection() );
62+
}
63+
return CompletionStages.voidFuture();
64+
}
65+
66+
@Override
67+
public void execute() throws HibernateException {
68+
throw new UnsupportedOperationException();
69+
}
70+
}

hibernate-reactive-core/src/main/java/org/hibernate/reactive/event/impl/AbstractReactiveFlushingEventListener.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
import org.hibernate.HibernateException;
1313
import org.hibernate.Interceptor;
14-
import org.hibernate.action.internal.QueuedOperationCollectionAction;
1514
import org.hibernate.engine.internal.CascadePoint;
1615
import org.hibernate.engine.internal.Collections;
1716
import org.hibernate.engine.spi.CollectionKey;
@@ -28,6 +27,7 @@
2827
import org.hibernate.reactive.engine.ReactiveActionQueue;
2928
import org.hibernate.reactive.engine.impl.Cascade;
3029
import org.hibernate.reactive.engine.impl.CascadingActions;
30+
import org.hibernate.reactive.engine.impl.QueuedOperationCollectionAction;
3131
import org.hibernate.reactive.engine.impl.ReactiveCollectionRecreateAction;
3232
import org.hibernate.reactive.engine.impl.ReactiveCollectionRemoveAction;
3333
import org.hibernate.reactive.engine.impl.ReactiveCollectionUpdateAction;
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/* Hibernate, Relational Persistence for Idiomatic Java
2+
*
3+
* SPDX-License-Identifier: Apache-2.0
4+
* Copyright: Red Hat Inc. and Hibernate Authors
5+
*/
6+
package org.hibernate.reactive;
7+
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import javax.persistence.CascadeType;
11+
import javax.persistence.Entity;
12+
import javax.persistence.FetchType;
13+
import javax.persistence.GeneratedValue;
14+
import javax.persistence.Id;
15+
import javax.persistence.JoinColumn;
16+
import javax.persistence.ManyToOne;
17+
import javax.persistence.OneToMany;
18+
import javax.persistence.Table;
19+
20+
import org.hibernate.cfg.Configuration;
21+
import org.hibernate.reactive.provider.Settings;
22+
23+
import org.junit.After;
24+
import org.junit.Test;
25+
26+
import io.vertx.ext.unit.TestContext;
27+
28+
public class FetchedAssociationTest extends BaseReactiveTest {
29+
30+
@Override
31+
protected Configuration constructConfiguration() {
32+
Configuration configuration = super.constructConfiguration();
33+
configuration.addAnnotatedClass( Parent.class );
34+
configuration.addAnnotatedClass( Child.class );
35+
configuration.setProperty( Settings.SHOW_SQL, "true");
36+
return configuration;
37+
}
38+
39+
@After
40+
public void cleanDb(TestContext context) {
41+
test( context, deleteEntities( Child.class, Parent.class ) );
42+
}
43+
44+
@Test
45+
public void testWithMutiny(TestContext context) {
46+
test( context, getMutinySessionFactory()
47+
.withTransaction( s -> {
48+
final Parent parent = new Parent();
49+
parent.setName( "Parent" );
50+
return s.persist( parent );
51+
} )
52+
.call( () -> getMutinySessionFactory()
53+
.withTransaction( s -> s
54+
.createQuery( "From Parent", Parent.class )
55+
.getSingleResult()
56+
.call( parent -> {
57+
Child child = new Child();
58+
child.setParent( parent );
59+
parent.getChildren().add( child );
60+
return s.persist( child );
61+
} )
62+
)
63+
)
64+
);
65+
}
66+
67+
@Entity(name = "Parent")
68+
@Table(name = "PARENT")
69+
public static class Parent {
70+
@Id
71+
@GeneratedValue
72+
private Long id;
73+
74+
private String name;
75+
76+
@OneToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY, mappedBy = "parent")
77+
private List<Child> children = new ArrayList<>();
78+
79+
public Long getId() {
80+
return id;
81+
}
82+
83+
public void setId(Long id) {
84+
this.id = id;
85+
}
86+
87+
public String getName() {
88+
return name;
89+
}
90+
91+
public void setName(String name) {
92+
this.name = name;
93+
}
94+
95+
public List<Child> getChildren() {
96+
return children;
97+
}
98+
}
99+
100+
@Entity(name = "Child")
101+
@Table(name = "CHILD")
102+
public static class Child {
103+
@Id
104+
@GeneratedValue
105+
private Long id;
106+
107+
private String name;
108+
109+
@ManyToOne
110+
@JoinColumn(name = "lazy_parent_id")
111+
private Parent parent;
112+
113+
public Long getId() {
114+
return id;
115+
}
116+
117+
public void setId(Long id) {
118+
this.id = id;
119+
}
120+
121+
public Parent getParent() {
122+
return parent;
123+
}
124+
125+
public void setParent(Parent parent) {
126+
this.parent = parent;
127+
}
128+
129+
public String getName() {
130+
return name;
131+
}
132+
133+
public void setName(String name) {
134+
this.name = name;
135+
}
136+
}
137+
}

0 commit comments

Comments
 (0)