Skip to content

Commit 3001e05

Browse files
committed
Fix collections on transaction query.
Closes #1569.
1 parent 7b89ea9 commit 3001e05

File tree

2 files changed

+143
-2
lines changed

2 files changed

+143
-2
lines changed

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

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

0 commit comments

Comments
 (0)