SpringBoot JPA 2.1 Converter 忽略 convertToDatabaseColumn

Posted

技术标签:

【中文标题】SpringBoot JPA 2.1 Converter 忽略 convertToDatabaseColumn【英文标题】:SpringBoot JPA 2.1 Converter ignores convertToDatabaseColumn 【发布时间】:2020-02-25 16:58:10 【问题描述】:

我有以下转换器

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

@Converter(autoApply = true)
public class ImportedReasonConverter implements AttributeConverter<ImportedReason, String> 
    @Override
    public String convertToDatabaseColumn(final ImportedReason pImportedReason) 
        return pImportedReason.getValue();
    

    @Override
    public ImportedReason convertToEntityAttribute(final String pImportedReason) 
        return ImportedReason.of(pImportedReason);
    

被诸如

之类的实体使用
@Entity
@Table(
        name = "IMPORT_RECORD"
)
public class ImportRecordEntity implements Serializable 

    private static final long serialVersionUID = 2483327758356663412L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private UUID id;

    @Column(name = "importedReason")
    private ImportedReason importedReason;

    public ImportedReason getImportedReason() 
        return importedReason;
    

    public void setImportedReason(final ImportedReason importedReason) 
        this.importedReason = importedReason;
    

...

启动应用程序后,表由 Hibernate 创建,使用正确的列类型(此处为:VARCHAR)。删除 autoApply=true 设置会导致启动失败 - 因此转换器本身会被识别。

但是:在保存实体时将值设置为importedReason,我得到一个

Caused by: java.io.NotSerializableException: ImportedReason
    at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1185)
    at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1553)
    at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1510)
    at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433)
    at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179)
    at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349)
    at org.hibernate.internal.util.SerializationHelper.serialize(SerializationHelper.java:115)
    ... 43 more

尝试调试我的转换器,方法 ImportedReasonConverter.convertToDatabaseColumn(ImportedReason) 没有被调用。因此 Hibernate 不会尝试写入 String 类型的值,而是输入 ImportedReason。

任何想法,为什么转换器不能正确使用?我已经使用 Java 8 + 11 和 Spring 2.1.5.RELEASE + 2.1.9.RELEASE (早期版本用于另一个项目)进行了尝试。 我也尝试过使用其他一些转换器(例如内部类型 -> LocalDateTime),但我在这里也遇到了同样的问题。

非常感谢。 斯蒂芬

【问题讨论】:

'@Column(name = "importedReason") @Converter(.......) private ImportedReason importReason;' @Zorglube 不,这不会改变它 真的!?奇怪,你的注释是这样的吗? @Convert(converter = ImportedReasonConverter.class),调试是否显示您通过 convertToDatabaseColumnconvertToEntityAttribute 方法? 我都尝试了,在转换器本身上设置 autoApply = true 并在实体的属性中添加 @Converter。转换器似乎被使用的两种方式(表格设置正确),但是当涉及到将它用于引用的实体时,它变得不可见。请参阅我的答案 - 我试图在那里更详细地描述它。 【参考方案1】:

更新: 进一步的调查表明,正在保存的实体已经过全面分析,并且其转换器得到了正确使用。 但是:如果该实体使用其他一些实体作为引用,而后者又使用要使用这些转换器转换的类型:那是它看到值但不使用转换器(出于某种原因)的时候。

所以我上面的 ImportRecordEntity 用于以下 PortfolioEntity,我实际上正在尝试保存它。保存 ImportRecordEntity 毫不费力。然后我将这个 ImportRecordEntity 实例(从保存方法返回)设置为 PortfolioEntity 实例并保存它。但是随后 Hibernate 崩溃了,因为它似乎分析了 PortfolioEntity 而不是 ImportRecordEntity ,它持有 ImportedReason - 所以它不知道 ImportedReasonConverter (参见 org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(Object, EntityKey, EntityPersister, boolean, Object, EventSource, boolean) - AbstractSaveEventListener.java:122 变量类型)。


@Entity
@Table(
        name = "PORTFOLIO"
)
public class PortfolioEntity implements Serializable 

    private static final long serialVersionUID = 3632725596750538287L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private UUID id;

    @Column(name = "IMPORT_RECORD")
    private ImportRecordEntity importRecord;

...

【讨论】:

【参考方案2】:

哦,天哪……这太垃圾了……

我忘了这是@OneToOne 映射

我的实体需要如下设置:

@Entity
@Table(
        name = "PROVIDER_PORTFOLIO"
)
public class PortfolioEntity implements Serializable 

    private static final long serialVersionUID = 3632725596750538287L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private UUID id;

    @OneToOne
    @JoinColumn(
            name = "IMPORT_RECORD_ID"
    )
    private ImportRecordEntity importRecord;
[...]


@Entity
@Table(
        name = "IMPORT_RECORD"
)
public class ImportRecordEntity implements Serializable 

    private static final long serialVersionUID = 2483327758356663412L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "ID")
    private UUID id;

    @Column(name = "importedReason")
    private ImportedReason importedReason;
[...]

好吧...抱歉给您带来了困惑,应该刚刚停止工作;)

斯蒂芬

【讨论】:

以上是关于SpringBoot JPA 2.1 Converter 忽略 convertToDatabaseColumn的主要内容,如果未能解决你的问题,请参考以下文章

带有 Spring Boot 和 websphere 8.5.0.1 的 JPA 2.1

springBoot整合JPA

从零开始学习springBoot(myqsl链接+端口修改+jpa映射)

java后端开发第一篇:springboot+jpa入门

java后端开发第一篇:springboot+jpa入门

springboot jpa自定义查询