javajpaforeign-keys

JPA is duplicating a column when inserting. Multiple columns as primary and foreign key


JPA is duplicating a column when trying to insert (column active) and it does not insert the value in one of the parameter.

The error is the following:

could not execute statement [No value specified for parameter 6.] 
[insert into payment_item 
(account_id,x,y,active,payment_id,active,id) 
values (?,?,?,?,?,?,?)]

The DDL is the followinng (simplfied for the explanation):

CREATE TABLE payment_item (
    id BIGINT,
    payment_id BIGINT NOT NULL,
    active BOOLEAN NOT NULL DEFAULT true ,
    PRIMARY KEY (id, active),
    FOREIGN KEY (payment_id, active) REFERENCES payment (id, active)
) PARTITION BY LIST (active);

The mapping jpa:

public class PaymentItemKey implements Serializable {
    private Long id;

    private Boolean active = true;
}
@Entity
@Table(name = "payment_item")
@IdClass(PaymentItemKey.class)
public class PaymentItemEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "payment_item_generator")
    @SequenceGenerator(name = "payment_item_generator", sequenceName = "payment_item_seq", allocationSize = 1)
    private Long id;

    @Id
    private boolean active = true;

    @ManyToOne
    @JoinColumns({
            @JoinColumn(name = "payment_id", referencedColumnName = "id"),
            @JoinColumn(name = "active", referencedColumnName = "active")
    })
    private PaymentEntity payment;

}

if only works if I remove active from the primary key. But it is necessary because I want to partition the table using active column

Note that if I use Embedded instead of IdClass it gives the same error.


Solution

  • This error occurs because you are trying to declare two different columns with the same name. One column is declared in the code private boolean active = true;, the second column is declared in the code @JoinColumn(name = "active", referencedColumnName = "active"). To solve the problem you need to get rid of one of the columns or use a different name.

    I suggest you get rid of the field in the code private boolean active = true;. Since the Payment entity is related to PaymentItem by a one-to-many relationship, its active field is propagated to each nested item.

    As a result, we get the following mapping.

    @Entity
    @IdClass(PaymentKey.class)
    @Table(name = "payments")
    public class Payment {
    
        @Id
        private Long id;
    
        @Id
        private Boolean active;
    
        @JsonManagedReference
        @OneToMany(mappedBy = "payment", cascade = CascadeType.ALL, orphanRemoval = true)
        private Set<PaymentItem> items;
    
      // ...
    }
    
    @Entity
    @Table(name = "payment_items")
    public class PaymentItem {
    
        @Id
        private Long id;
    
        @ManyToOne
        @JsonBackReference
        @JoinColumns({
                @JoinColumn(name = "payment_id", referencedColumnName = "id"),
                @JoinColumn(name = "active", referencedColumnName = "active")
        })
        private Payment payment;
    
      // ...
    }
    
    public class PaymentKey implements Serializable {
        
        private Long id;
        
        private Boolean active;
        
      // ...
    }