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;
}
Related
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
}
I have 2 tables that may be related to each other through non-PK secondary columns. Moreover, the column names for this match are different in each table. That is,
#Entity
#Table(name = "PLANS_T")
public class Plans {
private Integer id; // PK
//...
private String secondaryIdentifier; // Should be matched with TRAINEES.auxiliaryIdentifier
//...
}
#Entity
#Table(name = "TRAINEES_T")
public class Trainee {
private Integer id; // PK
//...
private String auxiliaryIdentifier; // Should be matched with PLANS.secondaryIdentifier
}
The relationship between PLANS and TRAINEE is Many-to-One: You can have many Plans for a Trainee.
I need to annotate these properly to indicate that PLANS_T.secondaryIdentifier should be used with TRAINEES_T.auxiliaryIdentifier for JOINs (such as in the Criteria API, which needs a Join Path from one table to the other).
But I can't use the typical examples e.g.
#Entity
class Trainee {
#OneToMany(mappedBy = "plan")
private Collection<Plans> plans = new ArrayList<Plans>();
}
#Entity
class Plans {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="auxiliary_identifier") // Where do I specify "secondaryIdentifier", a non-PK column?
private Trainee trainee;
}
I need a way to specify both of the non-PK columns in the annotations. When using Criteria API, these annotations provide the path to create Join paths.
You should correct your mapping in the following way:
#Entity
class Trainee {
#OneToMany(mappedBy = "trainee")
private List<Plans> plans = new ArrayList<Plans>();
}
#Entity
class Plans {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="secondary_identifier", referencedColumnName = "auxiliary_identifier")
private Trainee trainee;
}
The mappedBy of the #OneToMany is the name of the field that owns the relationship. This is trainee field of the Plans entity.
The referencedColumnName of the #JoinColumn is the name of the column referenced by this foreign key column.
I have two tables and they got many to one relationship. Below entity creates a join column called school_id. Problem is project manager said never use join column but use join table.
I think when I use join table it will create a new table like "school_student_relationship" and will store relationship with this table.Why do I need to use join table instead of join column? Is it about performance or something else?
#Entity
#Table(name = "student")
public class Student implements Serializable {
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "school_id")
private School school;
}
#Entity
#Table(name = "school")
public class School implements Serializable {
#Id
#Column(name = "school_id")
private String schoolId;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "school", fetch = FetchType.LAZY)
private List<Student> students= new ArrayList<>();
}
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.
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;
}