JPA JoinColumn does not work - jpa

I have started with an example from a book. It was really a dummy oriented one and I am having a problem with the code the book gave.
Here is the code;
#Entity
public class Customer {
#Id
#GeneratedValue
private Long id;
private String firstName;
private String lastName;
private String email;
#OneToOne (fetch = FetchType.LAZY,
cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
#JoinColumn(name = "address_fk")
private Address address;
//getter, setter, Constructor
--//-------------------------------------
#Entity
public class Address {
#Id #GeneratedValue(strategy = GenerationType.AUTO)
private String id;
private String city;
private String street;
private String number;
In here it is complaining on the JoinColumn annotation. It says the column address_fk is not found.
Is this an IDE related issue? Am I missing something?
Edit : No table yet created in the DB. I am expecting them to be seen on the DB automatically by my persistence.xml
<property name="eclipselink.ddl-generation" value="create-tables"/>

Using #JoinColumn annotation to specify foreign column name to be address_fk instead of default address_id is fine.
What likely does not to work is #GeneratedValue with String. According specification only integral types as generated primary keys are portable.

Related

Spring Data Jpa OneToMany save bidirectional

I have a problem with saving child entities.
Here is my example. My model classes look like this:
#Entity
public class ImportDocument {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
private boolean imported;
#Transient
private Status status;
#Basic
private char statusValue;
#OneToMany(mappedBy = "importDocument" , cascade = {CascadeType.ALL})
private List<ImportDocumentItem> importDocumentItems;
}
#Entity
public class ImportDocumentItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "import_document_id")
#JsonIgnore
private ImportDocument importDocument;
}
I have implemented JpaRepository interfaces for both domain classes.
I try to save with:
importDocumentRepository.save(importDocument);
When I save ImportDocument object, everything is inserted. But the problem is that, the import_document_item.import_document_id (which is foreign key of import_document_id) attribute is filled with null value, not with id of import_document that I expected. How can I fix this issue?
Thanks a lot.
You have to set entity relations on both side before saving. Here an example
ImportDocument importDocument = new ImportDocument();
//...
importDocument.setImportDocumentItems(items);
items.forEach(ImportDocumentItem::setImportDocument);
importDocumentRepository.save(importDocument);

Hibernate mapping camelCase field to snake_case

I am using hibernate with postgresql. I am curious if is it possible to map for example this entity:
#Entity
#Data
#NoArgsConstructor
class User {
#Id
private Long id;
private String firstName;
private String lastName;
}
in database I have columns like first_name, last_name. I got error that I introduce wrong names (when I add for example #Column(name = "first_name"); everything is ok. How can I avoid adding #Column neither change database?

JPA Get an entity by intermediate entity

I have 3 entities named Student, Course, and StudentCourse as follows
#Entity
#Table(name = "student")
public class Student {
#Id
#GeneratedValue
private Integer id;
private String fullName;
}
#Entity
#Table(name = "course")
public class Course {
#Id
#GeneratedValue
private Integer id;
private String courseName;
}
#Entity
#Table(name = "student_course")
public class StudeCourse {
#Id
#GeneratedValue
private Integer studentId;
private Integer courseId;
private String extraColumn;
}
Restrictions: There are a couple of restrictions
One student can have only one course or no course at all
An extra entity (StudentCourse) is required to hold the relation with primary key as studentId only
StudentCourse is required and hence cannot be skipped
Get Student with Course entity if there is one registered
Help required in some magical code to retrieve Course of Student if there is one assigned.
#Entity
#Table(name = "student")
public class Student {
#Id
#GeneratedValue
private Integer id;
private String fullName;
// this is not correct code but just what I want
#JoinEntity(entity=StudentCourse, column="courseId")
private Course course;
}
StudentCourse is required and hence cannot be skipped
Ok, lets work with that.
One student can have only one course or no course at all
Implies that there is a #OneToOne relationship between Student and StudentCourse.
With the given information, the following entity model will work:
#Entity
#Table(name = "student")
public class Student {
#Column(name = "id")
#Id
#GeneratedValue
private Integer id;
#Column(name = "full_name")
private String full_name;
#OneToOne
#PrimaryKeyJoinColumn
private StudentCourse studentCourse;
}
#Entity
#Table(name = "student_course")
public class StudentCourse {
#Column(name = "id")
#Id
#GeneratedValue
private Integer id;
#JoinColumn(name = "id", nullable = false, updatable = false)
#MapsId
#OneToOne
private Student student;
#JoinColumn(name = "course_id")
#ManyToOne
private Course course;
...
}
A quick review:
#OneToOne on the Student.studentCourse field signifies that for every Student, there can be only one StudentCourse, and no more.
#PrimaryKeyJoinColumn on the Student.studentCourse field signifies that the value of the primary key column for Student should be used as the foreign key for the related entity, that is, StudentCourse.
#OneToOne on the StudentCourse.student field signifies that for every StudentCourse, there can be only one Student.
#MapsId on the StudentCourse.student field signifies that the primary key column for StudentCourse should be used as the join column for the association.
To check if a student has a course assigned, simply check if student.getStudentCourse() != null and then get the assigned course as student.getStudentCourse().getCourse().

ERROR: update or delete on table "tablename" violates foreign key constraint

I'm trying to delete the parent student or parent course and I get this error:
Caused by: org.postgresql.util.PSQLException: ERROR: update or delete on table "student" violates foreign key constraint "fkeyvuofq5vwdylcf78jar3mxol" on table "registration"
RegistrationId class is a composite key used in Registration class. I'm using Spring data jpa and spring boot.
What am I doing wrong? I know that putting cascadetype.all should also remove the children when the parent is deleted but it is giving me an error instead.
#Embeddable
public class RegistrationId implements Serializable {
#JsonIgnoreProperties("notifications")
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "student_pcn", referencedColumnName="pcn")
private Student student;
#JsonIgnoreProperties({"teachers", "states", "reviews"})
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "course_code", referencedColumnName="code")
private Course course;
Registration class
#Entity(name = "Registration")
#Table(name = "registration")
public class Registration {
#EmbeddedId
private RegistrationId id;
When you're using a relational DB, you are setting entities with relationships between these entities.
The error that you're getting means that:
You're trying to delete a record that its primary key is functioning as a foreign key in another table, thus you can't delete it.
In order to delete that record, first, delete the record with the foreign key, and then delete the original that you wanted to delete.
I made it work by using hibernate #OnDelete annotation. Some how the JPA.persistence CascadeTypes were not working. They had no effect for whichever I chose.
Just like below. Now I can remove the parent Student or the parent Course and all children(Registrations) are deleted with them.
#Embeddable
public class RegistrationId implements Serializable {
#JsonIgnoreProperties("notifications")
#OnDelete(action = OnDeleteAction.CASCADE)
#OneToOne
#JoinColumn(name = "student_pcn", referencedColumnName="pcn")
private Student student;
#JsonIgnoreProperties({"teachers", "states", "reviews"})
#OnDelete(action = OnDeleteAction.CASCADE)
#OneToOne
#JoinColumn(name = "course_code", referencedColumnName="code")
private Course course;
Foreign keys guarantee that an entry will exist in another table. This is a way of ensuring data integrity. SQL will never allow you to delete this entry while it still deletes in the other table. Either (1) this is letting you know you would have made a grave mistake by deleting this thing which is required or (2) you would like to put in a cascading delete so that not only is this entry deleted but so is what is supposed to be referencing it in the other table. Information on cascading deletes can be found here and written fairly easily (https://www.techonthenet.com/sql_server/foreign_keys/foreign_delete.php). If neither of these two descriptions fits you, evaluate why your foreign key relationship exists in the first place because it probably should not.
Try this method too. I got the answer with this method,This is just a test to remove.
Pay attention to the cascade!
MyUser Entity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstname;
private String lastname;
private String mobile;
#Column(unique = true)
private String email;
private Long date;
private LocalTime localiime;
private LocalTime localiimeend;
#ManyToOne(fetch = FetchType.LAZY,cascade = CascadeType.MERGE)
#JoinColumn(foreignKey = #ForeignKey(name = "role_fk"))
private Role role;
Role Entity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#OneToMany(mappedBy = "role", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<MyUser> users;
#ManyToOne (fetch = FetchType.LAZY,cascade = CascadeType.MERGE)
#JoinColumn(foreignKey = #ForeignKey(name = "rolecat_fk"))
private rolecat rolecat;
rolecat Entity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToMany(mappedBy = "rolecat", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Role> roles;

how to develop JPA bi-directional entities in spring toot

I am new to spring boot and jpa/hibernate, please bear my inaccurate usage of the terminologies.
I have two entities: book and address. A book is published in a certain city which is stored in "address", a "address" can publish multiple books.
The DB schema for book is: id, name, author, price, addressid
schema for address: addressid, addressCountry, addressCity
The entity for book:
#Entity
#Table(name = "test_book")
public class Book implements Serializable {
private static final long serialVersionUID = 8025948150436422040L;
#Id
long id;
#Column(name = "name")
String name;
#Column(name = "author")
String author;
#Column(name = "price")
long price;
#ManyToOne
#JoinColumn(name = "addressid")
private Address address;
...//getter and setter
The entity for address
#Entity
#Table(name = "test_address")
public class Address implements Serializable{
private static final long serialVersionUID = -3541059157210384355L;
#Id
#Column(name= "addressid")
private long addressId;
#Column(name="addresscountry")
private String addressCountry;
#Column(name="addresscity")
private String addressCity;
#OneToMany(mappedBy = "address")
private Collection<Book> books;
...//getter setter
But when I call the Restful service, I get infinite loop...
[{"id":11,"name":"Java Book","author":"Jame Gosling","price":100,"address":{"addressId":1,"addressCountry":"China","addressCity":"Shanghai","books":[{"id":11,"name":"Java Book","author":"Jame Gosling","price":100,"address":...
I did some search. And my request is:
when I search a book, I can get the information: id, name, author, price, address..
And also I can query a address to get all the books the city published.
When I add Json Annotation #JsonManagedReference in address and #JsonBackReference in book entity, I can query book but cannot get address information.
Could you please help how to solve the problems? Thank you very much.
You can ignore the #JsonIgnore on getter for books. This will exclude the Collection<Book> books property from serialization the Address.
Link : JacksonAnnotations - Faster XML Wiki
Example:
#JsonIgnore
public Collection<Book> getBooks() {
...
}
#JsonIgnore
The Jackson annotation #JsonIgnore is used to tell Jackson to ignore a
certain property (field) of a Java object. The property is ignored
both when reading JSON into Java objects, and when writing Java
objects into JSON.
In your case this is happening as there is bidirectional relationship and so it will go into loop. To stop this you need to provide JsonIgnore
And so your code will be like :
-> The entity for address
#Entity
#Table(name = "test_address")
public class Address implements Serializable{
private static final long serialVersionUID = -3541059157210384355L;
#Id
#Column(name= "addressid")
private long addressId;
#Column(name="addresscountry")
private String addressCountry;
#Column(name="addresscity")
private String addressCity;
#OneToMany(mappedBy = "address")
#JsonIgnore
private Collection<Book> books;