Spring boot data rest/jpa #JoinTable insertion - java

I'm creating a MySQL database as followed :
database design
the Country and Province tables are pre-filled with data. I have the application running and can get stuff no problem, and also the join table person_has_address works when getting.
however, when I insert data using post I want to be able to set the ID of the province, and let spring data jpa just add that number to add_pro_id in the Address table. For example, when I post the following json:
{ "firstName":"bilbo", "lastName":"baggings", "address":{"street":"streetName", "streetNum":3, "zipcode":"1337GG", "city":"TheCity", "province":{"name":"aProvinceName"}} }
jpa should see that aProvinceName exists and grab that id and add that to add_pro_id.
Now it just insert aProvinceName as new value in province and add the new id to add_pro_id.
The person class:
#Entity
public class Person {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="per_id")
private int id;
#Column(name="per_name")
private String firstName;
#Column(name="per_surname")
private String lastName;
#Column(name="per_birth_date")
private String birthDate;
#Column(name="per_fax")
private String fax;
#Column(name="per_phone")
private String phone;
#Column(name="per_email")
private String email;
#OneToOne(optional = false, cascade = CascadeType.ALL)
#JoinTable(name="person_has_address", joinColumns = {#JoinColumn(name="pha_per_id", referencedColumnName = "per_id")}, inverseJoinColumns = {#JoinColumn(name="pha_add_id", referencedColumnName = "add_id")})
private Address address;
// getters and setters
This is the person repository:
#RepositoryRestResource(collectionResourceRel = "people", path = "people")
public interface PersonRepository extends PagingAndSortingRepository<Person, Long> {
List<Person> findByLastName(#Param("name") String name);
}
This is the address class:
#Entity
public class Address {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="add_id")
private int id;
#Column(name = "add_street")
private String street;
#Column(name="add_street_num")
private int streetNum;
#Column(name="add_zip")
private String zipcode;
#Column(name="add_city")
private String city;
#JoinColumn(name="add_pro_id", referencedColumnName = "pro_id")
#ManyToOne(optional=false, cascade = CascadeType.ALL)
private Province province;
// getters and setters
Province class:
#Entity
public class Province {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="pro_id")
private int id;
#Column(name="pro_name")
private String name;
#ManyToOne
#JoinColumn(name="pro_cou_id")
private Country country;
// getters and setters
And lastly country class:
#Entity
public class Country {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="cou_id", insertable = false, updatable = false)
private int id;
#Column(name="cou_name", insertable = false, updatable = false)
private String name;
// getters and setters
I've tried adding insertable = false and updatable = false, but the application then just inserts NULL values in my database. I've also tried working with #primarykeyjoins, but to no success.
if anyone knows how I should tackle this problem I would much appreciate it!
Thanks in advance.

Related

Hibernate Invalid Path Exception

New to Hibernate and HQL in general. I have these following entities.
Employee entity
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column
private Integer authority;
#Column
private String username;
#Column(name = "user_password")
private String password;
#Column
private String title;
#OneToOne
#JoinColumn(name = "person_id", referencedColumnName = "id")
private Person person;
#Column
private Integer gender;
#Column
private String ssn;
#Column(name = "car_info")
private String carInfo;
#Column(name = "birth_date")
private Date birthDate; //java.util Date vs java.sql.Date?
#OneToOne
#JoinColumn(name = "visa_status_id", referencedColumnName = "id")
private VisaStatus visaStatus;
#Column(name = "license_number")
private Integer licenseNumber;
#Column(name = "license_expiration_date")
private Date licenseExpirationDate;
#ManyToOne
#JoinColumn(name = "housing_id", referencedColumnName = "id")
private House house;
}
Person entity
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
#Column(name = "middle_name")
private String middleName;
#Column(name = "preferred_name")
private String preferredName;
#ManyToOne
#JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
#Column
private String email;
#Column(name = "ceil_phone")
private String ceilPhone;
#Column(name = "work_phone")
private String workPhone;
}
House entity
public class House implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#OneToOne
#JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;
#ManyToOne
#JoinColumn(name = "contact_id")
private Contact contactHR;
#ManyToOne
#JoinColumn(name = "landlord_id")
private Person landlord;
}
I'm trying to run this HQL:
select person from Employee where house.id=:houseId
I'm basically trying to get all the Person with the houseId from the Employee. Since I'm trying to only get the person, I'm using the select cause.
But I'm getting these errors:
org.hibernate.hql.internal.ast.InvalidPathException: Invalid path: 'house.id'
org.hibernate.hql.internal.ast.QuerySyntaxException: Invalid path: 'house.id' [select person from example.project.domain.entity.Employee where house.id=:houseId]
I tried the HQL without the select clause and it worked:
from Employee where house.id=:houseId
But I only need person so I didn't want to get everything. Any idea what's wrong?
You asked
Hibernate Invalid Path Exception using HQL
The problem lies on the written query
select person from Employee where house.id=:houseId
Which should contain proper referencing
select p.person from Employee p where p.house.id=:houseId
Why ?
The FROM clause defines which entities the data is going to be selected. Hibernate, or any other JPA implementation, maps the entities to the according database tables and the syntax of a JPQL FROM clause is similar to SQL and indeed uses the entity model for referencing database attributes, which in root query execution (same thing you are doing) one must use the same referencing strategy, which in your case is to use aliases, as A.Panfilov said in comments regardless of hibernate, even in aliasing in SQL is a good practice.
You may find more in here.

How to use the Primary Key of one table as Primary Key of another using Hibernate

Using Hibernate, I have created two entities - Employee and EmployeeDetails. Since EmployeeDetails cannot exist without a corresponding entry in Employee, I figured I don't need an extra ID for EmployeeDetails, but could instead use the ID of the Employee entity. Here is how I have implemented this idea:
Employee-Entity:
#Entity
#Table(name = "employees")
#Data
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "employee_id")
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
#OneToOne(cascade = CascadeType.ALL)
EmployeeDetails employeeDetails;
}
Employee-Details-Entity:
#Entity
#Table(name = "employee_details")
#Data
public class EmployeeDetails {
#Id
private Long id;
#Column(name = "address")
private String address;
#Column(name = "e_mail", nullable = false)
private String eMail;
#Column(name = "phone")
private String phone;
#MapsId
#OneToOne(mappedBy = "employeeDetails", cascade = CascadeType.ALL)
#JoinColumn(name = "employee_id")
private Employee employee;
}
By adding the #MapsId annotation to the employee-variable inside EmployeeDetails, I should be assigning the primary key of the Employee-entity to the Id-column of EmployeeDetails.
In a second step, I have written some data into both of my tables.
employee table in MySQL database:
employee_id first_name last_name employee_details_employee_id
1 John Smith null
2 Jennifer Adams null
The last column was somehow generated by Hibernate. I don't understand why. It appears to be some column for identification, but I don't need it.
employee_details table in MySQL database:
employee_id address e_mail phone
1 null john.smith#gmail.com null
2 null jennifer.adams#gmail.com null
I have only assigned an e-mail to the employees. Surprisingly, there is no employee-entry in this database table. I don't really need it anyways, but I was expecting it. So yeah, I think I am doing something terribly wrong and would really appreciate some help.
Change mappedBy side, here useful links
https://vladmihalcea.com/change-one-to-one-primary-key-column-jpa-hibernate/
https://vladmihalcea.com/the-best-way-to-map-a-onetoone-relationship-with-jpa-and-hibernate/
https://javabydeveloper.com/one-one-bidirectional-association/
#Entity
#Table(name = "employees")
#Data
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "employee_id")
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
#OneToOne(mappedBy = "employee", cascade = CascadeType.ALL)
EmployeeDetails employeeDetails;
}
Entity
#Table(name = "employee_details")
#Data
public class EmployeeDetails {
#Id
private Long id;
#Column(name = "address")
private String address;
#Column(name = "e_mail", nullable = false)
private String eMail;
#Column(name = "phone")
private String phone;
#MapsId
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "employee_id")
private Employee employee;
}
#MapId is not a popular solution in work with Hibernate.
Maybe in your case, #Embeddable will be a better option?
If I understand correctly, EmployeeDetails cannot exist without correlated Employee. So, EmployeeDetails could be a field in Employee as an embeddable field:
#Entity
#Table(name = "employees")
#Data
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "employee_id")
private Long id;
#Column(name = "first_name", nullable = false)
private String firstName;
#Column(name = "last_name", nullable = false)
private String lastName;
#Embedded
EmployeeDetails employeeDetails;
}
Then EmployeeDetails doesn't need ID and relation with the employee:
#Embeddable
public class EmployeeDetails {
#Column(name = "address")
private String address;
#Column(name = "e_mail", nullable = false)
private String eMail;
#Column(name = "phone")
private String phone;
}
As you can see, now in the database it's only one table employees, but in our hibernate model, we have two separated objects. Probably you don't need EmployeeDetails without Employee entity, so there is more efficient construction.
If you really need a separated table for EmployeeDetails with relation to Employee I recommend creating standard one-to-one mapping instead of #MapId construction.

Cascade type in Spring boot

I'm a new to Spring boot.
I'd like to know which cascade type I have to use in this case.
I have an Employee class and a Departement.
Many Employees can work in a Departement (So i guess it's a #ManyToOne Relation) and a Department can have one or more Employees (so it's a **#OneToMany).
I want to perform some edits on the Employee class which have to be propagated to the other dependency.
BUT if I delete one employee, I can't delete an entire Departement, so I have to just delete the Employee.
#Entity
public class Department
{
#Column(nullable = false)
private String name;
#Id
private String code;
#Column(nullable = false)
private String address;
#Column(unique = true, nullable = false)
private String website;
#Column(unique = true, nullable = false)
private String cellNumber;
#JsonIgnore
#OneToMany(mappedBy = "department")
private Set<Employee> emplpoyee;
//constructors, getters and setters
}
Here is the Employee Class
public class Employee
{
#Id
private String SSN;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private String lastname;
#Column(nullable = false)
private String username;
#Column(nullable = false)
private String gender;
#Column(unique = true, nullable = false)
private String email;
#Column(nullable = false)
private String password;
#Column(nullable = false)
private String role;
#ManyToOne(cascade = CascadeType.???, fetch = FetchType.LAZY)
#JoinTable(
name = "employee_works_in_dept",
joinColumns = {#JoinColumn(name = "employee_SSN")},
inverseJoinColumns = {#JoinColumn(name = "dept_code")}
)
private Department department;
}
What is that I have to replace with ??
I tried with CascadeType.ALL but it also deletes the Entire Department
First of all I don't think you need a separate join table if your Employee can only be a part of one Department.
If your employee can work on multiple departments then it is Many to Many relation.
When considering your question, you can simply remove your CascadeType. you don't need it in the Employee class.
If you need to delete all the employees under a Department when Department is deleted , then you can simply add your CascadeType.ALL on Department side.
There is a previous thread with better answer here.
What is the meaning of the CascadeType.ALL for a #ManyToOne JPA association

Hibernate "ManyToOne ... references an unknown entity" exception

I just cannot get the relationship working between my two classes mapped to SQL tables with Hibernate.
The Role class:
#Entity
#Table(name = "role")
public class Role {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private int id;
#Column(name="name")
private String name;
#OneToMany(mappedBy="memberinfo")
private Set<Memberinfo> members;
...
}
And the Memberinfo class:
#Entity
#Table(name = "memberinfo")
public class Memberinfo {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id", nullable = false)
private int id;
#Column(name = "userid", nullable = false)
private String userid;
#Column(name = "email", nullable = false)
private String email;
#Column(name = "password", nullable = false)
private String password;
#Column(name = "salt", nullable = false)
private String salt;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "address")
private String address;
#Column(name = "phonenum")
private String phonenum;
#ManyToOne(targetEntity=Role.class)
#JoinColumn(name="role_id")
private Role role;
...
}
When i try to fetch data from the DB, it connects, but throws an exception:
"HTTP Status 500 - #OneToOne or #ManyToOne on model.Memberinfo.role references an unknown entity: model.Role".
If i delete the variable "Role", then it works, and i can fetch the membership data, but i need the connection between the two tables, but in this case, the previously mentioned exception appears every time.
No other solutions on stackoverflow worked for me so far.
Any idea what am i doing wrong?
The "unknown entity error" can be thrown if the class is not actually an Entity (not annotated whith javax.persistence #Entity) or if the persitence provider doesn't "know" the class (package not scanned).
Is the Role class imported in Memberinfo the correct one ? Maybe you are importing another Role class from another library.

Hibernate user and friend relationship add other column

I want to make a friend system via hibernate, and I need to check user-friend's corresponding time when this user had related to other.
I hope table columns like following code:
table 1:
int id;
String name;
table 2:
int user_id;
int friend_id;
Date startdate;
POJO code:
public class User {
private int id;
private String name;
private Map<User,Date> friends;
}
I think you need nothing more then JPA. Something like this:
#Entity
#Table(name = "users")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column
private String name;
#ElementCollection(fetch = FetchType.EAGER)
#CollectionTable(name = "friendship", joinColumns = #JoinColumn(name = "user_id"))
#MapKeyColumn(name = "friend_id")
#Column(name = "start_date")
private Map<Long, Date> friendshipMap;
... getters/setters ...
}

Categories