简体   繁体   中英

Hibernate: Join table with extra columns, remove children from one side

The scenario is the following

I have 2 tables, Company and Activity. A company can have one or more activities. One of these activities is a "primary" activity, and all others become secondary.

To handle this, I created 2 entities (Activity, Company) and a third entity for the join table, which is CompanyActivity

I used this tutorial as a starting point

Below my code (getters and setters omitted)

Company.java

@Entity
@Table(name = "T_COMPANY")
public class Company {

    @Id
    @Column(name = "COM_ID")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "company")
    private List<CompanyActivity> activities = new ArrayList<>();
}

Activity.java

@Entity
@Table(name = "T_ACTIVITY")
public class Activity {

    @Id
    @Column(name = "ACT_ID")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String code;

    private String description;

    private boolean availableOnline;
}

CompanyActivity.java

@Entity
@Table(name = "T_COMPANY_ACTIVITY")
public class CompanyActivity {

    @Id
    @Column(name = "COM_ACT_ID")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "COM_ID")
    private Company company;

    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "ACT_ID")
    private Activity activity;

    private boolean primary;

}

Adding activities for a company works without a problem. The children collection contains the newly added activities, and there is always one marked as primary as expected.

The problem happens when updating a company.

  • When I add a new activity, all previous existing activities are persisted again.
  • When I remove an activity, it is not removed from the table.

I'm using this code to update a company' activities

    company.getActivities().clear();
    company.getActivities().addAll(newActivities);

    company = repository.save(company);

In this code, newActivities have the new activities that should be considered (this collection does not have the previous ones, I just replace them all)

I tried adding orphanRemoval=true to the @OneToMany(cascade = CascadeType.ALL, mappedBy = "company") on Company, but this deletes the activity type when no other company is using it, which is wrong as they should be available always.

Can you please help me sync the activities collection on Company without removing elements from Activity table ?

Thanks a lot!

I solved it. Here are the steps I followed.

First, I changed my Join table entity cascade types as follows

@Entity
@Table(name = "T_COMPANY_ACTIVITY")
public class CompanyActivity {

    @Id
    @Column(name = "COM_ACT_ID")
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinColumn(name = "COM_ID")
    private Company company;

    @ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @JoinColumn(name = "ACT_ID")
    private Activity activity;

    private boolean primary;

}

Then, I added again the "orphanRemoval" property to Company mapping, and changed my CascadeTypes too, as follows

@OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}, mappedBy = "empresa", orphanRemoval = true)
private List<CompanyActivity> activities = new ArrayList<>();

With these changes, my mapping works as expected with the same code I used to replace the relationships.

company.getActivities().clear();
company.getActivities().addAll(newActivities);

company = repository.save(company);

Thanks :)

The way you created your entities is not correct. You don't need to create an entity for your join table (CompanyActivity/T_COMPANY_ACTIVITY). Instead you should be using the @JoinTable on your activities entity. Something like below:

@OneToMany(cascade = CascadeType.ALL, mappedBy = "company")
@JoinTable(
    name = "T_COMPANY_ACTIVITY",
    joinColumns = @JoinColumn(name = "COM_ID"),
    inverseJoinColumns = @JoinColumn(name = "ACT_ID")
)
private List<CompanyActivity> activities = new ArrayList<>();

for more detailed explanation on how One-to-Many/Many-to-One with Join tables work here: http://www.codejava.net/frameworks/hibernate/hibernate-one-to-many-association-on-join-table-annotations-example

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM