So I am working with a database that uses a lot of composite keys. I am trying to use JPA/hibernate to set up an JoinTable for one of the relationships. Here is a trimmed down example of what is being done
Parent Class
#Entity
#Table(name = "PROTAGONIST")
public class Protagonist {
private Integer id;
private String name;
#Id
#Column(name = "id", nullable = false)
public Integer getId() {return id;}
public void setId(Integer id) { this.id = id;}
#Column(name = "pro_name", nullable = false, length = 50)
public String getName() {return name;}
public void setName(String name) { this.name = name;}
}
Item ID Class
#Embeddable
public class ItemId {
private int pId;
private short invSlot;
#Column(name = "P_Id", nullable = false)
public int getpId() {return pId;}
public void setpId(int pId) { this.pId = pId;}
#Column(name = "Inv_SlotNum", nullable = false)
public short getInvSlot() {return invSlot;}
public void setInvSlot(short invSlot) { this.invSlot = invSlot;}
}
Item Class
public class Item {
private ItemId id;
private String itemName;
private Double cost;
private Set<Buff> buffs;
#EmbeddedId
public ItemId getId() {return id;}
public void setId(ItemId id) { this.id = id;}
#Column(name = "Item_Name", nullable = false, length = 100)
public String getItemName() {return itemName;}
public void setItemName(String name) { this.itemName = name;}
#Column(name = "Item_Cost")
public Double getCost() {return cost;}
public void setCost(Double cost) { this.cost = cost;}
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "BUFFSONITEMS",
joinColumns = {
#JoinColumn(name = "P_Id", referencedColumnName = "P_Id"),
#JoinColumn(name = "Inv_SlotNum", referencedColumnName = "Inv_SlotNum")
},
inverseJoinColumns = {
#JoinColumn(name = "P_Id", referencedColumnName = "P_Id"),
#JoinColumn(name = "Buff_SlotNum", referencedColumnName = "Buff_SlotNum")
}
)
public Set<Buff> getBuffs() {return buffs;}
public void setBuffs(Set<Buff> buffs) { this.buffs = buffs;}
}
Buff ID Class
#Embeddable
public class BuffId {
private Integer pId;
private Short buffSlotNum;
#Column(name = "P_Id", nullable = false)
public Integer getpId() {return pId;}
public void setpId(Integer pId) { this.pId = pId;}
#Column(name = "Buff_SlotNum", nullable = false)
public Short getBuffSlotNum() {return buffSlotNum;}
public void setBuffSlotNum(Short buffSeqNum) { this.buffSlotNum = buffSeqNum;}
}
Buff Class
#Entity
#Table(name = "BUFF")
public class Buff {
private BuffId id;
private String buffName;
private Long duration;
private Set<Item> buffedItems;
#EmbeddedId
public BuffId getId() {return id;}
public void setId(BuffId id) { this.id = id;}
#Column(name = "Buff_Name", nullable = false, length = 100)
public String getBuffName() {return buffName;}
public void setBuffName(String name) { this.buffName = name;}
#Column(name = "Duration", nullable = false)
public Long getDuration() {return duration;}
public void setDuration(Long duration) { this.duration = duration;}
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "BUFFSONITEMS",
joinColumns = {
#JoinColumn(name = "P_Id", referencedColumnName = "P_Id"),
#JoinColumn(name = "Buff_SlotNum", referencedColumnName = "Buff_SlotNum")
},
inverseJoinColumns = {
#JoinColumn(name = "P_Id", referencedColumnName = "P_Id"),
#JoinColumn(name = "Inv_SlotNum", referencedColumnName = "Inv_SlotNum")
}
)
public Set<Item> getBuffedItems() {return buffedItems;}
public void setBuffedItems(Set<Item> buffedItems) { this.buffedItems = buffedItems;}
}
Whenever I attempt to start Spring Boot I get the following exception org.hibernate.MappingException: Repeated column in mapping for collection: com.blankd.composite.key.Item.buffs column: P_Id. The BUFFSONITEMS table uses all 3 columns as part of the primary key for each row in that table. All three columns also have forgein key constraints on the respective tables. This means that P_Id has a forgein key constraint on both Buff and Item.
I am not sure what I am doing wrong as I need the P_Id in order to uniquely identify the row in each table.
You have to choose one side of the many-to-many relationship to be the "owning" side. The "inverse" side must then use the mappedBy element.
If you were to choose Item.buffs to be the owning side, you would map Buff.buffedItems like this:
#ManyToMany(mappedBy="buffs")
public Set<Item> getBuffedItems() {return buffedItems;}
Related
I have a Spring Boot 2.1.13 (Java 8) project with MySQL 8.0.21 and mysql-connector-java version: 8.0.22 with the following classes.
#Entity
#Table(name = "user")
#JsonIgnoreProperties({"type", "location", "hibernateLazyInitializer", "handler"})
public class User implements java.io.Serializable {
private UserType userType;
private Location location;
private long id;
private String name;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "usertypeid")
public UserType getUserType() {
return this.userType;
}
public void setUserType(UserType userType) {
this.userType = userType;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "locationid", nullable = false)
public Location getLocation() {
return this.location;
}
public void setLocation(Location location) {
this.location = location;
}
#Column(name = "name", nullable = false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
#Entity
#Table(name = "usertype")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class UserType implements java.io.Serializable {
private long id;
private String name;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
#Column(name = "name", nullable = false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
#Entity
#Table(name = "location")
public class Location implements java.io.Serializable {
private long id;
private String name;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
#Column(name = "name", nullable = false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
I fetch the user record using the following code
User user = userRepository.findById(userId).orElse(null);
Now when I try to fetch the UserType object from my user object, I don't get any values, except the ID. For example, when I run the following command, it gives me a null value
user.getUserType().getName()
However, when I run the following code, I get the expected not null value.
user.getLocation().getName()
Both userType and location are lazy-loaded, however, the values of only location are available in the code.
I have checked the values in DB tables. They are not null for both user type and location.
I have checked the values in the debugger as well. The Location object seems to be a proxy, however, the UserType not a hibernate proxy object. I am not sure if this is an issue.
I am new in Hibernate association mapping. When I tried to implement a small mapping logic all the previous foreign key is automatically update to Null
File Employee.java
#Entity
public class Employee extends CommonFields implements Serializable {
private static final long serialVersionUID = -723583058586873479L;
#Id
#Column(name = "id", insertable = false, updatable = false)
#GeneratedValue
private Long id;
private String employeeName;
#OneToMany(cascade = {CascadeType.ALL}, fetch = FetchType.LAZY)
#JoinColumn(name = "emp_id", referencedColumnName = "id")
private List<Education> education;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getEmployeeName() {
return employeeName;
}
public void setEmployeeName(String employeeName) {
this.employeeName = employeeName;
}
public List<Education> getEducation() {
return education;
}
public void setEducation(List<Education> education) {
this.education = education;
}
}
File Education.java
#Entity
public class Education extends CommonFields implements Serializable {
public Education() {
}
private static final long serialVersionUID = -723583058586873479L;
#Id
#Column(name = "id", insertable = false, updatable = false)
#GeneratedValue
private Long id;
private String course;
#ManyToOne
#JoinColumn(name = "emp_id")
private Employee employee;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCourse() {
return course;
}
public void setCourse(String course) {
this.course = course;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
}
Please anyone suggest a solution. I really stuck on this part last two days.
I would like to sovle this problec, cause it's looks interesting, to get grouped objects directly from box
I have db sheme about this:
Teacher
-id
-name
Group
-id
-name
Subject
-id
-name
Several teachers can teaches one subject
Subject_teachers
-subject_id
-teacher_id
Group_subjects
-group_id
-subject_id
I would to get grouped lessons by groups for teacher
class Teacher{
#Id id ,
...
#ManyToMany
???
Map<Group,Subject) subjects;
};
#Entity
#Table(name = "Teacher")
public class Teacher {
private Integer teacherId;
private String name;
private Set<SubjectTeachers> subjectTeachers = new HashSet<SubjectTeachers>(0);
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "teacher_id", unique = true, nullable = false)
public Integer getTeacherId() {
return teacherId;
}
public void setTeacherId(Integer teacherId) {
this.teacherId = teacherId;
}
#Column(name = "name", nullable = false)
public String getName() {
return name;
}
public void setTitle(String name) {
this.name = name;
}
#OneToMany(mappedBy = "subjectTeachers", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
public Set<SubjectTeachers> getSubjectTeachers() {
return subjectTeachers;
}
public void setProjectTags(Set<SubjectTeachers> subjectTeachers) {
this.subjectTeachers = subjectTeachers;
}
}
#Entity
#Table(name = "Subject")
public class Subject{
private Integer subjectId;
private String name;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "subject_id", unique = true, nullable = false)
public Integer getSubjectId() {
return subjectId;
}
public void setSubjectId(Integer subjectId) {
this.subjectId = subjectId;
}
#Column(name = "name", nullable = false)
public String getName() {
return name;
}
public void setTitle(String name) {
this.name = name;
}
}
#Entity
#Table(name ="subject_teachers")
public SubjectTeachers{
private Integer subjectTeachersID;
private Teacher teacher;
private Subject subject;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "subjectTeachersID", unique = true, nullable = false)
public Integer getSubjectTeachersId() {
return subjectTeachersID;
}
public void setSubjectTeachersId(Integer subjectTeachersID) {
this.subjectTeachersID = subjectTeachersID;
}
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "teacher_id", nullable = false)
public Teacher getTeacher() {
return teacher;
}
public void setTeacher(Teacher teacher) {
this.teacher = teacher;
}
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "subject_id", nullable = false)
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
}
#Entity
#Table(name = "group_subjects")
public class GroupSubjects{
private Integer groupId;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "group_id", unique = true, nullable = false)
public Integer getGroupId() {
return groupId;
}
public void setGroupId(Integer groupId) {
this.groupId = groupId;
}
private Subject subject;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "subject_id", nullable = false)
public Subject getSubject() {
return subject;
}
public void setSubject(Subject subject) {
this.subject = subject;
}
}
Hope its help to you..
I'm new to JPA and getting this error when trying to set UserContact Entity.
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.Set, at table: USER_ID, for columns: [org.hibernate.mapping.Column(userContact)]
I have 2 Entity Classes and one #Embeddable class for composite key. There seems to be many solutions to this problem so I've mixed and matched attributes along getters/setters and fields. I've tried #JsonBackReference and #JsonManagedReference, #ElementCollection and other annotations. Using #Access(AccessType.PROPERTY) did start the server correctly but gave me this error when trying to perform db operation.
org.codehaus.jackson.map.JsonMappingException: failed to lazily initialize a collection of role:
Any help would be appreciated. Here are my Entities.
User
#Entity
#Table(name = "USER_RECORD")
public class User {
private UserRecordId id;
private String name;
private String address;
#Column
#ElementCollection(targetClass=UserContact.class)
private Set<UserContact> userContact = new HashSet<UserContact>(0);
#EmbeddedId
#AttributeOverrides({
#AttributeOverride(name = "userId", column = #Column(name = "USER_ID", nullable = false)),
#AttributeOverride(name = "userId2", column = #Column(name = "USER_ID2", nullable = false)) })
public UserRecordId getId() {
return this.id;
}
public void setId(UserRecordId id) {
this.id = id;
}
#OneToMany(fetch = FetchType.EAGER, mappedBy = "user")
public Set<UserContact> getUserContact() {
return this.userContact;
}
public void setUserContact(Set<UserContact> userContact) {
this.userContact = userContact;
}
#Column(name = "USER_NAME", nullable = false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#Column(name = "USER_ADDRESS", nullable = false)
public String getAddress() {
return this.address;
}
public void setAddress(String address) {
this.address = address;
}
UserContact
#Entity
#Table(name = "USER_CONTACT")
public class UserContact {
private String userContactId;
private String name;
private String country;
private User user;
#Id
#Column(name = "USER_CONTACT_ID", unique = true, nullable = false)
public String getUserContactId() {
return this.userContactId;
}
public void setUserContactId(String userContactId) {
this.userContactId = userContactId;
}
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID"),
#JoinColumn(name = "USER_ID2", referencedColumnName = "USER_ID2") })
public User getUser() {
return this.user;
}
public void setUser(User user) {
this.user = user;
}
#Column(name = "CONTACT_NAME", nullable = false)
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
#Column(name = "CONTACT_COUNTRY", nullable = false)
public String getCountry() {
return this.country;
}
public void setCountry(String country) {
this.country = country;
}
UserRecordId/Embeddable
#Embeddable
public class UserRecordId
private String userId;
private String userId2;
#Column(name = "USER_ID", nullable = false)
public String getUserId() {
return this.userId;
}
.../////getUserid2
......
.....
override equals & hash code
You seem to have annotated a field AND a getter (userContact). You should use either FIELD or PROPERTY access but not both (particularly for the same field!).
Also you have annotated it once as ElementCollection and once as OneToMany. Can't be both, and certainly can't be ElementCollection when the element is an Entity.
I try to gather statistics of visitors for two services. It consists of daily visitors statistics and overall record. Each service can be accessed by different names. For example, user, admin, support etc. Each will have its own record as own statistics.
Here is my DB structure:
service_one: id, name
service_two: id, name
daily_stats: id, date, service_one_id, service_one_visitors,
service_two_id, service_two_visitors, overall_visitors
record_stats: id, service_one_id, service_one_record,
service_one_record_date, service_two_id, service_two_record,
service_two_record_date
Here are the relations between tables:
service_one --- (one to many) ---> daily_stats(service_one_id)
service_one --- (one to many) ---> record_stats(service_one_id)
service_two --- (one to many) ---> daily_stats(service_two_id)
service_two --- (one to many) ---> record_stats(service_two_id)
Mapping for service_one (the same is for service_two). Also setters were omitted in order to shorten the example:
#Entity
#Table(name = "service_one")
public class ServiceOne implements Serializable {
private int id;
private String name;
private Set<RecordStats> recordStats = new HashSet<RecordStats>(0);
private Set<DailyStats> dailyStats = new HashSet<DailyStats>(0);
public ServiceOne() {}
public ServiceOne(int id, String name) {
this.id = id;
this.name = name;
}
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", nullable = false, unique = true)
public int getId() {
return id;
}
#Column(name = "name")
public String getName() {
return name;
}
#OneToMany(fetch = LAZY, mappedBy = "service_one_id")
public Set<RecordStats> getRecordStats() {
return recordStats;
}
#OneToMany(fetch = LAZY, mappedBy = "service_one_id")
public Set<DailyStats> getDailyStats() {
return dailyStats;
}
}
daily_stats mapping:
#Entity
#Table(name = "daily_stats", uniqueConstraints = {
#UniqueConstraint(columnNames = "date")
})
public class DailyStats implements Serializable{
private int id;
private Date date;
private ServiceOne service_one_id;
private int service_one_visitors;
private ServiceTwo service_two_id;
private int service_two_visitors;
private int overall_visitors;
public DailyStats() {}
public DailyStats(DailyStats rec) {
this.id = rec.getId();
//...
}
#Id
#GeneratedValue
#Column(name = "id", nullable = false)
public int getId() {
return id;
}
#Temporal(DATE)
#Column(name = "date")
public Date getDate() {
return date;
}
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "id", nullable = false)
public ServiceOne getService_one_id() {
return service_one_id;
}
#Column(name = "service_one_visitors")
public int getService_one_visitors() {
return service_one_visitors;
}
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "id", nullable = false)
public ServiceTwo getService_two_id() {
return service_two_id;
}
#Column(name = "service_two_visitors")
public int getService_two_visitors() {
return service_two_visitors;
}
#Column(name = "overall_visitors")
public int getOverall_visitors() {
return overall_visitors;
}
}
record_stats mapping:
#Entity
#Table(name = "record_stats", uniqueConstraints = {
#UniqueConstraint(columnNames = "service_one_record_date"),
#UniqueConstraint(columnNames = "service_two_record_date")
})
public class RecordStats implements Serializable {
private int id;
private ServiceOne service_one_id;
private int service_one_record;
private Date service_one_rec_date;
private ServiceTwo service_two_id;
private int service_two_record;
private Date service_two_rec_date;
public RecordStats() {}
public RecordStats(RecordStats rec) {
this.id = rec.getId();
//...
}
#Id
#GeneratedValue
#Column(name = "id", nullable = false)
public int getId() {
return id;
}
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "id", nullable = false)
public ServiceOne getService_one_id() {
return service_one_id;
}
#Column(name = "service_one_record")
public int getService_one_record() {
return service_one_record;
}
#Column(name = "service_one_record_date")
#Temporal(DATE)
public Date getService_one_rec_date() {
return service_one_rec_date;
}
#ManyToOne(fetch = LAZY)
#JoinColumn(name = "id", nullable = false)
public ServiceTwo getService_two_id() {
return service_two_id;
}
#Column(name = "service_two_record")
public int getService_two_record() {
return service_two_record;
}
#Column(name = "service_two_record_date")
#Temporal(DATE)
public Date getService_two_rec_date() {
return service_two_rec_date;
}
}
Trying to create new entry throws exception:
public static void main(String[] args) {
ServiceOne serviceOne = new ServiceOne();
serviceOne.setName("test");
SessionFactory sessionFactory = new Configuration().configure().buildSessionFactory();
Session session = sessionFactory.openSession();
session.beginTransaction();
session.save(serviceOne);
session.getTransaction().commit();
//get records
session = sessionFactory.openSession();
session.beginTransaction();
List result = session.createQuery("from service_one").list();
for (ServiceOne o : (List<ServiceOne>)result) {
System.out.println(o.getName());
}
session.getTransaction().commit();
session.close();
}
org.hibernate.MappingException: Repeated column in mapping for entity:
VisitorsCounter.model.entity.DailyStats column: id (should be
mapped with insert="false" update="false")
What is wrong with my mapping?
It seems to me that
#JoinColumn(name = "id", nullable = false)
public ServiceOne getService_one_id() {
return service_one_id;
}
in DailyStats is wrong; you should have name = "service_one_id".
You have the same problem in getService_two_id() and in methods of same names in RecordStats.
May I also ask why don't you name the references in the classes fields serviceOne and serviceTwo instead of service_one_id and service_two_id.