I have three classes, Site, GoupIP and IP
A Site has one or many GrouIPs.
A GroupIP has one or many IPs.
Here is the code:
Site
#Entity
#Table(name = "site")
public class Site implements Serializable {
private Set<GroupIp> groups;
#OneToMany(mappedBy = "site", fetch = FetchType.EAGER, cascade =CascadeType.ALL)
public Set<GroupIp> getGroups() {
return groups;
}
public void setGroups(Set<GroupIp> groups) {
this.groups = groups;
}
}
GroupIP
#Entity
#Table(name = "groupip")
public class GroupIp implements Serializable {
private Set<Ip> ips;
private Site site;
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name = "site_id")
public Site getSite() {
return site;
}
#OneToMany(mappedBy = "groupip", fetch = FetchType.EAGER, cascade =CascadeType.ALL)
public Set<Ip> getIps() {
return ips;
}
public void setIps(Set<Ip> ips) {
this.ips= ips;
}
}
IP
#Entity
#Table(name = "ip")
public class Ip implements Serializable {
private GroupIp groupIp;
#ManyToOne(targetEntity = GroupIp.class,cascade = CascadeType.MERGE)
#JoinColumn(name = "groupip_id", nullable=false)
public GroupIp getGroupIp() {
return groupIp;
}
public void setGroupIp(GroupIp groupIp) {
this.groupIp = groupIp;
}
}
On GroupIp class, I m getting:
In attribute 'ips', the "mapped by" value 'groupip' cannot be resolved to an attribute on the target entity.
Whats wrong on my code ??
The mappedBy name that you have to put in the relationship is the name of the class attribute, not the table name.
So put #OneToMany(mappedBy = "groupIp",... (note the uppercase) instead of #OneToMany(mappedBy = "groupip",...
Related
I have 2 tables question and question option. Question has a composite key. When I query question by an id how do i get question options as well. How can I ensure that I getting the question options as well. As of now I'm only getting the questions. Should I change the mapping or should I add some properties
Question
#Entity
#Getter
#Setter
#JsonIgnoreProperties({"assessment"})
public class Question implements Serializable {
#EmbeddedId
private QuestionAssessmentKey questionAssessmentKey;
public QuestionAssessmentKey getQuestionAssessmentKey() {
return questionAssessmentKey;
}
public void setQuestionAssessmentKey(QuestionAssessmentKey questionAssessmentKey) {
this.questionAssessmentKey = questionAssessmentKey;
}
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "assessmentId", insertable = false, updatable = false)
private Assessment assessment;
private String questionText;
private String questionURL;
private QuestionStatus questionStatus;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="assessmentId", referencedColumnName = "assessmentId"),
#JoinColumn(name="questionNumber", referencedColumnName = "questionNumber")
})
private List<QuestionOption> questionOptions;
public List<QuestionOption> getQuestionOptions() {
return questionOptions;
}
public void setQuestionOptions(List<QuestionOption> questionOptions) {
this.questionOptions = questionOptions;
}
public Assessment getAssessment() {
return assessment;
}
public void setAssessment(Assessment assessment) {
this.assessment = assessment;
}
// private int questionNumber;
private QuestionTypes questionType;
//Getters and setters
}
QuestionOptions
#Entity
public class QuestionOption {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int questionOptionId;
#ManyToOne
#JoinColumns({
#JoinColumn(name="assessmentId", referencedColumnName = "assessmentId"),
#JoinColumn(name="questionNumber", referencedColumnName = "questionNumber")
})
private Question question;
private Character questionOption;
//Getter and setter
}
QuestionAssessmentKey
#Embeddable
public class QuestionAssessmentKey implements Serializable {
private int questionNumber;
private String assessmentId;
}
AFAIK you cannot fetch all the data in a single query since it is a one-to-many relationship between questions and question options. However when you call getQuestionOptions on the fetched Question entity, it should load and return the corresponding set of options.
I have a tricky problem to let hibernate order two list of the same entity. With some code it could be easier to understand what I wanna do.
#MappedSuperclass
public abstract class AbstractParent {
List<CommonObject> commonObjects;
public abstract List<CommonObject> getCommonObjects();
}
#Entity
#Table
public class Child1 extends AbstractParent {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name="child1_id", nullable = false)
#OrderColumn(name = "sort_index")
public List<CommonObject> getCommonObject() {
return this.commonObjects;
}
}
#Entity
#Table
public class Child2 extends AbstractParent {
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name="child2_id", nullable = false)
#OrderColumn(name = "sort_index")
public List<CommonObject> getCommonObject() {
return this.commonObjects;
}
}
But because hibernate handle the mapping of the column "sort_index", it's mapped twice (for Child1 and Child2). So I get this error :
Caused by: org.hibernate.MappingException: Repeated column in mapping
for entity ... column: sort_index (should be mapped with
insert="false" update="false")
I know that I can resolve this problem if I put two different columns for sorting. But I would like to know if someone has a better solution to give me.
Thanks,
I added a test to replicate your issue on GitHub and it works after modifying the mappings to this:
#MappedSuperclass
public abstract class AbstractParent {
public abstract List<CommonObject> getCommonObjects();
}
#Entity(name = "Child1")
public class Child1 extends AbstractParent {
#Id
private Long id;
#OneToMany(targetEntity = CommonObject.class, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(name = "child_1_common_objects", joinColumns = #JoinColumn(name="child1_id", nullable = false))
#OrderColumn(name = "sort_index")
private List<CommonObject> commonObjects = new ArrayList<>();
public List<CommonObject> getCommonObjects() {
return this.commonObjects;
}
}
#Entity(name = "Child2")
public class Child2 extends AbstractParent {
#Id
private Long id;
#OneToMany(targetEntity = CommonObject.class, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(name = "child_2_common_objects", joinColumns = #JoinColumn(name="child2_id", nullable = false))
#OrderColumn(name = "sort_index")
private List<CommonObject> commonObjects = new ArrayList<>();
public List<CommonObject> getCommonObjects() {
return this.commonObjects;
}
}
#Entity(name = "CommonObject")
public class CommonObject {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
I didn't know how to describe my question in the title but I hope it will do.
So here is my situation.
I use hibernate to map my entities to db tables.
I got one entity like this:
#Entity
#Table(name = "EX.EXAMPLE")
public abstract class Entity
{
private CustomEntity customEntity;
public static final String CUSTOM_ENTITY = "customEntity";
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity()
{
return this.customEntity;
}
}
And my CustomEntity
#Entity
#Table(name = "EX.EXAMPLE2")
public class CustomEntity
{
private Entity entity;
public static final String ENTITY = "entity";
#OneToOne
#JoinColumn(name = "ID_ENTITY", nullable = true)
public Entity getEntity()
{
return this.ntity;
}
}
So here is my question: Is it possible to add another CustomEntity relation to Entity? And how do I map it?
Example what I mean:
#Entity
#Table(name = "EX.EXAMPLE")
public abstract class Entity
{
private CustomEntity customEntity;
public static final String CUSTOM_ENTITY = "customEntity";
private CustomEntity customEntity2;
public static final String CUSTOM_ENTITY2 = "customEntity2";
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity()
{
return this.customEntity;
}
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity2()
{
return this.customEntity2;
}
}
I only managed it by changing customEntity to a list in Entity.
Greetings
Yes, that is perfectly normal situation. You just need two fields with different mappedBy`, one for each relation
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY1, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
public CustomEntity getCustomEntity()
{
return this.customEntity;
}
#OneToOne(cascade = CascadeType.ALL, mappedBy = CustomEntity.ENTITY2, fetch = FetchType.LAZY)
#Fetch(FetchMode.SELECT)
#JoinColumn(name = "entity_2_id")
public CustomEntity getCustomEntity2()
{
return this.customEntity2;
}
And two fields in CustomEntity, one for each mapping
#OneToOne
#JoinColumn(name = "ID_ENTITY_1", nullable = true)
public Entity getEntity1()
{
return this.entity1;
}
#OneToOne
#JoinColumn(name = "ID_ENTITY_2", nullable = true)
public Entity getEntity2()
{
return this.entity2;
}
I've done the necessary changes to my models outlined here. However, I don't know what to put on my join table entity.
Note that my join table has a surrogate key , and two extra columns (date and varchar).
What I've got so far is:
User.java
#Entity
#Table (name = "tbl_bo_gui_user")
#DynamicInsert
#DynamicUpdate
public class User implements Serializable {
private String id;
private String ntName;
private String email;
private Set<GroupUser> groupUsers = new HashSet<GroupUser>(0);
// Constructors and some getters setters omitted
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.user", cascade=CascadeType.ALL)
public Set<GroupUser> getGroupUsers() {
return groupUsers;
}
public void setGroupUsers(Set<GroupUser> groupUsers) {
this.groupUsers = groupUsers;
}
}
Group.java
#Entity
#Table (name = "tbl_bo_gui_group")
#DynamicInsert
#DynamicUpdate
public class Group implements Serializable {
private String id;
private String groupName;
private String groupDesc;
private Set<GroupUser> groupUsers = new HashSet<GroupUser>(0);
// Constructors and some getters setters omitted
#OneToMany(fetch = FetchType.LAZY, mappedBy = "pk.group", cascade=CascadeType.ALL)
public Set<GroupUser> getGroupUsers() {
return groupUsers;
}
public void setGroupUsers(Set<GroupUser> groupUsers) {
this.groupUsers = groupUsers;
}
}
The problem is that I don't know what to put on my join table entity. Here it is.
GroupUser.java
#Entity
#Table (name = "tbl_bo_gui_group_user")
#DynamicInsert
#DynamicUpdate
#AssociationOverrides({
#AssociationOverride(name = "pk.user",
joinColumns = #JoinColumn(name = "id")),
#AssociationOverride(name = "pk.group",
joinColumns = #JoinColumn(name = "id")) })
public class GroupUser implements Serializable {
private String id;
private User userId;
private Group groupId;
private Date dateCreated;
private String createdBy;
// constructors and getters and setters for each property
// What now? ? No idea
}
user to group would be a Many-To-Many relation. Now, you are splitting that up into Two One-To-Many Relations. Therefore your Mapping Entity simple needs to complete the Many-To-Many relation, by using Many-To-One:
public class GroupUser implements Serializable {
private String id;
#ManyToOne
private User userId;
#ManyToOne
private Group groupId;
private Date dateCreated;
private String createdBy;
}
See also this example: Mapping many-to-many association table with extra column(s) (The Answer with 38 upvotes)
I'm using Spring with Hibernate as a JPA provider and are trying to get a #OneToMany (a contact having many phonenumbers) to save the foreign key in the phone numbers table. From my form i get a Contact object that have a list of Phone(numbers) in it. The Contact get persisted properly (Hibernate fetches an PK from the specified sequence). The list of Phone(numbers) also gets persisted with a correct PK, but there's no FK to the Contacts table.
public class Contact implements Serializable {
#OneToMany(mappedBy = "contactId", cascade = CascadeType.ALL, fetch=FetchType.EAGER)
private List<Phone> phoneList;
}
public class Phone implements Serializable {
#JoinColumn(name = "contact_id", referencedColumnName = "contact_id")
#ManyToOne
private Contact contactId;
}
#Repository("contactDao")
#Transactional(readOnly = true)
public class ContactDaoImpl implements ContactDao {
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void save(Contact c) {
em.persist(c);
em.flush();
}
}
#Controller
public class ContactController {
#RequestMapping(value = "/contact/new", method = RequestMethod.POST)
public ModelAndView newContact(Contact c) {
ModelAndView mv = new ModelAndView("contactForm");
contactDao.save(c);
mv.addObject("contact", c);
return mv;
}
}
Hopefully I got all of the relevant bits above, otherwise please let me know.
You have to manage the Java relationships yourself. For this kind of thing you need something like:
#Entity
public class Contact {
#Id
private Long id;
#OneToMany(cascade = CascadeType.PERSIST, mappedBy = "contact")
private List<Phone> phoneNumbers;
public void addPhone(PhoneNumber phone) {
if (phone != null) {
if (phoneNumbers == null) {
phoneNumbers = new ArrayList<Phone>();
}
phoneNumbers.add(phone);
phone.setContact(this);
}
}
...
}
#Entity
public class Phone {
#Id
private Long id;
#ManyToOne
private Contact contact;
...
}
In reply to Cletus' answer. I would say that it's important to have the #column annotation on the id fields, as well as all the sequence stuff. An alternative to using the mappedBy parameter of the #OneToMany annotation is to use the #JoinColumn annotation.
As a kinda aside your implementation of addPhone needs looking at. It should probably be something like.
public void addPhone(PhoneNumber phone) {
if (phone == null) {
return;
} else {
if (phoneNumbers == null) {
phoneNumbers = new ArrayList<Phone>();
}
phoneNumbers.add(phone);
phone.setContact(this);
}
}
If the Contact-Phone relationship is unidirectional, you can also replace mappedBy in #OneToMany annotation with #JoinColumn(name = "contact_id").
#Entity
public class Contact {
#Id
private Long id;
#OneToMany(cascade = CascadeType.PERSIST)
#JoinColumn(name = "contact_id")
private List<Phone> phoneNumbers;
// normal getter/setter
...
}
#Entity
public class PhoneNumber {
#Id
private Long id;
...
}
Similar in JPA #OneToMany -> Parent - Child Reference (Foreign Key)
I don't think the addPhone method is necessary, you only have to set the contact in the phone object:
phone.setContact(contact);
If you want your relationship unidirectional i.e. can navigate from Contact to Phone's only, you need to add
#JoinColumn(name = "contact_id", nullable = false)
Under your #OneToMany on your parent entity.
nullable = false IS VITAL if you want hibernate to populate the fk on the child table
Try this sample:
#Entity
public class Contact {
#Id
private Long id;
#JoinColumn(name = "contactId")
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Phone> phones;
}
#Entity
public class Phone {
#Id
private Long id;
private Long contactId;
}
In JPA this helped me
contact.getPhoneList().forEach(pl -> pl.setContact(contact));
contactRepository.save(contact);