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

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]));

Related

JpaRepository find by manyToMany attribute

Let's imagine classic example of manyToMany relation. I have entities Student and Course and manyToMany relation table between those two entities.
#Entity
data class Student(
#Id
val id: UUID,
val name: String,
#ManyToMany
val courses: Set<Course>
)
#Entity
data class Course(
#Id
val id: UUID,
val title: String,
#ManyToMany
val students: Set<Student>
)
repository can looks like this:
#Repository
interface StudentRepository : JpaRepository<Student, UUID> {
fun findAllByCoursesTitle(title: String): List<Student>
}
Function findAllByCoursesTitle should return all student signed up for math, for example.
My problem is:
How will function looks like when i need to return all students signed up for two specific courses. But i need it for dynamic number of courses, 1 or more.
for example input to function would looks like listOf("math", "geography") or in case of course_ids listOf("UUID", "UUID"). It does not matter if it will be ID or title. What is doalbe.
When i would want to write sql query for that it would looks like this:
SELECT student.id, sc1.course_id, sc2.course_id FROM student
JOIN student_course cs1
ON student.id = cs1.student_id
JOIN student_course cs2
ON student.id = cs2.student_id
WHERE cs1.course_id = ?
AND cs2.course_id = ?
But as i said, i need to have it done for various number of courses.
Please somebody advise.
If you want to resolve logic at SQL statement you can try something like this:
SELECT st.id, sc.course_id, sc.course_id FROM student as st
JOIN (
SELECT st.id as stId, sum(case when c.id IN (:courseIdList) then 1 else 0 end) as cc FROM student as std
JOIN student_course as cs ON std.id = cs.student_id
JOIN cource as c ON c.id = cs.cource_id
GROUP BY (st.id) having cc >= :minimumCourseNumbers
) A on A.stId = st.id
There're courseIdList: List course Ids. minimumCourseNumbers: Minimum course numbers that student must register.
However, I think the logic should move to application to filter.

jdbctemplate equivalent of following query

I have a long list of argument which I need to send to oracle database. I was able to do it by splitting the query but I am unable to find a way to do similar using jdbcTemplate. my query is:
select name,age from person where personId in (A1,F2,D3...G900)
or personId in (A901, C902 , ... R1800)
or personId in (A1801,G1802 .... H2700)
or personId in (P2701, G2702 ... R3600)
or since oracle allow more than 1000 touple but does not allow in so JDBC equivalent for
SELECT field1, field2, field3
FROM table1
WHERE (1, name) IN ((1, value1), (1, value2), (1, value3),.....(1, value5000));
List<Map<String, Object>> findPeeps(List<Long> personIds) {
String sql = "select name,age from person where personId in (:personIds)";
return namedParameterJdbcTemplate.queryForList(sql, new MapSqlParameterSource("personIds", personIds));
}
As #zaki said you can use that but the error that you are getting is from Oracle since there is limit of records you can put in WHERE IN clause. You can try something like this
insert into TEMP values ( ... );
select * from T where (a,b) in (select x,y from temp);
delete from TEMP;

How to write an inner join query to retrieve all records of three tables in Hibernate?

I want to retrieve all records from 3 tables: Personal_Info, Address, and Fitness. I have primary key as CandidateID in Personal_Info table and foreign key as CandidateID in Address table and UserID in Fitness table.
I have written inner query as below which seems to be incorrect as I am not getting all the records from three tables.
public void getAllRecords()
{
int searchId = 1;
Session currentSession = sessionFactory.getCurrentSession();
String query = "FROM Address as a, Fitness as f inner join a.personalInfo as p inner join f.personalInfo as p where p.candidateID=:userID";
Query theQuery = currentSession.createQuery(query);
theQuery.setParameter("userID", searchId);
List<?> list = theQuery.list();
for(int i=0; i<list.size(); i++) {
Object[] row = (Object[]) list.get(i);
System.out.println("Record"+i+": "+row[i]);
}
}
How can I correct this inner query?
You have to join all the three tables using CandidateID.
Try this:
FROM Address as a, Fitness as f, Personal_Info as p
WHERE p.CandidateID = a.CandidateID
AND p.CandidateID = f.UserID

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.

LINQ intersect?

I have two tables in form of these clases
public class Movie
{
private int MovieID;
private string Title;
}
public class transactions
{
private int TransactionID;
private int MovieID;
}
so first table contains ALL movies 2nd contains movies which are RENTED
How do I select ALL movies that are left in the store , ie not rented and are available. Tried soething like:
var moviesavailable =
(from m in db.Movies
select m.MovieID ).Intersect
(from trans in db.Transactions
select trans.MovieID)
but not working...
First way to do it is go over all movies and for each look if there is no transaction with the same MovieID:
db.Movies.Where(m => !db.Transactions.Any(t => t.MovieID == m.MovieID))
Second way is to make left join. We join all rows from Movies and their equivalent rows from Transactions. If there is no row in Transactions for a row in Movies, then for this row the transaction is null (DefaultIfEmpty):
from m in db.Movies
join t in db.Transactions on m.MovieID equals t.MovieID into g
from t in g.DefaultIfEmpty()
where t == null
select m.MovieID

Resources