Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ISSUE-776] add selectFrom spec #777

Open
wants to merge 3 commits into
base: develop
Choose a base branch
from
Open
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions docs/en/jpql-with-kotlin-jdsl/statements.md
Original file line number Diff line number Diff line change
@@ -126,6 +126,86 @@ from(
)
```

### Select From clause

Use `selectFrom()` and pass [Entity](entities.md) and [Join](statements.md#Join-For-SelectFrom) to specify the entities for selection.
In the `selectFrom()` function you can create the select and from clause at once.

```kotlin
// It can infer the result type.
selectFrom(path(Author::class))

// It cannot infer the result type.
selectFrom(path(Author::class))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is different from the above example? It's the same code, but the explanation seems to be the opposite. I won't add a separate comment to the Korean explanation. It seems like the same problem.

--- korean
위 예제와 다른게 무엇일까요? 동일 코드인데 설명은 반대같이 보입니다.
한국어 설명쪽엔 별도 댓글 달지 않겠습니다. 동일한 문제 같아보입니다.

Copy link
Contributor Author

@kihwankim kihwankim Oct 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ohh that's meaningless content. I fixted
thank you for checking it thoroughly!

--- korean
의미 없는 내용이 있었네요. 수정했습니다
꼼꼼하게 확인해 주셔서 감사합니다 (_ _)

```

'SelectFrom()' has the same effect as calling the existing 'select()' and 'from()', respectively.

```kotlin
// selectFrom stmt
selectFrom(entity(Author::class))

// select and from stms
select(
entity(Author::class)
).from(
entity(Employee::class)
)
```

#### Join For SelectFrom

It is the same as [Join](statements.md#Join) above.

```kotlin
@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,

// ...
)

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

Calling 'as()' after 'join()' can also achieve the same result as [Join](statements.md#Join)

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

### Where clause

Use `where()` and pass [Predicate](predicates.md) to restrict the data when building a where clause in the select statement.
81 changes: 81 additions & 0 deletions docs/ko/jpql-with-kotlin-jdsl/statements.md
Original file line number Diff line number Diff line change
@@ -128,6 +128,87 @@ from(
)
```

### Select From clause

select statement의 select clause와 from clause를 한 번에 만들기 위해, `selectFrom()`을 이용할 수 있습니다.
`selectFrom()`은 [Entity](entities.md)와 [Join](statements.md#Join-For-SelectFrom)을 파라미터로 받아 어떤 entity를 통해 조회가 되는지 표현합니다.


```kotlin
// It can infer the result type.
selectFrom(path(Author::class))

// It cannot infer the result type.
selectFrom(path(Author::class))
```

`selectFrom()`은 기존의 `select()`, `from()`을 각각 호출하는 것과 동일한 효과를 가져옵니다.

```kotlin
// selectFrom stmt
selectFrom(entity(Author::class))

// select and from stms
select(
entity(Author::class)
).from(
entity(Employee::class)
)
```

#### Join For SelectFrom

위의 [Join](statements.md#Join)과 동일한 역할을 합니다.

```kotlin
@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,

// ...
)

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

`join()` 이후에 `as()`를 호출하 것 또한 [Join](statements.md#Join)와 동일한 결과를 얻을 수 있습니다.

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

### Where clause

select statement의 where clause를 만들기 위해, `where()`를 사용할 수 있습니다.
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ import com.linecorp.kotlinjdsl.dsl.jpql.join.impl.AssociationJoinDsl
import com.linecorp.kotlinjdsl.dsl.jpql.join.impl.FetchJoinDsl
import com.linecorp.kotlinjdsl.dsl.jpql.join.impl.JoinDsl
import com.linecorp.kotlinjdsl.dsl.jpql.select.SelectQueryFromStep
import com.linecorp.kotlinjdsl.dsl.jpql.select.SelectQueryWhereStep
import com.linecorp.kotlinjdsl.dsl.jpql.select.impl.SelectQueryFromStepDsl
import com.linecorp.kotlinjdsl.dsl.jpql.sort.SortNullsStep
import com.linecorp.kotlinjdsl.dsl.jpql.sort.impl.SortDsl
@@ -33,6 +34,7 @@ import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expression
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressionable
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Expressions
import com.linecorp.kotlinjdsl.querymodel.jpql.expression.Subquery
import com.linecorp.kotlinjdsl.querymodel.jpql.from.Fromable
import com.linecorp.kotlinjdsl.querymodel.jpql.join.JoinType
import com.linecorp.kotlinjdsl.querymodel.jpql.path.Path
import com.linecorp.kotlinjdsl.querymodel.jpql.path.Pathable
@@ -3119,6 +3121,18 @@ open class Jpql : JpqlDsl {
)
}

/**
* Creates a select clause in a select from query.
*/
@SinceJdsl("3.5.3")
inline fun <reified T : Any> selectFrom(
expr: Entityable<T>,
vararg froms: Fromable?,
): SelectQueryWhereStep<T> {
return select(expr)
.from(expr, *froms)
}

/**
* Creates a select clause in a select query.
*/
Original file line number Diff line number Diff line change
@@ -88,4 +88,59 @@ class FromDslTest : WithAssertions {

assertThat(actual).isEqualTo(expected)
}

@Test
fun `selectFrom() with entity`() {
// when
val selectFrom = queryPart {
selectFrom(
entity1,
)
}.toQuery()
val actual: SelectQuery<Book> = selectFrom

// then
val expected = SelectQueries.selectQuery(
returnType = Book::class,
distinct = false,
select = listOf(entity1),
from = listOf(
entity1,
),
)

assertThat(actual).isEqualTo(expected)
}

@Test
fun `selectFrom() with froms`() {
// when
val selectFrom = queryPart {
selectFrom(
entity1,
null,
entity2,
null,
join1,
null,
join2,
)
}.toQuery()
val actual: SelectQuery<Book> = selectFrom

// then
val expected = SelectQueries.selectQuery(
returnType = Book::class,
distinct = false,
select = listOf(entity1),
from = listOf(
entity1,
entity2,
join1,
join2,
),
)

assertThat(actual).isEqualTo(expected)
}
}
Original file line number Diff line number Diff line change
@@ -70,6 +70,26 @@ class SelectExample : WithAssertions {
assertThat(actual).isEqualTo(listOf(4L))
}

@Test
fun `authors who haven't written a book with selectFrom`() {
// when
val query = jpql {
selectFrom(
entity(Author::class),
leftJoin(BookAuthor::class).on(path(Author::authorId).equal(path(BookAuthor::authorId))),
).where(
path(BookAuthor::authorId).isNull(),
).orderBy(
path(Author::authorId).asc(),
)
}

val actual = entityManager.createQuery(query, context).resultList

// then
assertThat(actual.map { it.authorId }).isEqualTo(listOf(4L))
}

@Test
fun `the book with the most authors`() {
// when
Original file line number Diff line number Diff line change
@@ -70,6 +70,26 @@ class SelectExample : WithAssertions {
assertThat(actual).isEqualTo(listOf(4L))
}

@Test
fun `authors who haven't written a book with selectFrom`() {
// when
val query = jpql {
selectFrom(
entity(Author::class),
leftJoin(BookAuthor::class).on(path(Author::authorId).equal(path(BookAuthor::authorId))),
).where(
path(BookAuthor::authorId).isNull(),
).orderBy(
path(Author::authorId).asc(),
)
}

val actual = entityManager.createQuery(query, context).resultList

// then
assertThat(actual.map { it.authorId }).isEqualTo(listOf(4L))
}

@Test
fun `the book with the most authors`() {
// when
Original file line number Diff line number Diff line change
@@ -71,6 +71,26 @@ class SelectExample : WithAssertions {
assertThat(actual).isEqualTo(listOf(4L))
}

@Test
fun `authors who haven't written a book with selectFrom`() {
// when
val query = jpql {
selectFrom(
entity(Author::class),
leftJoin(BookAuthor::class).on(path(Author::authorId).equal(path(BookAuthor::authorId))),
).where(
path(BookAuthor::authorId).isNull(),
).orderBy(
path(Author::authorId).asc(),
)
}

val actual = entityManager.createQuery(query, context).resultList

// then
assertThat(actual.map { it.authorId }).isEqualTo(listOf(4L))
}

@Test
fun books() {
// when
Original file line number Diff line number Diff line change
@@ -68,6 +68,28 @@ class SelectMutinySessionExample : WithAssertions {
assertThat(actual).isEqualTo(listOf(4L))
}

@Test
fun `authors who haven't written a book with selectFrom`() {
// when
val query = jpql {
selectFrom(
entity(Author::class),
leftJoin(BookAuthor::class).on(path(Author::authorId).equal(path(BookAuthor::authorId))),
).where(
path(BookAuthor::authorId).isNull(),
).orderBy(
path(Author::authorId).asc(),
)
}

val actual = sessionFactory.withSession {
it.createQuery(query, context).resultList
}.await().indefinitely()

// then
assertThat(actual.map { it.authorId }).isEqualTo(listOf(4L))
}

@Test
fun `the book with the most authors`() {
// when
Loading