This article looks at how MySQL can interact responsively in Spring.

The Spring Data R2DBC project is a database responsive programming framework provided by Spring. R2DBC is an acronym for Reactive Relational Database Connectivity. R2DBC is an API specification initiative that declares a responsive API that is implemented by driver vendors and accesses their relational databases in a responsive programming manner. Reactive database programming is not easy to implement. The traditional JDBC protocol is a completely blocking API, so reactive programming is a “subversion” of the JDBC protocol.

Again, reactive programming is a non-blocking asynchronous programming pattern, and Spring reactive programming provides a friendly, intuitive, and easy-to-understand coding pattern for handling asynchronous results (see the previous article). In other words, after the application sends SQL to the database, the application thread does not need to block waiting for the database to return the result, but directly returns to process other tasks. After the database SQL processing is completed, the Spring calling thread will process the result.

To date, the Spring Data R2DBC project supports the following databases: H2 (io.r2dbc:r2dbc-h2) MariaDB (org.mariadb:r2dbc-mariadb) Microsoft SQL Server (io.r2dbc:r2dbc-mssql) MySQL (dev.miku:r2dbc-mysql) jasync-sql MySQL (com.github.jasync-sql:jasync-r2dbc-mysql) Postgres (io.r2dbc:r2dbc-postgresql) Oracle (com.oracle.database.r2dbc:oracle-r2dbc)

The following is based on MySQL, introduce the use of Spring Data R2DBC.

Introduction of depend on

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-r2dbc</artifactId> </dependency> <dependency> <groupId>dev.miku</groupId> <artifactId>r2dbc-mysql</artifactId> < version > 0.8.2. RELEASE < / version > < / dependency >

The configuration file

Spring. R2dbc. Url = r2dbcs: mysql: / / 127.0.0.1:3306 / bin - springreactive? useSSL=false spring.r2dbc.username=... spring.r2dbc.password=...

Spring Data R2DBC can be used in conjunction with Spring Data JPA. In fact, R2DBC is not very different from the original JPA and is very simple to use. Only in Spring Data JPA, the method returns the real value, while in R2DBC, it returns the Data stream Mono, Flux.

A brief introduction to a Spring Data JPA. Spring Data JPA is a set of JPA (Java Persistence API) application framework encapsulated by Spring based on ORM framework and JPA specification. To put it simply, it is similar to MyBatis. Hibernate framework (Spring Data JPA underlying database operations through Hibernate).

Repository is an important concept in Spring Data R2DBC. It encapsulates operations on an entity, equivalent to a DAO (Data Access Object).

Suppose there is an entity DeliveryCompany in the application, corresponding to the table delivery_company. Entities are defined as follows:

public class DeliveryCompany { @Id private long id; private String name; private String label; private Integer level; . }

The @id annotation marks the Id attribute.

Here we define a DeliveryCompanyRepository interface, inheritance and R2dbcRepository.

@Repository
public interface DeliveryCompanyRepository extends R2dbcRepository<DeliveryCompany,Long> {
  ...
}

R2DBCrepository is a Spring implementation of the interface that inherits from the ReactiveCrudRepository, which provides template methods for adding, deleting, modifying and searching.

public interface ReactiveCrudRepository<T, ID> extends Repository<T, ID> { <S extends T> Mono<S> save(S var1); <S extends T> Flux<S> saveAll(Iterable<S> var1); <S extends T> Flux<S> saveAll(Publisher<S> var1); Mono<T> findById(ID var1); Mono<T> findById(Publisher<ID> var1); . }

Note that the results returned here are asynchronous results such as Mono, Flux, etc. This is the biggest difference between reactive and non-reactive interactions.

If you want to customize an action, you can do this by (1) defining the method name. As long as we define the method name by the rule, Spring will generate the SQL for us.

// DeliveryCompany <DeliveryCompany> findByName(String Name); // DeliveryCompany <DeliveryCompany> findByidGreaterThan (Long starId); // Find <DeliveryCompany> findByidGreaterThan (Long starId); // SELECT * FROM DeliveryCompany <DeliveryCompany> FindBynamestartingWith (String start); // PageFlux <DeliveryCompany> findByidGreaterThanEqual (Long startId, Pageable Pageable);

Note that the above method name needs to be defined by the specification

findByName -> findBy<fieldName>
findByIdGreaterThan -> findBy<fieldName>GreaterThan

Spring will generate the corresponding SQL for us, which is very convenient. This approach can satisfy most simple queries.

There is also a delete operation

Mono<Integer> deleteByName(String name);   

For detailed method naming rules, refer to the official documentation.

For complex SQL, developers can also write SQL by hand,

@Query("select  id,name from delivery_company where id in  (:ids)")
Flux<DeliveryCompany> findByIds2(List<Long> ids);

@Query("select  id,name from delivery_company where name = :name")
Flux<DeliveryCompany> findByName2(String name);

@Modifying
@Query("update delivery_company set name = :name where id = :id")
Mono<DeliveryCompany> update2(@Param("id") long id, @Param("name") String name);

As you can see, writing SQL is also very simple and has very good support for collection parameters. There is currently no way to use JPQL (Java Persistence Query Language), but using native SQL is fine.

If you have used MyBatis, you may have used the following method to determine that the parameter is not null

<select id="findByName2" resultType="DeliveryCompany"> SELECT * FROM delivery_company WHERE name = #{name} <if test="label ! = null"> AND label like #{label} </if> </select>

Unfortunately, there is no way to find support in JPA. If you know, please feel free to advise.

Alternatively, you can use R2DBCentityTemplate to automatically generate SQL

    @Autowired
    private R2dbcEntityTemplate template;
    
    public Flux<DeliveryCompany> getByName3(String name) {
        return template
                .select(DeliveryCompany.class)
                .from("delivery_company")
                .matching(Query.query(Criteria.where("name").is(name))).all();
        // Criteria.where("name").is(name).and
    }

    public Mono<Integer> update3(DeliveryCompany company) {
        return template
                .update(DeliveryCompany.class)
                .inTable("delivery_company")
                .matching(Query.query(Criteria.where("id").is(company.getId())))
                .apply(Update.update("name", company.getName()));
    }

This method can be used to determine the parameters of the non-empty query, but it is more cumbersome to use (we can also use some encapsulation to facilitate our use).

(4) the Spring Data also support Querydsl R2DBC, we define the Repository can be inherited in the ReactiveQuerydslPredicateExecutor < DeliveryCompany >, this interface provides the following template method

public interface ReactiveQuerydslPredicateExecutor<T> {
    Mono<T> findOne(Predicate var1);

    Flux<T> findAll(Predicate var1);

    Flux<T> findAll(Predicate var1, Sort var2);

    Flux<T> findAll(Predicate var1, OrderSpecifier... var2);

    Flux<T> findAll(OrderSpecifier... var1);

    Mono<Long> count(Predicate var1);

    Mono<Boolean> exists(Predicate var1);
}

Spring Data R2DBC also supports the @QueryDSLPredicate annotation, but I won’t go into it here.

Spring Data R2DBC supports transactions and can be used simply by adding @Transactional to a business method

    @Transactional
    public Flux<DeliveryCompany> save(List<DeliveryCompany> companyList) {
        Flux<DeliveryCompany> result = Flux.just();
        for (DeliveryCompany deliveryCompany : companyList) {
            result = result.concat(result, repository.save(deliveryCompany));
        }
        return result;
    }

To demonstrate the use of transactions, instead of calling the saveAll method of Repository, the data is inserted through a loop and the final result is returned. Note that the final result Flux, Mono must be the method return value, because reactive programming exception information is stored in these results (and not thrown when the method is invoked), so these results must be the method return value, otherwise Spring will not know if the method reported an error, and therefore cannot reverse the transaction.

Spring Data R2DBC is basically the same as Spring Data JPA, so this article is mainly an introduction to how Spring Data JPA is used. I haven’t used Spring Data JPA before, so this article is just an introduction. There are a lot of things I haven’t covered, such as ID generation, multi-table queries, etc. I won’t cover them here.

The official document: https://docs.spring.io/spring… The complete code: https://gitee.com/binecy/bin-…

If you think this article is good, welcome to pay attention to my WeChat public number, series of articles continue to update. Your attention is the power I insist!