Home > Back-end >  Why @OneToMany annotation create constraint table automatically and Why that table remove previous r
Why @OneToMany annotation create constraint table automatically and Why that table remove previous r

Time:07-15

I have 3 classes User, Bill, Payment. User may has many bills. Bill may has one Payment.

My problem is that when I use @OneToMany annotation on the user entity, one extra table is created in postgresql. This table's name users_bills and there are 2 foreign key for user id and bill id.

@Table(name = "Users")
public class User {

    @Id
    @GeneratedValue(generator = "UUID")
    @GenericGenerator(name = "UUID",strategy = "org.hibernate.id.UUIDGenerator")
    @Column(name = "id", updatable = false, nullable = false)
    private UUID id;

   //.....   
   
    @OneToMany(cascade = CascadeType.REMOVE)
    @ToString.Exclude
    private List<Bill> bills;
}


@Table(name = "Bills")
public class Bill {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private int billId;
 
 //........
}

I didn't specify any code to create the new table for one-to-many relationship and it created itself.

Hibernate: create table users_bills (user_id uuid not null, bills_bill_id int4 not null)
Hibernate: alter table if exists users_bills add constraint FKhudoici2ayeayhwyuq3usi7i5 foreign key (bills_bill_id) references bills
Hibernate: alter table if exists users_bills add constraint FK3o2h5qibmg208c927vg6vtm6h foreign key (user_id) references users

There is a problem with this table. When I add an bill to a user, it appears in the table. But when I add more than one bill, the previous bill is deleted and a new one is added.

Hibernate: select user0_.id as id1_2_, user0_.name as name2_2_, user0_.subscriber_no as subscrib3_2_, user0_.surname as surname4_2_ from users user0_ where user0_.subscriber_no=?
Hibernate: select nextval ('hibernate_sequence')
Hibernate: insert into bills (bill_no, bill_price, is_paid, payment_payment_id, process_date, subscriber_no, user_id, bill_id) values (?, ?, ?, ?, ?, ?, ?, ?)
Hibernate: delete from users_bills where user_id=?
Hibernate: insert into users_bills (user_id, bills_bill_id) values (?, ?)

Why is it deleting the previous record instead of adding a new one. How can I fix it ?

CodePudding user response:

I'm sorry the problem is not related to relationships. I wrote wrong the code for adding to the list I made in the Service class.

I changed code from:

 user.setBills(Collections.singletonList(bill));

to:

 user.getBills().add(bill);

CodePudding user response:

Based on the comments you have shared, I hope that

  1. You are not using the same class for Entity and DTO
  2. You have actually understood the details of how Entity Relationships work
  3. You understand how the table generation works in Spring Boot
  4. You understand the use ObjectMappers like Mapstruct

I am going to provide you the solution but it is important that you understand above concepts to avoid any further work arounds in the future

For your current problem of table generation you need Bi Directional mapping. And since you are using same class for Entity and DTO you need to add handlers for Jackson Mapping.

@Table(name = "bills")
public class Bill {

  @Id
  @Column(name = "bill_id", nullable = false)
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private int billId;

  @ManyToOne
  @JsonIgnore
  @ToString.Exclude
  @JoinColumn(name="user_id", nullable=false)
  private User user;

  //...
}

Few points to note:

  1. Use GenerationType.IDENTITY instead of GenerationType.AUTO
  2. If possible use different classes for Entity and DTO

Refer this Article for more details: https://www.baeldung.com/hibernate-one-to-many


For the second problem, the proper solution is to use Adder in class

@Table(name = "Users")
public class User {

  @Id
  @GeneratedValue(generator = "UUID")
  @GenericGenerator(name = "UUID",strategy = "org.hibernate.id.UUIDGenerator")
  @Column(name = "id", updatable = false, nullable = false)
  private UUID id;

 //.....   
   
  @OneToMany(cascade = CascadeType.REMOVE)
  @ToString.Exclude
  private List<Bill> bills;

  //... other fields

  public void addBill(final Bill bill) {
    if(bills == null) {
      bills = new ArrayList<>();
    }
    bills.add(bill);
  }
  
  public void addBills(final List<Bill> bills) {
    if(this.bills == null) {
      this.bills = new ArrayList<>();
    }
    this.bills.addAll(bills);
  }
}

  • Related