【问题标题】:dynamically choose columns in select with R2DB2使用 R2DB2 在 select 中动态选择列
【发布时间】:2022-01-13 11:16:06
【问题描述】:

我一直在尝试传递我想要选择的列,但开箱即用它似乎是不可能的。我尝试过类似

@Query("SELECT :columns FROM USERS u WHERE u.LOCALE = :locale AND u.id IN (:ids)")
Flux<Users> retrieveExportData(@Param("columns") String columns,
                                       @Param("locale") String locale,
                                       @Param("ids") String[] ids);

private final R2dbcEntityTemplate template;

我尝试创建自己的查询,但这不起作用,因为它必须是 Criteria 类型,而这只会造成不值得的复杂性。

如果我能添加这样的列就好了

    criteriaList.add(
        Criteria.where("LOCALE").is(locale)
    );
    Criteria criteria = Criteria.from(criteriaList);

然后像这样执行

    Flux<Users> users = this.template.select(User.class)
        .matching(Query.query(criteria))
        .all();

或者只是像我的第一个示例一样调用存储库。

有没有人能够成功地做到这一点?

----- 更新 1 -----

我试过这样做:

导入 org.springframework.r2dbc.core.DatabaseClient;

DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);

String sql = "SELECT " + columns + "  FROM USERS u WHERE u.LOCALE=" + locale + " AND u.id IN (" + ids + ")";

return databaseClient.sql(sql)
    .fetch()
    .all().cast(User.class);

但是从 Spring 4.3.6.RELEASE 开始,LinkedCaseInsensitiveMap 不再扩展 LinkedHashMap 和 HashMap,只实现了 Map 接口。

这会导致

Cannot cast org.springframework.util.LinkedCaseInsensitiveMap to User at java.base/java.lang.Class.cast

错误。

然后我尝试了答案中建议的 jooq 方法,但它只会产生语法错误。示例

private final DSLContext ctx = DSL.using(connectionFactory);
private final Users users = ctx.newRecord(Users.USERS); <-- USERS not found

@Query("SELECT :columns FROM USERS u WHERE u.LOCALE = :locale AND u.id IN (:ids)")
public Flux<Users> retrieveExportData(
    List<Field<?>> columns,
    String locale,
    String[] ids
) {
    return Flux.from(ctx
        .select(columns)
        .from("USERS")
        .where(users.LOCALE.eq(locale)) <--- LOCALE not found
        .and(users.ID.in(ids)) <--- ID not found
    ).map(r -> r.into(Users.class)); <---- into not found
}

图书馆看起来很有希望。我会努力让它发挥作用。

【问题讨论】:

    标签: spring-data-r2dbc r2dbc


    【解决方案1】:

    除了实际的绑定值之外,您不能用这样的语法元素替换绑定参数 (:columns)。对于这种类型的动态 SQL,您将不得不求助于某种查询构建机制。

    也许看看jOOQ, which has R2DBC support?您的实现将如下所示:

    @Query("SELECT :columns FROM USERS u WHERE u.LOCALE = :locale AND u.id IN (:ids)")
    public Flux<Users> retrieveExportData(
        List<Field<?>> columns,
        String locale,
        String[] ids
    ) {
        return Flux.from(ctx
            .select(columns)
            .from(USERS)
            .where(USERS.LOCALE.eq(locale))
            .and(USERS.ID.in(ids))
        ).map(r -> r.into(Users.class));
    }
    

    免责声明:我为 jOOQ 背后的公司工作。

    【讨论】:

    • 这看起来很有希望,但我希望文档更好。显然,剪切和粘贴是行不通的,但我真的很感兴趣。你能发布一些文档吗?
    • 这只是一篇宣布支持 R2DBC 的博文。 The full documentation is here.
    • 这看起来很不错,但是为查询需求增加另一层复杂性似乎有点过头了。简单地用查询构建一个字符串并注入它并不像问很多,但似乎不可能
    • @Query 注解用于静态 SQL。编写动态 SQL 的方法有很多,包括直接使用 R2DBC。
    • 我终于弄明白了。非常感谢您的宝贵时间。
    【解决方案2】:

    你可以这样写动态SQL。

    import org.springframework.r2dbc.core.DatabaseClient;
    
    DatabaseClient databaseClient = DatabaseClient.create(connectionFactory);
    
    String sql = "SELECT " + columns + "  FROM USERS u WHERE u.LOCALE=" + locale + " AND u.id IN (" + ids + ")";
    
    return databaseClient.sql(sql)
        .fetch()
        .all().cast(User.class);
    

    你将需要这个依赖

    implementation "org.springframework.boot:spring-boot-starter-data-r2dbc:2.6.2"
    

    【讨论】:

    • 我强烈建议您使用更合理的方式来创建动态 SQL 查询。除了可能的语法错误之外,这是在要求 SQL 注入麻烦!需要明确的是,如果 columns 在您的代码中是一个常量(而不是用户输入),那可能没问题。但请不要像这样将localeids 连接到您的查询中。请至少使用绑定变量。
    • columnslocale 和 ``ids`` 是动态的,这就是我这样做的原因。SQL 注入是一个好点。我能够让它工作,但铸造不起作用,所以我正在寻找另一种选择。 Cannot cast org.springframework.util.LinkedCaseInsensitiveMap
    • 如果有针对所有这些动态 SQL 问题的现成解决方案就好了 :)
    猜你喜欢
    • 2011-10-25
    • 2011-12-17
    • 1970-01-01
    • 2018-03-12
    • 2021-10-08
    • 1970-01-01
    • 2014-05-14
    • 1970-01-01
    • 2015-11-30
    相关资源
    最近更新 更多