如何在没有域类的 querydsl 中构造查询

Posted

技术标签:

【中文标题】如何在没有域类的 querydsl 中构造查询【英文标题】:How to construct query in querydsl without domain classes 【发布时间】:2014-03-04 04:03:03 【问题描述】:

在寻找 java 库以与数据库无关的方式构建查询时,我遇到了很多,包括 iciql、querydsl、jooq、joist、hibernate 等。

我想要一些不需要配置文件并且可以使用动态模式的东西。 对于我的应用程序,我会在运行时了解数据库和架构,因此我不会有任何配置文件或架构的域类。

这似乎是 querydsl 的核心目标之一,但通过 querydsl 的文档,我看到了很多使用域类构建动态查询的示例,但我没有遇到任何解释如何构建此类数据库不可知查询的内容仅使用我拥有的有关架构的动态信息。

Jooq 提供了这样的功能(请参阅:http://www.jooq.org/doc/3.2/manual/getting-started/use-cases/jooq-as-a-standalone-sql-builder/),但如果我想将注意力扩展到 Oracle 或 MS SQL(我可能不喜欢但需要支持),那么它有一个限制性许可。

有querydsl经验的人可以告诉我querydsl是否可以做这样的事情,如果可以,如何做。

如果有人知道其他可以满足我的要求的,将不胜感激。

【问题讨论】:

jOOQ 用户使用您描述的用例过度使用了 jOOQ,因此这是一个得到很好支持的场景。 谢谢卢卡斯。我知道 jOOQ 支持这一点。我想看看这是否可以用 querydsl 完成,因为它似乎拥有它所需的主干,而 jOOQ 需要获得许可才能添加对主要商业数据库(如 Oracle、MS SQL 和 Sybase)的支持,而我无法做到。 【参考方案1】:

更新:Timo 向我展示了如何在不必替换 SQLQuery 类的情况下执行我想要的操作,从而使我的原始响应无效。这是他的评论:

query.getSQL(field1, field2, ... fieldN), getSQL is consistent with the
other methods which also take the projection arguments at last

我已经删除了我不必要的课程。下面是一个直接使用 SQLQuery 获取 SQL 字符串而不执行查询的示例(例如,不使用list 方法):

SQLQuery rquery = new SQLQuery(connection , dialect);

// Use getSQL with projections
rquery.from(qtable)
    .where(qtable.qfield1.eq("somevalue"));

SQLBindings bindings = rquery.getSQL(qtable.qfield1, qtable.qfield2);

// Get the SQL string from the SQLBindings
System.out.println(bindings.getSql());

// Get the SQL parameters from the SQLBindings for the parameterized query
System.out.println(bindings.getBindings());

此回复回答了如何使用 QueryDSL 构建完整的 SQL 查询,而无需实际执行查询。它没有解决您对“动态模式”和“没有域对象”的额外要求...

【讨论】:

感谢您的回复。虽然这确实解释了查询的构建,但它确实需要创建域类,因此不能像我的用例那样使用动态模式。 @david-fleeman 可以通过querydsl.com/static/querydsl/3.3.0/apidocs/com/mysema/query/sql/…这个方法直接获取SQL字符串和绑定 @TimoWestkämper - 是的,如果您查看代码,我也会在我的回答中证明这一点。问题是查询的select field1, field2 部分不是字符串的一部分,直到您实际调用也执行查询的list() 方法。如果您想在不执行的情况下获得完整的 SQL,那么据我所知,您需要我的帮助程序类。如果我错了,请纠正我! 这样使用:query.getSQL(field1, field2, ... fieldN),getSQL 与其他方法一致,最后也采用投影参数。 @TimoWestkämper - 哈,不知道我是怎么错过的。谢谢蒂莫!会将您的评论纳入我的回答中,以便未来的读者了解如何正确处理。【参考方案2】:

这是使用 PathBuilder 的 ponzao 解决方案的一个小变体

@Transactional
public User findById(Long id)         
    PathBuilder<Object> userPath = new PathBuilder<Object>(Object.class, "user");
    NumberPath<Long> idPath = userPath.getNumber("id", Long.class);
    StringPath usernamePath = userPath.getString("username");
    Tuple tuple = new SQLQuery(getConnection(), getConfiguration())
      .from(userPath)
      .where(idPath.eq(id))
      .singleResult(idPath, usernamePath);
    return new User(tuple.get(idPath), tuple.get(usernamePath));

【讨论】:

【参考方案3】:

一个非常简单的 SQL 查询,例如:

@Transactional
public User findById(Long id) 
    return new SQLQuery(getConnection(), getConfiguration())
      .from(user)
      .where(user.id.eq(id))
      .singleResult(user);

...可以像这样动态创建(不添加任何糖):

@Transactional
public User findById(Long id) 
    Path<Object> userPath = new PathImpl<Object>(Object.class, "user");
    NumberPath<Long> idPath = Expressions.numberPath(Long.class, userPath, "id");
    StringPath usernamePath = Expressions.stringPath(userPath, "username");
    Tuple tuple = new SQLQuery(getConnection(), getConfiguration())
      .from(userPath)
      .where(idPath.eq(id))
      .singleResult(idPath, usernamePath);
    return new User(tuple.get(idPath), tuple.get(usernamePath));

【讨论】:

看起来很有希望。我会尝试并以成功或失败回应。谢谢。 谢谢。这是完美的。 @MickJ,没问题!这实际上是一种非常有趣(而且很少见?)的 Querydsl 使用方式,我们很想听听它的效果如何(twitter.com/querydsl)。 嗨@ponzao:我有一个后续问题。我在这里创建了它:***.com/questions/21689222/… 很高兴得到您的意见。基本上我没有得到 schema.table 符号,所有参数都显示为“?”而不是在查询中完全具体化。

以上是关于如何在没有域类的 querydsl 中构造查询的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 QueryDSL 在查询中使用 SAMPLE 关键字

如何使用 QueryDsl 在查询中按 id 删除重复行

如何模拟querydsl查询?

如何模拟从控制器测试类注入域类的服务?

Querydsl:如何按列进行左连接

如何将 Grails 域类映射到 DTO?