首页 > 解决方案 > 尽管 autoApply=false 且属性未使用 @Convert 注释,但仍应用 JPA 属性转换器

问题描述

我正在研究的系统有一堆旧数据,其中布尔值存储为'Y''N'。新表使用BIT列代替,并简单地存储01。没有表格混合了这两种方法。

为了支持遗留表,我们有以下转换器:

@Converter(autoApply = false)
public class BooleanToStringConverter implements AttributeConverter<Boolean, String> {

    private Logger.ALogger Log = Logger.of(BooleanToStringConverter.class);

    @Override
    public String convertToDatabaseColumn(final Boolean attribute) {
        Log.debug("Converting the boolean value {}", attribute);

        if (attribute == null) {
            return "N";
        }

        return attribute ? "Y" : "N";
    }

    @Override
    public Boolean convertToEntityAttribute(final String dbData) {
        return "Y".equalsIgnoreCase(dbData);
    }

}

由于这只需要应用于某些实体,因此 autoApply 属性已设置为 false。

我现在正在创建一个带有新表的全新实体。它有两个布尔属性,都使用 BIT 列样式而不是 Y/N:

@Entity
@Table(name = "MyEntity")
public class MyEntity {

    @Id
    @Column(name = "MyEntityId")
    private Long id;

    @Column(name = "IsClosed")
    private Boolean closed;

    ...
}

请注意,我没有应用@Convert注释。

我有一个查询需要过滤掉实体关闭的任何行:

query.where().eq(CLOSED, Boolean.FALSE)

正是在这一点上,我的问题出现了。每当运行此查询时,我都会看到来自 BooleanToStringConverter 的日志消息被写入日志,实际上,如果我转储从 MySQL 数据库执行的实际 SQL,那么我可以看到转换器确实应用于布尔属性,创建以下 SQL 片段:

select <columns>
  from MyEntity t0
 where <other predicates>
   and t0.IsClosed = 'N'
 order by <order clause>

这显然是错误的 - 不应该应用转换器,它没有设置为自动,并且关闭的属性没有用@Convert.

我试图通过创建第二个转换器来解决这个问题:

@Converter(autoApply = true)
public class BooleanConverter implements AttributeConverter<Boolean, Boolean> {

    private Logger.ALogger Log = Logger.of(BooleanConverter.class);

    @Override
    public Boolean convertToDatabaseColumn(final Boolean attribute) {
        Log.debug("Processing the value {}.", attribute);
        return attribute;
    }

    @Override
    public Boolean convertToEntityAttribute(final Boolean dbData) {
        return dbData;
    }
}

这导致两个转换器都应用于该属性,并且我看到两个调试语句都出现在日志中。

2019-07-29 14:19:53,994 [dispatcher-69] DEBUG BooleanConverter     Processing the value false.
2019-07-29 14:19:53,994 [dispatcher-69] DEBUG BooleanToStringConve I'm Converting the boolean value false

接下来,我尝试显式设置转换器以在实体本身上使用(我希望这可能会改变应用转换器的顺序,以便尽管其他转换器正在运行,但最终结果为真/假):

@Entity
@Table(name = "MyEntity")
public class MyEntity {

    @Id
    @Column(name = "MyEntityId")
    private Long id;

    @Convert(converter = BooleanConverter.class)
    @Column(name = "IsClosed")
    private Boolean closed;

    ...
}

结果完全相同;两个转换器都按顺序应用于值,BooleanToStringConverter笑到最后并破坏谓词。

我宁愿保留它,BooleanToStringConverter因为它可以减少处理遗留数据的痛苦,但除非我能弄清楚为什么在不应该应用它的时候应用它,否则我可能不得不删除它。

我正在使用 Ebean 版本 4.1.3 和 Play!2.6.21

我怎样才能阻止这个流氓转换器将自己应用于它无权触摸的属性?

标签: javajpaplayframeworkebean

解决方案


这是 Ebean 的一个(现在)已知限制,如Ebean 的 GitHub 页面上的Issue 1777所述。在撰写本文时,不打算修复它。


推荐阅读