英文:
Random errors when retrieving data after having inserted them
问题
我尝试构建了一组使用Spring Boot的CRUD REST API。我通过Spring Initializr创建了项目,并使用了spring-boot-starter-data-couchbase依赖。
我在这里上传了项目 https://github.com/RosarioB/spring-boot-rest-api-couchbase-crud/tree/basic_crud 测试类名为CustomerRepositoryTest。
我使用CouchbaseTemplate实现了一些Repository方法。例如:
@Override
public List<Customer> findAll() {
List<JsonObject> jsonObjects = couchbaseTemplate.getCouchbaseClientFactory().getScope()
.query(String.format("SELECT * FROM %1$s ", keySpace)).rowsAsObject();
return jsonObjects.stream().map(this::mapJsonToCustomer).collect(Collectors.toList());
}
我还为上述方法实现了一个测试(使用Testcontainers):
@Test
public void testFindAll() {
Customer alex = new Customer("customer1", "Alex", "Stone");
Customer jack = new Customer("customer2", "Jack", "Sparrow");
List<Customer> customerList = List.of(alex, jack);
Transactions transactions = couchbaseTemplate.getCouchbaseClientFactory().getCluster().transactions();
customerList.forEach(customer ->
transactions.run(ctx -> ctx.insert(collection, customer.getId(), customer)
)
);
List<Customer> customers = customerRepository.findAll();
Assertions.assertEquals(customerList, customers);
}
我的问题是,即使我在数据库中使用collection.insert插入了2个项目,但当我尝试使用customerRepository.findAll检索项目时,有时测试会失败,因为它只找到了其中一个项目。
如果我以调试模式运行测试,它就可以正常工作。我以为可能存在一些同步问题,尝试同步了这些方法,但问题没有解决。我做错了什么?
英文:
I have tried to build a set of CRUD REST APIs using Spring Boot. I have created the project via Spring Initializr with the dependency spring-boot-starter-data-couchbase.
I have uploaded the project here https://github.com/RosarioB/spring-boot-rest-api-couchbase-crud/tree/basic_crud The test class is called CustomerRepositoryTest.
I have implemented some Repository methods using the CouchbaseTemplate. For example:
@Override
public List<Customer> findAll() {
List<JsonObject> jsonObjects = couchbaseTemplate.getCouchbaseClientFactory().getScope()
.query(String.format("SELECT * FROM %1$s ", keySpace)).rowsAsObject();
return jsonObjects.stream().map(this::mapJsonToCustomer).collect(Collectors.toList());
}
And i have also implemented a test for the above method (with Testcontainers) :
@Test
public void testFindAll() {
Customer alex = new Customer("customer1", "Alex", "Stone");
Customer jack = new Customer("customer2", "Jack", "Sparrow");
List<Customer> customerList = List.of(alex, jack);
Transactions transactions = couchbaseTemplate.getCouchbaseClientFactory().getCluster().transactions();
customerList.forEach(customer ->
transactions.run(ctx -> ctx.insert(collection, customer.getId(), customer)
)
);
List<Customer> customers = customerRepository.findAll();
Assertions.assertEquals(customerList, customers);
}
My problem is that even if I perform an insert with 2 items in the database with collection.insert, when I try to recover the items with customerRepository.findAll some times the test fails because it finds just one item of the two.
If I run the test in debug it works. I thought there were some synchronization issue and I've tried to synchronize the methods but I have not solved the problem.
What am I doing wrong?
答案1
得分: 3
以下是您要求的中文翻译:
查询需要使用查询选项QueryScanConsistency.REQUEST_PLUS。问题在于,虽然kv.get()可以立即/瞬间找到使用kv.insert()插入的文档,但查询引擎只能找到已被索引的文档,这不是瞬间的过程。REQUEST_PLUS会导致查询等待,直到在发出查询请求时需要索引的所有文档都被索引,然后才执行。在调试模式下有效,因为文档有足够的时间变成索引。
"List
.query(String.format("SELECT * FROM %1$s ", keySpace)).rowsAsObject();"
为什么不使用
myRepository.withScope(scope).withCollection(collection).findAll()
您不需要编写任何代码。还有@Scope和@Collection注解可用。对于QueryScanConsistency,您可以将方法签名(无需实现方法体)添加到仓库,并使用WithConistency注解。请参考以下链接:
- https://docs.spring.io/spring-data/couchbase/docs/current/reference/html/#couchbase.collections
- https://github.com/couchbaselabs/try-cb-spring
- https://www.youtube.com/watch?v=MrplTeEFItk
只需明确一点,“couchbaseTemplate.getCouchbaseClientFactory().getCluster()”是您的代码中唯一使用spring-data-couchbase的部分。之后,一切都绕过了spring data couchbase,而是使用Couchbase Java SDK。使用@Transaction不会产生任何效果。
Spring Data模板和仓库提供了它们自己的映射器。不需要提供任何映射器。
关于 https://github.com/RosarioB/spring-boot-rest-api-couchbase-crud/blob/basic_crud/crud.couchbase/src/main/java/com/rosariob/crud/couchbase/repository/CustomerRepository.java - 请参阅try-cb-spring或其他示例应用程序,或者查看spring-data-couchbase测试以定义和使用仓库 - https://github.com/spring-projects/spring-data-couchbase/tree/main/src/test/java/org/springframework/data/couchbase/domain
英文:
The query needs to use the query option QueryScanConsistency.REQUEST_PLUS. The issue is that while a kv.get() would immediately/instantaneously find a document inserted with a kv.insert(), the query engine can only find documents that have been indexed - which is not instantaneous. REQUEST_PLUS will cause the query to wait until all documents that needed to be indexed at the time the query request was issued are indexed, and then it will execute. It works in debug because there is enough time for the documents to become indexed.
" List<JsonObject> jsonObjects = couchbaseTemplate.getCouchbaseClientFactory().getScope()
.query(String.format("SELECT * FROM %1$s ", keySpace)).rowsAsObject();"
Why not
myRepository.withScope(scope).withCollection(collection).findAll()
You don't have to write any code. (There are also @Scope and @Collection annotations). For the QueryScanConsistency, you could add the method signature (no body required) to the repository and use the WithConistency annotation. see
- https://docs.spring.io/spring-data/couchbase/docs/current/reference/html/#couchbase.collections
- https://github.com/couchbaselabs/try-cb-spring
- https://www.youtube.com/watch?v=MrplTeEFItk
Just to be clear, "couchbaseTemplate.getCouchbaseClientFactory().getCluster()" is the only thing in your code that uses spring-data-couchbase. After that everything bypasses spring data couchbase, it uses the Couchbase Java SDK. Using @Transaction is not going to have any effect.
Spring Data templates and repositories provide their own mapper. It's not necessary to provide any.
regarding https://github.com/RosarioB/spring-boot-rest-api-couchbase-crud/blob/basic_crud/crud.couchbase/src/main/java/com/rosariob/crud/couchbase/repository/CustomerRepository.java - please see the try-cb-spring or other sample application, or the spring-data-couchbase tests for defining and using repositories - https://github.com/spring-projects/spring-data-couchbase/tree/main/src/test/java/org/springframework/data/couchbase/domain
答案2
得分: 0
你的服务类缺少 @Transactional 注解,而且你的仓库类违反了单一职责原则。
为了进行数据转换,请创建映射器类(@Component)并在你的服务类中使用它们。
如果真的需要额外的配置,你可以通过在你的 '@Configuration' 类中使用 @Bean 注解来注入适当的配置。
请注意,“@Transactional” 注解在类级别只适用于公共方法。
另一个问题是你不必要地实现了你的 "GenericRepository",你应该扩展 JpaRepository。
如果真的需要通用的仓库,可以使用 @NoRepositoryBean 进行注解。
根据JDBC事务,请注意Spring Data的save和saveAndFlush方法之间的差异。
https://www.baeldung.com/spring-data-jpa-save-saveandflush
有许多示例可以展示如何正确使用Spring Rest和Spring Data来实现项目。
也许其中一个示例是:
https://github.com/thevarga04/ucimsa
英文:
You are missing @Transactional annotation in your service class,<br>
and you are breaking single responsibility principle in your repository classes.<br>
For a data transformation, create mapper class (@Component) and use them in your service class.<br>
And if really needed any extra configuration,<br>
you can do so injecting appropriate one by using a @Bean annotation in your '@Configuration' class(es).<br>
And be aware, that "@Transactional" annotation at class level applies only to a public methods.<br>
Another issue is unnecessary implementation of "GenericRepository" of yours, you should extend JpaRepository.<br>
And if really needed generic one, annotate it using @NoRepositoryBean.
According to JDBC transactions be aware of differences between
Spring Data save and saveAndFlush methods.
https://www.baeldung.com/spring-data-jpa-save-saveandflush
There are many examples how to properly implement a project using Spring Rest and Spring Data.
Maybe one of many:
https://github.com/thevarga04/ucimsa
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论