0

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?

Eddú Meléndez
  • 6,107
  • 1
  • 26
  • 35
RosarioB
  • 56
  • 6
  • 1
    You might need to commit before fetching all rows. `ctx.commit();` please let me know if it works – mahfuj asif May 21 '23 at 09:10
  • thank you @mahfujasif, unfortunately there is no ctx.commit() , it seems taht the transactions is autommitted. Anyway I have found that I can use ctx.insert and I have used it but still I got the issue... – RosarioB May 21 '23 at 15:27

2 Answers2

3

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 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

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

mn_test347
  • 354
  • 2
  • 6
0

You are missing @Transactional annotation in your service class,
and you are breaking single responsibility principle in your repository classes.
For a data transformation, create mapper class (@Component) and use them in your service class.
And if really needed any extra configuration,
you can do so injecting appropriate one by using a @Bean annotation in your '@Configuration' class(es).
And be aware, that "@Transactional" annotation at class level applies only to a public methods.
Another issue is unnecessary implementation of "GenericRepository" of yours, you should extend JpaRepository.
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