enter image description here
I am trying to map some entities to tables in MySQL database using Spring Boot JPA. I have a problem with one of the tables because in that one too many foreign keys are added. I highlighted the columns in the picture. I suppose that the problem might be linked with the fact that the Tutorial table has either One to Many or Many to Many relations with the other 3 tables, but I am not sure
#Entity(name = "authors")
public class Author {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "author_id")
private Long authorId;
#Column(name = "first_name", nullable = false, length = 100)
private String firstName;
#Column(name = "last_name", nullable = false, length = 100)
private String lastName;
#Column(name = "email", length = 320, unique = true)
private String email;
#Column(name = "job_title", length = 255)
private String jobTitle;
#Lob
#Type(type = "org.hibernate.type.BinaryType")
#Column(name = "profile_picture")
private byte[] profilePicture;
#Column(name = "about", length = 2000)
private String about;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "author_id")
private List<Tutorial> tutorials;
}
#Entity(name = "categories")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "category_id")
private Long categoryId;
#Column(name = "category_name", nullable = false, unique = true, length = 100)
private String categoryName;
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id")
private List<Tutorial> tutorials;
}
#Entity(name = "tutorials")
public class Tutorial {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "tutorial_id")
private Long tutorialId;
#Column(name = "tutorial_title", nullable = false, length = 150)
private String tutorialTitle;
#Column(name = "tutorial_description", nullable = false, length = 2000)
private String tutorialDescription;
#Column(name = "time_to_complete")
private Integer timeToComplete;
#Column(name = "date_published")
private Long datePublished;
#Column(name = "last_updated")
private Long lastUpdated;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "tutorials")
private List<User> users = new ArrayList<>();
#ManyToOne(fetch = FetchType.EAGER)
private Category category;
#ManyToOne(fetch = FetchType.EAGER)
private Author author;
}
Tutorials is the table where the problems appear as 4 foreign keys are generate instead of two
#Entity(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long userId;
#Column(name = "first_name", nullable = false, length = 100)
private String firstName;
#Column(name = "last_name", nullable = false, length = 100)
private String lastName;
#Column(name = "user_name", nullable = false, unique = true, length = 100)
private String userName;
#Column(name = "age")
private Integer age;
#Column(name = "email", length = 320, unique = true)
private String email;
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "users_tutorials",
joinColumns = { #JoinColumn(name = "user_id") },
inverseJoinColumns = { #JoinColumn(name = "tutorial_id") })
private List<Tutorial> tutorials = new ArrayList<>();
}
Try this changes:
remove #JoinColumn(name = "author_id")from Author and place in Tutorial:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "author_id")
private Author author;
remove #JoinColumn(name = "category_id")from Category and place it in Tutorial as well:
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "category_id")
private Author author;
To get more information look here: Baeldung - Hibernate One to Many
Related
I have a set of entities user and conference. I have implemented a method in which I assign users to a conference and save it. However, when I save the conferencce the intermediate table attendance_table is not updated.
My database erd diagram example:
My entities:
#Entity
#Table(name = "user_table", schema = "public")
public class User {
#Id
#Column(name = "user_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long user_id;
#Column(name = "name")
private String name;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
#Column(name = "confirmed")
private boolean confirmed;
#ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.DETACH}, fetch = FetchType.LAZY)
#JoinTable(name = "attendance_table",
joinColumns = {
#JoinColumn(name = "user_id", referencedColumnName = "user_id",
nullable = false, updatable = false)},
inverseJoinColumns = {
#JoinColumn(name = "conference_id", referencedColumnName = "conference_id",
nullable = false, updatable = false)})
private Collection<Conference> conferences = new HashSet<>();
#Entity
#Table(name = "conference_table", schema = "public")
public class Conference {
#Id
#Column(name = "conference_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long conference_id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private User creator ;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#Column(name = "startConference")
private Date startConference;
#Column(name = "endConference")
private Date endConference;
#ManyToMany(mappedBy = "conferences", fetch = FetchType.LAZY)
private Collection<User> students;
Any ideas? If you need any extra info I can update the question. Thanks in advance!
This was a interesting fix. I was messing around with the user and conference classes and found a solution.
I had to swap the implementations of #ManyToMany of both classes for it to work.
My User class now looks like:
#Entity
#Table(name = "user_table", schema = "public")
public class User {
#Id
#Column(name = "user_id")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long user_id;
#Column(name = "name")
private String name;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column(name = "email")
private String email;
#Column(name = "confirmed")
private boolean confirmed;
#ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.DETACH}, fetch = FetchType.EAGER)
#JoinTable(
name = "user_role_table",
joinColumns = #JoinColumn(name = "user_id", referencedColumnName = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id", referencedColumnName = "role_id"))
private Collection<Role> roles = new HashSet<>();
#ManyToMany(mappedBy = "students", fetch = FetchType.LAZY)
private Collection<Conference> conferences;
And my conference class:
#Entity
#Table(name = "conference_table", schema = "public")
public class Conference {
#Id
#Column(name = "conference_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long conference_id;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "user_id")
private User creator ;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#Column(name = "startConference")
private Date startConference;
#Column(name = "endConference")
private Date endConference;
#ManyToMany(cascade = {CascadeType.MERGE, CascadeType.PERSIST, CascadeType.DETACH}, fetch = FetchType.LAZY)
#JoinTable(name = "attendance_table",
joinColumns = {
#JoinColumn(name = "conference_id", referencedColumnName = "conference_id",
nullable = false, updatable = false)},
inverseJoinColumns = {
#JoinColumn(name = "user_id", referencedColumnName = "user_id",
nullable = false, updatable = false)})
private Collection<User> students = new HashSet<>();
Still not exactly sure why it works like this and not the other way around. If anyone knows please explain!
Is it possible to create one column for bi-directional relationship?
My Entities:
#Entity
#Table(name = "subscription")
#Proxy(lazy = false)
public class Subscription {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "subscription_id")
private long id;
#Column(name = "userid", nullable = false)
private String userId;
#Column(name = "saledate", nullable = false)
#Temporal(TemporalType.DATE)
private Date saleDate;
#Column(name = "finishdate", nullable = false)
#Temporal(TemporalType.DATE)
private Date finishDate;
#Column(name = "price", nullable = false)
private long price;
#Column(name = "description", nullable = false)
private String description;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "subscription")
private List<VisitDate> visitDates = new ArrayList<>();
}
#Entity
#Table(name="visitdate")
public class VisitDate {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "id")
private long id;
private long subscription;
#Column(name = "date", nullable = false)
#Temporal(TemporalType.DATE)
private Date date;
#ManyToOne
#JoinColumn(name="subscription_id")
private Subscription associatedSub;
}
Now I see two columns in the database and little bit confused.
I don't want to save the same data but want to display a report about how many users visit on some day.
Update:
You are not required to create a separate field "subscription" in VisitDate class. Hibernate will automatically create a field to store subscription id. The code needs to be slightly changed.
#Entity
#Table(name = "subscription")
public class Subscription {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name = "subscription_id")
private long id;
#Column(name = "userid", nullable = false)
private String userId;
#Column(name = "saledate", nullable = false)
#Temporal(TemporalType.DATE)
private Date saleDate;
#Column(name = "finishdate", nullable = false)
#Temporal(TemporalType.DATE)
private Date finishDate;
#Column(name = "price", nullable = false)
private long price;
#Column(name = "description", nullable = false)
private String description;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "associatedSub")
private List<VisitDate> visitDates = new ArrayList<>();
}
Notice, that I have changed the mappedBy property to point at associatedSub in the above class.
#Entity
#Table(name="visitdate")
public class VisitDate {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name = "id")
private long id;
#Column(name = "date", nullable = false)
#Temporal(TemporalType.DATE)
private Date date;
#ManyToOne
#JoinColumn(name="subscription_id")
private Subscription associatedSub;
}
You can use Uni-Directional relationship for the same purpose. You just need to add a list/set of Visits for a particular subscription, You don't have to create a list of subscription for a particular visit.
for reference Visit [Java JPA] :(https://en.wikibooks.org/wiki/Java_Persistence/OneToMany#Undirectional_OneToMany.2C_No_Inverse_ManyToOne.2C_No_Join_Table_.28JPA_2.0_ONLY.29)!
#Entity
#Table(name = "subscription")
#Proxy(lazy = false)
public class Subscription {
#Id #GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "subscription_id")
private long id;
#Column(name = "userid", nullable = false)
private String userId;
#Column(name = "saledate", nullable = false)
#Temporal(TemporalType.DATE)
private Date saleDate;
#Column(name = "finishdate", nullable = false)
#Temporal(TemporalType.DATE)
private Date finishDate;
#Column(name = "price", nullable = false)
private long price;
#Column(name = "description", nullable = false)
private String description;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "subscription")
private List<VisitDate> visitDates = new ArrayList<>();
}
#Entity
#Table(name="visitdate")
public class VisitDate {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
#Column(name = "id")
private long id;
private long subscription;
#Column(name = "date", nullable = false)
#Temporal(TemporalType.DATE)
private Date date;
}
I'm getting a problem with the #ManyToMany collections not populating on data load. I've tried FetchType.LAZY and FetchType.EAGER with no changes in the result.
When I am printing the User Object the collection Object of Roles is empty.
User [userId=2, firstName=Ajay, lastName=C, email=admin.demo#gmail.com, password=12345, roles=[]]
Also tried by adding referenced columns. But not worked.
Please assist in this.
User and Roles Entities as follows.
#Entity
#Table(name = "\"USER\"", schema = "\"PLATFORM_PROD_IOT\"", uniqueConstraints = {
#UniqueConstraint(columnNames = { "\"EMAIL_ID\"" }) })
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Size(min = 1, max = 50)
#Column(name = "\"USER_ID\"")
private Long userId;
#NotBlank
#Size(min = 3, max = 50)
#Column(name = "\"FIRST_NAME\"")
private String firstName;
#NotBlank
#Size(min = 3, max = 50)
#Column(name = "\"LAST_NAME\"")
private String lastName;
#NaturalId
#NotBlank
#Size(max = 50)
#Email
#Column(name = "\"EMAIL_ID\"")
private String email;
#NotBlank
#Size(min = 3, max = 100)
#Column(name = "\"PASSWORD\"")
private String password;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "\"USER_ROLE_MAPPING\"", schema = "\"\PLATFORM_PROD_IOT\"", joinColumns = #JoinColumn(name = "\"USER_ID\""), inverseJoinColumns = #JoinColumn(name = "\"ROLE_ID\""))
private Set<Role> roles = new HashSet<>();
//Getters and Setters
}
#Entity
#Table(name = "\"ROLE\"",schema="\"PLATFORM_PROD_IOT\"")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="\"ROLE_ID\"")
private Long roleId;
#Column(name="\"ROLE_NAME\"")
private RoleName name;
//Getters and Setters
}
You could try this -
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "\"USER_ROLE_MAPPING\"", catalog = "\"PLATFORM_PROD_IOT\"", joinColumns = {
#JoinColumn(name = "\"USER_ID\"", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "\"ROLE_ID\"",
nullable = false, updatable = false) })
private Set<Role> roles = new HashSet<>();
public Set<Role> getRoles() {
return roles;
}
public void setRoles(Set<Role> roles) {
this.roles = roles;
}
Here I have added
cascade = CascadeType.ALL
catalog = "\"PLATFORM_PROD_IOT\"" instead of schema = "\"PLATFORM_PROD_IOT\""
nullable = false, updatable = false in #JoinColumn
Also have found an related -
collection not populating in many to many relationship
I have 2 tables project and employee have multiple relationship. Project has many employees, employee can join many projects( many to many). project have one employee whose is team leader, an employee can manager many projects(many to one). So how do I design database, and classes model mapping with database. Some one help me
This is my code
class user
#Entity(name = "USERS")
public class Users {
#Id
#Column(name = "USER_ID", length = 6)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer userId;
#Column(name = "USER_NAME", length = 50, nullable = false)
private String userName;
#Column(name = "PASS_WORD", length = 50, nullable = false)
private String passWord;
#Column(name = "FULL_NAME", length = 50, nullable = false)
private String fullName;
#Column(name = "EMAIL", length = 50, nullable = false)
private String email;
#Column(name = "PHONE", length = 11, nullable = true)
private String phone;
#Column(name = "STATUS", nullable = true)
private Boolean status;
#ManyToMany(fetch = FetchType.LAZY)
#JsonIgnore
#JoinTable(name = "USERPROJECT", joinColumns = { #JoinColumn(name =
"USER_ID") }, inverseJoinColumns = {
#JoinColumn(name = "PROJECT_ID") })
private List<Project> projects;
#OneToMany(mappedBy = "teamlead")
private List<Project> projectOfTeamLead;
//get set.....
}
class project
#Entity(name = "PROJECTS")
public class Project {
#Id
#Column(name = "PROJECT_ID", length = 6)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer projectId;
#Column(name = "PROJECT_NAME", length = 50, nullable=false)
private String projectName;
#Column(name = "USER_CREATE_ID", length = 6, nullable=false)
private Integer userCreateId;
// #Column(name = "TEAM_LEAD_ID", length = 6, nullable=false)
// private Integer teamLeadId;
#Column(name = "TIME_START", nullable=true)
private Date timeStart;
#Column(name = "TIME_END", nullable=true)
private Date timeEnd;
#ManyToMany(mappedBy = "projects")
private List<Users> users;
#ManyToOne
#JoinColumn(name = "TEAM_LEAD_ID")
private Users teamlead;
//get set...
}
I think you need to make your database fit into 3nf (third normal form). You are describing a database that uses two tables, but I think you need three at least, that way you can describe what employees are on what project. A "team" table that uses the PK's from the other two tables should be used. Conventionally, you would name it something like "ProjectEmployees" as it is comprised of the PK's from the "Projects" table and the "Employees" table respectively. I hope that answers your question. If you don't understand what I'm talking about, I highly recommend you watch this.
I have 2 entities linked together using oneToMany mapping. In the Dao layer when i apply restrictions on the linked entity it fetches all the results. It seems that the restrictions are not working on the linked entity. I want to apply restrictions on both entities.
DAO
Criteria criteria = createEntityCriteria()
.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY)
.add(Restrictions.eq("status" , "APPROVED"))
.addOrder(Order.desc("approvedAt"))
.createAlias("purchaseDemandDetails" , "pds")
.add(Restrictions.ge("pds.approvedQuantity" , 1));
return criteria.list();
PurchaseDemand.java
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
#JoinColumn(name = "created_by", referencedColumnName = "id")
private User createdBy;
#Column(name = "created_at")
private Date createdAt;
#ManyToOne
#JoinColumn(name = "updated_by" , referencedColumnName = "id")
private User updatedBy;
#Column(name = "updated_at")
private Date updatedAt;
#ManyToOne
#JoinColumn(name = "approved_by" , referencedColumnName = "id")
private User approvedBy;
#Column(name = "approved_at")
private Date approvedAt;
#Column(name = "status")
private String status;
#OneToMany(mappedBy = "purchaseDemand", cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private Set<PurchaseDemandDetail> purchaseDemandDetails = new HashSet<PurchaseDemandDetail>();
public void setPurchaseDemandDetails(Set<PurchaseDemandDetail> purchaseDemandDetails)
{
this.purchaseDemandDetails.addAll(purchaseDemandDetails);
}
public Set<PurchaseDemandDetail> getPurchaseDemandDetails()
{
return this.purchaseDemandDetails;
}
PurchaseDemandDetail.java
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne
#JoinColumn(name = "purchase_demand_id",referencedColumnName = "id")
#JsonIgnore
private PurchaseDemand purchaseDemand;
#ManyToOne
#JoinColumn(name = "product_id",referencedColumnName = "id")
private Product product;
#Column(name = "requested_quantity", nullable = false)
#NotNull(message = "Quantity is required")
private int requestedQuantity;
#Column(name = "approved_quantity", nullable = false)
#NotNull(message = "Quantity is required")
private int approvedQuantity;
}