1

I cannot cascade merge with persist three levels of entities using @EmbeddedIdin the last entity. I have three following entities:
1. Voucher
2. Rate
3. RatePerChannelOverwrite

The Voucher is fetched by a service. Then I want to add new Rate to the voucher. Then I want to add new RatePerChannelOverwrite to the Rate. So both Rate and RatePerChannelOverwrite will have their @Id as NULL. This is working.

However later I tried to split RatePerChannelOverwrite into two classes and have: @Embeddable RatePerChannelOverwriteId. And this setup is failing. The Rate referened by RatePerChannelOverwriteId never gets its new ID populated.

@Entity
public class Voucher {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Integer id;

    @OneToMany(mappedBy = "voucher", fetch = FetchType.LAZY, orphanRemoval = true, cascade = CascadeType.ALL)
    private Set<Rate> rates = new HashSet<Rate>();
}

@Entity
public class Rate {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Integer id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "voucher_id", referencedColumnName = "id")
    private Voucher voucher;

    @Column(name = "rate", precision = 2, scale = 9, columnDefinition = "decimal")
    @NumberFormat(pattern = "#0.00")
    private double rate;

    @OneToMany(mappedBy = "rate", fetch = FetchType.EAGER, orphanRemoval = true, cascade = CascadeType.ALL)
    private Set<RatePerChannelOverwrite> ratePerChannelOverwrites;
}

@Entity
public class RatePerChannelOverwrite {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Integer id;

    @ManyToOne
    @JoinColumn(name = "rate_id", referencedColumnName = "id")
    private Rate rate;

    @Column(name = "channel_id")
    private Integer channelId;

    @Column(name = "rate", precision = 2, scale = 9, columnDefinition = "decimal")
    @NumberFormat(pattern = "#0.00")
    private double rate;
}

And this works fine when I do something like that:

Voucher voucher = service.getVoucher(123456); 
Rate rate = new Rate(); //NOTE ID IS NULL HERE
rate.setVoucher(voucher);
voucher.getRates().add(rate);

RatePerChannelOverwrite channelRate = new RatePerChannelOverwrite();
channelRate.setRate(rate);
rate.getRatePerChannelOverwrites.add(channelRate);   

voucher = voucher.merge();

However when I separate last entity into 2 separate classes, then the id of rate in RatePerChannelOverwriteId is ALWAYS NULL. Somehow cascading here fails.

NOTE: Third entity has composite key as @EmbeddedId with @ManyToOne, and this is where the problem shows up.

@Embeddable
public class RatePerChannelOverwriteId {
    @ManyToOne
    @JoinColumn(name = "rate_id", referencedColumnName = "id")
    private Rate rate;

    @Column(name = "channel_id")
    private Integer channelId;
}

@Entity
public class RatePerChannelOverwrite {
    @EmbeddedId
    private RatePerChannelOverwriteId id;

    @Column(name = "rate", precision = 2, scale = 9, columnDefinition = "decimal")
    @NumberFormat(pattern = "#0.00")
    private double rate;
}

Is it correct to do this?

Voucher voucher = service.getVoucher(123456);   
Rate rate = new Rate();
rate.setVoucher(voucher);
voucher.getRates().add(rate);

RatePerChannelOverwriteId channelRateId = new RatePerChannelOverwriteId();
channelRateId.setRate(rate);   
RatePerChannelOverwrite channelRate = new RatePerChannelOverwrite();
channelRate.setId(channelRateId);
rate.getRatePerChannelOverwrites.add(channelRate);   

voucher = voucher.merge();

The actual exception I get is NullPointer from 'org.hibernate.type.descriptor.AbstractTypeDescriptor.extractHashCode(T value)' where value is NULL, because id in Rate is still NULL (although SQL trace shows INSERT was performed on Rate ).

Dominika
  • 388
  • 3
  • 11
  • It is a good practice to have 3 entity classes.you may also go with @id instead of @ embeddedid – Akshay Feb 14 '17 at 03:49
  • Yes `@Id` is required on the `@Entity` and it's working as expected. But if the 3rd entity has a composite key, you need to use `@EmbeddedId` with `@ManyToOne` instead, and this is where it's failing. – Dominika Feb 14 '17 at 10:04
  • try with IdClass..make the primary class serializable – Akshay Feb 14 '17 at 10:10
  • 1
    Didn't try `@IdClass` but solved by moving `@ManyToOne` out of `@Embeddable` into the `@Entity` and adding `@MapsId` to it. So I kept `@EmbeddedId`, but it holds now only integers `channelId` and `rateId`; the `@ManyToOne` is in the entity and now is annotated with `@MapsId("rateId")` – Dominika Feb 14 '17 at 12:50

0 Answers0