Skip to content

Commit f35026c

Browse files
GH-2580 - Add examples for transactional event listeners.
Closes #2580.
1 parent c92fbe4 commit f35026c

File tree

3 files changed

+234
-0
lines changed

3 files changed

+234
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.events;
17+
18+
import static org.assertj.core.api.Assertions.assertThat;
19+
20+
import lombok.Data;
21+
import lombok.RequiredArgsConstructor;
22+
import lombok.extern.slf4j.Slf4j;
23+
24+
import java.util.Collection;
25+
import java.util.Collections;
26+
import java.util.Optional;
27+
import java.util.concurrent.atomic.AtomicBoolean;
28+
29+
import org.junit.jupiter.api.BeforeEach;
30+
import org.junit.jupiter.api.Test;
31+
import org.neo4j.driver.Driver;
32+
import org.neo4j.driver.Session;
33+
import org.springframework.beans.factory.annotation.Autowired;
34+
import org.springframework.context.annotation.Bean;
35+
import org.springframework.context.annotation.ComponentScan;
36+
import org.springframework.context.annotation.Configuration;
37+
import org.springframework.data.neo4j.core.DatabaseSelectionProvider;
38+
import org.springframework.data.neo4j.core.transaction.Neo4jBookmarkManager;
39+
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
40+
import org.springframework.data.neo4j.repository.Neo4jRepository;
41+
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
42+
import org.springframework.data.neo4j.test.BookmarkCapture;
43+
import org.springframework.data.neo4j.test.Neo4jExtension;
44+
import org.springframework.data.neo4j.test.Neo4jImperativeTestConfiguration;
45+
import org.springframework.data.neo4j.test.Neo4jIntegrationTest;
46+
import org.springframework.stereotype.Component;
47+
import org.springframework.stereotype.Repository;
48+
import org.springframework.transaction.PlatformTransactionManager;
49+
import org.springframework.transaction.annotation.EnableTransactionManagement;
50+
import org.springframework.transaction.annotation.Propagation;
51+
import org.springframework.transaction.annotation.Transactional;
52+
import org.springframework.transaction.event.TransactionPhase;
53+
import org.springframework.transaction.event.TransactionalEventListener;
54+
55+
/**
56+
* @author Michael J. Simons
57+
*/
58+
@Neo4jIntegrationTest
59+
class EventsPublisherIT {
60+
61+
protected static Neo4jExtension.Neo4jConnectionSupport neo4jConnectionSupport;
62+
63+
@BeforeEach
64+
void setupData(@Autowired Driver driver, @Autowired BookmarkCapture bookmarkCapture) {
65+
66+
try (Session session = driver.session()) {
67+
session.run("MATCH (n) DETACH DELETE n").consume();
68+
bookmarkCapture.seedWith(session.lastBookmark());
69+
}
70+
}
71+
72+
static AtomicBoolean receivedBeforeCommitEvent = new AtomicBoolean(false);
73+
74+
static AtomicBoolean receivedAfterCommitEvent = new AtomicBoolean(false);
75+
76+
@Test // GH-2580
77+
void beforeAndAfterCommitEventsShouldWork(@Autowired Neo4jObjectService service) {
78+
79+
service.save("foobar");
80+
assertThat(receivedBeforeCommitEvent).isTrue();
81+
assertThat(receivedAfterCommitEvent).isTrue();
82+
}
83+
84+
@Slf4j
85+
@Component
86+
@RequiredArgsConstructor
87+
static class Neo4jObjectListener {
88+
89+
private final Neo4jObjectService service;
90+
91+
@TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT)
92+
public void onBeforeCommit(Neo4jMessage message) {
93+
Optional<Neo4jObject> optionalNeo4jObject = service.findById(message.getMessageId());
94+
receivedBeforeCommitEvent.compareAndSet(false, optionalNeo4jObject.isPresent());
95+
}
96+
97+
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
98+
@Transactional(propagation = Propagation.REQUIRES_NEW)
99+
public void onAfterCommit(Neo4jMessage message) {
100+
Optional<Neo4jObject> optionalNeo4jObject = service.findById(message.getMessageId());
101+
receivedAfterCommitEvent.compareAndSet(false, optionalNeo4jObject.isPresent());
102+
}
103+
}
104+
105+
@Configuration
106+
@EnableTransactionManagement
107+
@EnableNeo4jRepositories(considerNestedRepositories = true)
108+
@ComponentScan
109+
static class Config extends Neo4jImperativeTestConfiguration {
110+
111+
@Bean
112+
public BookmarkCapture bookmarkCapture() {
113+
return new BookmarkCapture();
114+
}
115+
116+
@Override
117+
public PlatformTransactionManager transactionManager(
118+
Driver driver, DatabaseSelectionProvider databaseNameProvider) {
119+
120+
BookmarkCapture bookmarkCapture = bookmarkCapture();
121+
return new Neo4jTransactionManager(driver, databaseNameProvider,
122+
Neo4jBookmarkManager.create(bookmarkCapture));
123+
}
124+
125+
@Override
126+
protected Collection<String> getMappingBasePackages() {
127+
return Collections.singleton(Neo4jObject.class.getPackage().getName());
128+
}
129+
130+
@Bean
131+
public Driver driver() {
132+
133+
return neo4jConnectionSupport.getDriver();
134+
}
135+
136+
@Override
137+
public boolean isCypher5Compatible() {
138+
return neo4jConnectionSupport.isCypher5SyntaxCompatible();
139+
}
140+
}
141+
142+
@Data
143+
static class Neo4jMessage {
144+
private final String messageId;
145+
}
146+
147+
@Repository
148+
interface Neo4jObjectRepository extends Neo4jRepository<Neo4jObject, String> {
149+
}
150+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.events;
17+
18+
import lombok.AllArgsConstructor;
19+
import lombok.Data;
20+
import lombok.NoArgsConstructor;
21+
22+
import org.springframework.data.neo4j.core.schema.Id;
23+
import org.springframework.data.neo4j.core.schema.Node;
24+
import org.springframework.data.neo4j.core.schema.Property;
25+
26+
/**
27+
* @author Michael J. Simons
28+
*/
29+
@Data
30+
@NoArgsConstructor
31+
@AllArgsConstructor
32+
@Node(primaryLabel = "Neo4jObject")
33+
public class Neo4jObject {
34+
35+
@Id
36+
@Property(name = "id")
37+
private String id;
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright 2011-2022 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.neo4j.integration.issues.events;
17+
18+
import lombok.AllArgsConstructor;
19+
20+
import java.util.Optional;
21+
22+
import org.springframework.context.ApplicationEventPublisher;
23+
import org.springframework.stereotype.Service;
24+
import org.springframework.transaction.annotation.Transactional;
25+
26+
/**
27+
* @author Michael J. Simons
28+
*/
29+
@Service
30+
@Transactional
31+
@AllArgsConstructor
32+
public class Neo4jObjectService {
33+
34+
private final EventsPublisherIT.Neo4jObjectRepository neo4jObjectRepository;
35+
private final ApplicationEventPublisher publisher;
36+
37+
public Optional<Neo4jObject> findById(String id) {
38+
return neo4jObjectRepository.findById(id);
39+
}
40+
41+
public Neo4jObject save(String id) {
42+
Neo4jObject saved = neo4jObjectRepository.save(new Neo4jObject(id));
43+
publisher.publishEvent(new EventsPublisherIT.Neo4jMessage(id));
44+
return saved;
45+
}
46+
}

0 commit comments

Comments
 (0)