2

I have following scenario: There are companies and employees. Each company has a set of employees. Each employee can work for several companies. So I implemented following relationships:

Company.class:

@JoinTable(name = "company_employee", joinColumns = @JoinColumn(name = "company_id") , inverseJoinColumns = @JoinColumn(name = "employee_id") )
@ManyToMany(fetch = FetchType.LAZY)
private List<Employee> employees;

Employee.class:

@JoinTable(name = "company_employee", joinColumns = @JoinColumn(name = "employee_id") , inverseJoinColumns = @JoinColumn(name = "company_id") )
@ManyToMany(fetch = FetchType.LAZY)
private List<Company> companies;

Obviously, to work for several companies, each employee should have several not overlapping schedules assigned for each company he or she works. Also, there should be a list of schedules for each combination Company-Employee, as sometimes old schedule expires, and new schedule becomes effective. So I also have Schedule.class, which is supposed to have child to parent @ManyToOne relationships both to Company and Employee, and should work following way: each Schedule, and thus, List<Schedule> should correspond to exactly one combination of Company and Employee instances. How to implement this relationship?

Update 1

I only have in mind adding @OneToMany Schedule relationship to each Company and Employee, but then I need to put instances of Schedule both to Company and Employee each time, and this way just don't look right, also it's not obvious for me now how to fetch it back. So any help will be appreciated.


This post was updated to show real-life scenario I have, not just generic Entity1, Entity2, Entity3 names for classes.

Update 2

I accepted the answer, but I cannot use it if Schedule contain Lists. According to my plan, Schedule should contain List<Vacation> to know the set of Vacations over a year, and List of Days, each of which shows start of particular week day, break, and end of this day. Those Days are also unique for each Schedule instance.

It was supposed to be something like below, but obviously now I don't have schedule_id, so how to connect those lists to Schedule?

@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) 
@JoinColumn(name = "schedule_id") 
private List<Vacation> vacations;

@JoinTable(name = "schedule_week", joinColumns = @JoinColumn(name = "schedule_id") , inverseJoinColumns = @JoinColumn(name = "day_id") )
@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) 
private List<Day> week;

How to include those lists right?

tharindu_DG
  • 8,900
  • 6
  • 52
  • 64
Battle_Slug
  • 2,055
  • 1
  • 34
  • 60
  • It would be better if you add the real scenario. The solution may not be related to hibernate, but a design change. – tharindu_DG Dec 16 '15 at 02:10
  • @tharindu_DG I edited my post to show real scenario. Probably you are right, hope it makes things more clear. – Battle_Slug Dec 16 '15 at 02:31

1 Answers1

1

I would like to suggest the following solution.

An embeddable class that contains the Company and Employee for a particular schedule.

@Embeddable
public class ScheduleOwner implements Serializable{

    @MapsId("id")
    @ManyToOne(cascade = CascadeType.ALL)
    Company c;

    @MapsId("id")
    @ManyToOne(cascade = CascadeType.ALL)
    Employee e;
}

The Schedule class is embedding a ScheduleOwner instance.

@Entity
public class Schedule {

    @EmbeddedId
    ScheduleOwner owner;

    String description;
}

The Company and Employee classes(no change done to them)

@Entity
public class Company {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @JoinTable(name = "company_employee", joinColumns = @JoinColumn(name = "company_id") , inverseJoinColumns = @JoinColumn(name = "employee_id") )
    @ManyToMany(fetch = FetchType.LAZY)
    private List<Employee> employees;
}


@Entity
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @JoinTable(name = "company_employee", joinColumns = @JoinColumn(name = "employee_id") , inverseJoinColumns = @JoinColumn(name = "company_id") )
    @ManyToMany(fetch = FetchType.LAZY)
    private List<Company> companies;
}

UPDATE 1

Below is how you could save and fetch results.

  Employee e1 = new Employee();
  Company c1 = new Company();
  c1.employees.add(e1);

  e1.companies.add(c1);

  ScheduleOwner so = new ScheduleOwner();
  so.c = c1;
  so.e = e1;

  Schedule s = new Schedule();
  s.owner = so;

  session.save(c1);
  session.save(e1);
  session.save(s);

  // below query will fetch from schedule, where company id = 9
  Schedule ss = (Schedule) session.createQuery("From Schedule sh where sh.owner.c.id = 9").uniqueResult();

UPDATE 2

@Entity
public class Company {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @JoinTable(name = "company_employee", joinColumns = @JoinColumn(name = "company_id", referencedColumnName="id") 
                                        , inverseJoinColumns = @JoinColumn(name = "employee_id", referencedColumnName="id"))
    @ManyToMany(fetch = FetchType.LAZY)
    List<Employee> employees = new ArrayList<>();

    String name;
}

@Entity
public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @ManyToMany(fetch = FetchType.LAZY, mappedBy = "employees")
    List<Company> companies = new ArrayList<>();

    String name;
}

@Entity
public class Schedule {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    int schedule_id;

    @ManyToOne
    @JoinColumn(name = "company_id", insertable = false, updatable = false)
    private Company company;
    @ManyToOne
    @JoinColumn(name = "employee_id", insertable = false, updatable = false)
    private Employee employee;

    String description;

    @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "schedule")
    List<Vacation> vacations;

}

@Entity
public class Vacation {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int vacation_id;

    @ManyToOne
    @JoinColumn(name = "schedule_id" ) 
    Schedule schedule;

    @OneToMany(mappedBy = "vacation")
    List<Day> days;
}

Day entity directly relates to Vacation. Not to Schedule.

@Entity
public class Day {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;


    @ManyToOne
    @JoinColumn(name = "vacation_id")
    Vacation vacation;
}

Hope this helps.

tharindu_DG
  • 8,900
  • 6
  • 52
  • 64
  • Thanks a lot for this great effort to help! I have never used `@Embedded` feature. Could you explain further, how it should work in Java? Is following sequence of actions right? to set: instantiate `Schedule`, instantiate `ScheduleOwner` (it should not have it's own table in the db, as I understand), fetch `Company` and `Employee` from db, set `Company` and `Employee` to `ScheduleOwner`, set `ScheduleOwner` to `Schedule`, store `Schedule` to db. To fetch `schedule` by `employee_id` and `company_id` - I don't understand now... how this part should be done? Also be affected other app structure? – Battle_Slug Dec 16 '15 at 03:25
  • Also, if to do this way, why not to put `Employee` and `Company` to `Schedule` directly with `@ManyToOne` relationship (many `Schedule` to one `Employee` and to one `Company`), without this intermediate `ScheduleOwner` class? – Battle_Slug Dec 16 '15 at 03:42
  • @Battle_Slug: I changed the design and updated my answer. please check. Read this for more info : http://stackoverflow.com/questions/19341838/why-should-we-use-embeddable-in-hibernate – tharindu_DG Dec 16 '15 at 04:21
  • Thanks for that. Looks great, but I've read about `@Embeddable` and I see, that it is supposed to separate some common part of several entities in one class, like usual OOP pattern. But here you do something different: you just use it to have in Schedule only one foreign key `owner` instead of two foreign keys `company_id` and `employee_id` (like if I just put `company` and `employee` directly to `schedule`). Am I right or is there some other thoughts behind your implementation? – Battle_Slug Dec 16 '15 at 04:47
  • 1
    This solution will create only 3 tables. Customer, Employee and Schedule. Schedule table will have customer id, employee id and other schedule attributes. Schedule table will have a composite primary key which contains employee id and company id. (means there can be only one schedule for a given company id and a employee id) – tharindu_DG Dec 16 '15 at 04:55
  • `ScheduleOwner` is not an entity and it will not create a separate table. Try running the code. – tharindu_DG Dec 16 '15 at 04:56
  • Yes, thanks. I was sleeping, probably we are in different time zones ) – Battle_Slug Dec 16 '15 at 14:55
  • @Battle_Slug : Yeah... :-) – tharindu_DG Dec 16 '15 at 15:04
  • Sorry, it seems I am not able to use it this way. My `Schedule` contains also fields: `@OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) @JoinColumn(name = "schedule_id") private List vacations;` and another one: `@JoinTable(name = "schedule_week", joinColumns = @JoinColumn(name = "schedule_id") , inverseJoinColumns = @JoinColumn(name = "day_id") ) @OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true) private List week;` How to change it than to be able to contain lists of other entities? – Battle_Slug Dec 16 '15 at 17:19
  • @Battle_Slug: I'm not clear about the requirement. can you update the question with your full requirement? – tharindu_DG Dec 17 '15 at 01:25
  • I updated, please kindly check whether it's clear enough. Thanks! – Battle_Slug Dec 17 '15 at 01:31
  • 1
    @Battle_Slug: I have updated my answer as I understood the question. – tharindu_DG Dec 17 '15 at 02:35
  • Thanks, will do this way. It really helps a lot, both to my project and my understanding. – Battle_Slug Dec 17 '15 at 02:46