@Convert 注解在jpa中进行查询的注意事项
如果要实现实体类中属性的类型和数据库表中字段的类型相互转化,则需要使用 @Convert 注解
package javax.persistence; import java.lang.annotation.Repeatable; import java.lang.annotation.Target; import java.lang.annotation.Retention; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.RetentionPolicy.RUNTIME; @Repeatable(Converts.class) @Target({METHOD, FIELD, TYPE})
@Retention(RUNTIME) public @interface Convert { /** * 指定要使用的转换器 */ Class converter() default void.class; /** * 当注解 Convert 应用在基本类型的属性或基本类型的元素集合上,不能指定attributeName元素。 */ String attributeName() default ""; /** * 用于禁用自动应用或继承的转换器。如果 disableConversion 为真,则不应指定converter元素 */ boolean disableConversion() default false; }
由 Convert 源码可知:使用 @Convert 注解需要一个转换器,
自定义转换器需要实现 AttributeConverter 接口
1、AttributeConverter 接口
package javax.persistence; /** * * 把实体类中属性的类型与数据库表中字段的类型相互转换. * * @param <X> 实体类中的属性类型 * @param <Y> 数据库表中的字段类型 */ public interface AttributeConverter<X,Y> { /** * 实体类中属性的类型转换为数据库表中字段的类型 * * @param 实体类中属性的类型 * @return 数据库表中字段的类型 * */ public Y convertToDatabaseColumn (X attribute); /** * 数据库表中字段的类型转换为实体类中属性的类型 * * @param 数据库表中字段的类型 * @return 实体类中属性的类型 * */ public X convertToEntityAttribute (Y dbData); }
2、自定义的实现类
package com.chayiges; import javax.persistence.AttributeConverter; import javax.persistence.Converter; import java.time.YearMonth; /** * 使用 JPA 将 YearMonth 持久化为整数 * * @author chayiges */
// 标识该类是转换器 @Converter public class YearMonthIntegerAttributeConverter
implements AttributeConverter<YearMonth, Integer> { /** * 实体类 -> 数据库 * 向数据库中保存时调用 */ @Override public Integer convertToDatabaseColumn(YearMonth attribute) { if (attribute != null) { return (attribute.getYear() * 100) + attribute.getMonth().getValue(); } return null; } /** * 数据库 -> 实体类 * 向数据库中查询时调用 */ @Override public YearMonth convertToEntityAttribute(Integer dbData) { if (dbData != null) { int year = dbData / 100; int month = dbData % 100; return YearMonth.of(year, month); } return null; } }
1.2、创建数据库对应的实体类
package com.chayiges; import jp.co.isid.cas3.commons.api.jpa.AbstractAggregateRoot; import jp.co.isid.cas3.onecas.core.YearMonthIntegerAttributeConverter; import lombok.AccessLevel; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import javax.persistence.Column; import javax.persistence.Convert; import javax.persistence.EmbeddedId; import javax.persistence.Entity; import javax.persistence.Table; import java.io.Serializable; import java.time.YearMonth; import java.util.UUID; /** * 学生表对应的实体类 * * @author chayiges */ @Entity @Getter @Table @NoArgsConstructor(access = AccessLevel.PROTECTED, force = true) @AllArgsConstructor public class Student extends AbstractAggregateRoot<Student, UUID> implements Serializable { /** ID */ @EmbeddedId private UUID id; /** 姓名 */ @Column(name = "user_name", length = 100, nullable = false) private String userName; /** 年月 */ @Convert(converter = YearMonthIntegerAttributeConverter.class) @Column(name = "year_month") private YearMonth yearMonth; }
1.3、创建 jpa 查询
package com.chayiges; import org.springframework.data.jpa.repository.JpaRepository; import java.time.YearMonth; import java.util.UUID; /** * 学生表的jap查询 * * @author chayiges */ public interface Students extends JpaRepository<Student, UUID> { /** * 根据年月查询学生表 * @param yearMonth 年月 * @return 学生表 */ List<Student> findByYearMonth(YearMonth yearMonth); }
1.4、测试
package jp.chayiges; import lombok.RequiredArgsConstructor; import org.junit.jupiter.api.Test; import java.time.YearMonth; import java.util.UUID; import java.util.List; /** * jap查询测试 * * @author chayiges */ @RequiredArgsConstructor public class StudentJPAUnitTests { /** 实体类到数据库表的转换器 */ final YearMonthIntegerAttributeConverterauto yearMonthIntegerAttributeConverterauto; /** 学生表 jpa */ final Students students; /** * テスト */ @Test void findByYearMonthTest() { YearMonth yearMonth = YearMonth.of(2022, 7); // 创建学生对象 Student stu = new Student("a57988c3-1711-4288-9623-9e7c177ff078", "孙汐", yearMonth); // 调用 jpa 方法进行保存,保存的时候会进入自定义的Convert实现类中进行实体类属性值到数据 // 库中对应字段值的转换 this.students.save(stu); // 调用 jpa 方法查询 List<Student> studentList = this.students.findByYearMonth(yearMonth); } }
1.5、创建了错误的 jpa 方法
package com.chayiges; import org.springframework.data.jpa.repository.JpaRepository; import java.time.YearMonth; import java.util.UUID; /** * 学生表的jap查询 * * @author chayiges */ public interface Students extends JpaRepository<Student, UUID> { /** * 根据年月查询学生表 * @param yearMonth 年月 * @return 学生表 */ List<Student> findByYearMonth(Integer integer); }
1.6、错误的 jpa 方法的测试
package jp.chayiges; import lombok.RequiredArgsConstructor; import org.junit.jupiter.api.Test; import java.time.YearMonth; import java.util.UUID; import java.util.List; /** * jap查询测试 * * @author chayiges */ @RequiredArgsConstructor public class StudentJPAUnitTests { /** 实体类到数据库表的转换器 */ final YearMonthIntegerAttributeConverterauto yearMonthIntegerAttributeConverterauto; /** 学生表jpa */ final Students students; /** * テスト */ @Test void findByYearMonthTest() { YearMonth yearMonth = YearMonth.of(2022, 7); // 创建学生对象 Student stu = new Student("a57988c3-1711-4288-9623-9e7c177ff078", "孙汐", yearMonth); // 调用 jpa 方法进行保存,保存的时候会进入自定义的Convert实现类中进行实体类属性值到数据 // 库中对应字段值的转换 this.students.save(stu); // 调用 jpa 方法查询,会抛出 InvalidDataAccessApiUsageException 异常 List<Student> studentList = this.students.findByYearMonth(this .yearMonthIntegerAttributeConverterauto .convertToDatabaseColumn(yearmonth)); } }
注意:此处查询年月的时候用的是实体类中 YearMonth 类型,并不是转换到数据库中的 Integer 类型
至于数据保存到数据库中确实是 Integer 类型,但是查询的时候依然要使用实体类中属性的类型这个问题,本人也不知其中缘由,有懂的大佬麻烦指出
浙公网安备 33010602011771号