JPA: why were the join tables not created? - java

In my application, I have the following Student entity:
#Entity
public class Student extends Commenter implements Serializable {
...
#OneToMany
private List<Coupon> coupons;
#OneToMany
private List<Receipt> receipts;
#ManyToMany
private List<Course> courses;
#ManyToMany
private List<Sessions> sessions;
...
}
When I deploy the app locally, everything works fine. However, when I deployed the app on our server, the join tables student_coupon and student_receipt were not created.
I tried to drop the whole database and re-create all tables but I have no idea why the above 2 tables were never created. The other join tables (student_sessions and student_course) are always created correctly though.
I'd be very grateful if you could give me an advice.
Best regards,

For what you say I'm guessing that you have a many to many relationship with the other two Entity (Coupon and Receipt), But you marked them as "One To Many" relationships so there was never going to be a join table, so you must annotate the relationship fields with the #ManyToMany annotation instead (like you have done with the other two fields):
#Entity
public class Student extends Commenter implements Serializable {
#ManyToMany //Changed this
#JoinTable(name = "student_coupon",
joinColumns = {#JoinColumn(name="studentId")}, //This could be omitted
inverseJoinColumns = {#JoinColumn(name="couponId")} //and left defaults
private List<Coupon> coupons;
#ManyToMany //and this
#JoinTable(name = "student_receipt",
joinColumns = {#JoinColumn(name="studentId")},
inverseJoinColumns = {#JoinColumn(name="receiptId")}
private List<Receipt> receipts;
#ManyToMany
private List<Course> courses;
#ManyToMany
private List<Sessions> sessions;
}

Related

Multiple representation of child entity in parent entity

I'm curious if it is possible to have several #ManyToOne relations of same entity in parent entity with JPA/Hibernate.
Example:
I have bank transactions, each transaction has a BankPartner in tow roles Creditor and Debtor. The point is, I want to edit data only once. BankPartner with nickName "mistress" is only one :), doesn't matter if in role creditor or debtor. Once, it will be renamed to wife, so I don't want to change separately. Also, the Balance is SUM of all transactions for BankPartner in both roles.
#Entity
public class Transaction {
..
#ManyToOne(fetch = FetchType.LAZY, cascade={CascadeType.ALL}, orphanRemoval=true, targetEntity = PartnerEntity.class)
#JoinColumn(name = "CREDITOR_ID")
private BankPartner creditor
#ManyToOne(fetch = FetchType.LAZY, targetEntity = PartnerEntity.class)
#JoinColumn(name = "DEBTOR_ID")
private BankPartner debtor
..
}
#Entity
public class BankPartner {
...
private String name;
private String nickName;
private String description;
...
}
I can imagine that from "Transaction" direction in can somehow work, but can't find a way to work from BankPartner direction.
I can see two different approaches
create #ManyToMany with "ROLE_TYPE" in intersection table between BankPartner and Transaction
create two separate entities Debtor an Creditor from the same table.
But, as I said, I'm curios about the first approach ..
You can use a multi column join as such:
#Entity
public class Transaction {
#ManyToOne(fetch = FetchType.LAZY, cascade={CascadeType.ALL}, orphanRemoval=true, targetEntity = PartnerEntity.class)
#JoinColumns({
#JoinColumn(name = "DEBTOR_ID"),
#JoinColumn(name = "CREDITOR_ID")
})
private BankPartner partner
}

Delete orphans when deleting parent manyToOne annotaion

I have two entities, related as below
#Entity
#Table(name = "APPOINTMENT")
public class Appointment {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long codeAp;
#ManyToOne(fetch = FetchType.EAGER)
, #OnDelete(action = OnDeleteAction.CASCADE)
#JoinColumn(name = "codeP")
private Patient patient;
//attributes
//getters and setters
//constructors
#Entity
#Table(name = "PATIENT")
public class Patient {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long codeP;
//attributes
//getters and setters
//constructors
I'm using JpaRepository delete method.
There is a constraint between the tables PATIENT and APPOINTMENT in database,
I want to remove orphans, when I remove Patient.
I added #OnDelete hibernate annotation but it doesn't work for me!
Can you please tell me why?
I want to keep that unidirectional relationship, can you please help me in this?
If you want to keep using the association as unidirectional only, you can define the lazy-loaded inverse side in a field without exposing getters and setters for it:
#Entity
public class Patient {
#OneToMany(mappedBy = "patient", orphanRemoval = true)
private Collection<Appointment> appointments;
}
This way orphanRemoval logic is applied from patients to their appointments and as a bonus you get the ability to navigate from patients to appointments in HQL queries.
Notice the mappedBy attribute which tells that appointments are responsible for the association management, so you continue to associate appointments with patients by setting patients in the many-to-one relation defined in the Appointment.
There is no way that you could achieve that automatic behavior on the #ManyToOne side. Its just semantically incorrect, period.
Taking under consideration though, the fact that you only want to have an uni-directional mapping and do not specify the Set<Appointment> dependency on the Patient, then a kind of workaround to your situation would be to replace the #ManyToOne with a #OneToOne relationship. Then you would be able to use orphan-removal functionality:
#OneToOne(fetch = FetchType.EAGER, orphanRemoval=true)
#JoinColumn(name = "codeP")
private Patient patient;
Keep in mind though that if you follow this path, adapt you code and at some point you will be in need to introduce #OneToMany dependency on the `Patient' side then you will stumble upon problems. So i would recommend working out pros and cons first in relation to future possible alteration to the entity graph.

Annotation to join columns with OR condition instead of AND condition

I have 2 java classes, Relation and Person, which both are present in my database.
Person:
#Entity
#Table(name = "persons")
public class Person {
#Id
#Column
private int id;
#Column
private String name;
#OneToMany(fetch = FetchType.EAGER)
#JoinColumns({
#JoinColumn(name = "slave_id", referencedColumnName="id"),
#JoinColumn(name = "master_id", referencedColumnName="id")
})
private List<Relation> relations;
//Getters and setters
}
Relation:
#Entity
#Table(name = "relations")
public class Relation {
#Id
#Column
private int id;
#Column
private int child_id;
#Column
private int parent_id;
#Column
private String type;
//Getters and setters
}
Each Person has a list of relations (or not), the relation should be added to the list when the child_id or the parent_id of the relation is equal to the id of the person.
TL;DR:
When relation.child_id OR relation.parent_id = person.id => add relation to list of relations to the person
The issue I am facing is that this annotation:
#JoinColumns({
#JoinColumn(name = "child_id", referencedColumnName="id"),
#JoinColumn(name = "parent_id", referencedColumnName="id")
})
creates following SQL (just the necessary part):
relations relations6_
on this_.id=relations6_.slave_id
and this_.id=relations6_.master_id
What is the correct annotation in Java Hibernate to generate an SQL statement saying OR instead of AND
Some of the options that you could utilize:
Database views. Create the view that does custom join for you and map the entity to the view.
Join formula. I managed to make them work only on many-to-one associations. Nevertheless, you could make the association bidirectional and apply the formula in the Relation entity.
#Subselect. This is a kind of Hibernate view, suitable if you can't afford to create a real database view or change the db schema to better suit the entity model structure.
This and this answer could also be helpful.
Also, you can always use two separate associations for slaves and masters:
public class Person {
#OneToMany
#JoinColumn(name = "slave_id"),
private List<Relation> slaves;
#OneToMany
#JoinColumn(name = "master_id"),
private List<Relation> masters;
public List<Relation> getRelations() {
List<Relation> result = new ArrayList<>(slaves);
result.addAll(masters);
return result;
}
}
However, keep in mind that joining all of them in a single query requires full Cartesian product between masters and slaves.
You can use #FilterDef and #Filter annotations.

OneToMany issue in hibernate

I have models like this:
#Table(name="EMPLOYEE")
public class Employee {
#Column(name="firstname")
private String firstname;
// and others
#ManyToOne( targetEntity = Employee.class,
fetch=FetchType.EAGER, cascade=CascadeType.ALL)
#JoinColumn(name="department_id",
insertable=false, updatable=false,
nullable=false)
private Department department;
=============================================================================
#Table(name="DEPARTMENT")
public class Department {
#Column(name="DEPARTMENT_ID")
private Long departmentId;
#OneToMany( targetEntity = Employee.class,
fetch=FetchType.EAGER, cascade=CascadeType.ALL)
#JoinColumn(name="department_id")
#IndexColumn(name="idx")
private List<Employee> employees;
and my DepartmentDaoImpl is
public class DepartmentDaoImpl implements DepartmentDao{
#Autowired
private SessionFactory sessionFactory;
#Transactional
public void addDepartment(Department department) {
sessionFactory.getCurrentSession().save(department);
}
when i run project this exception appear in output
org.hibernate.MappingException: Unknown entity: org.springmvc.form.Department
what is this problem and how solved it?
Department department = new Department();
department.setDepartmentName("Sales");
department.setDepartmentId(90);
Employee emp1 = new Employee("reza", "Mayers", "101",department.getDepartmentId());
// Employee emp2 = new Employee("ali", "Almeida", "2332");
department.setEmployees(new ArrayList<Employee>());
department.getEmployees().add(emp1);
Many problems in this code.
First problem: Unknown entity: org.springmvc.form.Department. This means that Department is not annotated with #Entity and/or is not listed in the entities in the hibernate configuration file.
Second problem:
#ManyToOne( targetEntity = Employee.class)
private Department department;
This doesn't make sense. The target entity is obviously Department, not Employee. targetEntity is useless unless the type of the field is an abstract class or interface, and you need to tell Hibernat what it should use as concrete entity class. Otherwise, Hibernate know the target entity from the type of the field.
Third problem:
#OneToMany( targetEntity = Employee.class,
fetch=FetchType.EAGER, cascade=CascadeType.ALL)
#JoinColumn(name="department_id")
This OneToMany is the inverse side of the bidirectional association that you have already declared and mapped in Employee. So you MUST NOT repeat the mapping here. Instead, you MUST declare it as the inverse side, using the mappedBy attribute:
#OneToMany(mappedBy = "department", fetch=FetchType.EAGER, cascade=CascadeType.ALL)
#IndexColumn(name="idx")
private List<Employee> employees;
Using eager fetching for a toMany association is a really bad idea as well. You really don't want to load the 200 employees of a department every time you load a department. That will kill the performance of your application.

Persistence: ManyToMany to same class

I am trying to set a ManyToMany annotation on my code:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name = "user")
public class User extends AbstractUser
{
#ManyToMany(mappedBy = "promotors", cascade = CascadeType.PERSIST)
#JoinTable(name = "user_student",
joinColumns=#JoinColumn(name="promotor_id", referencedColumnName="id"),
inverseJoinColumns=#JoinColumn(name="student_id", referencedColumnName="id")
)
private Collection<User> students;
#ManyToMany
private Collection<User> promotors;
}
However every time i try to run the application and the db gets generated, it creates 2 tables for the ManyToMany, 1 new table that is called user_student as i defined beneath, but it also creates a second table user_user which i didn't define but is generated from the promotors.
It's correct you cannot map many to many relationship on one table. As you have only one possible column to map it to. What enforces one to one relationship.
You always have to have mapping table. Its also most convenient way to map many to many relationships on different tables.
Apparently, i didn't define the second ManyToMany correctly, this is the correct code:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#Table(name = "user")
public class User extends AbstractUser
{
#ManyToMany
#JoinTable(name = "user_student",
joinColumns={#JoinColumn(name="promotor_id", referencedColumnName="id")},
inverseJoinColumns={#JoinColumn(name="student_id", referencedColumnName="id")}
)
private Collection<User> students;
#ManyToMany(mappedBy = "students", cascade = CascadeType.PERSIST)
private Collection<User> promotors;
}

Categories