Home > Blockchain >  How to build DSL paths with Hibernate
How to build DSL paths with Hibernate

Time:12-08

Is there a way to implement DSL querying with Hibernate? I mean mostly the easier and typo-safer HQL statement building. For example, an HQL query like this: select u.id from Users u where u.person.identityCard.lastName like :lastNameVar would be written like this:

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

As an experiment, I have already written the builder itself, but what I can’t seem to figure out is how to make those multi-step DSL paths (User.person().identityCard().lastName()). I’d obviously have to add something to my entity classes like:

@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.

}

But I seem to lack knowledge as of how to call named methods of abstract classes like DSLProperty when it comes to chaining the property paths.

P.s. This is more of an experimental feature that I want to figure out for myself. I know about libraries like QueryDSL and jOOQ, but I’m not sure if they support property chaining and, again, I’m more interested in implementing this feature from scratch.

CodePudding user response:

The old and verbose solution -- CriteriaBuilder

The modem one -- QueryDSL

CodePudding user response:

So, having spent some time around this (as I have stated this was more of a an experimental thing) I came to such solution:

First, I have created a base 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; }
}

Then, in my abstract base entity class I've created such static inner 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 );
    }
}

And basically extend (probably use your IDE or Maven to generate) this base DSL class in all child entities, e.g. 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.
}

and 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.
}

This way, this code:

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() );

will produce the following output:

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

There is no limit to the length of your path chain and you may use it to generate HQL queries any way you wish using this domain-specific property path generation.

The only thing that bugs me a bit is the amount of code that you need to write (again, you can generate it) and how many objects will be created for each path (depends on the amount of steps), but the pro of this solution is that in case you change the name of your property you won't have to worry about finding all the places where it was used in HQL queries - just change the property in the corresponding inner DSL class using your IDE's refactoring features.

  • Related