I have a
company table
department table
employee table.
I am using hibernate to persist data in the database.
1. One to Many Relationship between Company and Department .
A company can have multiple departments and a department can have multiple employees.
I have done corresponding one to many mapping of entities as mentioned in below code. Request to update these entities comes from UI in the JSON format.
I have provided company Id, department Id, and employee Id in the request.
Now Suppose If for a particular company , there is one department in the database with dept_id 3 . . In the Json request, I get a request to update that particular company with one more department. So after updation, previous entry should remain as it is, i.e department with ID 3 ,should remain untouched and new entry should be added with some department Id, say 4,.
Now that company would have two departments one with Id 3 and other with id 4.
How could this be achieved??..Also department entries , which are not there in the request, should be deleted from the database... Same goes for the relation between employee and department,.request may ask to add new employee for a particular department,keeping the existing one.
Please help me with this, what configuration/approach has to be done in my code, to achieve this.
Here is the code for these three tables:
#Entity
#Table(name = "COMPANY")
#Getter
#Setter
public class Company implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "company_id")
private long companyId;
#Column(name = "company_region")
private String companayRegion;
#Column(name = "company_code")
private String companyCode;
#OneToMany(mappedBy = "company", cascade = CascadeType.ALL)
private List<department> departments;
public Company() {
}
}
#Entity
#Table(name = "Department")
#Getter
#Setter
public class Department implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "dept_id")
private long departmentID;
#Column(name = "dep_code")
private String departmentCode;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "company_id")
private Company company;
#OneToMany(mappedBy = "department", cascade = CascadeType.ALL)
private List<Employee> employees;
}
#Entity
#Table(name = "Employee")
#Getter
#Setter
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "employee_id")
private long employeeId;
#Column(name = "emp_code")
private String empCode;
#Column(name = "emp_name")
private String empname;
#Column(name = "employee_city")
private String employeeCity;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "dept_id")
private Department department;
}
the mappings seem correct, what you need is to use merge to obtain an Hibernate-managed entity and copy the fields from the detached instance (coming from the REST api):
Company c = (Company) session.merge(companyFromDto);
Merge should take care to create a new Company, or update an existing one (depending if it is already in the DB) as well as cascade the associations.
Make sure your JSON contains companyId / departmentID / employeeId
Load the company by companyId
INSERT all department entries where departmentID == 0, and fetch the generated ID (Hibernate will update the POJO, or return a new one with the ID set)
UPDATE all department entries where departmentID > 0, and remember the used departmentID
Iterate over company.getDepartments() and iterator.remove() entries with departmentID not contained in the collection built from (3) and (4)
By persisting the company Hibernate will detect which departments were removed, and delete those
Do basically the same for the department -> employee relationship
Related
I am new to Spring JPA and I am having some trouble with setting foreign keys on an entity when inserting a row.
I have the following entities
#Entity
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String departmentName;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "department_id")
List<Employee> employees;
}
#Entity
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String employeeName;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "employee_id")
List<Role> roles;
#ManyToOne
#JoinColumn(name = "department_id", insertable = false, updatable = false)
private Department department;
}
#Entity
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
private String roleName;
#ManyToOne
#JoinColumn(name = "employee_id", insertable = false, updatable = false)
private Employee employee;
#ManyToOne
#JoinColumn(name = "department_id", insertable = false, updatable = false)
private Department department;
}
And here is the Department Repository:
#Repository
public interface DepartmentRepository extends JpaRepository<Department, Integer> {
Department findById(long id);
}
With this approach, the department_id in the employee table and the employee_id in the role table is set correctly when I save a Department object using departmentRepository.save(department).
But I also want it to set the department_id in the role table. How can this be achieved? Currently the relationship Department and Role is indirect (i.e it is through Employee) but would I have to create a direct relationship between the 2 entities? I am not sure how to achieve this though. Any input will be appreciated.
Edit:
I want to model the relationship in this Entity Relationship diagram
As I see that you already using
#OneToMany(cascade = CascadeType.ALL)
So you knew that every update on Department with list Employee can do "many" things.
So Employee do the same to Roles (your list) but the magic auto save/update won't help.
Condition now:
Save action in repo
List of Employee
List of Role in employee (each object don't have Department Id)
As I understand:
You have Department that 1-n to Employee (Employee can only have 1 Department)
Employee can have many Role
As normal logic: Deparment 1-n Employee 1-n Roles
But some how you want Role have object of Department also.
So my first suggestion is change of logic code.
And second: a way to solve your problem!
Create Department
when add Role to Employee, please access and add deparment_id also.
(Department save List Employee, when save Employee - list Roles also save with same logic, just make sure to add it)
Could you please advise how to write query in Spring Data JPA repository? Here is the situation:
I have 2 Entities: Customer and Product with relationship 'OneToMany' - means one Customer may have many products. In code it looks like Customer entity has Set products and Product has reference to Customer customer, very simple. If I retrieve Customer from DB JSON would look like this: {"id":10, "name":'John Smith',"personalCode":12345678,"products":[ {"id":15,"type":"productType1"}, {"id":20,"type":"productType2"}] }
The question is how can I write query to DB to find all customers whose products match passed products collection? For example I want to find all customers who owns products with type1 and type2. THANKS!
#Entity
#Table(name = "customer")
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private int age;
private String personalCode;
#Enumerated(EnumType.STRING)
private Country country;
private String internetBankUserId;
#Enumerated(EnumType.STRING)
private CustomerType type;
#JsonManagedReference
#OneToMany(mappedBy = "customer", cascade = CascadeType.ALL)
#SortNatural
private SortedSet<Product> products = new TreeSet<>();
#Entity
#Table(name = "product")
public class Product implements Comparable<Product>{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Enumerated(EnumType.STRING)
private ProductType type;
#JsonBackReference
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "customer_id")
private Customer customer;
This solution worked for me:
#Query("SELECT c FROM Customer c join c.products p where p.type in :products
and SIZE(c.products) >= :count")
Set<Customer> findAllByProductType (#Param("products")Set<ProductType> products,
#Param("count") Integer count );
I tried searching for this specific issue but could not get any help
While creating Employee --> Account should get created.
My entities are --> Employee entity
#Entity
#Table(name = "EMPLOYEE")
public class EMPLOYEE implements Serializable {
private static final long serialVersionUID = -1798070786993154676L;
#Id
#Column(name = "EMPLOYEE_id")
#GeneratedValue(strategy = GenerationType.AUTO)
private int EMPLOYEE_id;
#Column(name = "EMPLOYEE_name")
private String EMPLOYEE_name;
#Column(name = "EMPLOYEE_desc")
private String EMPLOYEE_desc;
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "EMPLOYEE_id")
#MapsId
private ACCOUNT account;
// getters and setters
}
account entity -->
#Entity
#Table(name = "ACCOUNT")
public class ACCOUNT implements Serializable {
private static final long serialVersionUID = -6790693372846798580L;
#OneToOne( fetch = FetchType.LAZY)
#JoinColumn(name = "EMPLOYEE_id")
private EMPLOYEE employee
#Id
#Column(name = "account_id")
private int account_id;
#Column(name = "account_desc")
private String account_desc;
// getters setters
}
While creating Employee --> Account should get created.
My pojo is getting mapped correctly as shown below -->
employee = { EMPLOYEE_id = 0 ,
EMPLOYEE_name = 'abc' ,
EMPLOYEE_desc = 'new employee' ,
account = { account_id = 1 ,
account_desc = 'my account' ,
EMPLOYEE_id = 0 }
}
so when i same employee object - it generates employee_id , but the same employee_id is not getting populated in account table
It is partly working --> in the Account table Employee_id is not getting populated.
I tried searching so many questions on stackOverflw - but no luck so far.
Any help is appreciated.
Thank you in advance
If the #OneToOne is defined on both sides of the relationship, one side should be the owner. That is the entity that has the foreign key. The other side should have the mappedBy attribute. So the following works for me
#Entity
#Table(name = "EMPLOYEE")
public class EMPLOYEE implements Serializable {
#OneToOne(cascade= CascadeType.ALL, mappedBy = "employee") // non-owner side
private Account account;
....
}
#Entity
#Table(name = "ACCOUNT")
public class Account implements Serializable {
#OneToOne( fetch = FetchType.LAZY)
#JoinColumn(name = "EMPLOYEE_id") // owner-side
private Employee employee;
}
I know only basics of DB and JPA/Hibernate. I have to manage a User table, where a user can have many roles. The roles are contained in a catalog table which in my User formulary i do not pretend to manage/modify, i just need the catalog values as a reference to add or delete to my user.
I think the best approach would be to create a relationship table between User and Role to hold the users and their roles 'User_Roles' (unless there is a more efficient approach).
I am not allowed to modify the Role entity since it is used for different purposes in a lot of other areas of my app that are independent of the User table.
I've seen a lot of examples but I still do not know which one exactly aplies to my specific needs. How can I map my User and its roles in a sigle Entity with JPA and Hibernate?
Maybe the next image describes better what I want:
Thank you very much in advance for your answers.
In your case you have to use #ManyToMany to associate both tables.
That should look at this:
#Entity
#Table(name = "User")
public class User {
...
#ManyToMany
#JoinTable(name = "User_Roles", joinColumn = "id_person")
private Set<Role> roles = new HashSet<>;
}
#Entity
#Table(name = "Role")
public class Role {
...
#ManyToMany(mappedBy = "roles")
private Set<User> users = new HashSet<>;
}
What you're describing is a one-to-many relationship but it's between User and the joining table - User_Roles. Since there is not much you can do to avoid the joining table, the best thing would be to use #ManyToMany with #JoinTable annotations to map the relationship. Remember to use Set instead of List. You don't need an entity for the joinint table then.
You can find a discussion about this topic in this blog post.
As per your above screen, what I understood user can be assigned more than 1 role.
i.e. 1 user can be mapped to multiple role and 1 role can be mapped to multiple users.
Hence relationship between user and role is many to many.
many to many relationship can be achieved using third table which is called mapping table.
so , we have following tables in your example :-
user
user_roles
role
#Entity
#Table(name = "user")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class User {
#Id
#SequenceGenerator(name = "USER_ID_GENERATOR", sequenceName = "USER_SEQ",
allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_ID_GENERATOR")
#Column(name = "user_id")
private Long userId;
#OneToOne
#JoinColumn(name = "persion_id")
private person person;`
enter code
here`
#Basic
#Column(name = "date")
private Date date;
#Basic
#Column(name = "observations")
private String observations;
#Basic
#Column(name = "text")
private String text;
#JsonIgnore
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<UserRoles> users = new ArrayList<>();
}
#Entity
#Table(name = "role")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Role {
#Id
#SequenceGenerator(name = "ROLE_ID_GENERATOR", sequenceName = "ROLE_SEQ",
allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ROLE_ID_GENERATOR")
#Column(name = "role_id")
private Long roleId;
#Basic
#Column(name = "id1")
private Long idOne;
#Basic
#Column(name = "id1")
private Long idTwo;
#Basic
#Column(name = "id1")
private Long idThree;
#Basic
#Column(name = "text")
private String text;
#JsonIgnore
#OneToMany(mappedBy = "role", cascade = CascadeType.ALL)
private List<UserRoles> users = new ArrayList<>();
}
#Entity
#Getter
#Setter
#Table(name = "user_roles")
#JsonInclude(JsonInclude.Include.NON_NULL)
#Audited
public class UserRoles {
private static final long serialVersionUID = 1L;
#EmbeddedId
UserRolesKey userRoleId;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("role_id")
#JoinColumn(name = "role_id")
Roles role;
#JsonIgnore
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("user_id")
#JoinColumn(user_id)
User user;
#PrePersist
private void prePersist() {
super.onPrePersist();
if (this.getId() == null) {
UserRolesKey mapKey = new UserRolesKey();
mapKey.setRoleId(this.getRole().getRoleId());
mapKey.setUserRoleId(this.getUser().getUserId());
this.setId(mapKey);
}
}
}
While saving you just need to populate user entity with all the uaerRoles mapping entity and persist it. jpa will save all the details.
while updating role assign to user you need to fetch the user entity and update the mapping by adding new userRoles entity and nullifying the while is going to be removed.
I have a table PATIENT which has some fields. There's also a CONTACT table that has a field called 'patientId' that needs to store PATIENT's ID (which is autogenerated), and a PATIENT_CONTACT table that only relates the two tables.
Now, here comes the tricky part. There are three other tables: CONTACT_ADDRESS, CONTACT_PHONE, CONTACT_EMAIL. A row in CONTACT will have the same ID as one (and only one) of CONTACT_ADDRESS, CONTACT_PHONE and CONTACT EMAIL. How do I get this all to work?
I have tried so many approaches, this is what I have right now:
#Entity
#Table(name = "patient", schema = "patient")
public class PatientEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
//... more fields
#OneToOne
private ContactEmailEntity contactEmailEntity;
#OneToOne
private ContactAddressEntity contactAddressEntity;
#OneToOne
private ContactPhoneEntity contactPhoneEntity;
}
The three CONTACT_* classes are similar and they look like this:
#Table(name = "contact_address", schema = "patient")
public class ContactAddressEntity {
#Id
#Column(name = "id")
private Long id;
// ... more fields
#OneToOne(cascade = {CascadeType.ALL})
#MapsId
private ContactEntity contact;
}
And my CONTACT class looks like this:
#Table(name = "contacto", schema = "paciente")
public class ContactEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
//... more fields
Can you see things that don't look right or could be done better? I get all sorts of errors with every approach. My latest one is:
ERROR: column patientent0_.contact_address_entity_contact_id does not exist
when trying to do a simple patient find. Please, any help is appreciated!