java manytomany mapping not creating - java

I created two simple entities for trying out the java persistence manytomany mapping. But whatever I try, the jointable won't be populated with a mapping and remains empty.
UserClass:
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#ManyToMany(targetEntity = Order.class ,fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinTable(
name = "users_orders",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "id"),
inverseJoinColumns = #JoinColumn(name = "order_id", referencedColumnName = "id")
)
#JsonIgnoreProperties(value = "orderUsers")
private Set<Order> userOrders = new HashSet<>();
}
OrderClass:
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#ManyToMany(mappedBy = "userOrders", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JsonIgnoreProperties(value = "userOrders")
private Set<User> orderUsers = new HashSet<>();
}
I added Getter/Setter/Constructor via Lombok.
Create and save an user. Create an order, add the user and save it. But still the jointable remains empty.
Any ideas?

Related

hibernate many-to-many association with extra columns in join table example

I have 3 tables as #Entity, and 2 join tables in my spring + hibernate app.
In one of join table i have extra column. I want to take info from this info column when i take info from my main table.
Main table code:
#Entity
#Table(name = "items")
public class Items {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private int id;
#Column(name = "name")
private String name;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "crafts"
,joinColumns = #JoinColumn(name = "item_id")
,inverseJoinColumns = #JoinColumn(name = "plot_id"))
private Set<Plots> plotInfo = new HashSet<>();
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "item_materials"
,joinColumns = #JoinColumn(name = "item_id")
,inverseJoinColumns = #JoinColumn(name = "material_id"))
private Set<Materials> materialsInfo = new HashSet<>();
Table item_materials have this columns "id, item_id(fkey), material_id(fkey), expense" and one of this which names as "expense" i need to have in my final result.
How can i code my class to have "expense" in my result?
I read about #embeddable but still dont understand how to use in my project.
Don't use a #ManyToMany association. Map the join table as entity and model it similar to this:
#Entity
#Table(name = "items")
public class Items {
#OneToMany(mappedBy = "item")
private Set<Crafts> plotInfo = new HashSet<>();
}
#Entity
#Table(name = "plots")
public class Plots {
#OneToMany(mappedBy = "plot")
private Set<Crafts> items = new HashSet<>();
}
#Entity
#Table(name = "crafts")
public class Crafts {
#EmbeddedId
private CraftsId id;
#ManyToOne
#JoinColumn(name = "item_id", insertable = false, updatable = false)
private Items item;
#ManyToOne
#JoinColumn(name = "plot_id", insertable = false, updatable = false)
private Plots plot;
}
#Embeddable
public class CraftsId {
#Column(name = "item_id")
private Integer itemId;
#Column(name = "plot_id")
private Integer plotId;
// equals + hashCode
}

Hibernate OneToMany mapping & Query generation : More than one row with the given identifier was found

I am using spring-boot-starter-data-jpa 1.5.1.RELEASE which internally uses hibernate-core 5.0.11.Final
My entity looks like this:
AreaDto
#Entity
#Table(name = "AREA")
#EntityListeners(AuditingEntityListener.class)
public class AreaDto {
#Id
#Column(name = "AREA_ROWID")
private String areaRowId;
#OneToMany(cascade = CascadeType.DETACH)
#JoinColumn(name = "AREA_ROWID")
private Collection<FestivalDto> festival;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<ActionDto> actions;
#OneToMany(fetch = FetchType.LAZY)
#JoinTable(name = "FESTIVAL", joinColumns = {
#JoinColumn(name = "AREA_ROWID", referencedColumnName = "AREA_ROWID")}, inverseJoinColumns = {
#JoinColumn(name = "FESTIVAL_ROWID", referencedColumnName = "FESTIVAL_ROWID")})
private Collection<ActionDto> festivalActions;
}
FestivalDto
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "FESTIVAL")
public class FestivalDto {
#Id
#Column(name = "FESTIVAL_ROWID")
#GeneratedValue(generator = "FESTIVAL_ROWID_SEQ")
private Long festivalRowId;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "festival")
private Collection<ActionDto> actions = Lists.newArrayList();
}
ActionDto
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "ACTION")
public class ActionDto implements Serializable {
...
#Id
#Column(name = "ACTION_ID")
#GeneratedValue(generator = "ACTION_ID_SEQ")
private Long actionId;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "FESTIVAL_ROWID")
private FestivalDto festival;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
}
I'm trying to make sense of the below ideas:
What is the strategy used by hibernate to decide on the festival_rowid (or festival_row ids) used to get all the associated action? How will hibernate generated SQL query vary if i change festivalActions fetch strategies between LAZY and EAGER? I know about proxying, collection proxying and all, my question is specific to how those sql is generated and how it may have an impact on deciding the value of bind parameter.
Is my mapping accurate or should I be using a multimap for this relationship since an area could have multiple festival and each festival could have multiple actions
Background:
I am getting below error which goes away if I change the fetch type from LAZY to EAGER. Hoping to understand the behaviour for gaining some confidence in the fix. I have read SO and error
org.hibernate.HibernateException: More than one row with the given identifier was found: data.dto.ActionDto#280856b5
This mapping does not make much sense. You can't map festivalActions this way because there is no way to persist the state properly through such a mapping. Also festival in AreaDto should be mapped by the area in FestivalDto. Try the following instead:
#Entity
#Table(name = "AREA")
#EntityListeners(AuditingEntityListener.class)
public class AreaDto {
#Id
#Column(name = "AREA_ROWID")
private String areaRowId;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<FestivalDto> festival;
#OneToMany(cascade = CascadeType.DETACH, mappedBy = "area")
private Collection<ActionDto> actions;
public Collection<ActionDto> getFestivalActions() {
return festival.stream().flatMap(f -> f.actions.stream()).collect(Collectors.toList());
}
}
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "FESTIVAL")
public class FestivalDto {
#Id
#Column(name = "FESTIVAL_ROWID")
#GeneratedValue(generator = "FESTIVAL_ROWID_SEQ")
private Long festivalRowId;
#ManyToOne(cascade = CascadeType.DETACH, fetch = FetchType.LAZY, optional = true)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "festival")
private Collection<ActionDto> actions = Lists.newArrayList();
}
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "ACTION")
public class ActionDto implements Serializable {
...
#Id
#Column(name = "ACTION_ID")
#GeneratedValue(generator = "ACTION_ID_SEQ")
private Long actionId;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "FESTIVAL_ROWID")
private FestivalDto festival;
#Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
#ManyToOne(cascade = DETACH, fetch = FetchType.LAZY)
#JoinColumn(name = "AREA_ROWID")
private AreaDto area;
}

Hibernate Criteria Api with Inner Join and Many to Many

I'm new with Hibernate and Criteria Query.
How can I implement it with Hibernate Criteria Object?
SELECT stateslocalization.StateId, stateslocalization.localization AS name
FROM processstate
Join states ON states.id = processstate.StateId
JOIN stateslocalization ON stateslocalization.StateId = states.id
WHERE processstate.ProcessId = 38 and processstate.StateId = states.id AND stateslocalization.StateId = states.id
Entities:
Process:
#Entity
#Table(name = "processes")
public class Process {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)
#JoinTable(
name = "processstate",
joinColumns = {#JoinColumn(name = "ProcessId")},
inverseJoinColumns = {#JoinColumn(name = "StateId")}
)
private Set<State> states;
//getters and setters.....
}
State:
#Entity
#Table(name = "states")
public class State {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private int id;
private String name;
#ManyToMany(mappedBy = "states", cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
private Set<Process> processes;
#OneToOne(mappedBy = "state")
private StateLocalization stateLocalization;
//getters and setters.....
}
StateLocalization:
#Entity
#Table(name = "stateslocalization")
public class StateLocalization {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "StateId", referencedColumnName = "id")
private State state;
private String localization;
//getters and setters.....
}
I did it with native query but I don't know how I can implement it to Hibernate Criteria, because I don't have entity processstate (it's only table).

Unable to save data using hibernate.save()

I am trying to add Staff object which has staff information, roles and subjects. The code below saves only Staff data not its associated collections data.
I have tried to debug it but didn't understand the issue. The staff object has roles and subjects before saving into database but they are not getting saved in the DB. Surprisingly, the similar code is working while saving Course table data; however, Course does also have collection of Subject class.
It seems to me that Subjects have Course object which may be creating problem, I am not sure why. Please advise how to fix it.
The complete project is available on GitHub(https://github.com/ravinain/practice/tree/master/Java/Spring/SchoolProject)
Staff.java
#Entity
#Table
public class Staff extends Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private double salary;
#OneToMany(mappedBy = "staff", fetch=FetchType.EAGER)
#Cascade({ CascadeType.ALL})
private Set<Role> roles = new HashSet<Role>();
#ManyToMany(mappedBy = "staffs", fetch=FetchType.EAGER)
#Cascade({CascadeType.ALL})
private Set<Subject> subjects = new HashSet<Subject>();
Role.java
#Entity
#Table
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#NotNull
private int id;
private String name;
#ManyToOne
#JoinTable(name = "role_staff", joinColumns = #JoinColumn(name = "role_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "staff_id", referencedColumnName = "id"))
#JsonIgnore
private Staff staff;
Subject.java
#Entity
#Table
public class Subject implements Comparable<Subject>{
#Id
#Column
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String description;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "subject_staff", joinColumns = #JoinColumn(name = "subject_id", referencedColumnName = "id"), inverseJoinColumns = #JoinColumn(name = "staff_id", referencedColumnName = "id"))
#JsonIgnore
private Set<Staff> staffs = new HashSet<Staff>();
#ManyToMany(mappedBy = "subjects", fetch = FetchType.EAGER)
#Cascade({ CascadeType.DELETE, CascadeType.SAVE_UPDATE })
#JsonIgnore
private Set<Course> courses = new HashSet<Course>();
#ManyToMany(mappedBy = "subjects", fetch = FetchType.EAGER)
#Cascade({ CascadeType.DELETE, CascadeType.SAVE_UPDATE })
#JsonIgnore
private Set<Student> students = new HashSet<Student>();
DAO Code:
public Staff addStaff(Staff staff) {
Session session = sessionFactory.getCurrentSession();
session.save(staff);
return staff;
}
POST Request:
{"name":"New Test","age":22,"gender":"Female","salary":12000,"roles":[{"id":3,"name":"Teacher"}],"subjects":[{"id":1,"description":"Math"},{"id":2,"description":"English"}]}

Hibernate two parents one child mapping

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?
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.

Categories

Resources