Spring JPA difficult delete - jpa

Can someone help with the following model, implemented, but unable to delete as desired
Author OneToMany with Book
Book ManyToMany with Genre
Genre ManyToMany with Book
I can create the db and add records. I believe my cascade designation is wrong as I cannot do the following
From Author, delete a book(yes odd,copyright issues:) have it deleted from Books BUT also have it removed from Genre
The problem is depending on what cascade I either get FK violation on Genre delete or FK violation on Book delete. Is it a deadlock? It is a trivial pattern and must be solveable. Thank you

Ultimately I think that cascade is best used to handle the case when all child relations are deleted but still I think it's a complicated annotation and not well understood by most. I think that if the Book entity owns the authors and genres relationships then the ORM has no choice but to delete the relations when a book is deleted.
When I setup a simple springboot project it seems to take case of all this.
#Entity
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Author {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY) private Long id;
#ManyToMany(mappedBy = "authors")
#lombok.ToString.Exclude
private Set<Book> books;
}
#Entity
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Genre {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(mappedBy = "genres")
#lombok.ToString.Exclude
private Set<Book> books;
}
#Entity
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class Book {
#Id #GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany
private Set<Author> authors;
#ManyToMany
private Set<Genre> genres;
}
And an simple example:
Author author = authorRepository.save(Author.builder().build());
Genre genre = genreRepository.save(Genre.builder().build());
Book book = bookRepository.save(Book.builder().authors(Collections.singleton(author)).genres(Collections.singleton(genre)).build());
System.out.println(book);
bookRepository.delete(bookRepository.getOne(book.getId()));
And the logs seem to indicate that authors and genres are cleaned up when a book is deleted.
Hibernate: insert into author (id) values (null)
Hibernate: insert into genre (id) values (null)
Hibernate: insert into book (id) values (null)
Hibernate: insert into book_authors (books_id, authors_id) values (?, ?)
Hibernate: insert into book_genres (books_id, genres_id) values (?, ?)
Book(id=1, authors=[Author(id=1)], genres=[Genre(id=1)])
Hibernate: select book0_.id as id1_1_0_ from book book0_ where book0_.id=?
Hibernate: delete from book_authors where books_id=?
Hibernate: delete from book_genres where books_id=?
Hibernate: delete from book where id=?
I think this is doing what your questioned is about.

Related

how to store one table primary as an another table foreign key using one-to-one relationship

I have two entities with one to one relationship, I want parent entity primary as a child entity foreign key along with child entity primary key
I have been trying with JPA #MapsId() but I could not succeed
my parent entity
#Id
#SequenceGenerator(initialValue=10000, name = "parent_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator="parent_seq")
#Column(name = "parent_id")
private long parentid;
#OneToOne(mappedBy="parentEntity", cascade = CascadeType.ALL)
private ChildEntity childEntity;
and in my child entity
#SequenceGenerator(initialValue=10000, name = "child_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator="child_seq")
#Column(name = "child_id")
private long childid;
#MapsId("parent_id")
#OneToOne
private ParentEntity parentEntity;
here I would like to create tables using JPA for that I have given
spring.jpa.hibernate.ddl-auto=create
this is working fine but I am expecting that parent_id column should be created inside my childEntity table but it is not creating and the parent_id should be inserted into child tables parent_id column.
When you are going to use #MapsId feature then your 'child' entity should have the 'simple' identifier without any generation. For example:
#Entity
#Table(name = "parents")
public class Parent {
#Id
#GeneratedValue(...)
private Long id;
// other stuff...
}
#Entity
#Table(name = "children")
public class Child {
#Id
private Long id;
#MapsId
#OneToOne(fetch = FetchType.LAZY)
private Parent parent;
// other stuff...
}
In this case, the children table will be like the following:
create table children (
parent_id bigint not null constraint children_pkey primary key,
-- other stuff...
constraint fk_children_parent_id foreign key (parent_id) references parents(id)
);
More info: The best way to map a #OneToOne relationship with JPA and Hibernate

ManyToOne doing multiple queries to fetch records

I have two entities, and dept_id is the foreign key here.
public class Student implements Serializable {
...
#Id
#Column(name="id")
private Integer id;
#ManyToOne
#JoinColumn(name = "dept_id")
private Department department;
...
}
and
public class Department implements Serializable {
...
#Id
#Column(name="id")
private Integer id;
#Column(name = "name")
private String name;
...
}
Now I am doing the following JPQL where I have around 100 parameters inside in query:
select o from Student o where o.id in(1,2,7,9,15,16, ...)
When I see the JPA log, I found it is fetching 100 records from the Student by one query. After that it is doing 100 separate queries to fetch the Department for each Student. So far my understanding is the I/O operation should be slow. Is there any way so that it fetches everything by a single query?
I found this worked for me:
query.setHint("eclipselink.join-fetch", "o.department");
Also, I found this one is handy as it does not make any joining, but fetches the records separately in a bulk.
query.setHint("eclipselink.batch", "o.department");

How to disable delete for JoinTable?

I have problem with disable delete for JoinTable.
#Entity
class Employee
{
#Id
Long id;
#ManyToOne( cascade = { CascadeType.REFRESH } )
#JoinTable( name = "Employee2AddressOracleView",
joinColumns = #JoinColumn( name = "employee_id" ),
inverseJoinColumns = #JoinColumn( name = "address_id" )
private Address address;
)
Address for Employee is calculated in View. It works, I can load Employee but when I want delete employee JPA want delete row from view to. It is possible to disable this delete query?
JPA query in console:
delete from Employee where employee_id = ?
delete from Employee2AddressOracleView where employee_id = ?
The accepted answer has a link to hibernate forums which are dead. I managed to pull the link out on archive.org.
The solution is to create a separate entity representing the join table, mapped to the view, instead of using #JoinTable.
Main entity mappings:
#Entity
#Table(name="Main")
public class MainEntity {
#Id
#Column(name="id")
private Integer id;
#OneToOne
#PrimaryKeyJoinColumn
private JoinTableViewEntity joinEntity;
}
Join table view entity mappings:
#Entity
#Table(name="TableView")
public class JoinTableViewEntity {
#Id
#Column(name="id")
private Integer mainEntityId;
#ManyToOne
#JoinColumn(name="other_id", updatable=false, insertable=false)
private OtherEntity other;
}
It also works without updateable and insertable attributes.
If you are using EclipseLink you can use a DescriptorCustomizer to make the mapping readOnly.

JPA: implicity persist #ManyToOne entities

I have a similar table structure to the following:
Table_A(
a_id,
data,
primary key(a_id)
)
Table_B(
b_id,
a_id,
data,
primary key (b_id),
foreign key (a_id) references Table_A(a_id)
)
There is a one to many relationship between Table_A and Table_B. My question is, if I have an entity for each of these tables, where:
Entity Table_A is composed of a list of Table_B entities and
Entity Table_B does not necessarily need a reference to Table_A (just the fields b_id, a_id, data)
is it possible to annotate these entities in a way where I can persist a Table_A entity and the process will implicitly persist all Table_B entities with the value of the new generated Table_A primary key a_id.
Thanks in advance.
Here is what I have essentially . But I get the below exception. Looks like Table_B is being persisted prior to Table_A, therefore no a_id value exists.
#Entity
public class Table_A {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "a_id")
private Integer id;
#OneToMany (cascade = CascadeType.PERSIST)
#JoinColumn(name="a_id")
private List<Table_B> bList;
private String data;
}
#Entity
public class Table_B {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "b_id")
private Integer id;
private String data;
}
ERROR: null value in column "a_id" violates not-null constraint
Detail: Failing row contains (null, Testing, 16)
Yes, with a cascade set to PERSIST on the OneToMany association.
Note that you shouldn't have aId in the B entity. Either make the association bidirectional, and have a field of type TableA, or leave it unidirectional and don't have any field in B mapped to a_id.
EDIT: AFAIR, you need to tell Hibernate that the column is not null, else it tries to insert everything first, and then populate the foreign keys:
#OneToMany (cascade = CascadeType.PERSIST)
#JoinColumn(name="a_id", nullable=false)
private List<Table_B> bList;
#OneToMany(mappedBy = "a_id", cascade = CascadeType.PERSIST)
instead of
#OneToMany(cascade = CascadeType.PERSIST) #JoinColumn(name="a_id")

JPA Relationship Mapping Concept

I have several questions on bidirectional mapping.
I have these entities:
Employee(1) - (1) Parking_Lot
Employee(*) - (1) Department
Employee(*) - (1) Project
What is the source and target entity for the above relationship?
What is the owner for ManyToOne relationship. I wonder the owner is
on Many entity or One entity?
Do mappedBy specify on owner side or inverse side?
Please help.
EDIT:
I have the following table:
Project - Proj_Client - Client (Many to Many relationship) and persist the project table but the client is not get persist. What wrong with it?
Thanks.
#Entity
#Table(name="empoyee")
public class Employee {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#ManyToOne
#JoinColumn(name="department_id", referencedColumnName="id")
private Department department;
#ManyToOne
#JoinColumn(name="project_id", referencedColumnName="id")
private Project projects;
#OneToOne(mappedBy="employee")
private ParkingLot parkingLot;
//Other properties,constructors, getters and setters and so on
}
#Entity
#Table(name="department")
public class Department implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToMany(mappedBy="department")
private List<Employee> employees;
//Other properties,constructors, getters and setters and so on}
#Entity
#Table(name="parking_lot")
public class ParkingLot implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToOne
#JoinColumn(name="employee_id",referencedColumnName="id")
private Employee employee;
//Other properties,constructors, getters and setters and so on}
#Entity
#Table(name="project")
public class Project implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToMany(mappedBy="project")
private List<Employee> employees;
//Other properties,constructors, getters and setters and so on
}
If the relationship is unidirectional there really isn't an owning side, and there isn't any mappedBy annotations.
If the relationship is bidirectional there is a side with the mappedBy annotation - the other side is the owning side.
The owning side is the side that owns the relationship. So the term is not ment to be applied like a ParkingLot owns its Employee or an Employee owns its ParkingLot, but rather like the relationship between ParkingLot and Employee is owned by the Employee (or ParkingLot see below).
For ManyToOne there is no mappedBy, so it is always the entity specified under the OneToMany annotation that owns the relationship (makes sense, since the for example the project table can't have foreign keys to all its employees)
So for the two ManyToOne/OneToMany mappings in your example we don't have a choice in which side owns the relationship. Between Employee and ParkingLot we have a choice, I choosed ParkingLot.
When given a choice, what does it matter? Well, the main difference is that the mappedBy have the porperty cascade. Please note that it doesn't matter which table have the foreign key, or if the relationship is in its own table - JPA supports all cases (#InverseJoinColumn etc).
For bidirectional mappings there isn't a clear target and source for the mapping, it depends on which way you at the mapping from. The term is more applicable to unidirectional mappings, and there the source side is of course the entity with the mapping (that is possible knowledge of the target entity)
4) Not applicable (unless you make the relationship between ParkingLot and Employee unidirectional).
5) The owner of the relationship is always "on the One entity"
6) inverse side
Final note:
"owning side" is confusing, for example we could design so that a Department owns its Employees and if we delete a Department all its employees would also be deleted. This would be done by changing #OneToMany(mappedBy="department") into #OneToMany(mappedBy="department", cascade= CascadeType.REMOVE) then it would really make sense to say "the Department entity owns its Employee entities" but the relationship would still be owned by the Employee entity.