Skip to content

Commit deb6e81

Browse files
authored
Fix collections on transaction query. (#1571)
Closes #1569.
1 parent 7b89ea9 commit deb6e81

File tree

2 files changed

+147
-2
lines changed

2 files changed

+147
-2
lines changed

spring-data-couchbase/src/main/java/org/springframework/data/couchbase/core/ReactiveFindByQueryOperationSupport.java

+5-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@
2727
import org.springframework.data.couchbase.core.support.TemplateUtils;
2828
import org.springframework.util.Assert;
2929

30+
import com.couchbase.client.core.io.CollectionIdentifier;
3031
import com.couchbase.client.java.ReactiveScope;
32+
import com.couchbase.client.java.codec.JsonSerializer;
3133
import com.couchbase.client.java.query.QueryOptions;
3234
import com.couchbase.client.java.query.QueryScanConsistency;
3335
import com.couchbase.client.java.query.ReactiveQueryResult;
@@ -190,8 +192,9 @@ public Flux<T> all() {
190192
: rs.query(statement, opts);
191193
} else {
192194
TransactionQueryOptions opts = buildTransactionOptions(pArgs.getOptions());
193-
return (AttemptContextReactiveAccessor.createReactiveTransactionAttemptContext(s.get().getCore(),
194-
clientFactory.getCluster().environment().jsonSerializer())).query(statement, opts);
195+
JsonSerializer jSer = clientFactory.getCluster().environment().jsonSerializer();
196+
return AttemptContextReactiveAccessor.createReactiveTransactionAttemptContext(s.get().getCore(), jSer)
197+
.query(rs.name().equals(CollectionIdentifier.DEFAULT_SCOPE) ? null : rs, statement, opts);
195198
}
196199
});
197200

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Copyright 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+
17+
package org.springframework.data.couchbase.transactions;
18+
19+
import static org.junit.jupiter.api.Assertions.assertNotEquals;
20+
import static org.junit.jupiter.api.Assertions.assertNotNull;
21+
import static org.junit.jupiter.api.Assertions.assertNull;
22+
import static org.springframework.data.couchbase.transactions.util.TransactionTestUtil.assertInTransaction;
23+
import static org.springframework.data.couchbase.transactions.util.TransactionTestUtil.assertNotInTransaction;
24+
25+
import java.util.List;
26+
import java.util.UUID;
27+
import java.util.function.Consumer;
28+
29+
import org.junit.jupiter.api.AfterEach;
30+
import org.junit.jupiter.api.BeforeAll;
31+
import org.junit.jupiter.api.BeforeEach;
32+
import org.junit.jupiter.api.DisplayName;
33+
import org.junit.jupiter.api.Test;
34+
import org.springframework.beans.factory.annotation.Autowired;
35+
import org.springframework.data.couchbase.CouchbaseClientFactory;
36+
import org.springframework.data.couchbase.core.CouchbaseTemplate;
37+
import org.springframework.data.couchbase.domain.User;
38+
import org.springframework.data.couchbase.domain.UserRepository;
39+
import org.springframework.data.couchbase.transaction.error.TransactionSystemUnambiguousException;
40+
import org.springframework.data.couchbase.util.ClusterType;
41+
import org.springframework.data.couchbase.util.IgnoreWhen;
42+
import org.springframework.data.couchbase.util.JavaIntegrationTests;
43+
import org.springframework.stereotype.Service;
44+
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
45+
import org.springframework.transaction.annotation.EnableTransactionManagement;
46+
import org.springframework.transaction.annotation.Transactional;
47+
48+
/**
49+
* Tests @Transactional with repository methods.
50+
*
51+
* @author Michael Reiche
52+
*/
53+
@IgnoreWhen(clusterTypes = ClusterType.MOCKED)
54+
@SpringJUnitConfig(
55+
classes = { TransactionsConfig.class, CouchbaseTransactionalRepositoryIntegrationTests.UserService.class })
56+
public class CouchbaseTransactionalRepositoryIntegrationTests extends JavaIntegrationTests {
57+
// intellij flags "Could not autowire" when config classes are specified with classes={...}. But they are populated.
58+
@Autowired UserRepository userRepo;
59+
@Autowired CouchbaseClientFactory couchbaseClientFactory;
60+
@Autowired UserService userService;
61+
@Autowired CouchbaseTemplate operations;
62+
63+
@BeforeAll
64+
public static void beforeAll() {
65+
callSuperBeforeAll(new Object() {});
66+
}
67+
68+
@BeforeEach
69+
public void beforeEachTest() {
70+
assertNotInTransaction();
71+
}
72+
73+
@AfterEach
74+
public void afterEachTest() {
75+
assertNotInTransaction();
76+
}
77+
78+
@Test
79+
public void findByFirstname() {
80+
operations.insertById(User.class).one(new User(UUID.randomUUID().toString(), "Ada", "Lovelace"));
81+
82+
List<User> users = userService.findByFirstname("Ada");
83+
84+
assertNotEquals(0, users.size());
85+
}
86+
87+
@Test
88+
public void save() {
89+
String id = UUID.randomUUID().toString();
90+
91+
userService.run(repo -> {
92+
assertInTransaction();
93+
94+
User user0 = repo.save(new User(id, "Ada", "Lovelace"));
95+
96+
assertInTransaction();
97+
98+
// read your own write
99+
User user1 = operations.findById(User.class).one(id);
100+
assertNotNull(user1);
101+
102+
assertInTransaction();
103+
104+
});
105+
106+
User user = operations.findById(User.class).one(id);
107+
assertNotNull(user);
108+
}
109+
110+
@DisplayName("Test that repo.save() is actually performed transactionally, by forcing a rollback")
111+
@Test
112+
public void saveRolledBack() {
113+
String id = UUID.randomUUID().toString();
114+
115+
assertThrowsWithCause(() -> {
116+
userService.run(repo -> {
117+
User user = repo.save(new User(id, "Ada", "Lovelace"));
118+
SimulateFailureException.throwEx("fail");
119+
});
120+
}, TransactionSystemUnambiguousException.class, SimulateFailureException.class);
121+
122+
User user = operations.findById(User.class).one(id);
123+
assertNull(user);
124+
}
125+
126+
@Service // this will work in the unit tests even without @Service because of explicit loading by @SpringJUnitConfig
127+
static class UserService {
128+
@Autowired UserRepository userRepo;
129+
130+
@Transactional
131+
public void run(Consumer<UserRepository> callback) {
132+
callback.accept(userRepo);
133+
}
134+
135+
@Transactional
136+
public List<User> findByFirstname(String name) {
137+
return userRepo.findByFirstname(name);
138+
}
139+
140+
}
141+
142+
}

0 commit comments

Comments
 (0)