Statements

The JPQL supports select, update, and delete statements. Kotlin JDSL provides a DSL to build them.

Select statement

Call select() in jpql() to build a select statement.

val query = jpql {
    select(
        path(Employee::employeeId),
    ).from(
        entity(Employee::class),
        join(Employee::departments),
    ).where(
        type(entity(Employee::class)).eq(FullTimeEmployee::class)
    ).groupBy(
        path(Employee::employeeId),
    ).having(
        count(Employee::employeeId).greaterThan(1L),
    ).orderBy(
        count(Employee::employeeId).desc(),
        path(Employee::employeeId).asc(),
    )
}

Select clause

Use select() and pass Expression to build a select clause in the select statement. If you pass only one Expression to select(), it will infer a return type from Expression. However, if you pass more than one Expression, you need to specify the type as it cannot infer the type.

// It can infer the result type.
select(path(Author::authorId))

// It cannot infer the result type.
select(path(Author::authorId), path(Author::name))

// This allows it to know the result type.
select<CustomEntity>(path(Author::authorId), path(Author::name))

DTO projection

Specify a DTO class and pass parameters of the constructor to selectNew() to build a DTO projection.

data class Row(
    val departmentId: Long,
    val count: Long,
)

selectNew<Row>(
    path(EmployeeDepartment::departmentId),
    count(Employee::employeeId),
)

From clause

Use from() and pass Entity and Join to specify the entities for selection when building a from clause in the select statement.

from(
    entity(Author::class),
    join(BookAuthor::class).on(path(Author::authorId).equal(path(BookAuthor::authorId))),
)

Join

Use join() and fetchJoin() to combine the entities for selection. There are two types of join(): Join and Association Join This is distinguished by whether join() is used between two unrelated entities or between two related entities.

@Entity
// ...
class Book(
    // ...

    @OneToMany(mappedBy = "book", cascade = [CascadeType.ALL], orphanRemoval = true)
    val authors: MutableSet<BookAuthor>,
)

@Entity
// ...
class BookAuthor(
    @Id
    @Column(name = "author_id")
    val authorId: Long,
) {
    @Id
    @ManyToOne
    @JoinColumn(name = "isbn")
    lateinit var book: Book
}

@Entity
// ...
class Author(
    @Id
    @Column(name = "author_id")
    val authorId: Long,

    // ...
)

from(
    entity(Book::class),
    join(Book::authors), // Association Join
    join(Author::class).on(path(BookAuthor::authorId).eq(path(Author::authorId))), // Join
)

Call as() after join() to alias the entity being joined. This can be useful if you use multiple entities with the same type in a from clause.

from(
    entity(Book::class),
    join(Book::authors).`as`(entity(BookAuthor::class, "author")),
)

Where clause

Use where() and pass Predicate to restrict the data when building a where clause in the select statement. You can use whereAnd() as a short form of where() and and(). You can also use whereOr() as a short form of where() and or().

where(
    path(Book::publishDate).between(
        OffsetDateTime.parse("2023-01-01T00:00:00+09:00"),
        OffsetDateTime.parse("2023-06-30T23:59:59+09:00"),
    ),
)

Group by clause

Use groupBy() and pass Expression to create unique groups of data when building a group by clause in the select statement.

groupBy(
    path(EmployeeDepartment::departmentId),
)

Having clause

Use having() and pass Expression to further restrict the data when building a having clause in the select statement. You can use havingAnd() as a short form of having() and and(). You can also use havingOr() as a short form of having() and or().

having(
    count(Employee::employeeId).greaterThan(1L),
)

Order by clause

Use orderBy() and pass Sort to return data in the declared order when building an order by clause in the select statement.

orderBy(
    path(Book::isbn).asc(),
)

Update statement

Call update() in jpql() to build an update statement.

val query = jpql {
    update(
        entity(Book::class)
    ).set(
        path(Book::price)(BookPrice::value),
        BigDecimal.valueOf(100)
    ).set(
        path(Book::salePrice)(BookPrice::value),
        BigDecimal.valueOf(80)
    ).where(
        path(Book::isbn).eq(Isbn("01"))
    )
}

Update clause

Use update() and pass Entity to specify the entity to modify when building an update clause in the update statement.

update(
    entity(Employee::class),
)

Set clause

Use set() and pass Expression to assign values when building a set clause in the update statement. You can use multiple assignments by adding set() after set().

set(
    path(Book::price)(BookPrice::value),
    BigDecimal.valueOf(100)
).set(
    path(Book::salePrice)(BookPrice::value),
    BigDecimal.valueOf(80)
)

Where clause

Use where() and pass Predicate to restrict the data when building a where clause in the update statement. You can use whereAnd() as a short form of where() and and(). You can also use whereOr() as a short form of where() and or().

where(
    path(Book::publishDate).between(
        OffsetDateTime.parse("2023-01-01T00:00:00+09:00"),
        OffsetDateTime.parse("2023-06-30T23:59:59+09:00"),
    ),
)

Delete statement

Call deleteFrom() in jpql() to build a delete statement.

val query = jpql {
    deleteFrom(
        entity(Book::class),
    ).where(
        path(Book::publishDate).ge(OffsetDateTime.parse("2023-06-01T00:00:00+09:00")),
    )
}

Delete from clause

Use deleteFrom() and pass Entity to specify the entity to delete when building a delete from clause in the delete statement.

deleteFrom(
    entity(Book::class),
)

Where clause

Use where() and pass Predicate to restrict the data when building a where clause in the delete statement. You can use whereAnd() as a short form of where() and and(). You can also use whereOr() as a short form of where() and or().

where(
    path(Book::publishDate).between(
        OffsetDateTime.parse("2023-01-01T00:00:00+09:00"),
        OffsetDateTime.parse("2023-06-30T23:59:59+09:00"),
    ),
)

Last updated