Add native support for @Convert on JPA entities
This commit infers the reflection hints required for converters when they are specified with the @Convert annotation at class or field level. It also refines the hints generated for @Converter in order to just generate the construct hint, not the public method one which should be not needed. Closes gh-29771
This commit is contained in:
parent
e2832ea596
commit
3348e74ab8
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -23,6 +23,7 @@ import java.util.List;
|
||||||
|
|
||||||
import javax.lang.model.element.Modifier;
|
import javax.lang.model.element.Modifier;
|
||||||
|
|
||||||
|
import jakarta.persistence.Convert;
|
||||||
import jakarta.persistence.Converter;
|
import jakarta.persistence.Converter;
|
||||||
import jakarta.persistence.EntityListeners;
|
import jakarta.persistence.EntityListeners;
|
||||||
import jakarta.persistence.IdClass;
|
import jakarta.persistence.IdClass;
|
||||||
|
|
@ -156,9 +157,20 @@ class PersistenceManagedTypesBeanRegistrationAotProcessor implements BeanRegistr
|
||||||
|
|
||||||
private void contributeConverterHints(RuntimeHints hints, Class<?> managedClass) {
|
private void contributeConverterHints(RuntimeHints hints, Class<?> managedClass) {
|
||||||
Converter converter = AnnotationUtils.findAnnotation(managedClass, Converter.class);
|
Converter converter = AnnotationUtils.findAnnotation(managedClass, Converter.class);
|
||||||
|
ReflectionHints reflectionHints = hints.reflection();
|
||||||
if (converter != null) {
|
if (converter != null) {
|
||||||
hints.reflection().registerType(managedClass, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS);
|
reflectionHints.registerType(managedClass, MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
|
||||||
}
|
}
|
||||||
|
Convert convertClassAnnotation = AnnotationUtils.findAnnotation(managedClass, Convert.class);
|
||||||
|
if (convertClassAnnotation != null) {
|
||||||
|
reflectionHints.registerType(convertClassAnnotation.converter(), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
|
||||||
|
}
|
||||||
|
ReflectionUtils.doWithFields(managedClass, field -> {
|
||||||
|
Convert convertFieldAnnotation = AnnotationUtils.findAnnotation(field, Convert.class);
|
||||||
|
if (convertFieldAnnotation != null && convertFieldAnnotation.converter() != void.class) {
|
||||||
|
reflectionHints.registerType(convertFieldAnnotation.converter(), MemberCategory.INVOKE_DECLARED_CONSTRUCTORS);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void contributeCallbackHints(RuntimeHints hints, Class<?> managedClass) {
|
private void contributeCallbackHints(RuntimeHints hints, Class<?> managedClass) {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package org.springframework.orm.jpa.domain;
|
package org.springframework.orm.jpa.domain;
|
||||||
|
|
||||||
import jakarta.persistence.Column;
|
import jakarta.persistence.Column;
|
||||||
|
import jakarta.persistence.Convert;
|
||||||
import jakarta.persistence.Entity;
|
import jakarta.persistence.Entity;
|
||||||
import jakarta.persistence.Id;
|
import jakarta.persistence.Id;
|
||||||
import jakarta.persistence.IdClass;
|
import jakarta.persistence.IdClass;
|
||||||
|
|
@ -24,6 +25,7 @@ import jakarta.persistence.PreRemove;
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@IdClass(EmployeeId.class)
|
@IdClass(EmployeeId.class)
|
||||||
|
@Convert(converter = EmployeeKindConverter.class, attributeName = "kind")
|
||||||
public class Employee {
|
public class Employee {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
|
|
@ -36,6 +38,10 @@ public class Employee {
|
||||||
|
|
||||||
private EmployeeLocation location;
|
private EmployeeLocation location;
|
||||||
|
|
||||||
|
@Convert(converter = EmployeeCategoryConverter.class)
|
||||||
|
private EmployeeCategory category;
|
||||||
|
|
||||||
|
private EmployeeKind kind;
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
|
|
@ -61,6 +67,22 @@ public class Employee {
|
||||||
this.location = location;
|
this.location = location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EmployeeCategory getCategory() {
|
||||||
|
return category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCategory(EmployeeCategory category) {
|
||||||
|
this.category = category;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EmployeeKind getKind() {
|
||||||
|
return kind;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKind(EmployeeKind kind) {
|
||||||
|
this.kind = kind;
|
||||||
|
}
|
||||||
|
|
||||||
@PreRemove
|
@PreRemove
|
||||||
public void preRemove() {
|
public void preRemove() {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.orm.jpa.domain;
|
||||||
|
|
||||||
|
public class EmployeeCategory {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.orm.jpa.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.AttributeConverter;
|
||||||
|
|
||||||
|
public class EmployeeCategoryConverter implements AttributeConverter<EmployeeCategory, String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String convertToDatabaseColumn(EmployeeCategory employeeCategory) {
|
||||||
|
if (employeeCategory != null) {
|
||||||
|
return employeeCategory.getName();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EmployeeCategory convertToEntityAttribute(String data) {
|
||||||
|
if (data != null) {
|
||||||
|
EmployeeCategory employeeCategory = new EmployeeCategory();
|
||||||
|
employeeCategory.setName(data);
|
||||||
|
return employeeCategory;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.orm.jpa.domain;
|
||||||
|
|
||||||
|
public class EmployeeKind {
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2023 the original author or authors.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.springframework.orm.jpa.domain;
|
||||||
|
|
||||||
|
import jakarta.persistence.AttributeConverter;
|
||||||
|
|
||||||
|
public class EmployeeKindConverter implements AttributeConverter<EmployeeKind, String> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String convertToDatabaseColumn(EmployeeKind employeeKind) {
|
||||||
|
if (employeeKind != null) {
|
||||||
|
return employeeKind.getName();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EmployeeKind convertToEntityAttribute(String data) {
|
||||||
|
if (data != null) {
|
||||||
|
EmployeeKind employeeKind = new EmployeeKind();
|
||||||
|
employeeKind.setName(data);
|
||||||
|
return employeeKind;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2022 the original author or authors.
|
* Copyright 2002-2023 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|
@ -40,7 +40,9 @@ import org.springframework.orm.jpa.JpaVendorAdapter;
|
||||||
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
|
||||||
import org.springframework.orm.jpa.domain.DriversLicense;
|
import org.springframework.orm.jpa.domain.DriversLicense;
|
||||||
import org.springframework.orm.jpa.domain.Employee;
|
import org.springframework.orm.jpa.domain.Employee;
|
||||||
|
import org.springframework.orm.jpa.domain.EmployeeCategoryConverter;
|
||||||
import org.springframework.orm.jpa.domain.EmployeeId;
|
import org.springframework.orm.jpa.domain.EmployeeId;
|
||||||
|
import org.springframework.orm.jpa.domain.EmployeeKindConverter;
|
||||||
import org.springframework.orm.jpa.domain.EmployeeLocation;
|
import org.springframework.orm.jpa.domain.EmployeeLocation;
|
||||||
import org.springframework.orm.jpa.domain.EmployeeLocationConverter;
|
import org.springframework.orm.jpa.domain.EmployeeLocationConverter;
|
||||||
import org.springframework.orm.jpa.domain.Person;
|
import org.springframework.orm.jpa.domain.Person;
|
||||||
|
|
@ -96,8 +98,11 @@ class PersistenceManagedTypesBeanRegistrationAotProcessorTests {
|
||||||
assertThat(RuntimeHintsPredicates.reflection().onType(EmployeeId.class)
|
assertThat(RuntimeHintsPredicates.reflection().onType(EmployeeId.class)
|
||||||
.withMemberCategories(MemberCategory.DECLARED_FIELDS)).accepts(hints);
|
.withMemberCategories(MemberCategory.DECLARED_FIELDS)).accepts(hints);
|
||||||
assertThat(RuntimeHintsPredicates.reflection().onType(EmployeeLocationConverter.class)
|
assertThat(RuntimeHintsPredicates.reflection().onType(EmployeeLocationConverter.class)
|
||||||
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, MemberCategory.INVOKE_PUBLIC_METHODS))
|
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(hints);
|
||||||
.accepts(hints);
|
assertThat(RuntimeHintsPredicates.reflection().onType(EmployeeCategoryConverter.class)
|
||||||
|
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(hints);
|
||||||
|
assertThat(RuntimeHintsPredicates.reflection().onType(EmployeeKindConverter.class)
|
||||||
|
.withMemberCategories(MemberCategory.INVOKE_DECLARED_CONSTRUCTORS)).accepts(hints);
|
||||||
assertThat(RuntimeHintsPredicates.reflection().onType(EmployeeLocation.class)
|
assertThat(RuntimeHintsPredicates.reflection().onType(EmployeeLocation.class)
|
||||||
.withMemberCategories(MemberCategory.DECLARED_FIELDS)).accepts(hints);
|
.withMemberCategories(MemberCategory.DECLARED_FIELDS)).accepts(hints);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue