Why hibernate generates cross join on delete? - postgresql

I'm using PostgreSQL 9.6 with Hibernate 5.2.10 and have 3 entities:
#Entity
#Table(name = "entity1")
public class Entity1 {
#Id
private long id;
#ManyToOne
#JoinColumn(name = "entity2_id")
private Table2 table2;
}
#Entity
#Table(name = "entity2")
public class Entity2 {
#Id
private long id;
#ManyToOne
#JoinColumn(name = "entity3_id")
private Table3 table3;
}
#Entity
#Table(name = "entity3")
public class Entity3 {
#Id
private long id;
}
I need to all delete the occurencies of Entity1 when it joins Entity2 filtering by Entity3, like this:
em.createQuery("delete from Entity1 e where e.entity2.entity3 = :entity3")
And Hibernate generates the following SQL:
delete from entity1 cross join entity2 entity2_ where entity3_id=?
The problem is that PostgreSQL does not recognized the cross join and I didn't find any other way to do this (other than using native query).
PS: in the database all the tables have foreign keys.

Now I found that it's not possible to use joins in delete queries (actually a was migrating from EclipseLink to Hibernate and on EclipseLink it worked, just on Hibernate it doesn't).
Hibernate Exception on MySQL Cross Join Query

Related

JPA join same child table with a differen key

I have situation that the same table is linked two different keys
all names either it manager's name or name of the employee stored in "employee_table" reason is manager is also an employee
manager_link_table
id
manager_id
employee_table
id
name
Now I want two child objects as below
I am trying to create JPA Entity with OneToOne as
#Entity
#Table(name="manager_link_table")
class ManagerLinkTable {
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn( name="id", referencedColumnName="id")
private EmployeeTable employee;
#OneToOne(fetch = FetchType.EAGER)
#JoinColumn( name="id", referencedColumnName="manager_id")
private EmployeeTable manager;
}
#Entity
#Table(name="employee_table")
class EmployeeTable {
private String id;
private String name;
}
I am writing JPA query as
SELECT m FROM ManagerLinkTable m
I want m.employee.name and m.manager.name to be different
But I am getting the same, it seems the referencedColumnName is ignored while joining the table
Does anyone faced this issue ?

Spring JPA difficult delete

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.

Hibernate Postgresql select for update with outer join issue

I have faced with issue trying to select for update row using Spring data with Hibernate as JPA implementation and Postgresql.
Suppose we have entities:A,B,C.
public class A{
#Id
private Long id;
#OneToMany(fetch = FetchType.EAGER)
private Set<B> bSet;
#OneToMany(fetch = FetchType.EAGER)
private Set<C> cSet;
}
Suppose we want to select A with all related B and C entities for update i.e. with locking row related to A table.
#Query(SELECT a FROM A a
LEFT JOIN FETCH a.bSet
LEFT JOIN FETCH a.cSet
WHERE a.id=?)
#Lock(LockModeType.PESSIMISTIC_WRITE)
public A selectAndLockA(Long Aid);
The query will look like
SELECT a.column1, ... from tableA a LEFT JOIN tableB b ... FOR UPDATE of a,c
FOR UPDATE of a,c
The query will try to lock two tables what leads to exception like :
org.postgresql.util.PSQLException: ERROR: FOR UPDATE cannot be applied to the nullable side of an outer join
What I try to archive is locking only first table "FOR UPDATE OF a"
Is it possible to configure somehow or tell Hibernate to lock only first table.
This is not supported by PostreSQL. If you do an outer SELECT nothing can prevent somebody from inserting a row into the LEFT JOINED table thereby modifiying the result set you are looking at (e.g. the columns would not be NULL anymore on a repeated read).
For a detailed explanantion see here
It's been a long time since question was created, but I have a similar problem and hope my answer will help somebody.
Suppose that we have this JPA entities:
#Entity
#Table(name = "card_transactions")
public class CardTransactionsEntity {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "card_trans_seq")
#SequenceGenerator(name = "card_trans_seq", sequenceName = "card_trans_seq")
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "ofd_id", referencedColumnName = "ofd_id"),
#JoinColumn(name = "receipt_id", referencedColumnName = "receipt_id")})
private ReceiptsEntity receipt;
#Column
#Enumerated(EnumType.STRING)
private CardTransactionStatus requestStatus;
...
}
#Entity
#Table(name = "receipts")
public class ReceiptsEntity {
#EmbeddedId
private OfdReceiptId id;
...
}
#Embeddable
public class OfdReceiptId implements Serializable {
#Column(name = "ofd_id")
#Enumerated(EnumType.STRING)
private OfdId ofdId;
#Column(name = "receipt_id")
private String receiptId;
...
}
And we want select CardTransactionsEntity with fetched ReceiptsEntity for pessimistic update only CardTransactionsEntity. This can be done using Hibernate and Spring Data JPA repository as
public interface CardTransactionRepository extends JpaRepository<CardTransactionsEntity, Long> {
#Query("select ct from CardTransactionsEntity ct left join fetch ct.receipt r where ct.requestStatus = :requestStatus")
#Lock(value = LockModeType.PESSIMISTIC_WRITE)
#QueryHints(value = {
#QueryHint(name = "javax.persistence.lock.timeout", value = "-2"), // LockOptions.SKIP_LOCKED
#QueryHint(name = "org.hibernate.lockMode.r", value = "NONE") // "r" is alias for ct.receipt and will excluded from PESSIMISTIC_WRITE
})
List<CardTransactionsEntity> loadCardTransactions(#Param("requestStatus") CardTransactionStatus requestStatus, Pageable pageable);
}
This repository method will execute query like
SELECT ct.*, r.* from card_transactions ct LEFT OUTER JOIN receipts r ON ct.ofd_id = r.ofd_id and ct.receipt_id = r.receipt_id WHERE ct.request_status=? LIMIT ? FOR UPDATE OF ct SKIP LOCKED
You can bypass this error with joining the tables with FetchType.LAZY. This fetch type is the default one and it is not required to specify for #OneToMany joins.
public class A{
#Id
private Long id;
#OneToMany
private Set<B> bSet;
#OneToMany
private Set<C> cSet;
}

JPA wrong number of columns

Using Spring Framework JPA I have an Entity
#Entity
#IdClass(ComposityKey.class)
#Table(name="table1")
public class Table1 {
#Id
#Column(name="tableId)
private int tableId;
#Id
private int tableId2;
...
}
2nd Entity contains a reference to Table1
#Entity
#Table(name = "table2")
public class Table2 {
#ManyToOne
#JoinColumn(name="tableId")
private Table1 table1;
}
Now I'm getting the obvious AnnotationException
A Foreign key refering Table1 from Table2 has the wrong number of column. should be 2
Table2 doesn't have tableId2 as a value
So how can i JoinColumn on a Composite Key?

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.