How exactly does the Hibernate #OneToMany annotation work? - java

I am pretty new to Hibernate and I am studying it on a tutorial. I have some problems understanding how exactly the OneToMany annotation works.
So I have these 2 entity classes: Student that represents a student and Guide that represents a person that guides the student. So each student is associated with a single guide but a single guide can follow more that one student. I want a guide to know the students associated to him.
So I have:
Student:
#Entity
public class Student {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(name="enrollment_id", nullable=false)
private String enrollmentId;
private String name;
#ManyToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE})
#JoinColumn(name="guide_id")
private Guide guide;
public Student() {}
public Student(String enrollmentId, String name, Guide guide) {
this.enrollmentId = enrollmentId;
this.name = name;
this.guide = guide;
}
public Guide getGuide() {
return guide;
}
public void setGuide(Guide guide) {
this.guide = guide;
}
}
So the #ManyToOne annotation on the guide field:
#ManyToOne(cascade={CascadeType.PERSIST, CascadeType.REMOVE})
#JoinColumn(name="guide_id")
private Guide guide;
means that a single guide is associated a single student but a guide can follow many students. Is it right? What exactly does the specified cascade settings do? I think it means that when I persist a Student object that contains a Guide object as field also this Guide object is also automatically persisted. And the same thing happens when I remove a Student object, the related Guide record is deleted...but I am not absolutely sure about it...
Ok, doing it this way I will have a mono directional relationship between a record in the Student table and a record in the Guide table because in the Student table I will have a foreign key to join the Guide table so the student can know its guide but, doing it this way, the guide can not know the followed student...and this is not smart.
To do it the Guide class is implemented in this way:
#Entity
public class Guide {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
#Column(name="staff_id", nullable=false)
private String staffId;
private String name;
private Integer salary;
#OneToMany(mappedBy="guide", cascade={CascadeType.PERSIST})
private Set<Student> students = new HashSet<Student>();
public Guide() {}
public Guide(String staffId, String name, Integer salary) {
this.staffId = staffId;
this.name = name;
this.salary = salary;
}
public Set<Student> getStudents() {
return students;
}
public void setSalary(Integer salary) {
this.salary = salary;
}
public void addStudent(Student student) {
students.add(student);
student.setGuide(this);
}
}
So, as you can see, this class contains:
#OneToMany(mappedBy="guide", cascade={CascadeType.PERSIST})
private Set<Student> students = new HashSet<Student>();
that it is used to declare the bidirectional relationship.
So it seems to me that this annotation automatically create a guide_id field into the Student table that represent the foreign key that implement the bidirectional relation.
In fact using this mapping the Student table is automatically created in this way in my database:
'id', 'bigint(20)', 'NO', 'PRI', NULL, 'auto_increment'
'enrollment_id', 'varchar(255)', 'NO', '', NULL, ''
'name', 'varchar(255)', 'YES', '', NULL, ''
'guide_id', 'bigint(20)', 'YES', 'MUL', NULL, ''
So in the Student entity class I have not defined the guide_id field but I have it in the Student table on the database. So I think that the creation of this field in the table depends on the previous #OneToMany annotation defined in the Guide entity class. Is that correct or am I missing something?

Yes, you can define a #OneToMany entity without a bidirectional association, and the added column is on the Many entity side in the database (even though the entity doesn't know it is linked to the One-side entity).
You can also use a join table for this, but it's not necessary.

Related

hibernet one to many on web

I am completely new in Hibernate ORM world, recently reading mapping specially one-to-many relation. But i am facing some problem to understand.
I am using this link to understand hibernate relation.
https://dzone.com/tutorials/java/hibernate/hibernate-example/hibernate-mapping-one-to-many-using-annotations-1.html
please have a look the code.
public class Student {
private long studentId;
private String studentName;
private Set<Phone> studentPhoneNumbers
//setter getter
}
public class Phone {
private long phoneId;
private String phoneType;
private String phoneNumber;
//setter getter
}
i understand it.
confusion is here.
Set<Phone> phoneNumbers = new HashSet<Phone>();
phoneNumbers.add(new Phone("house","32354353"));
phoneNumbers.add(new Phone("mobile","9889343423"));
Student student = new Student("Eswar", phoneNumbers);
session.save(student);
Does this only one code session.save(student), store value in both table ? if no then why we didn't write code to save phoneNumbers like session.save(phoneNumbers).
Once this above code is executed then the value will be stored in both table or object(STUDENT and PHONE) right ?. I think each time we execute this code, this code insert value in both table. i don't like it. i want value should be store on only second table(PHONE).
So how will i store value in only PHONE object ?
or how will we implement this relation where user will select STUDENT through combobox and fill value in PHONE object through simple textboxes. then finally save it.like product/category.
3.please help or suggest some good tutorial where hibernate is implemented (specially one-to-many and many to many relation) in web application. i have seen many tutorial but all these are implemented through main method.
sorry for the poor English
thanks.
Yes it should save both student and phone number in their own table. If you annotate the right way. Student should be something like this:
#Entity
#Table(name = "student")
public class Student {
#id
#GeneratedValue
private long studentId;
#column(name = "studentName")
private String studentName;
#OneToMany
private Set<Phone> studentPhoneNumbers
//setter getter
}
public class Phone {
private long phoneId;
private String phoneType;
private String phoneNumber;
//setter getter
}
Then there is also some configuration to do. I'd advice you to read this tutorial: https://www.tutorialspoint.com/hibernate/index.htm
In the case of your code it doesn't much. But if you configure hibernate the right way you can decide in which table/column a value should be saved
Hibernate tutorial - Tutorials point

JPA Hibernate one to many

Really confused by how one to many works in JPA, all the documents that I read, uses both one to many and many to one in their example, and I don't know if they are necessary or not, and it doesn't work when I tried it.
My question is, suppose I have two tables, and I want to populate the College object using findCollegeData() method, so that all the student in this college are in a list when I initialize the object.
Below is my approach, I am able to store all the students in the college list using storeCollegeData() method, but I am not able to retrieve the college object fully, the student list is always empty, even though the data is in the database, and it works if I try to search for student using college name directly.
public static EntityManager entityManager = something;
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public College {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private int cId;
private String collegeName;
private int numOfStudent;
#OneToMany(mappedBy="collegeName", cascade=CascadeType.ALL, orphanRemoval=true)
private List<Student> studentList = new ArrayList<>();
}
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public Student {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private int sId;
private String name;
private String collegeName;
private String city;
}
// college.getStudentList is always empty and I don't know why
public findCollegeData(String collegeName) {
College college = entityManager.find(College.class, collegeName);
}
// Student data in the studentList are inserted into student table
public storeCollegeData(College college) {
entityManager.persist(college);
}
// This method works
public findStudent(String collegeName) {
CriteriaBuilder cb = provider.get().getCriteriaBuilder();
CriteriaQuery<Student> query = cb.createQuery(Student.class);
Root<Student> student = query.from(Student.class);
query.where(
cb.and(
cb.equal(student.get("collegeName"), collegeName)
)
);
JobStatisticDB Student = provider.get().createQuery(query).getSingleResult();
}
Am i missing something??? Is join more appropriate than map here??? I dont know wat to do man
EDITED:
Got it to work by changing both of the collegeName as the primary key of table by adding #Id annotation, however though, how can I add an sId and cId to the table, so they can have duplicate college name???? Right now, I can't have duplicate college with the same name, and student that that goes to the same college!
Final Edited:
Changed database design to use foreign key see solution below
The accepted answer is incorrect: you define relationships between entities. The mappings should be as below for a bi-directional #OneToMany
College:
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public College {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private int cId;
private String collegeName;
private int numOfStudent;
#OneToMany(mappedBy="college", cascade=CascadeType.ALL, orphanRemoval=true)
private List<Student> studentList = new ArrayList<>();
}
Student:
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public Student {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private int sId;
private String name;
private String city;
//student table has a FK column college_id
#ManyToOne
#JoinColumn(name = "college_id")
private College college;
}
EntityManager find() takes the PK as an argument:
public findCollege(int collegeId) {
College college = entityManager.find(College.class, collegeId);
college.getStudents(); //will be populated
}
public findStudent(int studentId) {
Student student = entityManager.find(Student.class, studentId);
student.getCollege(); //will be populated
student.getCollege().getStudents(); //will be populated
}
If you want to find a college by name create a JPQL or Criteria query:
The field you reference in mappedBy must contain a value that equates to College's id field. Change it to collegeName instead of city, and it should work.

ORMLite android class field is id of other class

How to implement relationship between 2 classes using ORMLite? I know about foreign field, but i can't add non-string Department field to product class
Class Product
#DatabaseTable(tableName = "PRODUCTS")
public class Product {
#DatabaseField(id = true)
private String id;
#DatabaseField()
private String name;
#DatabaseField() //This field is id of Department class
private String department;
Department class
#DatabaseTable(tableName = "DEPARTMENTS")
public class Department {
#DatabaseField(id = true)
private String id;
#DatabaseField()
private String name;
How to implement relationship between 2 classes using ORMLite? I know about foreign field, but i can't add non-string Department field to product class
RTFM please. Did you look at any of the documentation or examples? Here's the docs on foreign objects. You can see plainly that you put a Department into Product not a String. The docs show the Order object having an Account field.
#DatabaseField
private Department department;
Behind the scenes, what ORMLite does is actually store just the id from the department into your Product. If you actually look at the schema then you will see a string there but you don't do that yourself.
Then when you retrieve your Product, it will have a Department field but only the id will be filled out.
There is also a foreign object example in the code that may also help.

Join Table and OneToMany relation in Hibernate

I created two entities for the tables HOBBY and STUDENT, but I have difficulties to retrieve the information I need from the Join Table.
This is the schema:
STUDENT STUDENT_HOBBY HOBBY
-------- ----------- --------
id ------------------ student_id
name hobby_id ---------- id
lastname hobby_name
--------- ---------- --------
where
Student to student_hobby = one to many
Hobby to student_hobby = one to many
(that means a student may have many hobbies and a hobby can belong to more than one student)
This is the Student entity class:
#Entity
#Table(name="STUDENT")
public class Student {
#OneToMany
#JoinTable(name="student_hobbies", joinColumns=#JoinColumn(name="student_id"),
inverseJoinColumns=#JoinColumn(name="hobby_id"))
private Collection<Hobby> hobbies;
#Id
#Column(name="ID")
#GeneratedValue
private Integer id;
#Column(name="NAME")
private String name;
#Column(name="LASTNAME")
private String lastName;
// Getters and setters here
}
This is the Hobby entity class:
#Entity
#Table(name="HOBBY")
public class Hobby {
#OneToMany
#JoinTable(name="student_hobbies", joinColumns=#JoinColumn(name="hobby_id"),
inverseJoinColumns=#JoinColumn(name="student_id"))
private Collection<Student> students;
#Id
#Column(name="ID")
#GeneratedValue
private Integer id;
#Column(name="HOBBY_NAME")
private String hobby_name;
// Getters and setters here
}
Now I would like to implement the following DAO, but I don't know exactly how to do:
public interface MyDAO {
public void addHobbyForStudent(int student_id, int hobby_id);
public void RemoveHobbyForStudent(int student_id, int hobby_id);
}
Should I create an other entity class for the Join Table? Could someone give me some indication on the way to follow?
You can create an entity class for the join table, but you don't need to and you probably shouldn't. This will just create more code to maintain. You should create an entity for the join table if there is data in the join table you need to query. EG: if there was a start_time or something in the join table and you wanted to be able to see the start_time.
You should be using the #ManyToMany annotation when you're avoiding a join table entity. The documentation I linked to gives some good examples of how to do this:
Example 1:
// In Customer class:
#ManyToMany
#JoinTable(name="CUST_PHONES")
public Set<PhoneNumber> getPhones() { return phones; }
// In PhoneNumber class:
#ManyToMany(mappedBy="phones")
public Set<Customer> getCustomers() { return customers; }
Example 2:
// In Customer class:
#ManyToMany(targetEntity=com.example.PhoneNumber.class)
public Set getPhones() { return phones; }
// In PhoneNumber class:
#ManyToMany(targetEntity=com.example.Customer.class, mappedBy="phones")
public Set getCustomers() { return customers; }
Example 3:
// In Customer class:
#ManyToMany
#JoinTable(name="CUST_PHONE",
joinColumns=
#JoinColumn(name="CUST_ID", referencedColumnName="ID"),
inverseJoinColumns=
#JoinColumn(name="PHONE_ID", referencedColumnName="ID")
)
public Set<PhoneNumber> getPhones() { return phones; }
// In PhoneNumberClass:
#ManyToMany(mappedBy="phones")
public Set<Customer> getCustomers() { return customers; }

How to store some of the entity's values in another table using hibernate?

is there a simple way to persist some of the fields in another class and table using hibernate.
For example, I have a Person class with name, surname, email, address1, address2, city, country fields. I want my classes to be:
public class Person
{
private String name;
private String surname;
private String email;
private Address address;
// ..
}
public class Address
{
private Person person; // to whom this belongs
private String address1;
private String address2;
private String city;
private String country;
// ..
}
and I want to store Address in another table. What is the best way to achieve this?
Edit: I am using annotations. It does not have to be the way I described, I am looking for best practices.
Edit 2: What will be the Id of Address?
PS. If there is a way to make Address immutable (to use as a value object) that is even better, or maybe not because I thought everything from wrong perspective :)
map Address as an entity and add a primary key (an auto-generated id)
map the relation between Person and Address as one-to-one (#OneToOne on each field)
With Hibernate 3.5 it is possible to define foreign generators (aka. JPA mapping), details are here.
It is pretty straight forward Person should implement Serializable then #Id annotation is added to person.
#Entity
#AccessType(value = "field")
#Table(name = "addresses")
public class Address
{
#Id
#OneToOne
#JoinColumn(name = "person_id")
private Person person;
// ...
}
There is an alternative but I really like the first one:
#Entity
#AccessType(value = "field")
#Table(name = "addresses")
public class Address
{
#Id
private int personId;
#MapsId
#OneToOne
#JoinColumn(name = "person_id")
private Person person;
// ...
}

Categories

Resources