DEV Community

Masui Masanori
Masui Masanori

Posted on

[Micronaut] Accessing SQL Server 3

Intro

I will try inserting and update in this time.

Inserting and updating data

In simple cases, I can override method like below.

CategoryRepository.java

package micronaut.sample.posts;

import io.micronaut.data.annotation.Join;
import io.micronaut.data.annotation.Query;
import io.micronaut.data.model.query.builder.sql.Dialect;
import io.micronaut.data.r2dbc.annotation.R2dbcRepository;
import io.micronaut.data.repository.reactive.ReactiveStreamsCrudRepository;
import micronaut.sample.posts.dto.SearchCategory;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@R2dbcRepository(dialect = Dialect.SQL_SERVER) 
public interface CategoryRepository extends ReactiveStreamsCrudRepository<Category, Long> {
...
    // Insert
    @Override
    Mono<Category> save(Category category);

    // Update
    @Override
    Mono<Category> update(Category category);    
}
Enter fullscreen mode Exit fullscreen mode

Because "save" and "update" don't differetiate whether the model class data is newly created or retrieved from the DB, So I must use them properly.

PostService.java

package micronaut.sample.posts;

import java.time.LocalDateTime;
import io.micronaut.transaction.annotation.Transactional;
import jakarta.inject.Singleton;
import micronaut.sample.posts.dto.SearchCategory;
import micronaut.sample.users.UserRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Singleton
public class PostService {
    private final PostRepository posts;
    private final CategoryRepository categories;
    private final UserRepository users;
    public PostService(PostRepository posts,
        CategoryRepository categories,
        UserRepository users) {
        this.posts = posts;
        this.categories = categories;
        this.users = users;
    }
...
    public Mono<String> updateCategory(String categoryName) {
        return categories.findByName(categoryName)
            .flatMap(c -> {
                c.setLastUpdateDate(LocalDateTime.now());
                // Don't do this because this insert new "Category" data.
                // I should write "categories.update(c)";
                return categories.save(c);
            })
            .switchIfEmpty(Mono.defer(() -> {
                Category c = new Category();
                c.setName(categoryName);
                c.setLastUpdateDate(LocalDateTime.now());
                return categories.save(c);
            }))
            .map(c -> "OK");
    }
}
Enter fullscreen mode Exit fullscreen mode

Insert data into multiple tables

Unlike Entity Framework Core, they don't automatically insert or update data into relational tables.
Therefore, each data must be inserted or updated in order.

PostService.java

package micronaut.sample.posts;

import java.time.LocalDateTime;
import io.micronaut.transaction.annotation.Transactional;
import jakarta.inject.Singleton;
import micronaut.sample.posts.dto.SearchCategory;
import micronaut.sample.users.UserRepository;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

@Singleton
public class PostService {
...
    public Mono<String> addSamplePost(String title, String contents, String categoryName) {
        return categories.findByName(categoryName)
            .map(c -> {
                Post newPost = new Post();
                newPost.setTitle(title);
                newPost.setContents(contents);
                newPost.setCategories(c);
                newPost.setLastUpdateDate(LocalDateTime.now());
                return newPost;
            })
            .switchIfEmpty(Mono.defer(() -> {
                Category c = new Category();
                c.setName(categoryName);
                c.setLastUpdateDate(LocalDateTime.now());
                // Insert new "Category" data
                return categories.save(c)
                  .map(newCategory -> {
                      Post newPost = new Post();
                      newPost.setTitle(title);
                      newPost.setContents(contents);
                      newPost.setCategories(newCategory);
                      newPost.setLastUpdateDate(LocalDateTime.now());
                      return newPost;
                  });
            }))
            .flatMap(p -> users.findById(1L).map(u -> {
                    p.setUsers(u);
                    return p;
            }))
            .flatMap(p -> 
                // Insert new "Post" data
                posts.save(p).map(pst -> "OK: " + pst.getId()));
    }
}
Enter fullscreen mode Exit fullscreen mode

Transaction

When the program cases some exceptions on inserting and updating data, I want to rollback.
To do this, I can add a "@Transactional" annotation.

...
    // Add this
    @Transactional()
    public Mono<String> updateCategory(String categoryName) {
        return categories.findByName(categoryName)
            .flatMap(c -> {
                c.setLastUpdateDate(LocalDateTime.now());
                return categories.update(c);
            })
            .switchIfEmpty(Mono.defer(() -> {
                Category c = new Category();
                c.setName(categoryName);
                c.setLastUpdateDate(LocalDateTime.now());
                return categories.save(c);
            }))
            .flatMap(c -> {
                // Intentionally raise an exception to validate the rollback
                c.setName(null);
                c.setLastUpdateDate(LocalDateTime.now());
                return categories.update(c);
            })
            .map(c -> "OK");
            // Don't add this line
            // .onErrorResume(e -> Mono.just(e.getMessage()));
    }
}
Enter fullscreen mode Exit fullscreen mode

In this code, I must not add "onErrorResume".
If I add it, rollback is not performed because exception occurrence cannot be detected.

Top comments (0)