@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 类型,但是查询的时候依然要使用实体类中属性的类型这个问题,本人也不知其中缘由,有懂的大佬麻烦指出

 

对应到本人CSDN的博客



posted @ 2022-07-27 15:07  海底月,眼前人  阅读(1569)  评论(0)    收藏  举报