When I run my project, the Hibernate creates automatically tables with wrong names.
I have two tables User and Role and also three classes:
abstract class IdField.java:
#Entity
public abstract class IdField {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
//constructors and getters setters
User.java class:
#Entity
#Table(name = "user", schema = "quiz_app")
public class User extends IdField{
#Column(name = "user_name")
private String userName;
#Column(name = "password")
private String password;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "email")
private String email;
#ManyToMany(fetch = FetchType.EAGER)
private Collection<Role> roles = new ArrayList<>();
//constructors and getters setters
and Role.java class:
#Entity
#Table(name = "role", schema = "quiz_app")
public class Role extends IdField{
#Enumerated(EnumType.ORDINAL)
#Column(name = "role_name")
private RoleName roleName;
//constructors and getters setters
And Hibernate creates two tables with wrong names as id_field and id_field_roles:
but I want table names as it is in #Table annotation like "user" and "role"
Get familiar with inheritance strategies:
https://thorben-janssen.com/complete-guide-inheritance-strategies-jpa-hibernate/
It seems to me you are looking for #MappedSuperclass
If you just want to share state and mapping information between your entities, the mapped superclass strategy is a good fit and easy to implement. You just have to set up your inheritance structure, annotate the mapping information for all attributes and add the #MappedSuperclass annotation to your superclass. Without the #MappedSuperclass annotation, Hibernate will ignore the mapping information of your superclass.
On top of that: If your shared part is only id field, as the name suggests, inheritance looks like overkill.
Related
I'm developing one simple app where where I have an one entity class class Employee. And now I want to create/copy new similar entity called ActiveEmployees from existing Employee. I want to add functionality that If I hit the new api endpoint ->POST: http://locahost:8080/api/employee/active/john -> So, it should save existing Employee John Record in the new table active_employees with the all Table data.
#Entity
#Table(name="employee")
public class Employee{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column
#NotNull
private String firstNname;
#Column
#NotNull
private String lastNname;
#Column
#NotNull
private String department;
#JsonManagedReference
#OneToOne(fetch = FetchType.LAZY,
mappedBy = "employee",
cascade = CascadeType.ALL,
orphanRemoval = true)
ActiveEmployee activeEmployee;
... Constructor, getters and setters
}
#Entity
#Table(name="active_employees")
public class ActiveEmployees {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JsonBackReference
#OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#JoinColumn(name = "employee_id")
private Employee employee;
}
I think you should use inheritance mapping in hibernate instead having two table with same fields. There are multiple strategies. Check and use best one which fits your requirement.
Read the tutorial here https://www.javatpoint.com/hibernate-inheritance-mapping-tutorial
You can use inhertiance with #MappedSuperclass. But if I will design this application I will add boolean field "active" to Employee class.
I have a parent entity 'contracts' that has a one-to-one relation with another entity 'child-contract'. the interesting thing is that the mapping field ('contract_number')id not a primary key-foreign key but is rather a unique field in both the tables. Also it is possible for a contracts to not have any child contract altogether. With this configuration I have observed hibernate to generate 1 additional query every time a contracts does not have a child-contract. I filed this behavior very strange. Is there a way to stop these unnecessary query generation or have I got something wrong.
below is a piece of my code configuration.
#Data
#Entity
#Table(name = "contracts")
public class Contracts implements Serializable {
#Id
#JsonIgnore
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
#OneToOne(fetch=FetchType.EAGER)
#Fetch(FetchMode.JOIN)
#JsonProperty("crm_contracts")
#JoinColumn(name = "contract_number", referencedColumnName = "contract_number")
private ChildContract childContract ;
}
#Data
#NoArgsConstructor
#Entity
#Table(name = "child_contract")
#BatchSize(size=1000)
public class ChildContract implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#JsonProperty("id")
#Column(name = "id")
private String id;
#JsonProperty("contract_number")
#Column(name = "contract_number")
private String contractNumber;
}
Please help.
Thank-you
You can use NamedEntityGraph to solve multiple query problem.
#NamedEntityGraph(name = "graph.Contracts.CRMContracts", attributeNodes = {
#NamedAttributeNode(value = "crmContract") })
Use this on your repository method as
#EntityGraph(value = "graph.Contracts.CRMContracts", type = EntityGraphType.FETCH)
// Your repo method in repository
Having the following entities:
#Entity
#Table
public class Employee {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#NotBlank
private String firstName;
#NotBlank
private String lastName;
private Gender gender;
private Instant birthDate;
private String email;
private String corporateEmail;
#Embedded
private Address address;
// and many more
// + all getters and setters
}
#Entity
#Table
public class Discipline {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#NotNull
private Instant date;
private String infraction;
}
A Discipline is for a specific Employee. An Employee may have 0 to several disciplines. A discipline is for only one Employee, no more no less. In the Discipline world (micro-service), it requires only a few attributes from the full Employee class (id, firstName and lastName). In the Employee World (micro-service), all Employee fields are relevant.
How do I properly set my relation between both entities without fetching the Employee entity for each Discipline but only the required fields as I would do with a projection?
Must I refactor my Employee entity by extracting a superclass with only the attributes' subset?
In a magical world, I would like to have Discipline entity defines as follow:
#Entity
#Table
public class Discipline {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#NotNull
private Instant date;
private String infraction;
#ManyToOne
private EmployeeProfile employeeProfile;
}
public interface EmployeeProfile {
UUID getId();
String getFirstName();
String getLastName();
}
Where EmployeeProfile here should look similar to what a Spring Data JPA interface based projection would use.
Aimed goals by doing this:
Avoid incoming issues when our entities will be versionned. In fact, we don't want an addDiscipline request to fail due to an outdated employee instance on an irrelevant attribute. This could happen if we link Discipline to the full `Employee
Improve performance by reducing load to our DB (slimmer entities the better)
Reduce coupling between our entities as much as possible.
Keep entities and DB design as simple as possible.
Thanks to #crizzis who proposed what I was looking for. Here's the solution if anybody else is looking for this in future.
SOLUTION:
Simply have two entities, one with all the attributes and another one with only the subset you're interested in and have both entities using same table as follow:
#Entity
#Table(name = "EMPLOYEE")
public class Employee {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#NotBlank
private String firstName;
#NotBlank
private String lastName;
private Gender gender;
private Instant birthDate;
private String email;
private String corporateEmail;
#Embedded
private Address address;
...
}
#Entity
#Table(name = "EMPLOYEE")
#Immutable
public class EmployeeProfile {
#Id
private UUID id;
private String firstName;
private String lastName;
}
Than you can have link other entities on this EmployeeProfile class as follow:
#Entity
#Table
public class Discipline {
#Id
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private UUID id;
#NotNull
private Instant date;
private String infraction;
#ManyToOne
#JoinColumn(name = "employee_id")
private EmployeeProfile employeeProfile;
}
As by default on ManyToOnerelationship no operations are cascaded, this suits perfectly our needs.
#AlanHay proposed to go down the REST way by having a REST endpoint returning this specific DTO. It is another great solution especially in a micro-service architecture.
As in our case, all our entities are still persisted in the same DB, we are going with the above solution as first step of doing micro-services is to build a great/decoupled monolithic application and because it will handle everything in only one DB query. And when the day come to split Discipline and Employee in different micro-services, it will be very simple to do so as Discipline table hold only the employee id, avoiding painful DB migration.
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