==========UPDATE==========
when i change the annotation to @get:NotNull , @get:Min and @get:Max, the hibernate-validator can read these annotations success.
But what i still want to know is:
why the validation-api's annotation, such as @NotNull , @Min and @Max cannot be used on data class's members directly, while JPA's annotations can be????
==========Bellow is the origin question===========
when i tried to use validation-api's annotation on data class, the Validator class (from hibernate-validator) cannot get the annotations, so the validation failed.
i wrote a test case, which include 3 data classes:
The target and retention of @Column , @NotNull, @Min and @Max are all: RUNTIME and FIELD
So, what happened in the behind? how can i use the validation annotations properly?
here is the test case:
import org.hibernate.validator.constraints.NotEmpty
import org.junit.Test
import javax.persistence.Column
import javax.persistence.Id
import javax.validation.constraints.Max
import javax.validation.constraints.Min
import kotlin.reflect.KFunction
import kotlin.reflect.KProperty
import kotlin.reflect.jvm.javaField
import kotlin.reflect.jvm.javaMethod
class KotlinFeatureTest2 {
@Test
fun test_get_annotation() {
// can get field's annotation for BeanUseJPAAnnotation success
println("Getting field annotations for BeanUseJPAAnnotation :")
BeanUseJPAAnnotation::class.members.forEach {
if (it is KProperty) {
val field = it.javaField
println("${field?.name}'s annotations:")
field?.annotations?.forEachIndexed { i, an ->
println(" $i is: $an")
}
}
}
println("--------------------")
println("Getting field annotations for BeanUseValidationAnnotation :")
// CANT get field's annotation for BeanUseJPAAnnotation success
BeanUseValidationAnnotation::class.members.forEach {
if (it is KProperty) {
val field = it.javaField
println("${field?.name}'s annotations:")
field?.annotations?.forEachIndexed { i, an ->
println(" $i is: $an")
}
}
}
println("--------------------")
println("Getting field annotations for BeanUseValidationAnnotationOnMethod :")
// CANT get field's annotation for BeanUseJPAAnnotation success
BeanUseValidationAnnotationOnMethod::class.members.forEach {
if (it is KFunction) {
val method = it.javaMethod
println("${method?.name}'s annotations: ")
method?.annotations?.forEachIndexed { i, an ->
println(" $i is: $an")
}
}
}
}
}
data class BeanUseJPAAnnotation(
@Column(name = "id") @Id val id: String,
@Column(name = "user_name") val name: String)
data class BeanUseValidationAnnotation(
@NotEmpty(message = "name can not be empty")
val name: String,
@Min(value = 1)
@Max(value = 100)
val age: Int
)
data class BeanUseValidationAnnotationOnMethod(
@get:NotEmpty(message = "name can not be empty")
val name: String,
@get:Min(value = 1)
@get:Max(value = 100)
val age: Int)
and here are the output of this test case:
Getting field annotations for BeanUseJPAAnnotation :
id's annotations:
0 is: @javax.persistence.Column(nullable=true, unique=false, precision=0, name=id, length=255, scale=0, updatable=true, columnDefinition=, table=, insertable=true)
1 is: @javax.persistence.Id()
name's annotations:
0 is: @javax.persistence.Column(nullable=true, unique=false, precision=0, name=user_name, length=255, scale=0, updatable=true, columnDefinition=, table=, insertable=true)
--------------------
Getting field annotations for BeanUseValidationAnnotation :
age's annotations:
name's annotations:
--------------------
Getting field annotations for BeanUseValidationAnnotationOnMethod :
component1's annotations:
component2's annotations:
copy's annotations:
equals's annotations:
hashCode's annotations:
toString's annotations:
The following is the signature part of javax.persistence.Column:
@Target({METHOD, FIELD})
@Retention(RUNTIME)
public @interface Column {
On the contrary here's the same part of javax.validation.constraints.Min:
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(List.class)
@Documented
@Constraint(validatedBy = { })
public @interface Min {
As you can see the JPA Persistence annotations target METHOD and FIELD hence Kotlin emits them on a FIELD level. However the validators API annotations target more constructs, the PARAMETER in particular. Given that when generating annotations for constructor declared properties Kotlin compiler chooses to annotate parameters only. The BeanUseValidationAnnotation constructor signature equivalent in Java would look like:
public BeanUseValidationAnnotation(@NotEmpty(message = "name can not be empty") @NotNull String name, @Min(1L) @Max(100L) int age) {
This behavior is stated in the documentation:
If you don't specify a use-site target, the target is chosen according to the
@Targetannotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following list is used:
parampropertyfield
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With