I don't know if this is a problem with Hiberante or if I'm doing something wrong.
I tried to make this example as simple as possible.
My entities:
Book.java:
@Entity
@Table(name="books")
@SQLDelete(sql="UPDATE books set is_deleted = true where id = ?")
@Where(clause="is_deleted = false")
public class Book implements Serializable {
@Id
@Column(name = "id", updatable = false, nullable = false)
private long id;
@Column (name="is_deleted")
private boolean deleted;
@Column(name="title")
private String title;
@ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
@JoinTable(
name="books_authors",
joinColumns = {
@JoinColumn(name="id_books")
},
inverseJoinColumns = {
@JoinColumn(name="id_authors")
}
)
private Set<Author> authors = new HashSet<>();
//getters/setters
}
Author.java:
@Entity
@Table(name="autors")
@SQLDelete(sql="UPDATE autors set is_deleted = true where id = ?")
@Where(clause="is_deleted = false")
public class Author implements Serializable {
@Id
@Column(name = "id", updatable = false, nullable = false)
private long id;
@Column(name="name")
private String name;
@Column (name="is_deleted")
private boolean deleted;
@ManyToMany(mappedBy = "authors", fetch = FetchType.LAZY)
private Set<Book> books = new HashSet<>();
//getters/setters
}
underlying tables:
create table autors (
id int8 not null,
is_deleted boolean,
name varchar(255),
primary key (id)
);
create table books (
id int8 not null,
is_deleted boolean,
title varchar(255),
primary key (id)
);
create table books_authors (
id_books int8 not null,
id_authors int8 not null,
primary key (id_books, id_authors)
);
alter table if exists books_authors add constraint FK9xru8ocoxufr5eva6atfyfjxh foreign key (id_authors) references autors;
alter table if exists books_authors add constraint FKx5d5025t9ct5kcbwwxm6tipo foreign key (id_books) references books;
Data:
select * from books;
| id | is_deleted | title |
|---|---|---|
| 1 | false | TITLE_1 |
| 2 | false | TITLE_2 |
select * from authors;
| id | is_deleted | name |
|---|---|---|
| 1 | false | not deleted |
| 2 | true | DELETED! |
select * from books b
join books_authors ba on b.id = ba.id_books
join autors a on a.id = ba.id_authors;
| id | is_deleted | title | id_books | id_authors | id | is_deleted | name |
|---|---|---|---|---|---|---|---|
| 1 | false | TITLE_1 | 1 | 1 | 1 | false | not deleted |
| 2 | false | TITLE_2 | 2 | 2 | 2 | true | DELETED! |
JPQL:
public Optional<Book> getBookWithSoftDeletedAuthor(long bookId) {
List<Book> myBooks = em.createQuery(
"select b from " +
"Book b " +
"left join fetch b.authors a " +
"where b.id = :bookId", Book.class)
.setParameter("bookId", bookId)
.getResultList();
return myBooks.size() > 0 ? Optional.of(myBooks.get(0)) : Optional.empty();
}
persistence.xml:
<persistence xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd"
version="2.1">
<persistence-unit name="WildflyUnit">
<description> Persistence unit for Wildfly </description>
<jta-data-source>java:jboss/datasources/WildflyDataSource</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL95Dialect"/>
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
<property name="hibernate.generate_statistics" value="true" />
</properties>
</persistence-unit>
</persistence>
datasource from Wildfly's standalone.xml:
<datasource jndi-name="java:jboss/datasources/WildflyDataSource" pool-name="WildflyDataSource" enabled="true" use-java-context="true" statistics-enabled="true">
<connection-url>jdbc:postgresql://localhost:5432/TestDB</connection-url>
<driver>postgresql</driver>
<security>
<user-name>wildfly_user</user-name>
<password>********</password>
</security>
</datasource>
Hibernate generates sql statement like this:
select
book0_.id as id1_1_0_,
author2_.id as id1_0_1_,
book0_.is_deleted as is_delet2_1_0_,
book0_.title as title3_1_0_,
author2_.is_deleted as is_delet2_0_1_,
author2_.name as name3_0_1_,
authors1_.id_books as id_books1_2_0__,
authors1_.id_authors as id_autho2_2_0__ -- this column is not null even if the author is soft-deleted
from
books book0_
left outer join
books_authors authors1_
on book0_.id=authors1_.id_books
left outer join
authors author2_
on authors1_.id_authors=author2_.id
and (
author2_.is_deleted = false
)
where
(
book0_.is_deleted = false
)
and book0_.id=2
imho Hibernate should generate this query like this:
select
book0_.id as id1_1_0_,
author2_.id as id1_0_1_,
book0_.is_deleted as is_delet2_1_0_,
book0_.title as title3_1_0_,
author2_.is_deleted as is_delet2_0_1_,
author2_.name as name3_0_1_,
book0_.id as id_books1_2_0__,
author2_.id as id_autho2_2_0__
from
books book0_
left outer join
books_authors authors1_
on book0_.id=authors1_.id_books
left outer join
authors author2_
on authors1_.id_authors=author2_.id
and (
author2_.is_deleted = false
)
where
(
book0_.is_deleted = false
)
and book0_.id=2
the id_autho2_2_0__ column should come from author2_ (authors table), not from authors1_ (books_authors table). Since the value of the authors1_.id_authors column is not null, hibernate tries to fetch a soft deleted entity from the authors table.
I use Hibernate 5.4.27.Final