9

与使用 JPQL 或原始 SQL 相比,Criteria 具有一些优势,如以下答案所述:类型安全;重构友好性;减少对字符串的依赖(但仍然有一些)。还有一个很大的缺点:它们的可读性较差,而且很丑。是否有(非 JPA)Java API 用于访问类型安全且可读的关系数据库?

4

4 回答 4

8

Timo Westkämper在QueryDSL方面做得很好。这个库提供了一个用于查询不同持久性提供者(JPA、MongoDB、Lucene...)的 DSL。

但是我经常使用手工制作的解决方案来简化最常见的查询(列出限制其某些字段的实体),从而阻止我编写始终相同的行。对于大多数复杂的查询,我切换到不可读和冗长的 Criteria API。

于 2012-04-12T23:21:24.030 回答
1

我的巴蒂斯。

MyBatis 是一流的持久性框架,支持自定义 SQL、存储过程和高级映射。MyBatis 消除了几乎所有的 JDBC 代码和手动设置参数和检索结果。MyBatis 可以使用简单的 XML 或 Annotations 进行配置和映射原语、映射接口和 Java POJO(普通旧 Java 对象)到数据库记录。

或者,正如nobeh 建议的那样:jOOQ

于 2012-04-12T18:37:56.883 回答
1

面对以下实用程序类,我找到了更轻松的 JPA 搜索的“终极”解决方案:DynamicQueryBuilder

  1. 它提供了元模型,因此您无需使用连接来描述关系。

  2. 它通过模板 pojo 搜索!只需将值放在实体实例中,它们将用作标准!

  3. 它使用构建器模式,因此非常易读!

    Bank bank = new Bank();
    bank.setId(12L);
    bank.setAchCode("1213");
    bank.setCbeCode("1234");
    bank.setStatus(new Lookups(1L));
    bank.setAchShortName("121");
    
    List<integer> ids = new ArrayList<integer>();
    ids.add(1);
    ids.add(2);
    ids.add(3);
    ids.add(4);
    ids.add(5);
    
    List<string> cbeCodes = new ArrayList<string>();
    cbeCodes.add("1111");
    cbeCodes.add("2222");
    
    DynamicQueryBuilder queryDyncBuilder1 =
    new DynamicQueryBuilder.Builder(null).select(bank).withOperType(Operator.OperType.AND).
    withAdvancedParam("cbeCode", LIKE, PERCENT_AROUND).withAdvancedParam("id", IN, ids)
    .withAdvancedParam("achCode", BETWEEN, cbeCodes).withAdvancedParam("achShortName", GT)
    .orderBy("id").orderBy("cbeCode", true).orderBy("status.code", true).build();
    
    System.out.println(queryDyncBuilder1.getQueryString());
    

如果您运行上述调用,该组件将构建以下结果 JPQL 查询:

SELECT b 
FROM Bank b 
WHERE b.status = :status 
AND b.cbeCode LIKE :cbeCode 
AND b.achShortName > :achShortName 
AND b.id IN :id 
AND (b.achCode BETWEEN :achCodeFrom AND :achCodeTo) 
ORDER BY b.status.code DESC, b.id ASC, b.cbeCode DESC
于 2013-11-03T19:45:41.273 回答
0

我有一个项目,我需要一种理智的方式来动态组合许多复杂的 WHERE 子句,但我不需要 Hibernate 的所有额外功能以及随之而来的复杂性(如模式管理和模式生成、二级缓存、bean 映射、对象状态跟踪等)。找到这个问题后,我仍然对替代方案不满意,所以我改用了这个:

public class Pred {

    private String opcode;

    private Pred left;

    private Pred right;

    private Pred() {
    }

    public Pred(String opcode, Pred left, Pred right) {
        super();
        this.opcode = opcode;
        this.left = left;
        this.right = right;
    }

    private static Collection<Pred> collect(Pred left, Pred ... right) {
        List<Pred> all = new ArrayList<>();
        all.add(left);
        all.addAll(Arrays.asList(right));
        return all;
    }

    private static Pred lambda(Supplier<String> out) {
        return new Pred() {
            @Override
            public String toSql() {
                return "(" + out.get() + ")";
            }
        };
    }

    private static Pred quoted(String constantValue) {
        return lambda(() -> "'" + constantValue + "'");
    }

    private static Pred unquoted(String constantValue) {
        return lambda(() -> constantValue);
    }

    public static Pred column(String constantValue) {
        return unquoted(constantValue);
    }

    public static Pred eq(String colname, String val) {
        return new Pred("=", column(colname), quoted(val));
    }

    public static Pred eq(String colname, Number val) {
        return new Pred("=", column(colname), unquoted(val.toString()));
    }

    public static Pred gt(String colname, Number val) {
        return new Pred(">", column(colname), unquoted(val.toString()));
    }

    public static Pred lte(String colname, Number val) {
        return new Pred("<=", column(colname), unquoted(val.toString()));
    }

    public static Pred lt(String colname, Number val) {
        return new Pred("<", column(colname), unquoted(val.toString()));
    }

    public static Pred gte(String colname, Number val) {
        return new Pred(">=", column(colname), unquoted(val.toString()));
    }

    public static Pred and(Collection<Pred> subpreds) {
        return lambda(() -> subpreds.stream().map(Pred::toSql).collect(Collectors.joining(" AND ")));
    }

    public static Pred and(Pred left, Pred ... right ) {
        return Pred.and(collect(left,right));
    }

    public static Pred or(Collection<Pred> subpreds) {
        return lambda(() -> subpreds.stream().map(Pred::toSql).collect(Collectors.joining(" OR ")));
    }

    public static Pred or(Pred left, Pred ... right ) {
        return Pred.or(collect(left,right));
    }

    public static <T extends Number> Pred in(String column, Collection<T> list) {
        return lambda(() -> column + " IN (" + list.stream().map(String::valueOf).collect(Collectors.joining(",")) + ")");
    }

    public String toSql() {
        return "(" + left.toSql() + opcode + right.toSql() + ")";
    }
}

我建议将它与 spring-jdbc 结合使用。典型用法:

Pred pred = Pred.and(Pred.gt(VALIDUNTIL, cutoffTime.toEpochMilli()),
  Pred.lte(FIRSTSEEN, cutoffTime.toEpochMilli()),
  Pred.gt(JSON.ListPrice, minPrice),
  Pred.eq(JSON.Status, "Active"));
...
List<...> = jdbcTemplate.query("select ...  from ... where " + pred.toSql(), new BeanPropertyRowMapper(...);

使用风险自负 - 没有针对 SQL 注入的保护(不是我的项目中的要求)。

这是仓促写的,老实说,我希望有人能“借用”它并将其变成一个受欢迎的图书馆 :-)

于 2018-11-11T18:09:55.217 回答