【问题标题】:How to programmatically add attribute converter for specific entity fields?如何以编程方式为特定实体字段添加属性转换器?
【发布时间】:2019-02-14 07:03:26
【问题描述】:

假设我有一个名为 FooConverter 的 javax.persistence.AttributeConverter 实现,我想将此转换器应用于特定的实体字段。

如果我使用注释方法,它看起来像:

@Column
@Convert(converter = FooConverter.class)
private String barField;

在 orm XML 映射上它看起来像:

<entity class="com.example.FooBarEntity">
  <convert converter="com.example.FooConverter" attribute-name="barField"/>
</entity>

但是,我想在javax.persistence.EntityManagerFactory 配置期间以编程方式添加此转换器。

import org.hibernate.cfg.Configuration;
import org.springframework.orm.hibernate5.LocalSessionFactoryBuilder;

private EntityManagerFactory buildEntityManagerFactory() {
  Configuration config = new LocalSessionFactoryBuilder(dataSource);
  config.addAnnotatedClass(com.example.FooBarEntity.class);
  // I looking for something like config.addConverter(FooConverter, FooBarEntity, barField);
  return config.buildSessionFactory();
}

有config.addAttributeConverter 方法,但它似乎只创建转换器实例而不将其绑定到特定的实体属性。但是,我只想将转换器应用于特定的 String 字段,因此 autoApply 不是一个选项。

【问题讨论】:

标签: java hibernate


【解决方案1】:

JPA 规范仅提供 2 个选项来应用转换器:全局 @Converter(autoApply = true) 和字段级 @Convert(converter = FooConverter.class)(或它们的 XML 等效项)。

JPA 转换器只知道实体属性类型和数据库列类型。无法访问实体实例甚至实体类型来构建条件(按实体字段)转换逻辑。

您可以像这样以编程方式添加AttributeConverter 转换器:

LocalSessionFactoryBuilder builder = new LocalSessionFactoryBuilder(dataSource);
builder.addAnnotatedClasses(FooBarEntity.class);
builder.addAttributeConverter(FooConverter.class, /*autoApply:*/true);
EntityManagerFactory entityManagerFactory = builder.buildSessionFactory();

我不确定是否有比 @Convert(converter = FooConverter.class) 更简洁的方法来告诉 Hibernate 仅转换特定实体字段。

仅将转换器应用于特定实体字段的解决方法:

包装类

为需要转换的字符串创建一个包装器:

public class Bar {

  private final String value;

  public Bar(String value) {
    this.value = value;
  }

  public String getValue() {
    return value;
  }
}

@Entity
public class FooBarEntity {

  private Bar barField;

  public void setBarField(String value) {
    this.barField = new Bar(value);
  }

  //...
}

声明一个自动应用的转换器:

@Converter(autoApply = true)
public class FooConverter implements AttributeConverter<Bar, String> {

  @Override
  public String convertToDatabaseColumn(Bar bar) {
    return Optional.ofNullable(bar).map(Bar::getValue/*anything*/).orElse(null);
  }

  @Override
  public Status convertToEntityAttribute(String value) {
    return Optional.ofNullable(value).map(Bar::new).orElse(null);
  }
}

JPA 实体监听器

如果从String 转换为String(源和目标类型相同),则可以使用实体侦听器。实体监听器可以访问实体本身,因此可以有复杂的条件逻辑。

@Entity
public class FooBarEntity {

  private String barField;
}

创建实体监听器:

public class FooEntityListener {

  @PrePersist
  @PreUpdate
  public void convertToDatabaseColumn(Object entity) {
    if (entity instanceof FooBar) {
      entity.setBarField(/*conversion to DB column*/entity.getBarField());
    }
  }

  @PostLoad
  public void convertToEntityAttribute(Object entity) {
    if (entity instanceof FooBar) {
      entity.setBarField(/*conversion to entity attribute*/entity.getBarField());
    }
  }
}

在META-INF/orm.xml中注册一个默认实体监听器:

<entity-mappings>
    <persistence-unit-metadata>
        <persistence-unit-defaults>
            <entity-listeners>
                <entity-listener class="com.example.FooEntityListener"/>
            </entity-listeners>
        </persistence-unit-defaults>
    </persistence-unit-metadata>
</entity-mappings>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-02-05
    • 1970-01-01
    • 1970-01-01
    • 2020-11-21
    • 2019-09-12
    • 2017-11-24
    • 1970-01-01
    相关资源
    最近更新 更多