#Embedded object gererating CROSS JOIN on JPQL - jpa

i'm try use JPA #Embedded object in Entity. That Embedded has #OneToOne relationsip, but wen i try get filter using JPQL. there is generating CROSS JOIN, but i expected INNER JOIN. See the axample:
#Entity
Class A{
#Id
Long id;
#Embedded
private B b;
}
#Emdeddable
Class B{
#OneToOne
private C c;
}
#Entity
Class C{
#Column
private name;
}
In database, the FK column to "C" table is in "A" table. When i try this JPQL: SELECT FROM a WHERE a.b.c.name = :name i expected JPA does the folowing: A inner join C on A.id=C.id, but JPA does A cross join B where A.id=B.id
I dont want put C relationship in entity A becouse my specific code organization. Have any solution to this? Is this a bug?

Related

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;
}

Why hibernate generates cross join on delete?

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

JPA how to aggregate columns in a join

I'm trying to implement a query like this in JPA:
SELECT
ta.field_aggr_1,
ta.field_aggr_2,
MIN(tb.date_inv) AS min_date_inv,
MAX(tb.date_inv) AS max_date_inv
FROM table_a ta
INNER JOIN table_b tb ON ta.idB = tb.id
GROUP BY
ta.field_aggr_1,
ta.field_aggr_2
The key point is the MIN and MAX functions that apply in one column of a joined table.
I've created the entities:
#Entity
#Table(name="table_a")
public class EntityA extends Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Column(name="field_aggr_1")
private String field_aggr_1;
#Column(name="field_aggr_2")
private String field_aggr_2;
#ManyToOne
#JoinColumn(name="idB")
private EntityB entityB;
// Getters & Setters & HashCode & equals & toString
}
#Entity
#Table(name="table_b")
public class EntityB extends Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long id;
#Column(name="date_inv")
private String date_inv;
// Getters & Setters & HashCode & equals & toString
}
And in the service I want to create query:
EntityManager em = ...;
Root<EntityA> root = criteriaQuery.from(EntityA.class);
EntityType<EntityA> type = this.em.getMetamodel().entity(EntityA.class);
Join<EntityA, EntityB> join = root.join(type.getDeclaredSingularAttribute("entityB", EntityB.class));
List<Selection<?>> fields = new ArrayList<Selection<?>>();
// grouping fields
fields.add(root.<EntityA>get("field_aggr_1"));
fields.add(root.<EntityA>get("field_aggr_2"));
I've managed to include fields from the joined table,
fields.add(join.<EntityB>get("date_inv"));
BUT I haven't succeed in implement the min aggregation.
Thanks in advance for your answers!
I have managed to solve the question. First, I needed to have one more "root" and one more "entityType" for the joined entity:
Root<EntityB> rootB = criteriaQuery.from(EntityB.class);
EntityType<EntityB> typeB = this.em.getMetamodel().entity(EntityB.class);
With these, now I can do what I needed:
fields.add(builder.least(rootB.get(typeB.getDeclaredSingularAttribute("date_inv", String.class))));
fields.add(builder.greatest(rootB.get(typeB.getDeclaredSingularAttribute("date_inv", String.class))));
Hope that it helps someone!

How to write complex query using criteria builder

Please help me to get the criteria builder version of following sql for the below given entity class:
sql:
select u.userid from employee e inner join user u on e.userid=u.userid
inner join employee_project ep on ep.empId=e.empId
inner join ep.projectId=p.projectId where p.projectId in (?,?...);
And also this should be able to used as a sub query too.
#Entity
#Table(name="Employee")
public class Employee {
#Id
#GeneratedValue(strategy= GenerationType.AUTO)
private Long id;
#OneToOne(optional=false, cascade=CascadeType.ALL, orphanRemoval=true)
#JoinColumn(name="userId")
private User user;
#ManyToMany
#JoinTable(
name="Employee_Project",
joinColumns={ #JoinColumn(name="empId", referencedColumnName="empId") },
inverseJoinColumns={ #JoinColumn(name="prjId", referencedColumnName="prjId") }
)
private List<Project> project;
}

RIGHT JOIN in JPQL

I have the following JPA entities:
#Entity
class UserClient{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
}
#Entity
class UserAccess{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToOne(optional = false, cascade = { CascadeType.REFRESH })
private UserClient user;
#Temporal(TemporalType.TIMESTAMP)
private Date accessTs;
}
Now I wanted to run a JPQL query to get the list of the users with their last access date.
Unfortunately the following query doesn't return the users that never accessed the system, i.e exist in UserClient table, but don't have any record in UserAccess one.
SELECT ua.user, MAX(ua.accessTs) FROM UserAccess ua RIGHT JOIN ua.user
Do I miss something? Is that correct use of RIGHT JOIN?
I'm using the latest Hibernate JPA release (4.0.0.CR1)
You should make the UserClient table the owner side of the relationship (which make more logical sense IMO). Then you can use a LEFT JOIN instead of a RIGHT JOIN.
SELECT uc, MAX(ua.accessTs) FROM UserClient uc LEFT JOIN uc.userAccess ua
Here's why left join on UserAccess works: