I have 3 tables in the db. I am trying to write the JPA entities. I am facing some issues with Association table entity. My entities are as follows,
Person.java
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
private String firstName;
#Column(nullable = false)
private String lastName;
//setter and getter
}
Exam.java
#Entity
#Table(name = "exam")
public class Exam {
#Id
#GeneratedValue
private long examId;
#Column(nullable = false)
private String examName;
#Column(nullable = false)
private int marks;
//Setters and getters
}
The table structure for association table is,
create table person_exam (
personId BIGINT not null,
examId BIGINT not null,
primary key (personId, examId)
);
I tried the association table entity with #ManyToMany annotation for both the properties which is not giving me the result.
Can anyone please suggest me what should I need to use (ManyToMany/OneToOne/ManyToOne/OneToMany ) in my entity for the above person_exam table.
from the PRO JPA 2nd Ed. book:
the only way to implement a many-to-many relationship is with a separate join table. The consequence of not having any join columns in either of the entity tables is that there is no way to determine which side is the owner of the relationship. Because every bidirectional relationship has to have both an owning side and an inverse side, we must pick one of the two entities to be the owner.
So I chose the the Person entity. Applying the needed changes to your incomplete code:
#Entity
#Table(name = "person")
public class Person {
#Id
#GeneratedValue
private Long id;
#Column(nullable = false)
private String firstName;
#Column(nullable = false)
private String lastName;
/**
* we need to add some additional metadata to the Person designated
* as the owner of the relationship, also you must fully specify the names of
* the join table and its columns because you already provided a schema
* for the association table, otherwise the JPA provider would generate one.
*/
#ManyToMany
#JoinTable(name="person_exam",
joinColumns=#JoinColumn(name="personId"),
inverseJoinColumns=#JoinColumn(name="examId"))
private Collection<Exams> exams;
//setter and getter
}
#Entity
#Table(name = "exam")
public class Exam {
#Id
#GeneratedValue
private long examId;
#Column(nullable = false)
private String examName;
#Column(nullable = false)
private int marks;
//Setters and getters
/**
* As in every other bidirectional relationship,
* the inverse side must use the mappedBy element to identify
* the owning attribute.
*/
#ManyToMany(mappedBy="exams")
private Collection<Person> people;
}
Related
I have a table with two columns user_id and role_id. There's no unique column in table and I can't add one. How can I create Entity and Repository in Spring without a primary key?
This is my UserRole.class
public class UserRole {
#Column(name = "user_id")
private int userId;
#Column(name = "role_id")
private int roleId;
//getters and setters
}
But with this class i get the following error:
nested exception is org.hibernate.AnnotationException: No identifier specified for entity:
I saw that one of the answers is to use all of the columns as the id, but i have no idea how to do it.
Please see the awnser in this post. This should help you.
PK Explained
Another Option is if this is a join table, than you could make Embeded PK
#Embeddable
#Data
#NoArgsConstructor
#AllArgsConstructor
#Builder(toBuilder = true)
public class PersonGroupPK implements Serializable {
//default serial version id, required for serializable classes.
private static final long serialVersionUID = 1L;
#Column(insertable=false,unique = false, updatable=false, nullable=false)
private Long personId;
#Column(insertable=false, unique = false,updatable=false, nullable=false)
private Long groupId;
}
I have been struggled into OneToMany mapping on entity with composite primary key when I try to get List of items of child table.
I have entitties Person and PhoneNumbers. One person may have multiple phone numbers. PhoneNumber entity have primary key consist of two fields: id and person_id which is foreign key to Person entity.
This is a database schema:
person:
id (pk)
person_name
phone_number:
id (pk)
person_id (pk)(fk)
phone_number
Person class
#Entity(name = "person")
public class Person {
#Id
#Column(name = "id")
private Long id;
#Column(name = "person_name")
private String name;
#OneToMany(mappedBy = "person")
private List<PhoneNumber> phoneNumbers;
// constructor, getters, setters...
}
PhoneNumber class
public class PhoneNumber {
#EmbeddedId
private PhoneNumberPk phoneNumberPk;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "id", insertable = false, updatable = false)
private Person person;
#Column(name = "phone_number")
private String phoneNumber;
// constructor, getters, setters...
}
PhoneNumberPk class
#Embeddable
public class PhoneNumberPk {
#Column(name = "id")
private Long id;
#Column(name = "person_id")
private Long personId;
// constructor, getters, setters...
}
Problem is when I get Person entity from database and call person.getPhoneNumbers() I always get empty list. Hibernate debugging shows that query that fetch data from phone_number table is executed when I call person.getPhoneNumbers() but I always get empty list.
I'm using Spring Boot - Spring Data JPA 2.0.7 and Hibernate 5.2.17
I have a question regarding ManyToOne relationship.
Assume I have 2 beans:
#Entity
#Table(name = "accounts")
public class Account {
#Id
#Column(name = "account_id")
private int account_id;
}
#Entity
#Table(name = "broker_account")
public class BrokerAccount {
#Id
#Column(name = "broker_account_id")
private int broker_account_id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name="account_id", referencedColumnName = "account_id")
private Account account;
}
I am querying the entity below (plain get all query)
entityManager.createQuery("from BrokerAccount", BrokerAccount.class)
I thought that if I query BrokerAccount entity the account_id column will be populated by default on the Account object, since it exists in the BrokerAccount table as well, however all the Account fields are empty.
Am I missing something, should I define this field/column on the BrokerAccount entity itself as well to get its value?
You have defined the Account association as #ManyToOne(fetch = FetchType.LAZY). This means that while performing the entityManager.createQuery("from BrokerAccount", BrokerAccount.class), there will be no join on the Account and its data will not be fetched at that time.
In order to make the persistence provider fetch the Account data you would need to interact with the reference while being in the same transactional method, f.e.: brokerAccount.getAccount().getAccountId();
If you want to simply have a repeated column for the fk you can do:
#Column(name = "account_id", insertable=false, updatable=false)
private int account_id;
I am very new to hibernate and I am working with JPA and Hibernate4. Trying to insert parent object in child as onetoone relationship.
I went through some tutorials but All the example in the web shows, inserting both parent and child tables.
I want to insert data in child table only.
I have two tables called user and department.
User table consists of user details with department as onetoone relationship, as follows,
#Entity
#Table(name = "User")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "_id")
private String id;
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "departmentId")
private Department departmentId;
// getters and setters...
}
Below is my Department entity,
#Entity
#Table(name = "Department")
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "_id")
private String id;
#Column(name = "name")
private String name;
// getters and setters...
}
In department table there is only 4 data. I want to insert data only in user data while insert into it and don't want to insert in Department.
How can I do that.Please assist.
You have to use mappedBy for this, as mentoned below in child Table, Department in your case
#OneToOne(mappedBy="department")
private UserEntity user;
These posts explain you better this,
JPA JoinColumn vs mappedBy
Understanding mappedBy annotation in Hibernate
You need to specify the relationship owner using mappedBy property in the OneToOne mapping in the owner side, here in your case in the Department class, you should add:
#OneToOne(mappedBy="department")
private UserEntity user;
I updated your code, to included the stated annotation and also renamed the Department property in your UserEntity class from departmentId to department to avoid confusion between relationship owner and its id:
#Entity
#Table(name = "User")
public class UserEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "_id")
private String id;
#Column(name = "firstName")
private String firstName;
#Column(name = "lastName")
private String lastName;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "departmentId")
private Department department;
// getters and setters...
}
Below is the Department entity,
#Entity
#Table(name = "Department")
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "_id")
private String id;
#Column(name = "name")
private String name;
#OneToOne(mappedBy="department")
private UserEntity user;
// getters and setters...
}
This will give you the right mapping with the expected behaviour.
In the #OneToOne annotation, the default value for parameter optional is true. So your annotation is the same as #OneToOne(fetch = FetchType.EAGER, optional = true). This means you can simply leave the Department in a UserEntity instance empty. In that case, persisting it results in persisting only a user entity and no department.
Even if you created a Department instance and assigned it to a UserEntity instance, persisting the UserEntity would not automatically persist the Department, since you don't have any cascade parameter in your annotation. If you don't automatically cascade persists, you would have to persist the Department first and then persist the corresponding user entity.
Maybe you're asking about using existing departments for your user entities. In that case, you first need to get the department via Hibernate (or the JPA API) from an entity manager. The entity instance you get is managed by Hibernate, and you can then set it in a UserEntity and persist that, to have it refer to the department.
Finally, I think one department will probably have more than one user. It might make more sense to have a #ManyToOne annotation instead of #OneToOne, indicating multiple users can refer to the same department, but that depends on your domain model.
I can't propper map DB tables with JPA annotation.
Tables Subject and Place is ManyToMany through JoinTable.
Subject.java
#Entity
#Table(name = "SUBJECT")
public class Subject implements Serializable {
#Id
#Column(name = "SID")
private Integer sid;
#Column(name = "NAME")
private String name;
// getters and setters
}
SubjectPlace.java
#Entity
#Table(name = "SUBJECT_PLACE")
public class SubjectPlace implements Serializable {
#Id
#Column(name = "SPID")
private Integer spid;
#ManyToOne
#JoinColumn(name = "SUB_KEY") //Subject FK
private Subject subject;
#ManyToOne
#JoinColumn(name = "PLC_KEY") //Place FK
private Place place;
// getters and setters
}
Place.java
#Entity
#Table(name = "PLACE")
public class Place implements Serializable {
#Id
#Column(name = "PID")
private Integer pid;
#Column(name = "NAME")
private String name;
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.PERSIST)
#JoinTable(name = "SUBJECT_PLACE",
joinColumns = { #JoinColumn(name = "PLC_KEY", nullable = false, updatable = false) },
inverseJoinColumns = { #JoinColumn(name = "SUB_KEY", nullable = false, updatable = false) })
private Set<Subject> subjects;
// getters and setters
}
But than I need to link Person with Subject in selected Places. I mean that each Place has its own collection of Subject. And a Person have link to Subject whitch resides in particular Place.
like This:
Subject (M) -- (M) Place through JoinTable Subject (1) -- (M) Subject_Place (M) -- (1) Place
Person (M) -- (M) Subject_Place through JoinTable Person (1) -- (M) Person_Subject_Place (M) -- (1) Subject_Place
Person.java
#Entity
#Table(name = "PERSON")
public class Person implements Serializable {
#Id
#Column(name = "PRSID")
private Integer prsid;
#Column(name = "NAME")
private String name;
// How to annotate this code?
// I experience problem in this part of code
#OneToMany
#JoinColumn(name="SPID_KEY")
private List<SubjectPlace> subjectPlaces;
// getters and setters
}
PersonSubjectPlace.java
#Entity
#Table(name = "PERSON_SUBJECT_PLACE")
public class PersonSubjectPlace implements Serializable {
#Id
#Column(name = "PSPID") // Person_Subject_Place ID
private Integer pspid;
#ManyToOne
#JoinColumn(name = "PER_KEY") //Person FK
private Person person;
// How to annotate this code?
// I experience problem in this part of code
#ManyToOne
#JoinColumn(name = "SPID_KEY") //Subject_Place FK
private SubjectPlace subjectPlace;
// getters and setters
}
And when I try so get Persons and its Subjects, I get this error:
Caused by: org.hibernate.MappingException: Foreign key (FK2C3B79384AABC975:PERSON_SUBJECT_PLACE [SPID_KEY])) must have same number of columns as the referenced primary key (SUBJECT_PLACE [PLC_KEY,SUB_KEY])
What, How shoul I map?
In your OneToMany mapping you don't need to specify the foreign key, you just need to use mappedBy property to refer your mapping object, you can learn more about it in OneToMany Mapping Documentation, and here's what you need to map Person and PersonSubjectPlace entities:
In your Person class:
#OneToMany(mappedBy="person")
private List<PersonSubjectPlace> personsubjectPlaces;
In your PersonSubjectPlace class:
#ManyToOne
#JoinColumn(name="PRSID") //Specify the primary key of Person
private Person person;
For further information about the difference between JoinColumn and mappedBy you can take a look at this answer.
EDIT:
For the mapping between SubjectPlace and PersonSubjectPlace:
In your SubjectPlace class:
#OneToMany(mappedBy="subjectPlace")
private List<PersonSubjectPlace> personsubjectPlaces;
In your PersonSubjectPlace class:
#ManyToOne
#JoinColumn(name="SPID") //Specify the primary key of SubjectPerson
private SubjectPlace subjectPlace;
Note:
The best approach to map those classes is to use #JoinTable between Person and SubjectPlace, take a look at this #JoinTable example, because PersonSubjectPlace is pratically an asociation-entity between Person and SubjectPlace.
You should remove #Joincolumn annotation and add mappedBy variable to #OneToMany annotation.
#OneToMany(mappedBy = "spid")
You should have a variable in SubjectPlace that has a Person where you should put #JoinColumn annotation