如何使用 Hibernate 构建 DSL 路径

How to build DSL paths with Hibernate

有没有办法用 Hibernate 实现 DSL 查询?我的意思主要是更容易和打字更安全的 HQL 语句构建。例如,像这样的 HQL 查询:select u.id from Users u where u.person.identityCard.lastName like :lastNameVar 可以这样写:

Select.from(User.class, “u”).where(eq(“u”, User.person().identityCard().lastName(), lastNameVar))

作为实验,我已经编写了构建器本身,但我似乎无法弄清楚如何制作那些多步 DSL 路径 (User.person().identityCard().lastName())。我显然必须向我的实体 类 添加一些东西,例如:

@Entity
public class User {

    public static DSLProperty<Person> person() = new DSLProperty<>(“person”); // how do I write DSLProperty class to achieve this?

    @OneToOne
    @JoinColumn(name=“person_id”)
    private Person person;

    //getters, setters etc.

}

但是我似乎缺乏关于如何调用抽象的命名方法的知识 类 就像 DSLProperty 在链接 属性 路径时。

P.s。这更多是我想自己弄清楚的实验性功能。我知道 QueryDSL 和 jOOQ 等库,但我不确定它们是否支持 属性 链接,而且,我更感兴趣的是从头开始实现此功能。

旧的和冗长的解决方案 -- CriteriaBuilder

调制解调器一 -- QueryDSL

所以,花了一些时间解决这个问题(正如我所说,这更像是一个实验性的东西)我得出了这样的解决方案:

首先,我创建了一个基地DSLProperty.class:

public class DSLProperty {
    protected String previousPath; // this will hold the path till the current step of the path chain

    public DSLProperty( String previousPath ) {
        this.previousPath = previousPath;
    }

    public String getPath() { return previousPath; }
}

然后,在我的抽象基础实体中 class 我创建了这样的静态内部 class:

public static class DSL extends DSLProperty {
    protected DSL( String input ) {
        super( input );
    }

    public DSLProperty id() {
        return property( "id" );
    }

    protected DSLProperty property( String property ) {
         return new DSLProperty( ( previousPath != null ? previousPath + "." : "" ) + property );
    }
}

并且基本上在所有子实体中扩展(可能使用您的 IDE 或 Maven 生成)这个基础 DSL class,例如DSLTestUser.class:

@Entity
@Table
public class DSLTestUser extends DSLTestBaseEntity {
    public static DSL alias( String alias ) { return new DSL( alias ); }

    public static class DSL extends DSLTestBaseEntity.DSL {
        public DSL( String input ) {
            super( input );
        }
        public DSLProperty username() { return property( "username" ); }
        public DSLTestPerson.DSL person() { return DSLTestPerson.alias( previousPath + ".person" ); }
    }

    // all the columns, getters & setters etc.
}

DSLTestPerson.class

@Entity
@Table
public class DSLTestPerson extends DSLTestBaseEntity {
    public static DSL alias( String alias ) { return new DSL( alias ); }

    public static class DSL extends DSLTestBaseEntity.DSL {
        public DSL( String input ) {
            super( input );
        }
        public DSLProperty name() { return property( "name" ); }
        public DSLTestUser.DSL user() { return DSLTestUser.alias( previousPath + ".user" ); }
    }

    // all the columns, getters & setters etc.
}

这样,这段代码:

System.out.println( DSLTestUser.alias( "tu" ).person().id().getPath() );
System.out.println( DSLTestUser.alias( "tu" ).username().getPath() );
System.out.println( DSLTestPerson.alias( "tp" ).user().username().getPath() );
System.out.println( DSLTestPerson.alias( "tp" ).getPath() );

将产生以下输出:

tu.person.id
tu.username
tp.user.username
tp

路径链的长度没有限制,您可以使用此特定于域的 属性 路径生成以任何方式生成 HQL 查询。

唯一让我有点烦恼的是您需要编写的代码量(同样,您可以生成它)以及将为每个路径创建多少对象(取决于步骤量),但此解决方案的 pro 是,如果您更改 属性 的名称,您将不必担心找到它在 HQL 查询中使用的所有位置- 只需使用 IDE 的重构功能更改相应内部 DSL class 中的 属性。