{"id":83,"date":"2015-07-23T14:57:22","date_gmt":"2015-07-23T14:57:22","guid":{"rendered":"http:\/\/www.solewing.org\/blog\/?p=83"},"modified":"2015-07-23T16:27:38","modified_gmt":"2015-07-23T16:27:38","slug":"hibernate-envers-onetomany-list-and-ordercolumn","status":"publish","type":"post","link":"http:\/\/www.solewing.org\/blog\/2015\/07\/hibernate-envers-onetomany-list-and-ordercolumn\/","title":{"rendered":"Hibernate Envers, @OneToMany List, and @OrderColumn"},"content":{"rendered":"<p>Using <a href=\"http:\/\/hibernate.org\/orm\/envers\/\">Hibernate Envers<\/a> with a <code>@OneToMany<\/code> association for a <code>List<\/code> property that uses <code>@OrderColumn<\/code> requires a little extra glue.  Without this extra glue, Envers will throw a <code>NullPointerException<\/code> in a lazy initializer when you try to load revisions.<\/p>\n<p>Suppose our domain model has a Sponsor entity.  A Sponsor can have multiple business names, represented by a Sponsor Name entity.  The names are ordered by preference, so we use a <code>@OneToMany<\/code> with a <code>List<\/code>.  Here&#8217;s how we might map the Sponsor entity.<\/p>\n<p>[java]<br \/>\n@Entity<br \/>\n@Audited<br \/>\n@Access(AccessType.FIELD)<br \/>\npublic class SponsorEntity {<\/p>\n<p>  @OneToMany(mappedBy = &#8220;sponsor&#8221;, fetch = FetchType.LAZY, orphanRemoval = true)<br \/>\n  @OrderColumn(name = &#8220;name_index&#8221;)<br \/>\n  private List<SponsorNameEntity> names = new ArrayList<>();<\/p>\n<p>}<br \/>\n[\/java]<\/p>\n<p>And here&#8217;s the SponsorNameEntity.<\/p>\n<p>[java]<br \/>\n@Entity<br \/>\n@Audited<br \/>\n@Access(AccessType.FIELD)<br \/>\npublic class SponsorNameEntity {<\/p>\n<p>  @Column(nullable = false)<br \/>\n  private String name;<\/p>\n<p>  @ManyToOne(optional = false, fetch = FetchType.LAZY)<br \/>\n  private SponsorEntity sponsor;<\/p>\n<p>}<br \/>\n[\/java]<\/p>\n<p>If we use these entities with Hibernate Envers, we&#8217;ll get an NPE when we try to load revisions of the <code>SponsorEntity<\/code>.  It happens because the <code>name_index<\/code> column specified by the <code>@OrderColumn<\/code> annotation is not included in the audit table for <code>SponsorNameEntity<\/code>.<\/p>\n<p>We can fix this easily, with an additional field on <code>SponsorNameEntity<\/code> and an extra annotation on <code>SponsorEntity<\/code>.  The extra field on <code>SponsorNameEntity<\/code> is used to expose the value of the order column as a field:<\/p>\n<p>[java]<br \/>\n@Entity<br \/>\n@Audited<br \/>\n@Access(AccessType.FIELD)<br \/>\npublic class SponsorNameEntity {<\/p>\n<p>  \/\/ This field is used to capture the value of the column named<br \/>\n  \/\/ in the @OrderColumn annotation on the referencing entity.<br \/>\n  @Column(insertable = false, updatable = false)<br \/>\n  private int name_index;<\/p>\n<p>  @Column(nullable = false)<br \/>\n  private String name;<\/p>\n<p>  @ManyToOne(optional = false, fetch = FetchType.LAZY)<br \/>\n  private SponsorEntity sponsor;<\/p>\n<p>}<br \/>\n[\/java]<\/p>\n<p>In <code>SponsorEntity<\/code> we use an additional annotation to inform Envers about the field that contains the order column value:<\/p>\n<p>[java]<br \/>\n@Entity<br \/>\n@Audited<br \/>\n@Access(AccessType.FIELD)<br \/>\npublic class SponsorEntity {<\/p>\n<p>  @OneToMany(mappedBy = &#8220;sponsor&#8221;, fetch = FetchType.LAZY, orphanRemoval = true)<br \/>\n  @OrderColumn(name = &#8220;name_index&#8221;)<br \/>\n  @AuditMappedBy(mappedBy = &#8220;sponsor&#8221;, positionMappedBy = &#8220;name_index&#8221;)<br \/>\n  private List<SponsorNameEntity> names = new ArrayList<>();<\/p>\n<p>}<br \/>\n[\/java]<\/p>\n<p>The <code>positionMappedBy<\/code> attribute of the <code>@AuditMappedBy<\/code> annotation informs Envers that the position of each entry in the list is given by the value of the <code>name_index<\/code> field that we added to <code>SponsorNameEntity<\/code>.  It seems a little redundant, but we&#8217;re required to also specify the value of the <code>mappedBy<\/code> attribute, which should be the same as the value given in the <code>@OneToMany<\/code> annotation.<\/p>\n<p>If you discover that you need this fix after you&#8217;ve already got some revisions out there in your audit tables, don&#8217;t forget to<\/p>\n<ol>\n<li>Add the column specified by <code>@OrderColumn<\/code> to the appropriate audit table.  In our example, this column goes into the audit table for <code>SponsorNameEntity<\/code>.<\/li>\n<li>Initialize the column value in each of the existing rows.  You can usually query the table associated with the entity to get the appropriate index value &#8212; e.g. in our case we&#8217;d query the table for  <code>SponsorNameEntity<\/code> to get the <code>name_index<\/code> column value and use it to update the corresponding rows in the audit table.<\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>Using Hibernate Envers with a @OneToMany association for a List property that uses @OrderColumn requires a little extra glue. Without this extra glue, Envers will throw a NullPointerException in a lazy initializer when you try to load revisions. Suppose our domain model has a Sponsor entity. A Sponsor can have multiple business names, represented by [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[14],"tags":[28,27,16,29],"class_list":["post-83","post","type-post","status-publish","format-standard","hentry","category-javaee","tag-envers","tag-hibernate","tag-javaee-2","tag-jpa"],"_links":{"self":[{"href":"http:\/\/www.solewing.org\/blog\/wp-json\/wp\/v2\/posts\/83","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.solewing.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.solewing.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.solewing.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.solewing.org\/blog\/wp-json\/wp\/v2\/comments?post=83"}],"version-history":[{"count":7,"href":"http:\/\/www.solewing.org\/blog\/wp-json\/wp\/v2\/posts\/83\/revisions"}],"predecessor-version":[{"id":90,"href":"http:\/\/www.solewing.org\/blog\/wp-json\/wp\/v2\/posts\/83\/revisions\/90"}],"wp:attachment":[{"href":"http:\/\/www.solewing.org\/blog\/wp-json\/wp\/v2\/media?parent=83"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.solewing.org\/blog\/wp-json\/wp\/v2\/categories?post=83"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.solewing.org\/blog\/wp-json\/wp\/v2\/tags?post=83"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}