How can I replace SQL query with UNION to receive same responce using JPA? - spring

I've got some troubles to get distinct sender and receiver from database for my chat application:
This is my message model:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#NotNull
private String Content;
#Temporal(TemporalType.TIMESTAMP)
private Date date = new Date();
#ManyToOne
#JoinColumn(name = "sender_id")
private User sender;
#ManyToOne
#JoinColumn(name = "receiver_id")
private User receiver;
Assuming we are logged in as user A and there's database like this:
sender
receiver
A
B
B
A
A
C
D
A
The result I would like to achieve is:
B
C
D
Using SQL I can achieve this using this query:
select distinct receiver_id from message where sender_id =4 union
select distinct sender_id from message as m where receiver_id = 4
But there is the problem, JPA does not support UNION :(
Send Help PLS

You can select either the sender or the receiver based on specification of a target value. (see demo)
select string_agg(res, ' ' order by res)
from (
select distinct
case when sender = 'A'
then receiver
else sender
end res
from test
where 'A' in (sender, receiver)
) sq;
That is of course straight SQL. Unfortunately I do not know your obscurification manager (JPA?) so you will have to translate. Or just use raw SQL.

Related

Inner join with more than one OR conditions in spring boot

I am using spring boot specification and trying to execute a query that looks like this -
SELECT DISTINCT
p.name
FROM
partner p
INNER JOIN
detail d ON p.detail_id = d.id
INNER JOIN
account a ON d.account_id = a.id
OR d.crm_id = a.top_parent
OR d.crm_id = a.global_partner
I have used the code
Join<Partner, Detail> details = root.join("detail");
Join<Detail, Account> account = details.join("account");
Predicate global = cb.equal(details.get("crm_id "), account.get("top_parent"));
Predicate top = cb.equal(details.get("crm_id "), account.get("global_partner"));
account.on(cb.or(global, top));
However, it creates the query
SELECT DISTINCT
p.name
FROM
partner p
INNER JOIN
detail d ON p.detail_id = d.id
INNER JOIN
account a ON d.account_id = a.id
AND (d.crm_id = a.top_parent
OR d.crm_id = a.global_partner)
Notice the AND operator in the query...I need to replace it OR operator
Another use case I am struggling with
#Entity
public class Detail {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#OneToMany(mappedBy = "detail", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<Skill> skills;
}
I am trying to generate the query below using specifications
order by (select count(s.detail_id) from skill s where detail.id = s.detail_id AND s.category is not null) desc
I have used the code below
cq.orderBy(cb.desc(cb.size(details.get("skills"))));
But the query it generates is
order by (select count(s.detail_id) from skill s where detail.id = s.detail_id) desc
Notice that I am unable to add an extra AND to the order by clause
I believe you can not change that AND.
could you change the query in the following way
SELECT DISTINCT p.name
FROM partner p INNER JOIN detail d ON p.detail_id = d.id, account a
where d.account_id = a.id
OR d.crm_id = a.top_parent
OR d.crm_id = a.global_partner
and the jpa criteria similar to
Join<Partner, Detail> details = root.join("detail");
Root<Account> account = criteria.from(Account.class);
Predicate global = cb.equal(details.get("crm_id"), account.get("top_parent"));
Predicate top = cb.equal(details.get("crm_id"), account.get("global_partner"));
Predicate byId = cb.equal(details.get("account").get("id"), account.get("id"));
Predicate or = cb.or(global, top, byId);
criteria.where(or);

Hibernate, select where foreign key is null

I have entity as follows:
#Entity
#Table(name = "LORRY")
#NamedQueries({
#NamedQuery(name = "Lorry.findSuitableLorries",
query = "SELECT l from Lorry l " +
"WHERE l.order IS NULL")
})
public class Lorry {
#Id
#Column(name = "ID", length = 7)
#Pattern(regexp="[a-zA-Z]{2}\\d{5}")
#Size(min = 7, max = 7)
private String regNum;
#OneToOne(mappedBy = "lorry")
private Order order;
}
Now I need to select all rows where order id is null in the LORRY table, but it doesn't seem to work as I get empty list every time. How can I check if foreign key is null using JPQL?
Try either SELECT l from Lorry l WHERE l.order.id IS NULL or SELECT l from Lorry l left join l.order o WHERE o IS NULL

how to use JPQL query to count values from a table & combine with an other table related data?

I have two tables X and Y. In Table X (Oracle sql), an unique column(primary key) code along other columns in X table.. code column may have some records in table Y which have column code_id. I want to get count of rows in table Y for code with code and other columns in table Y
and I have springboot entity called Entity I want to map results to using jpql so I want the query in JPQL:
public class Entity {
private int id;
private char code;
private String name;
// constructor & setters / getters
}
and Y table have entity Counter
public class Counter {
private int codeid;
}
I want to use jpql query equivalent to this Oracle sql query
select x.*,
(select count(*) from Y y where x.code = y.code_id) as theCount
from X x ORDER BY theCount desc , x.name asc ;
Example:
Code "A" has 3 entries, Code "B" has 2 entries and code "C" has 0 entries in table Y.
code name count
A name1 3
B name2 2
C name3 0
I did some assumptions because I miss your project code. Hope my example will fit your needs. It is not the same SQL but it is still just 1 statement producing the same type of output.
Primary entity Code having collection of Counters:
#Data
#Entity
public class Code {
#Id
private Integer id;
private String code;
private String name;
#OneToMany
#JoinColumn(name = "CODE_ID")
private List<Counter> counterList;
}
#Data
#Entity
public class Counter {
#Id
private Integer id;
}
Spring Data repository:
public interface CodeRepository extends JpaRepository<Code, Integer> {
#Query("select c.code, c.name, count(l) as amount from Code c join c.counterList l group by c.code, c.name")
List<Object[]> getSummary();
}
It returns:
A, TESTNAME1, 3
B, TESTNAME2, 2
In case following is inserted into database:
INSERT INTO CODE (ID, CODE, NAME) VALUES (1, 'A', 'TESTNAME1');
INSERT INTO COUNTER (ID, CODE_ID) VALUES (123,1);
INSERT INTO COUNTER (ID, CODE_ID) VALUES (124,1);
INSERT INTO COUNTER (ID, CODE_ID) VALUES (125,1);
INSERT INTO CODE (ID, CODE, NAME) VALUES (2, 'B', 'TESTNAME2');
INSERT INTO COUNTER (ID, CODE_ID) VALUES (234,2);
INSERT INTO COUNTER (ID, CODE_ID) VALUES (235,2);
This is how the result is produced:
codeRepository.getSummary()
.forEach(sum -> System.out.println(sum[0] + ", " + sum[1] + ", " + sum[2]));

Get distinct column values with a Spring Data JpaRepository

I have this entity (annotations omitted for brevity):
#Entity
class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Lob
private String category;
#Lob
private String name;
#Lob
private String customer_number;
// more attributes omitted
}
I have to get a list of distinct category value for a particular uid
In my JpaRepository I have this:
#Query("SELECT DISTINCT product.category FROM Product as product WHERE product.customerNumber = :cn ORDER BY product.category")
Page<String> findDistinctCategoryByCustomerNumber(String cn,
Pageable pageable);
Without the #Query annotation, the ids are returned, instead of the category values. The generated SQL looks like this:
select distinct product0_.id as id1_0_, product0_.customer_number as customer2_0_, product0_.category as directory3_0_, product0_.name as name4_0_ from product product0_
where product0_.customer_number=?
order by product0_.id desc limit ?
But I need the distinct categories not the product entities. Short of another idea I added the #Query annotation above. But now I get this error:
Order by expression "PRODUCT0_.ID" must be in the result list in this case; SQL statement:
select distinct product0_.directory as col_0_0_ from product product0_ where product0_.customer_number=? order by product0_.directory, product0_.id desc limit ? [90068-197]
But I cannot add id to the result list because that would make the DISTINCT useless, as id is the primary key.
So I either need a suitable method name for automatic query generation or a way to stop JpaRepository from adding its order by clause to the end of my #Query.
The unwanted order by gets created since you are requesting a paged result. The notion of the n-th page only makes sense when your results are ordered and the ordering is coming from your Pageable method argument.
Remove the order by from your query and set the sort attribute of your Pageable to sort by category.

JPA/Hibernate map #OneToMany for Oracle hierarchical data

Say I have parent-child info in table Organization as followed:
id name parent_id
1 A 1
2 A1 1
3 A2 1
4 A11 2
With Oracle, I can get all descendants or ancestors of an organization using "start with/connect by". For example, the following sql will get all the subtree under "A" include itself (i.e. A, A1, A2, A11)
select * from Organization start with id=1 connect by nocycle prior id=parent_id;
Or this sql will get all ancestors of A11 including itself (i.e. A11, A1, A)
with o_hier as (select o.id, o.parent_id, CONNECT_BY_ISCYCLE as lvl from Organization o start with id=4 connect by nocycle prior parent_id = id) select o.* from Organization o, o_hier where o.id = o_hier.id union all select o.* from Organization o, o_hier where o.id = o_hier.parent_id and o_hier.lvl = 1;
Now I want to map this table into OrganizationEntity like this:
#Entity
#Table(name = "Organization")
public class OrganizationEntity {
//getter/setter omitted for readability
#Id
#Column(name = "ID")
private String id;
#Column(name = "NAME")
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#???
List<OrganizationEntity> descendants = new ArrayList<>();
#ManyToOne(fetch = FetchType.LAZY)
#???
List<OrganizationEntity> ancestors= new ArrayList<>();
}
I'm aware of possible performance issue, but can we map something like this using Hibernate/JPA?
This is a tricky one. You can use standard parent and childern mappings.
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID")
OrganizationEntity parent;
#OneToMany(fetch = FetchType.LAZY)
#mappedBy(mappedBy="parent")
List<OrganizationEntity> childern;
And then use standard tree taversals algorithms to get all the ancestors (simple while loop) or all the descendants (some DFS variant, usually preorder).
Performace wise this wound be very slow.
Other, and better idea is just do the traversals within the database with CONNECT BY and then map the result set to objects. You can do that with pure JPA calls or Hibernate specific calls.

Resources