【问题标题】:How to map java.time.Year and others java.time types using Hibernate如何使用 Hibernate 映射 java.time.Year 和其他 java.time 类型
【发布时间】:2017-03-28 01:31:37
【问题描述】:

hibernate-java8 JAR 为 InstantLocalDate 等几个类提供适配器,但缺少来自 java.time 的一些类,例如 YearMonthYearMonth .这些类被存储为未知的Serializable,这是不必要的浪费。

当然我可以使用int year 代替Year year,但我不认为这是个好主意。

看起来写YearJavaDescriptor 应该很容易,但是,我想知道为什么它不见了。特别是在YearMonth 的情况下,我更喜欢现有的适配器,有吗? 还是我在做傻事?

我不确定谷歌搜索没有返回任何结果。

【问题讨论】:

  • 我认为这回答了你的问题:stackoverflow.com/questions/40825495/…
  • YearMonth 之类的类型进行序列化/编码时的一条建议。 Think of your database statistics,并且可能避免使用人类可读的格式以支持更好的优化格式。
  • @LukasEder 谢谢,我已阅读您的博文。 IUIC,你会推荐类似months since 2000DATE('2017-03-01') 而不是'2017-03'201703 这样统计数据才能更好地工作。可惜数据库没有toString/fromString的自定义类型,所以我们可以兼得可读性和速度。
  • @maaartinus:是的,这是我的建议。在这种情况下,DATE 类型(以及将日期值强制为 1 的 CHECK 约束)可能是一个很好的折衷方案。 PostgreSQL 知道domain,这是一个使CHECK 约束可重用的工具:postgresql.org/docs/current/static/sql-createdomain.html。仍然没有 toString() / fromString() 格式化选项,但至少它更容易记录...注意,我的建议仅在您使用这种类型作为谓词查询大量数据时才重要。如果只存储值,那么这种优化可能为时过早。
  • @maaartinus 你的问题解决了吗?

标签: java hibernate java-8 java-time


【解决方案1】:

尝试为此创建一个转换器 - AttributeConverter 实现。

我过去使用过类似以下内容:

@Entity
public class RealEstateAgency {
    @Column(name = "createdAt")
    @Convert(converter = ZonedDateTimeConverter.class)
    private ZonedDateTime creationDate;
}

@Converter(autoApply = true)
public class ZonedDateTimeConverter implements AttributeConverter<ZonedDateTime, Date> {
 
    public Date convertToDatabaseColumn(ZonedDateTime toConvert) {
        return toConvert == null ? null : Date.from(toConvert.toInstant());
    }
 
    public ZonedDateTime convertToEntityAttribute(Date toConvert) {
        return toConvert == null ? null : ZonedDateTime.from(toConvert
                .toInstant());
    }
}

【讨论】:

  • 重要的是要记住它必须是java.sql.Date。像 LocalDate 这样的东西虽然被 Hibernate 独立正确序列化,但在这种情况下似乎不起作用。
【解决方案2】:

如果您的 JPA 提供者没有以合理的方式持久化一个类型(在这种情况下,因为它是一个 Java8 类,它是在 JPA 2.1 获得批准后添加的),那么您需要定义一个 JPA 2.1 AttributeConverter 来将其转换为标准 JPA 可持久类型(在本例中类似于 java.sql.Date)。

【讨论】:

  • 它是 java 8,但我使用的是 hibernate-java8 JAR。
【解决方案3】:

使用 JPA AttributeConverter

对于YearMonth,您可以像这样创建以下YearMonthDateAttributeConverter

public class YearMonthDateAttributeConverter
        implements AttributeConverter<YearMonth, java.sql.Date> {
 
    @Override
    public java.sql.Date convertToDatabaseColumn(
            YearMonth attribute) {
        return java.sql.Date.valueOf(attribute.atDay(1));
    }
 
    @Override
    public YearMonth convertToEntityAttribute(
            java.sql.Date dbData) {
        return YearMonth
                .from(Instant.ofEpochMilli(dbData.getTime())
                .atZone(ZoneId.systemDefault())
                .toLocalDate());
    }
}

并且,在 YearMonth JPA 实体属性上使用 @Convert

@Column(name = "published_on", columnDefinition = "date")
@Convert(converter = YearMonthDateAttributeConverter.class)
private YearMonth publishedOn;

使用 Hibernate 自定义类型

您可以通过扩展 AbstractSingleColumnStandardBasicType 来创建自定义 Hibernate 类型:

public class YearMonthDateType
        extends AbstractSingleColumnStandardBasicType<YearMonth> {
 
    public static final YearMonthDateType INSTANCE = 
        new YearMonthDateType();
 
    public YearMonthDateType() {
        super(
            DateTypeDescriptor.INSTANCE,
            YearMonthTypeDescriptor.INSTANCE
        );
    }
 
    public String getName() {
        return "yearmonth-date";
    }
 
    @Override
    protected boolean registerUnderJavaType() {
        return true;
    }
}

并定义YearMonthTypeDescriptor如下:

public class YearMonthTypeDescriptor
        extends AbstractTypeDescriptor<YearMonth> {
 
    public static final YearMonthTypeDescriptor INSTANCE = 
        new YearMonthTypeDescriptor();
 
    public YearMonthTypeDescriptor() {
        super(YearMonth.class);
    }
 
    @Override
    public boolean areEqual(
            YearMonth one, 
            YearMonth another) {
        return Objects.equals(one, another);
    }
 
    @Override
    public String toString(
            YearMonth value) {
        return value.toString();
    }
 
    @Override
    public YearMonth fromString(
            String string) {
        return YearMonth.parse(string);
    }
 
    @SuppressWarnings({"unchecked"})
    @Override
    public <X> X unwrap(
            YearMonth value, 
            Class<X> type, 
            WrapperOptions options) {
        if (value == null) {
            return null;
        }
        if (String.class.isAssignableFrom(type)) {
            return (X) toString(value);
        }
        if (Date.class.isAssignableFrom(type)) {
            return (X) java.sql.Date.valueOf(value.atDay(1));
        }
        throw unknownUnwrap(type);
    }
 
    @Override
    public <X> YearMonth wrap(
            X value, 
            WrapperOptions options) {
        if (value == null) {
            return null;
        }
        if (value instanceof String) {
            return fromString((String) value);
        }
        if (value instanceof Date) {
            Date date = (Date) value;
            return YearMonth
                .from(Instant.ofEpochMilli(date.getTime())
                .atZone(ZoneId.systemDefault())
                .toLocalDate());
        }
        throw unknownWrap(value.getClass());
    }
}

你不需要自己写这个类型,你可以使用Hibernate Types然后像这样声明Maven依赖:

<dependency>
    <groupId>com.vladmihalcea</groupId>
    <artifactId>hibernate-types-52</artifactId>
    <version>${hibernate-types.version}</version>
</dependency>

就是这样!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-01-21
    • 2022-08-15
    • 2015-03-09
    • 2011-02-03
    • 2016-10-03
    • 1970-01-01
    • 1970-01-01
    • 2019-12-10
    相关资源
    最近更新 更多