简体   繁体   English

spring 数据 r2dbc 可以生成模式吗?

[英]Can spring data r2dbc generate a schema?

I am creating a quick project using R2DBC and H2 to familiarize myself with this new reactive stuff.我正在使用 R2DBC 和 H2 创建一个快速项目,以使自己熟悉这种新的反应性东西。 Made a repository that extends ReactiveCrudRepository and all is well with the world, as long as i use the DatabaseClient to issue a CREATE TABLE statement that matches my entity first...制作了一个扩展 ReactiveCrudRepository 的存储库,一切都很好,只要我使用 DatabaseClient 发出一个与我的实体匹配的 CREATE TABLE 语句...

I understand spring data R2DBC is not as fully featured as spring data JPA (yet?) but is there currently a way to generate the schema from the entity classes?我了解 spring 数据 R2DBC 的功能不如 spring 数据 JPA (是吗?)但目前有办法从实体类生成模式吗?

Thanks谢谢

No, there is currently no way to generate schema from entities with Spring Data R2DBC.不,目前无法从具有 Spring 数据 R2DBC 的实体生成模式。

I'm using it in a project with Postgres DB and it's complicated to manage database migrations, but I managed to wire in Flyway with synchronous Postgre driver (Flyway doesn't work with reactive drivers yet) at startup to handle schema migrations.我在一个带有 Postgres DB 的项目中使用它,并且管理数据库迁移很复杂,但我设法在启动时将 Flyway 与同步 Postgre 驱动程序(Flyway 尚不与反应式驱动程序一起使用)连接以处理模式迁移。

Even though you still have to write your own CREATE TABLE statements which shouldn't be that hard and you could even modify your entities in some simple project to create JPA entities and let Hibernate create schema then copy-paste it into a migration file in your R2DBC project.即使您仍然必须编写自己的 CREATE TABLE 语句,这不应该那么难,您甚至可以在一些简单的项目中修改您的实体以创建 JPA 实体并让 Hibernate 创建架构,然后将其复制粘贴到您的迁移文件中R2DBC 项目。

It is possible for tests and for production.可以进行测试和生产。

I production make sure your user has no access to change schema otherwise you may delete tables by mistake.!!我生产确保您的用户无权更改架构,否则您可能会错误地删除表。!! or use a migration tool like flyway.或使用像flyway这样的迁移工具。

You need to put your schema.sql in the main resources and add the relevant properties您需要将您的 schema.sql 放在主要资源中并添加相关属性

spring.r2dbc.initialization-mode=always

h2 for test and postgres for prod h2 用于测试和 postgres 用于生产

I use gradle and the versions of driver are:我使用 gradle 驱动程序的版本是:

    implementation 'org.springframework.boot.experimental:spring-boot-actuator-autoconfigure-r2dbc'
    runtimeOnly 'com.h2database:h2'
    runtimeOnly 'io.r2dbc:r2dbc-h2'
    runtimeOnly 'io.r2dbc:r2dbc-postgresql'
    runtimeOnly 'org.postgresql:postgresql'
    testImplementation 'org.springframework.boot.experimental:spring-boot-test-autoconfigure-r2dbc'

The BOM version is BOM 版本为

dependencyManagement {
    imports {
        mavenBom 'org.springframework.boot.experimental:spring-boot-bom-r2dbc:0.1.0.M3'
    }
}

That's how I solved this problem:这就是我解决这个问题的方法:

Controller: Controller:

    @PostMapping(MAP + PATH_DDL_PROC_DB)  //PATH_DDL_PROC_DB = "/database/{db}/{schema}/{table}"
    public Flux<Object> createDbByDb(
            @PathVariable("db") String db,
            @PathVariable("schema") String schema,
            @PathVariable("table") String table) {
        return ddlProcService.createDbByDb(db,schema,table);

Service:服务:

    public Flux<Object> createDbByDb(String db,String schema,String table) {
        return ddl.createDbByDb(db,schema,table);
    }

Repository:存储库:

    @Autowired
    PostgresqlConnectionConfiguration.Builder connConfig;

    public Flux<Object> createDbByDb(String db,String schema,String table) {
        return createDb(db).thenMany(
                Mono.from(connFactory(connConfig.database(db)).create())
                    .flatMapMany(
                            connection ->
                                    Flux.from(connection
                                                      .createBatch()
                                                      .add(sqlCreateSchema(db))
                                                      .add(sqlCreateTable(db,table))
                                                      .add(sqlPopulateTable(db,table))
                                                      .execute()
                                             )));
    }

    private Mono<Void> createDb(String db) {

        PostgresqlConnectionFactory
                connectionFactory = connFactory(connConfig);

        DatabaseClient ddl = DatabaseClient.create(connectionFactory);

        return ddl
                .execute(sqlCreateDb(db))
                .then();
    }

Connection Class:连接 Class:

@Slf4j
@Configuration
@EnableR2dbcRepositories
public class Connection extends AbstractR2dbcConfiguration {

    /*
     **********************************************
     * Spring Data JDBC:
     *      DDL: does not support JPA.
     *
     * R2DBC
     *      DDL:
     *          -does no support JPA
     *          -To achieve DDL, uses R2dbc.DataBaseClient
     *
     *      DML:
     *          -it uses R2dbcREpositories
     *          -R2dbcRepositories is different than
     *          R2dbc.DataBaseClient
     * ********************************************
     */
    @Bean
    public PostgresqlConnectionConfiguration.Builder connectionConfig() {
        return PostgresqlConnectionConfiguration
                .builder()
                .host("db-r2dbc")
                .port(5432)
                .username("root")
                .password("root");
    }

    @Bean
    public PostgresqlConnectionFactory connectionFactory() {
        return
                new PostgresqlConnectionFactory(
                        connectionConfig().build()
                );
    }
}

DDL Scripts: DDL 脚本:

@Getter
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class DDLScripts {

    public static final String SQL_GET_TASK = "select * from tasks";

    public static String sqlCreateDb(String db) {
        String sql = "create database %1$s;";
        String[] sql1OrderedParams = quotify(new String[]{db});
        String finalSql = format(sql,(Object[]) sql1OrderedParams);
        return finalSql;
    }

    public static String sqlCreateSchema(String schema) {
        String sql = "create schema if not exists %1$s;";
        String[] sql1OrderedParams = quotify(new String[]{schema});
        return format(sql,(Object[])  sql1OrderedParams);
    }

    public static String sqlCreateTable(String schema,String table) {

        String sql1 = "create table %1$s.%2$s " +
                "(id serial not null constraint tasks_pk primary key, " +
                "lastname varchar not null); ";
        String[] sql1OrderedParams = quotify(new String[]{schema,table});
        String sql1Final = format(sql1,(Object[])  sql1OrderedParams);

        String sql2 = "alter table %1$s.%2$s owner to root; ";
        String[] sql2OrderedParams = quotify(new String[]{schema,table});
        String sql2Final = format(sql2,(Object[])  sql2OrderedParams);

        return sql1Final + sql2Final;
    }

    public static String sqlPopulateTable(String schema,String table) {

        String sql = "insert into %1$s.%2$s values (1, 'schema-table-%3$s');";
        String[] sql1OrderedParams = quotify(new String[]{schema,table,schema});
        return format(sql,(Object[]) sql1OrderedParams);
    }

    private static String[] quotify(String[] stringArray) {

        String[] returnArray = new String[stringArray.length];

        for (int i = 0; i < stringArray.length; i++) {
            returnArray[i] = "\"" + stringArray[i] + "\"";
        }
        return returnArray;
    }
}

It is actually possible to load a schema by defining a specific class in this way:实际上可以通过以这种方式定义特定的 class 来加载模式:

import io.r2dbc.spi.ConnectionFactory
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.io.ClassPathResource
import org.springframework.data.r2dbc.repository.config.EnableR2dbcRepositories
import org.springframework.r2dbc.connection.init.ConnectionFactoryInitializer
import org.springframework.r2dbc.connection.init.ResourceDatabasePopulator

@Configuration
@EnableR2dbcRepositories
class DbConfig {
    @Bean
    fun initializer(connectionFactory: ConnectionFactory): ConnectionFactoryInitializer {
        val initializer = ConnectionFactoryInitializer()
        initializer.setConnectionFactory(connectionFactory)
        initializer.setDatabasePopulator(
            ResourceDatabasePopulator(
                ClassPathResource("schema.sql")
            )
        )
        return initializer
    }


}

Pay attention that IntelliJ gives an error " Could not autowire. No beans of 'ConnectionFactory' type found " but it is actually a false positive.请注意,IntelliJ 给出了一个错误“无法自动装配。没有找到 'ConnectionFactory' 类型的 bean ”,但它实际上是误报。 So ignore it and build again your project.所以忽略它并重新构建你的项目。

The schema.sql file has to be put in resources folder. schema.sql文件必须放在资源文件夹中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM