Relations are like
A project can have many employees.
on a JSP, On selecting a project, all employees are populated.
A text box for task.
Provide a task name for that selected project , select employee(s) for that task and save.
I am able to save task but not in link table.
Entity classes are,
Employee, Task, Project
Employee
#Entity
#Table(name="Employee")
public class Employee implements Serializable{
#Id
#Column(name="employee_id")
private String employeeId;
#Column(name="employee_name")
private String employeeName;
#ManyToMany(mappedBy="employees")
private Set<Task> tasks;
}
Project
#Entity
#Table(name = "Project")
public class Project implements Serializable {
#Id
#Column(name = "project_id")
private int projectId;
#Column(name = "project_name")
private String projectName;
#OneToMany(cascade = CascadeType.ALL)
#JoinTable(name = "PROJECT_EMPLOYEE",
joinColumns = { #JoinColumn(name = "project_id") },
inverseJoinColumns = { #JoinColumn(name = "employee_id") })
private Set<Employee> employees;
}
Task
#Entity
#Table(name = "Task")
public class Task implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "task_id")
private int taskId;
#Column(name = "task_details")
private String taskDetails;
#Temporal(TemporalType.DATE)
#Column(name = "start_date")
private Date startDate;
#Temporal(TemporalType.DATE)
#Column(name = "end_date")
private Date endDate;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "project_id", nullable = false)
private Project project;
#ManyToMany(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinTable(name = "Task_Employee", joinColumns = { #JoinColumn(name = "task_id") }, inverseJoinColumns = {
#JoinColumn(name = "employee_id") })
#Embedded
private Set<Employee> employees;
}
Not able to save into Task_Employee table.
Dao code:
From jsp, I am getting all string values and creating task object like this, empView is the view object getting from JSP page and creating Task object to save in dao.
Set<Employee> empSet = new HashSet<Employee>();
for(String employeeId : empView.getEmployees()) {
Employee emp = new Employee();
emp.setEmployeeId(employeeId);
//emp.setEmployeeName("aaa");
//emp.setProject(new Project(empView.getProjectId()));
empSet.add(emp);
}
return new Task(0, empView.getTaskDesc(), new Project(empView.getProjectId()), toDate(empView.getStartDate()),
toDate(empView.getEndDate()), empSet);
Save the above returned task object.
sessionFactory.openSession().save(task);
Thanks
Related
I'm new in Spring Boot JPA
I have questions in JPA Entity mappings.
there is 4 tables in my MySql DB
SPACE, PROJECT, ISSUE, MEMBER
SPACE is Big Project which contains multiple PROJECT.
PROJECT contains multiple ISSUE.
and MEMBER can join only 1 SPACE and multiple PROJECT which MEMBER belongs to SPACE.
MEMBER can write multiple ISSUE
in this situation, my ERD model is correct?
my ERD
and please check my jpa mappings.
If there's anything wrong, please point it out.
SPACE
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "space_no")
private Long spaceNo;
#NotEmpty
#Column(name = "space_name", unique=true, length = 100)
private String spaceName;
/** 1:N relation */
#OneToMany(mappedBy = "smsSpace")
private List<PmsProject> pmsProjects = new ArrayList<>();
PROJECT
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "project_no")
private Long projectNo;
#Column(name ="space_no")
private Long spaceNo;
#Column(name = "project_name", length = 100)
private String projectName;
/** 1:N relation */
#OneToMany(mappedBy = "pmsProject")
private List<ImsIssue> imsIssues = new ArrayList<>();
#OneToMany(mappedBy = "pmsProject")
private List<PmsProjectMember> projectMembers = new ArrayList<>();
/** N:1 relation */
#ManyToOne
#JoinColumn(name = "space_no", referencedColumnName = "space_no", insertable = false, updatable = false)
private SmsSpace smsSpace;
MEMBER
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "member_no")
private Long memberNo;
#Column(name = "mail_address", unique=true, length = 100)
private String mailAddress;
#Column(name = "name", length = 100)
private String name;
#Column(name = "keyword", length = 1000)
private String keyword;
#Column(name = "image", length = 1000)
private String image;
#Column(name = "password", length = 1000)
private String password;
#Column(name = "user_id", length = 50)
private String userId;
#Enumerated(EnumType.STRING)
private MemberRole role;
public void encodingPassword(String password) {
this.password = password;
}
/** 1:N realtion */
#OneToMany(mappedBy = "mmsMember")
private List<PmsProjectMember> projectMembers = new ArrayList<>();
ISSUE
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "issue_no")
private Long issueNo;
#Column(name ="project_no")
private Long projectNo;
#Column(name = "issue_name", length = 1000)
private String issueName;
#Column(name = "priority")
private Long priority;
#Column(name = "status", length = 20)
private String status;
#Column(name = "summary", length = 100)
private String summary;
#Column(name = "is_overdue")
private Long isOverdue;
#Column(name = "issue_type_cd")
private String issueTypeCd;
/** N:1 relation */
#ManyToOne
#JoinColumn(name = "project_no", referencedColumnName = "project_no", insertable = false, updatable = false)
private PmsProject pmsProject;
PROJECTMEMBER
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "group_no")
private Long groupNo;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "member_no")
private MmsMember mmsMember;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "project_no")
private PmsProject pmsProject;
I've been thinking about it for days, but I can't solve it because I lack knowledge. Please help me.
Assuming I got your situation right, you have A member that can have one Space and multiple project, space has multiple projects, every project can have more than one issue, every member can write more then one issue for each project.
Due to the suggestion the ERD you posted it's not corrected.
Here is the correct ERD
(I just wrote the Foreign Keys and Primary Keys, the rest its up to you)
And here you have all the entites:
Member
#Entity
#Table(name = "MEMBERS")
public class Member {
//members is the property name in Project entity.
#ManyToMany(mappedBy = "members")
Set<Project> projects;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "native")
#Column(name = "MEMBER_ID")
private Long id;
#ManyToOne
#JoinColumn(name = "SPACE_ID")
private Space space;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Space getSpace() {
return space;
}
public void setSpace(Space space) {
this.space = space;
}
public Set<Project> getProjects() {
return projects;
}
public void setProjects(Set<Project> projects) {
this.projects = projects;
}
}
Space
#Entity
#Table(name = "SPACES")
public class Space {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "native")
#Column(name = "SPACE_ID")
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
Issue
#Entity
#Table(name = "ISSUES")
public class Issue {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "native")
#Column(name = "ISSUE_ID")
private Long id;
#ManyToOne
#JoinColumn(name = "MEMBER_ID")
private Member member;
#ManyToOne
#JoinColumn(name = "PROJECt_ID")
private Project project;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Member getMember() {
return member;
}
public void setMember(Member member) {
this.member = member;
}
public Project getProject() {
return project;
}
public void setProject(Project project) {
this.project = project;
}
}
Project
#Entity
#Table(name = "PROJECTS")
public class Project {
#ManyToMany
#JoinTable(
name = "PROJECTS_MEMBERS",
joinColumns = #JoinColumn(name = "PROJECT_ID"),
inverseJoinColumns = #JoinColumn(name = "MEMBER_ID"))//Is referring to the id of the other Entity, in this case, members
Set<Member> members;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY, generator = "native")
#Column(name = "PROJECT_ID")
private Long id;
#ManyToOne
#JoinColumn(name = "SPACE_ID")
private Space space;
public Set<Member> getMembers() {
return members;
}
public void setMembers(Set<Member> members) {
this.members = members;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Space getSpace() {
return space;
}
public void setSpace(Space space) {
this.space = space;
}
}
You don't have to put necessarily both #ManyToOne and #OneToMany annotation, the back-reference could be useful in some use case, you have to see if you need it or not. Remember the back reference could cause problems with deserialization, creating a stack overflow by circular reference. You can avoid this, using transient keyword or various annotation (depending on the library you are using, Jackson, Gson, ecc..).
Be careful to don't use FetchType.EAGER randomly here's the why => Difference between FetchType LAZY and EAGER in Java Persistence API?
I have two entities, sale and sale_details. One sale will have many sale_details, but each sale_detail belongs to only one sale, my current code gives me a Repeated column in mapping for entity error.
Sale:
#Entity
#Table(name="Sales")
public class Sale implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sale_id", nullable = false)
private int sale_id;
#Column(name = "promotionid_fk")
private int promotionid_fk;
#Column(name = "grand_total", nullable = false)
private double grand_total;
#Column(name = "salespersonid_fk", nullable = false)
private int salespersonid_fk;
#Column(name = "customerid_fk", nullable = false)
private int customerid_fk;
#Column(name = "storeid_fk", nullable = false)
private int storeid_fk;
#Column(name = "expected_date", nullable = false)
private Date expected_date;
#Column(name = "pickup_date")
private Date pickup_date;
#Column(name = "initial_deposit_date", nullable = false)
private LocalDateTime initial_deposit_date;
#Column(name = "initial_deposit_type", nullable = false)
private String initial_deposit_type;
#Column(name = "initial_deposit_amount", nullable = false)
private double initial_deposit_amount;
#Column(name = "final_payment_date")
private LocalDateTime final_payment_date;
#Column(name = "final_payment_type")
private String final_payment_type;
#Column(name = "final_payment_amount")
private double final_payment_amount;
//maps one sale to many sale details relationship
#OneToMany(mappedBy = "sale", fetch = FetchType.LAZY)
private List<SaleDetails> sale_detail_list; //stores list of sale_detail entries where FK saleid_fk field is the ID of this sale
//default constructor, never used
public Sale() {
}
//creates new sale
public Sale(int promotionid_fk, double grand_total, int salespersonid_fk, int customerid_fk, int storeid_fk, Date expected_date, LocalDateTime initial_payment_date, String initial_payment_type, double initial_payment_amount) {
this.promotionid_fk = promotionid_fk;
this.grand_total = grand_total;
this.salespersonid_fk = salespersonid_fk;
this.customerid_fk = customerid_fk;
this.storeid_fk = storeid_fk;
this.expected_date = expected_date;
this.initial_deposit_date = initial_payment_date;
this.initial_deposit_type = initial_payment_type;
this.initial_deposit_amount = initial_payment_amount;
}
Sale_details:
#Entity
#Table(name = "sale_Details")
public class SaleDetails implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sale_detail_id")
private int saleDetailId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "saleid_fk")
private Sale sale;
#Column(name = "saleid_fk")
private int saleid_fk;
#Column(name = "productid_fk")
private int productid_fk;
#Column(name = "quantity_sold")
private int quantity_sold;
public SaleDetails(){
}
public SaleDetails(int saleid_fk, int productid_fk, int quantity_sold){
this.saleid_fk = saleid_fk;
this.productid_fk = productid_fk;
this.quantity_sold = quantity_sold;
}
Table structures:
Im trying to make the relationship bi-directional, what am I doing wrong here? From my understanding the owning side of the relationship is the many-to-one on the sale_details entity, and the mappedby in the sale entity is just referencing that there already is a mapping on the inverse side?
Full error stack trace:
Unable to build Hibernate SessionFactory
org.hibernate.MappingException: Repeated column in mapping for entity: com.owl.server.entities.Sale_Details column: saleid_fk (should be mapped with insert="false" update="false")
I would suggest you to follow java naming conventions. According to this roles:
Classes: Class names should be nouns, in mixed case with the first letter of each internal word capitalized.
Variables: Except for variables, all instance, class, and class constants are in mixed case with a lowercase first letter. Internal words start with capital letters.
So, I would suggest you to correct your mapping in the following way:
#Entity
#Table(name="Sales")
public class Sale implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sale_id", nullable = false)
private int saleId;
#Column(name = "promotionid_fk")
private int promotionId;
// ...
//maps one sale to many sale details relationship
#OneToMany(mappedBy = "sale", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<SaleDetails> saleDetails;
// default constructor, should be present
// It is used by hibernate for entity instantiation
public Sale() {
saleDetails = new ArrayList<>();
}
// getters, setters
// The addSaleDetail() and removeSaleDetail() are utility methods that
// synchronize both ends whenever a child element is added or removed.
public void addSaleDetail(SaleDetails saleDetail)
{
saleDetails.add(saleDetail);
saleDetail.setSale(this);
}
public void removeSaleDetail(SaleDetails saleDetail)
{
saleDetails.remove(saleDetail);
saleDetail.setSale(null);
}
}
#Entity
#Table(name = "Sale_Details")
public class SaleDetails implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sale_detail_id")
private int saleDetailId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "saleid_fk")
private Sale sale;
// This should be removed
// #Column(name = "saleid_fk")
// private int saleid_fk;
#Column(name = "productid_fk")
private int productId;
// ...
public SaleDetails(){
}
// getters, setters
}
The value in the mappedBy referred to the field name of another side of the association.
You can omit to use the referencedColumnName if the foreign key referred to the primary key field of target entity.
And an example of creation and saving a new Sale:
Sale sale = new Sale();
sale.setPromotionId(1);
// ...
SaleDetails saleDetail1 = new SaleDetails();
saleDetail1.setProductId(2);
// set other fields except sale
sale.addSaleDetail(saleDetail1);
SaleDetails saleDetail2 = new SaleDetails();
saleDetail2.setProductId(3);
// set other fields except sale
sale.addSaleDetail(saleDetail2);
entityManager.persist(sale);
I am working on a project management system which has three users namely employee, Manager and HRM. The employee and Manager are in the same entity having many-to-many recursive relationship(let's call this Employee entity). The Employee entity and the HRM entity inherits a User entity. The hibernate inheritance strategy used here is single-table.
Initially when a user is registered he is saved as an instance of User( Repository of type User). I want to update the user instance to an instance of an employee or instance of Manager when he is assigned to a particular project. How can this be implemented using spring data jpa. I am doing the project using spring boot.
I have created the entities using java classes and mapped each entity.
I have not provided the Project and Tasks class in the following code. If necessary I can provide.
Following is the User class.
#Entity
#Table(name = "users")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(
name="User_Type",
discriminatorType=DiscriminatorType.STRING
)
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="user_id")
private Long id;
private String name;
private String email;
private String password;
private String mobilenumber;
private String gender;
private String resetToken;
#ManyToMany(fetch = FetchType.LAZY, cascade={CascadeType.ALL})
#JoinTable(name = "user_roles",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();//roles refer to the employee,manager and HRM roles
//public getters and setters
Following is the inherited Employee class
#Entity
#DiscriminatorValue("Employee")
public class Employee extends User {
#ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL })
#JoinTable(name = "employee_project",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "project_id")
)
private Set<Project> project = new HashSet<>();
#ManyToMany
#JoinTable(name = "employee_tasks",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "task_id")
)
private Set<Task> tasks = new HashSet<>();
#ManyToMany
#JoinTable(name = "rm_employee",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "rm_id")
)
private Set<Employee> managers = new HashSet<>();
#ManyToMany
#JoinTable(name = "rm_employee",
joinColumns = #JoinColumn(name = "rm_id"),
inverseJoinColumns = #JoinColumn(name = "user_id")
)
private Set<Employee> employees = new HashSet<>();
//public getters and setters
I tried the following which is to downcast the instance of User to the instance of Employee which results in CastException.
Optional<User> optional = userRepo.findById(id);
Employee employee = (Employee)optional.get();
following is a sketch of the er model
I was able to solve the question without using inheritance. Actually I had to avoid inheritance since as java is concerned the type cannot be changed from one to another. As in this example the type User cannot be converted to Employee or Manager as you wish. So the solution is to create two separate classes for the Manager and Employee(This should not extend the User class as I have done in the question) . These two should be annotated as Entity and should have an Id field. You can have a constructor with the above said Id field in both classes and when ever you assign a user to a project you can use this constructor depending on whether he is manager or employee to create a respective instance of manager or employee.
the employee class is as follows
public class Employee{
#Id
private Long employeeId;
#ManyToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL })
#JoinTable(name = "employee_project",
joinColumns = #JoinColumn(name = "employee_id"),
inverseJoinColumns = #JoinColumn(name = "project_id")
)
private Set<Project> project = new HashSet<>();
#ManyToMany
#JoinTable(name = "employee_rm",
joinColumns = #JoinColumn(name = "employee_id"),
inverseJoinColumns = #JoinColumn(name = "rm_id")
)
private Set<Manager> managers = new HashSet<>();
#ManyToMany
#JoinTable(name = "employee_tasks",
joinColumns = #JoinColumn(name = "employee_id"),
inverseJoinColumns = #JoinColumn(name = "task_id")
)
private Set<Task> tasks = new HashSet<>();
public Employee() {}
public Employee(Set<Project> project, Set<Manager> managers, Set<Task> tasks, Long employeeId ) {
super();
this.project = project;
this.managers = managers;
this.tasks = tasks;
this.employeeId=employeeId;
}
//with public getters and setters
and the Managers class is as follows
#Entity
public class Manager {
#Id
private Long managerId ;
#ManyToMany
#JoinTable(name = "manager_employee",
joinColumns = #JoinColumn(name = "manager_id"),
inverseJoinColumns = #JoinColumn(name = "user_id")
)
private Set<Employee> employees = new HashSet<>();
#OneToOne
#JoinColumn(name="project_id")
private Project project;
public Manager() {}
public Manager(Long managerId) {
super();
this.managerId = managerId;
}
//public getters and setters
Pardon me if this looks too basic, I am just a beginner in springboot. I am trying to create a simple project. I have entity class "company.java" and another "jobs.java"
I have already mapped them such that it will be saving both "companyID,jobID"
now my question is here :
1) how can I ensure that the record is captured into that new table when i feed data from thymeleaf fields. I have tried a dummy entry and its saving jobs into the jobs table but its not capturing anything to insert into the joined table
2) How do I query such that it saves and also when I search a job by its ID it shows me the company.
#Entity
#Table(name = "company_account")
#EntityListeners(AuditingEntityListener.class)
#SecondaryTable(name = "company_profile")
public class CompanyAccount {
private int id;
private String CompanyName;
private String PhoneNumber;
private String Number_of_employees;
private String CompanyURL;
private String OrganisationType;
private String PrimaryIndustry;
private byte[] Logo;
private String Location;
private String DateEstablished;
private String Headquarters;
private String SocialMedia;
public CompanyAccount() {
}
///////////////////////here i tried the mapping
#OneToMany(fetch = FetchType.LAZY, mappedBy = "company_account")
private List<NewJobs> jobs = new ArrayList<NewJobs>();
#Id
// #org.hibernate.annotations.ColumnDefault("001")
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
//////////////////////////////////
deleted the getters and setters
///////////////////////////////////////////////////////////////
#Entity
#Table(name = "newjobs")
#EntityListeners(AuditingEntityListener.class)
public class NewJobs {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#org.hibernate.annotations.ColumnDefault("001")
private int newjobid;
public NewJobs() {
}
tried my mapping here
#ManyToOne(fetch = FetchType.LAZY)
// #JoinColumn(name = "company_account")
#JoinTable(name = "company_jobs", joinColumns = { #JoinColumn(name = "id") }, inverseJoinColumns = {
#JoinColumn(name = "newjobid") })
private CompanyAccount CompanyAccount;
}
How can I query my insert and also my search by id ?
I am trying to write a RESTful API using Spring Boot and I am not able to figure out a way to map my relations in the database. I have a User and a Reports table. Each User can have multiple Reports, and a single report consists of "FROM USER" and "TO USER" columns to indicate who sent the report and to whom. My User ID is the primary key and for the Report table, I am generating REPORT ID as the primary key using AUTO INCREMENT. Here is my User model class -
#Entity
#Table (name = "user")
#EntityListeners(AuditingEntityListener.class)
public class User {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private String email;
private String password;
#OneToMany(mappedBy = "user",cascade = CascadeType.ALL)
private List<Report> reportReceivedList;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Report> reportSentList;
/* Getters and setters ..... */
}
Here is my Report Model class -
#Entity
#Table (name = "report")
#EntityListeners(AuditingEntityListener.class)
public class Report {
#Id
#Column (name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne()
#JoinColumn(name = "from_user_id")
private Long fromUserId; //THIS SHOULD BE FROM "USER" TABLE
#ManyToOne()
#JoinColumn(referencedColumnName = "to_user_id")
private Long toUserId; //THIS SHOULD BE FROM "USER" TABLE
#Temporal(TemporalType.DATE)
#CreatedDate
private Date createdAt;
private String observation;
private String context;
//Other variables and getters and setters .....
}
Can someone please show me a way to correctly define this relationship. My current model doesn't work. Also, I want rows from REPORT class to be deleted as soon as a user is deleted. Thanks!
I finally fixed it by changing my User class as follows -
#OneToMany(cascade = CascadeType.ALL, targetEntity = Report.class)
#JoinColumn(name = "to_user_id")
private List<Report> reportReceivedList;
#OneToMany(cascade = CascadeType.ALL, targetEntity = Report.class)
#JoinColumn(name = "from_user_id")
private List<Report> reportSentList;
And by changing my Report class as -
#Column(name = "from_user_id")
private Long fromUserId;
#Column(name = "to_user_id")
private Long toUserId;