使用键java.time.YearMonth的JPA/Hibernate映射

使用键java.time.YearMonth的JPA/Hibernate映射,java,hibernate,jpa,Java,Hibernate,Jpa,我正在处理一个财务应用程序,其中时间单位为月,计划为(年,月)。FTT代表,给一点上下文 我们公司提倡干净数据库优先的方法。我们使用Hibernate5.1。我知道这是下线,但不能改变 我目前正在重构一个实体 @Entity @Table(name="FTT_REPORT") public class FttReport { @Column(name="REPORT_ID") private final Long id = null;

我正在处理一个财务应用程序,其中时间单位为月,计划为(年,月)。FTT代表,给一点上下文

我们公司提倡干净数据库优先的方法。我们使用Hibernate5.1。我知道这是下线,但不能改变

我目前正在重构一个实体

@Entity
@Table(name="FTT_REPORT")
public class FttReport {

    @Column(name="REPORT_ID")
    private final Long id = null;

    @Column(name="REFERENCE_YEAR")
    private int referenceYear; //Old code, I could use YearMonth directly
    @Column(name="REFERENCE_MONTH")
    private int referenceMonth; //Old code, I could use YearMonth directly


    private BigDecimal [a lot of tax figures];

    @ElementCollection(fetch = FetchType.EAGER)
    @MapKeyClass(YearMonth.class)
    @OrderBy("REFERENCE_YEAR, REFERENCE_MONTH")
    // @MapKeyType(@Type(type = "com.acme.FttYearMonthUserType")) #I'll explain soon why it's commented out
    @AttributeOverrides({//
        @AttributeOverride(name = "key.year", column = @Column(name = "REFERENCE_YEAR")),//
        @AttributeOverride(name = "key.month", column = @Column(name = "REFERENCE_MONTH")),//
    })
    @CollectionTable(//
        name = "FTT_REPORT_ADJUSTMENTS",//
        foreignKey = @ForeignKey(value = ConstraintMode.CONSTRAINT, name = "FK_FTT_ADJUSTMENT_REPORT"),//
        joinColumns = @JoinColumn(name = "REPORT_ID")//
    )
    private final SortedMap<YearMonth, FttAdjustment> adjustments = new TreeMap<>();

}
@实体
@表(name=“FTT\U报告”)
公共类FttReport{
@列(name=“REPORT\u ID”)
私有最终长id=null;
@列(名称=“参考年”)
private int referenceYear;//旧代码,我可以直接使用YearMonth
@列(name=“REFERENCE\u MONTH”)
private int referenceMonth;//旧代码,我可以直接使用YearMonth
私人大十进制[许多税收数字];
@ElementCollection(fetch=FetchType.EAGER)
@MapKeyClass(YearMonth.class)
@订购人(“参考年,参考月”)
//@MapKeyType(@Type(Type=“com.acme.FttYearMonthUserType”)#我很快会解释为什么它会被注释掉
@属性溢出({//
@AttributeOverride(name=“key.year”,column=@column(name=“REFERENCE\u year”)//
@AttributeOverride(name=“key.month”,column=@column(name=“REFERENCE\u month”)//
})
@收集表(//
name=“FTT\U报告调整”//
foreignKey=@foreignKey(value=ConstraintMode.CONSTRAINT,name=“FK\U FTT\U调整报告”)//
joinColumns=@JoinColumn(name=“REPORT\u ID”)//
)
私有最终分类映射调整=新树映射();
}
fttajustment
只是一个只包含数字列的可嵌入程序

一个简短的解释。我们的应用程序每月计算纳税报告,但有时出现延迟交易,客户必须向政府支付罚款/附加税。我们不会将该数字并入总税额,但需要向用户显示所有“调整”月份的所有附加税的完整报告

此时,我知道我可以通过使用
LocalDate
作为映射键并假设day始终是第一天来命名它

但我必须先尝试更干净的方法

通过上述映射,以下操作是成功的:

  • Hibernate使用我喜欢的正确列正确创建数据库创建脚本(没有
    @MapKeyType
  • Hibernate成功添加了预期的主键和外键约束(无
    @MapKeyType

  • Hibernate成功地
    INSERT
    s单元测试的第一次税务调整我在Hibernate中看到了非常相似的映射,但有一个重要的区别:映射键映射到单个列。我在文档中找不到证据,但对于
    @ElementCollection
    关联,恐怕无法使用映射到多个列的map key进行类似映射

    然而,我能够为
    @OneToMany
    协会做这件事。因此,下面我将提供一个例子

    假设我们有以下模式:

    创建表FTT\U报告
    (
    报告ID int不为空,
    主键(报表ID)
    );
    创建表FTT\U报告\U调整
    (
    REPADJ_ID int不为空,
    REPADJ_报告_ID int不为空,
    重漆,
    重新油漆,
    主键(REPADJ_ID),
    外键(REPADJ\u REPORT\u ID)引用FTT\u REPORT(REPORT\u ID)
    );
    
    我们可以使用以下映射:

    @实体
    @表(name=“FTT\U报告”)
    公共类FttReport
    {
    @身份证
    @列(name=“REPORT\u ID”)
    私人长id;
    @OneToMany(级联=级联类型.ALL)
    @订购人(“重新订购年,重新订购月”)
    @JoinColumn(name=“REPADJ\u REPORT\u ID”,updateable=false)
    @映射键(name=“adjYearMonth”)
    私有分类地图调整;
    }
    @实体
    @表(name=“FTT\U报告调整”)
    公共类FTTAD调整
    {
    @身份证
    @列(name=“REPADJ_ID”)
    私人长id;
    //这只需要添加新的FTTA调整
    @列(name=“REPADJ\u REPORT\u ID”)
    私有长报告ID;
    @类型(Type=“com.acme.FttYearMonthUserType”)
    @列(列={
    @列(name=“REPADJ_YEAR”),
    @列(name=“REPADJ_月”)
    })
    私人年月调整年月;
    }
    
  • @MapKey
    注释只能在
    FttAdjustment
    为实体时使用
  • updateable=false
    已添加到
    @JoinColumn
    中,以避免在插入新的
    fttajustment
    时进行额外的更新查询
  • 使用hibernate
    5.4.10.Final对该映射进行了测试

  • 另请注意,还有一个补充说明。
    @AttributeOverride
    是JPA为列名定义的注释,但您尝试使用它来设置列的列名。恐怕这是个错误。

    如何实现
    com.acme.FttYearMonthUserType
    ?查看
    ComponentType
    的代码,Hibernate似乎试图实例化一个空的“container”对象来代替
    null
    ,因此出现了错误(另外,您是否碰巧启用了
    Hibernate.create_empty_composites.enabled
    ?此设置似乎触发了该行为)@crizzis否,我没有启用该属性。我有一个日志,其中显示了所有有效的Hibernate属性。我将发布用户类型,asapI已经看到Jadira框架为310个类型实现了映射器。我可以从它们的实现中复制/获取灵感,看看它是否有效。此外,我还可以尝试CompositeUserType1,我在实现中发现的一个问题是
    isMutable()
    方法
    YearMonth
    是不可变的,因此将
    true
    切换到
    false
    可能会使Hibernate不愿意实例化它。不确定这是否有帮助,但值得一试