Java - A JPQL Query to delete a oneToMany relationship - java

If I have this 3 entities :
#Entity
public class Student {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
protected Long id;
private String name;
}
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public class Course {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
protected Long id;
#OneToMany
private List<Student> students;
private String name;
}
#Entity
#Inheritance(strategy=InheritanceType.JOINED)
public class Group {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
protected Long id;
#OneToMany
private List<Student> students;
private String name;
}
How can I delete students with a JPQL query ?
I try
DELETE FROM Student s
WHERE s.name = "John Doe"
But I have
Cannot delete or update a parent row: a foreign key constraint fails (database, CONSTRAINT FK_course_student_students_ID FOREIGN KEY (students_ID) REFERENCES student (ID))
I need to do this in pure JPQL for performance, I can't do an entity.remove, because I have 10000 John doe and I need to delete them in a second.
Why JPQL doesn't say : "Hey, let's remove this john doe from this biology course, he doesn't exist" instead of "Hey, the biology course is so important that no student can be remove from this course ! "
What I am missing and what sort of annotation I have to use ?
Thanks !
Edit : Add a #JoinColumn to the OnToMany relationship could work, unless the students are referenced by different tables...

By default unidirectional one-to-many relationship is mapped via join table. If you don't have any special requirements about using join talbe you can use foreign key in Student instead, it can be configured as follows:
#OneToMany
#JoinColumn
private List<Student> students;
It also should solve your problem with constrain violation.

Sometimes you can clear the references to the objects being deleted in a deleted all using an update all query.
You can also configure you constraint on the database to cascade or null on delete to avoid constraint issues.

Related

in Spring jpa using the #ManyToMany relationship, why create a new class with #Embeddable?

According to the Spring JPA documentation, in the Many-To-Many relationship (student - course) we must create a new table (student_course)
class student ---> class student_course <--- class course
According to the documentation, if we want to add a new property to the table (student_course) we must create a new class that will contain the compound keys of the student class and the course class
#Embeddable
class CourseStudentKey implements Serializable {
#Column(name="student_id")
Long studentId;
#Column(name = "course_id")
Long courseId;
}
_ Then to the Student_Course class we assign the id of type CourseStudentKey that contains the compound keys:
#Entity
class StudentCourse {
#EmbeddedId
CourseRatingKey id;
#ManyToOne
#MapsId("studentId")
#JoinColumn(name = "student_id")
Student student;
#ManyToOne
#MapsId("courseId")
#JoinColumn(name = "course_id")
Course course;
}
My question is: What is the difference in creating only the StudentCourse class and doing the #ManyToOne mapping to the Student class and the Course class??... in this way we can also add attributes to the StudentCourse class
_Clase Student
#Entity
class Student {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private idStudent;
#JsonIgnore
#OneToMany(mappedBy = "student")
List<StudentCourse> studentCourses = new ArrayList<>();
_Clase Course
#Entity
class Course{
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private idCourse;
#JsonIgnore
#OneToMany(mappedBy = "course")
List<StudentCourse> studentCourses = new ArrayList<>();
}
_Clase StudentCourse
#Entity
class StudentCourse {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private idStudentCourse;
#ManyToOne
#JoinColumn(name = "student_id")
Student student;
#ManyToOne
#JoinColumn(name = "course_id")
Course course;
}
The only difference in the examples posted by you, is, in case of Embeddable, the student_id course_id would be a composite key, so there would only be one row allowed per student_id course_id combination. Whereas, in the second example, you have used generated primary key, ensuring multiple rows for each student_id course_id combination. This would be particularly useful if the student fails the course for the first time and attempts it again. You can then add parameters like attemped_on, is_completed, etc. to the student_course entity
Your examples show differences in the key, and as Chetan's answer states, this affects the key used in the table. The choices here isn't necessarily in using a separate class/embbeded class, but in using a single generated Identifier vs using a composite primary key for the entity.
In the embedded example you've posted, you have a composite primary key based on foreign key mappings. There are many other ways to map this same setup though, but the common parts will be:
composite PKs need an ID class. It doesn't have to be embedded in your class (see JPA derived IDs) but does need to exist. This is part of the JPA spec and allows em.find operations to deal with a single object.
ID values are immutable. They cannot change without remove/persist operations as per the JPA specification. Many providers don't like you even attempting to modify them in an Entity instance. In your embeddable example, you cannot change the references, while in the generated id example, you can.
It also affects what JPA requires you to use in foreign keys. If you use a composite ID, any references to that entity (*ToOne) that require foreign keys to that table are required to use its defined IDs - all columns that make up that ID. Some providers don't enforce this, but it will affect entity caching; since entities are cached on their IDs, using something else as the target of FKs might mean database hits for entities already in the cache.

Spring Hibernate JPA Bidirectional OneToOne - not updating id on delete

I have 2 entities in a OneToOne relationship:
A User entity :
#Entity
public class User{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToOne
#JoinColumn()
private StudentInfo studentInfo;
}
And a StudentInfo entity:
#Entity
public class StudentInfo {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToOne(cascade = CascadeType.REMOVE)
#JoinColumn()
private User user;
}
When I delete the first StudentInfo instance from the database (with id=1) using studentRepository.deleteById((long) 1) ; , the instance does get deleted, so does the User associated with it, but the problem is that the ids of the other objects in the database don't get updated.
After deleted a student, the users in the database look like this :
And the students remaining (only one):
How can I make so that the ids are automatically updated on delete ?
(2, 3, 4) -> (1, 2, 3).
Apparently this is quite a dumb question because while the ids in a database do have to be unique for each line in a table, they don't have to be consecutive.
As such, when a registry (a line) in a database table is deleted, the other registries's ids don't get updated because there is no need for it. This is true for all sql databases and has nothing to do with Spring or JPA.

How to correctly set up #OneToOne relationship with JPA Hibernate?

I'm quite new to this and I am attempting to set up a simple one-to-one relationship between a Student object and their Grades object. I'm using spring boot with the hsqldb in-memory database. I'll show my code first then explain my issues.
Student.java
#Entity
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
#OneToOne(cascade = CascadeType.ALL)
private Grades grades;
//Getters and setters
}
Grades.java
#Entity
public class Grades {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private int midterm;
private int finalExam;
#OneToOne(cascade = CascadeType.ALL)
private Student student;
//Getters and setters
}
StudentRepository.java
public interface StudentRepository extends JpaRepository<Student, Long> {
}
Is my setup for Student and Grades correct? I just one a simple one-to-one relationship where I store the Grades ID in a Student object as a foreign key.
Whenever I create a Grades object using new Grades() and pass that into a Student object in the constructor, it allows me to assign the same Grades object to multiple students. How is that possible when they're annotated with one-to-one?
I turned on hibernate sql logging to see what is happening when using the database. It seems to store grades_id in a Student object just fine, but it shows student_id in the Grades object as being null. Why is this null?
Thanks for any help.
Is my setup for Student and Grades correct?
It's questionable. Both sides of the relationship are mapped as owners of the relationship. One of them should be mapped as the owner, and other should use mappedBy attribute of the #OneToOne annotation. #OneToOne
Whenever I create a Grades object using new Grades() and pass that
into a Student object in the constructor, it allows me to assign the
same Grades object to multiple students. How is that possible when
they're annotated with one-to-one?
You should create composite primary key or use uniqueness constraint to forbid using the same Grades record by multiple Students.
I turned on hibernate sql logging to see what is happening when using
the database. It seems to store grades_id in a Student object just
fine, but it shows student_id in the Grades object as being null. Why
is this null?
Looks like Hibernate generated both tables with a foreign key column. Only one should have been generated.
You have to specify one side as an owner of the relationship. The other side should use mappedBy attribute of the #OneToOne annotation to tell Hibernate where is the mapping of the relationship.
...
#OneToOne(mappedBy="grades")
private Student student;
...

How configure for a (Optional) OneToOne Composite Primary Key?

I am using Hibernate and have two tables, STUDENTS and DORM_ROOMS, that are related with a composite key:
STUDENTS table:
CAMPUS(String) Part of Composite Key
STUDENT_ID (int) Part of Composite Key
NAME (String)
...
DORM_ROOMS table:
CAMPUS(String) Part of Composite Key
STUDENT_ID (int) Part of Composite Key
ROOM_NUMBER(int)
...
The relationship is one to one because a student can be associated with exactly one dorm room and and a dorm room is associated with one student (wow - a private room!). However, not all students have a dorm room.
My initial code (stripped down) looks like:
FOR STUDENTS:
#Embeddable
public class StudentsPK implements Serializable {
#Column(name = "CAMPUS")
private String Campus;
#Column(name = "STUDENT_ID")
private String StudentID;
...
}
#Entity
#Table(name = "STUDENTS")
public class Students implements Serializable {
#EmbeddedId
private StudentsPK studentsPK;
...
}
FOR DORM_ROOMS:
#Embeddable
public class DormRoomsPK implements Serializable {
#Column(name = "CAMPUS")
private String Campus;
#Column(name = "STUDENT_ID")
private String StudentID;
...
}
#Entity
#Table(name = "DORM_ROOMS")
public class DormRooms implements Serializable {
#EmbeddedId
private DormRoomsPK dormRoomsPK;
...
}
Assume that the database schema is already defined and created. In particular, CAMPUS+STUDENT_ID is a PK for STUDENTS and CAMPUS+STUDENT_ID is a FK for DORM_ROOMS that serves as the PK in that table. At this point I can successfully insert a row into STUDENTS and a row into DORM_ROOMS. I can also retrieve any student from STUDENTS even if the student does not have a dorm room. However, I have not yet "informed" Hibernate about the relationship between the two tables. That is where I am confused.
I tried to "relate" the two tables by using a "JOIN" annotation but I discovered that this causes any attempt to fetch a student that has no dorm room to return an empty result set. I suppose that makes since if "JOIN" states that the tables are to always be viewed as joined then joining a student having no dorm room with no matching rows in the DORM_ROOMS table would result in an empty result set.
Since using a "JOIN" annotation doesn't work, how do I modify my code to describe the relationship between the two tables but still allow me to fetch students that have no matching dorm rooms?
Thank you.
It sounds like you are looking for the #OneToOne annotation, which also has the ability to specify if the relationship is optional. There are some examples described in the JBoss JPA 2.1 docs, here is one of them.
Example 3: One-to-one association from an embeddable class to another entity.
#Entity
public class Employee {
#Id int id;
#Embedded LocationDetails location;
...
}
#Embeddable
public class LocationDetails {
int officeNumber;
#OneToOne ParkingSpot parkingSpot;
...
}
#Entity
public class ParkingSpot {
#Id int id;
String garage;
#OneToOne(mappedBy="location.parkingSpot") Employee assignedTo;
}
Found the problem! I discovered that in a #OneToOne relationship with a composite key, using a separate FK class to manage the composite key in both entities causes the error. The problem is shown in my original posting where I define and use StudentsPK and DormRoomsPK! Once I changed to use a single "PK" class instead of these two my problem was eliminated. (This doesn't appear to be a well documented requirement!)

Unidirectional OneToMany using Foreign Key Column

I want to maintain ACL information for each of my entities. For this i have the following entity:
#Entity
public class ACLEntry {
...
#Id
private Long id;
private Long sid;
private Integer permissionMask;
}
The entity for which the acl information should be maintained looks like the following:
#Entity
public class Folder {
...
#Id
private Long id;
#OneToMany
#JoinColumn(name="entity_id")
private List<ACLEntry> aclEntries;
}
From the hibernate docs:
A unidirectional one to many using a foreign key column in the owned entity is not that common
and not really recommended
Why is this not recommended and why should i use a jointable ?
Is there a better way to solve this ?
ACLEntry should have a reference to parent Folder #ManyToOne

Categories

Resources