I have below 1-m relationship on entities which Mentor to Students. The mentor has composite primary key which i use as foreign key in student
#Entity
public class Mentor implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private MentorPK id;
private String email;
#OneToMany(mappedBy="mentor")
private Set<Student> students;
public MentorPK getId() {
return id;
}
//getters and setters
}
#Embeddable
public class MentorPK implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private String add;
//getters and setters
//override equals and hashcode
}
#Entity
public class Student implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
#ManyToOne
#MapsId("id")
#JoinColumns({
#JoinColumn(name="name_fk", referencedColumnName="name"),
#JoinColumn(name="address_fk", referencedColumnName="address")
})
private Mentor mentor;
//Getters and setters
}
I then persist the above as below but only the mentor is persisted where student table is empty.
How can I persist the mentor with students?
Set<Student> students = new HashSet<Student>();
Student s1 = new Student();
s1.setName("Student 1");
Student s2 = new Student();
s2.setName("Student 2");
students.add(s1);
students.add(s2);
MentorPK mpk = new MentorPK();
mpk.setAddress("C");
mpk.setName("D");
Mentor m = new Mentor();
m.setId(mpk);
m.setEmail("emaill");
m.setStudents(students);
studentManager.saveMentor(m);
Try changing annotation of students field to
#OneToMany(mappedBy="mentor", cascade = CascadeType.PERSIST)
When you use a composite-key, mapped as an Embeddable you need to use #EmbeddedId:
#Entity
public class Mentor {
#EmbeddedId
private MentorPK id;
private String email;
#OneToMany(mappedBy="mentor")
private Set<Student> students;
public MentorPK getId() {
return id;
}
//getters and setters
}
and the Student becomes:
#Entity
public class Student {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private int id;
private String name;
#ManyToOne
#JoinColumns({
#JoinColumn(name="name_fk", referencedColumnName="name"),
#JoinColumn(name="address_fk", referencedColumnName="address")
})
private Mentor mentor;
//Getters and setters
}
The #MapsId is used when both the #Id and the #ManyToOne share the same database columns, which is not your case, since you have a numeric identifier and a composite foreign-key.
You may need to create the reference from Student to Mentor in each Student.
So in your code, after you create m you need to:
s1.setMentor(m);
s2.setMentor(m);
Otherwise Hibernate may not know what to populate columns name_fk and address_fk with.
How about changing:
#OneToMany(mappedBy="mentor")
private Set<Student> students;
to:
#OneToMany(mappedBy="mentor")
private Set<Student> students = new HashSet();
or
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="student_id")
#org.hibernate.annotations.IndexColumn(name="idx")
private Set<Student> students = new HashSet();
also try not to skip
= new HashSet();
part
As I understand your request, you would like to have the Mentor be the owner of the relationship. You do not obtain this with the line #OneToMany(mappedBy="mentor"). This actually puts the Student as the owner of the relation.
I have tested this domain model and did a few modifications to the annotations in order to have the test code work as you expect.
Student
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private int id;
private String name;
#ManyToOne
#JoinColumns({
#JoinColumn(name="name_fk", referencedColumnName="name", insertable = false, updatable = false),
#JoinColumn(name="address_fk", referencedColumnName="address", insertable = false, updatable = false )
})
private Mentor mentor;
//setters and getters
}
Mentor
public class Mentor implements Serializable {
private static final long serialVersionUID = 1L;
#Id
private MentorPK id;
private String email;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumns({
#JoinColumn(name="name_fk", referencedColumnName="name"),
#JoinColumn(name="address_fk", referencedColumnName="address")
})
private Set<Student> students;
//setters and getters
}
It even works without doing:s1.setMentor(m);s2.setMentor(m);. I did not expect it but it seems that hibernate is dealing with this.
A related article is here.
Attention: drop the database tables after you change the annotations in order to allow hibernate to recreate the tables.
Related
Given the following entity classes, examine the possible way to
Create bidirectional One (Student) to One (Account) relationship
Create unidirectional One (Student) to Many (Account) relationship
Here down is Student entity:
#Entity
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
private int password;
public Student() {
}
}
Here down is Account entity:
#Entity
public class Account implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private double balance;
public Account() {
}
}
I am currently learning hibernate and created a simple database that is simulating a library:
I got 3 classes: Book, Customer, Order:
#Entity
public class Book
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private int ISBN;
private String name;
private String autorName;
private double price;
//Getters setters etc...
}
#Entity
public class Customer
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String name;
private String lastName;
private String city;
//Getters setters etc...
}
#Entity
#Table(name = "TB_order")
public class Order
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private Date date;
private String status;
#OneToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
#JoinColumn
private List<Book> books;
#ManyToOne(cascade = CascadeType.ALL)
private Customer customer;
//Getters setters etc...
}
When I try to change some order by saveOrUpdate() (eg. change the book that customer ordered) I create new instance of Order and I pass it to saveOrUpdate() method:
Order o = new Order(Date.valueOf("2019-06-01"),"old",bookslist.subList(2,3), customerslist.get(0));
o.setId(1);
session.saveOrUpdate(o);
It does not only change values in table order, but it also create new entities in table book and table Customers. I guess it is like this because i put the list of books and customer instace created in the main method.
So what instance of Order object shall be passed into the saveOrUpdate() method to only change the values in table Order to already existing values from database?
Student.java
#Entity
#Table(name = "Student")
#Data
#NoArgsConstructor
public class Student implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "studentId", cascade = CascadeType.ALL)
private List<Subject> subjectList = new ArrayList<>();
public void addSubject(Subject subject) {
subjectList.add(subject);
subject.setStudentId(this);
}
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
}
Subject.java
#Entity
#Table(name = "Subject")
#Data
#NoArgsConstructor
public class Subject implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="studentId", nullable = false)
private Student studentId;
#Column(name = "subjectName")
private String subjectName;
#Column(name = "subjectCode")
private int subjectCode;
}
SubjectRepository.java
#Repository
public interface SubjectRepository extends JpaRepository<Subject, Long> {
}
As shown in the code structure above, I have 2 entities (Student, Subject) and a repository class (SubjectRepository). When i try to save into the Subject table, somehow the student name "Thomas" from the Student table gets updated as well in the database. I would like to only insert into Subject table without having any values from the Student table getting updated. Need some help on this. Thanks!
public static void main(String[] args) {
#Autowired protected SubjectRepository subjectRepository;
Student student = new Student();
student.setFirstName("Thomas");
Subject subject = new Subject();
subject.setSubjectName("Chemistry");
subject.setSubjectCode(12345);
student.addSubject(subject)
subjectRepository.save(subject)
}
I would like to only insert into Subject table without having any values from the Student table getting updated
You can achieve this with following code :
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="studentId", nullable = false, updatable = false)
private Student studentId;
When using Spring JPA I would suggest using the JpaRepository API. You just need to pass in your entity, and it should save as desired.
Ex:
subjectRepository.save(subject);
You have try this
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REFRESH)
#JoinColumn(name="studentId", nullable = false, updatable = false)
private Student studentId;
#MaxExplode You have to use cascade = CascadeType.REFRESH then other details will not update. but if you are try to set updateStudent.setfirstNamr(student.getFirstName()); and then save parent object then i will update. otherwise it will not update.
I have the next relationship:
Currently, I have the next code:
#Embedded
public class StockPK implements Serializable {
private int storeId;
private int productId
}
#Entity
public class Stock implements Serializable {
#EmbeddedId
private StockPK id;
private int cantidad;
#ManyToOne
private Store store;
#ManyToOne
private Product product;
}
But the DDL generated (I'm using OpenJPA in TomEE) adds two aditional fields.
CREATE TABLE STOCK (
productId INTEGER NOT NULL,
storeId INTEGER NOT NULL,
quantity INTEGER NOT NULL,
PRODUCT_ID INTEGER ,
STORE_ID INTEGER ,
PRIMARY KEY (productId, storeId)
)
How should specify this relationship?
Thanks JBNizet :) — The solution was as follows:
#Embeddable
public class StockPK implements Serializable {
#Column(name = "store_id")
private int storeId;
#Column(name = "product_id")
private String productId;
// Getters, setters, hashCode, equals
}
#Entity
#Table(name = "stock")
public class Stock implements Serializable {
#EmbeddedId
private StockPK id;
#MapsId("storeId")
#ManyToOne
private Store store;
#MapsId("productId")
#ManyToOne
private Product product;
#Column(nullable = false)
private int quantity;
// Getters, setters
}
#Entity
#Table(name = "store")
public class Store implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
// Other fields, getters, setters ...
}
#Entity
#Table(name = "product")
public class Product implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
// Other fields, getters, setters ...
}
I have simple entity like :
#Entity
public class University implements Serializable {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column
String name;
#Column
String address;
#Column
Student student;
}
And entity Student :
#Entity
public class Student implements Serializable {
/** The Constant serialVersionUID. */
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column
String Firstname;
#Column
String LastName;
#Column
StudentStatus status;
}
Can i create #JoinFormula or something like that to create query , which will take students from Student entity to University entity ?
I tried something like that:
#JoinFormula("SELECT l FROM Student l where l.id = 1")
Student student;
but it doesn't work.
Can i create query to select some students ?
UPDATED :
#JoinFormula never called.
I probably miss the poit, but if you just want to reference another entity you can use the #ManyToOne Annotation.
Example:
#ManyToOne
public MediaLibrary getParentLibrary() {
return parentLibrary;
}
Similary, if you have a list you can use the #ManyToMany Annotation and then specify the correct join Table:
Example:
#ManyToMany(fetch = FetchType.EAGER, cascade = { CascadeType.MERGE, CascadeType.REFRESH, })
#JoinTable(name = "MediaDataTag", joinColumns = { #JoinColumn(name = "MediaData_Id") }, inverseJoinColumns = { #JoinColumn(name = "Tag_Id") })
public Set<Tag> getTags() {
return this.tags;
}
If you provide a clear use case, it would be easier to provide a specific solution.
EclipseLink has a great set of JPA documentation and examples (even if you're using a different implementation)
Try using this instead (I assume a university can have more than one student)
#OneToMany(mappedBy="university")
private Set<Student> student;
And in Student you'll have the reverse mapping
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name="UniversityID")
private University university;