-
Notifications
You must be signed in to change notification settings - Fork 92
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
Add path method for KFunction1
#521
Changes from 3 commits
341ddfc
e5e3a00
a8cd463
d0d35c4
04cef12
581d5d3
84fc35c
6362262
74ab44d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,75 @@ entity(Book::class, "b").path(Book::isbn).path(Isbn::value) | |
entity(Book::class, "b")(Book::isbn)(Isbn::value) | ||
``` | ||
|
||
## Java entity | ||
|
||
`path()` and `invoke()` take `KProperty1` or `KFuction1` as an argument. | ||
`KFunction1` is useful when you use Java entity with private property and public getter. | ||
|
||
```java | ||
@Entity | ||
public class Book { | ||
@Id | ||
private Long id; | ||
|
||
private String title; | ||
|
||
public String getTitle() { | ||
return title; | ||
} | ||
} | ||
``` | ||
|
||
```kotlin | ||
// compile error | ||
path(Book::title) | ||
|
||
// Book.title | ||
path(Book::getTitle) | ||
``` | ||
|
||
Kotlin JDSL follows the following rules to infer property name from getter name. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to change the sentence to something like this: |
||
|
||
- If the name starts with `is`, use the name as it is. | ||
- If the name starts with `get`, remove `get` and change the first letter to lowercase. | ||
- Otherwise, use the name as it is. | ||
|
||
```kotlin | ||
// Book.isAvailable | ||
path(Book::isAvailable) | ||
|
||
// Book.available | ||
path(Book::getAvailable) | ||
``` | ||
|
||
If you want to use your own rule instead of the above rules, you need to implement `JpqlPropertyIntrospector` and provide it to `RenderContext`. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would like to change There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be nice to leave a guide and link to the Custom DSL for more details, and to Spring Support if the user is using Spring. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also it would be nice to add Spring AutoConfigure, since there is no Spring Support for open class KotlinJdslAutoConfiguration {
@Bean
@ConditionalOnMissingBean
@SinceJdsl("3.0.0")
open fun jpqlRenderContext(
serializers: List<JpqlSerializer<*>>,
introspectors: List<JpqlIntrospector>,
): JpqlRenderContext {
val userDefinedSerializers = object : JpqlRenderModule {
override fun setupModule(context: JpqlRenderModule.SetupContext) {
context.addAllSerializer(serializers.reversed())
introspectors.reversed().forEach {
context.prependIntrospector(it)
}
}
}
return JpqlRenderContext()
.registerModules(userDefinedSerializers)
}
// ...
} |
||
|
||
```kotlin | ||
class MyIntrospector : JpqlPropertyIntrospector() { | ||
override fun introspect(property: KCallable<*>): JpqlPropertyDescription? { | ||
if (property is KFunction1<*, *>) { | ||
// resolve a name with your own rule | ||
val name = ... | ||
return MyProperty(name) | ||
} | ||
|
||
return null | ||
} | ||
|
||
private data class MyProperty( | ||
override val name: String, | ||
) : JpqlPropertyDescription | ||
} | ||
|
||
val myModule = object : JpqlRenderModule { | ||
override fun setupModule(context: JpqlRenderModule.SetupContext) { | ||
context.prependIntrospector(MyIntrospector()) | ||
} | ||
} | ||
|
||
val myContext = JpqlRenderContext().registerModule(myModule) | ||
``` | ||
|
||
## Expression | ||
|
||
`Path` can be used as [`Expression`](expressions.md), such as in a [select clause](statements.md#select-clause) or [predicate](predicates.md). | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,75 @@ entity(Book::class, "b").path(Book::isbn).path(Isbn::value) | |
entity(Book::class, "b")(Book::isbn)(Isbn::value) | ||
``` | ||
|
||
## Java entity | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 한국어까지 같이 작성해주셔서 정말 감사합니다. ❤️ |
||
|
||
`path()` 와 `invoke()`는 `KProperty1` 또는 `KFuction1`를 인자로 받습니다. | ||
`KFunction1`의 경우, getter만 public인 Java로 선언한 entity를 사용할 때 유용합니다. | ||
|
||
```java | ||
@Entity | ||
public class Book { | ||
@Id | ||
private Long id; | ||
|
||
private String title; | ||
|
||
public String getTitle() { | ||
return title; | ||
} | ||
} | ||
``` | ||
|
||
```kotlin | ||
// compile error | ||
path(Book::title) | ||
|
||
// Book.title | ||
path(Book::getTitle) | ||
``` | ||
|
||
Kotlin JDSL은 getter 이름에서 프로퍼티 이름을 추론하기 위해 다음 규칙을 따릅니다. | ||
|
||
- `is`로 시작하는 경우 이름 그대로 사용합니다. | ||
- `get`으로 시작하는 경우 `get`을 제거하고 이후 첫 글자를 소문자로 변경합니다. | ||
- 그 외의 경우, 이름 그대로 사용합니다. | ||
|
||
```kotlin | ||
// Book.isAvailable | ||
path(Book::isAvailable) | ||
|
||
// Book.available | ||
path(Book::getAvailable) | ||
``` | ||
|
||
위 규칙 대신 나만의 규칙을 사용하고 싶다면, `JpqlPropertyIntrospector`를 구현하고 이를 이를 `RenderContext`에 추가해야 합니다. | ||
|
||
```kotlin | ||
class MyIntrospector : JpqlPropertyIntrospector() { | ||
override fun introspect(property: KCallable<*>): JpqlPropertyDescription? { | ||
if (property is KFunction1<*, *>) { | ||
// 나만의 규칙으로 이름을 추론합니다 | ||
val name = ... | ||
return MyProperty(name) | ||
} | ||
|
||
return null | ||
} | ||
|
||
private data class MyProperty( | ||
override val name: String, | ||
) : JpqlPropertyDescription | ||
} | ||
|
||
val myModule = object : JpqlRenderModule { | ||
override fun setupModule(context: JpqlRenderModule.SetupContext) { | ||
context.prependIntrospector(MyIntrospector()) | ||
} | ||
} | ||
|
||
val myContext = JpqlRenderContext().registerModule(myModule) | ||
``` | ||
|
||
## Expression | ||
|
||
`Path`는 [select clause](statements.md#select-clause) 나 [predicate](predicates.md) 등에서 [`Expression`](expressions.md)으로 사용될 수 있습니다. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,6 +41,7 @@ import java.math.BigDecimal | |
import java.math.BigInteger | ||
import kotlin.internal.Exact | ||
import kotlin.reflect.KClass | ||
import kotlin.reflect.KFunction1 | ||
import kotlin.reflect.KProperty1 | ||
|
||
/** | ||
|
@@ -194,6 +195,15 @@ open class Jpql : JpqlDsl { | |
return Paths.path(property) | ||
} | ||
|
||
/** | ||
* Creates a path expression with the property. | ||
* The path starts from the entity which is the owner of the property. | ||
*/ | ||
@SinceJdsl("3.1.0") | ||
fun <T : Any, V> path(getter: KFunction1<T, @Exact V>): Path<V & Any> { | ||
return Paths.path(getter) | ||
} | ||
|
||
/** | ||
* Creates a path expression with the entity and property. | ||
*/ | ||
|
@@ -202,6 +212,14 @@ open class Jpql : JpqlDsl { | |
return Paths.path(this.toEntity(), property) | ||
} | ||
|
||
/** | ||
* Creates a path expression with the entity and property. | ||
*/ | ||
@SinceJdsl("3.1.0") | ||
fun <T : Any, V> Entityable<T>.path(getter: KFunction1<T, @Exact V>): Path<V & Any> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible to put There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It works in the test, but it's probably because the receiver's T is cast to a supertype. This causes the type to be broken when chaining occurs. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. when i first put
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aha, I see. Thank you for the confirmation. |
||
return Paths.path(this.toEntity(), getter) | ||
} | ||
|
||
/** | ||
* Creates a path expression with the path and property. | ||
*/ | ||
|
@@ -210,6 +228,14 @@ open class Jpql : JpqlDsl { | |
return Paths.path(this.toPath(), property) | ||
} | ||
|
||
/** | ||
* Creates a path expression with the path and property. | ||
*/ | ||
@SinceJdsl("3.1.0") | ||
fun <T : Any, V> Pathable<T>.path(getter: KFunction1<T, @Exact V>): Path<V & Any> { | ||
return Paths.path(this.toPath(), getter) | ||
} | ||
|
||
/** | ||
* Creates a path expression with the entity and property. | ||
*/ | ||
|
@@ -218,6 +244,14 @@ open class Jpql : JpqlDsl { | |
return Paths.path(this.toEntity(), property) | ||
} | ||
|
||
/** | ||
* Creates a path expression with the entity and property. | ||
*/ | ||
@SinceJdsl("3.1.0") | ||
operator fun <T : Any, V> Entityable<T>.invoke(getter: KFunction1<T, @Exact V>): Path<V & Any> { | ||
return Paths.path(this.toEntity(), getter) | ||
} | ||
|
||
/** | ||
* Creates a path expression with the path and property. | ||
*/ | ||
|
@@ -226,6 +260,14 @@ open class Jpql : JpqlDsl { | |
return Paths.path(this.toPath(), property) | ||
} | ||
|
||
/** | ||
* Creates a path expression with the path and property. | ||
*/ | ||
@SinceJdsl("3.1.0") | ||
operator fun <T : Any, V> Pathable<T>.invoke(getter: KFunction1<T, @Exact V>): Path<V & Any> { | ||
return Paths.path(this.toPath(), getter) | ||
} | ||
|
||
/** | ||
* Creates an aliased expression with the alias expression. | ||
* The aliased expression can be referenced by the alias expression. | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about replacing
take
withcan take
?