JOOQ Howto fetch a result of join into POJO without flattening properties? - java

I have a following query where I join tables A,B, and C:
C is related to B via C.B_ID
B is related to A via B.A_ID
I want to retrieve a report, where for each C, I want to retrieve also fields from corresponding B and A.
If only a subset of fields is required, a projection and fetching to a POJO (with required properties from C, B, A) is an obvious approach.
class CReportDTO {
Long c_id;
Long c_field1;
Long c_bid;
Long b_field1;
// ...
CReportDTO(Long c_id, Long c_field1, Long c_bid, Long b_field1) {
// ...
}
// ..
}
public List<CReportDTO> getPendingScheduledDeployments() {
return dslContext.select(
C.ID,
C.FIELD1,
C.B_ID,
B.FIELD1,
B.A_ID
A.FIELD1,
A.FIELD2
)
.from(C)
.join(B)
.on(C.B_ID.eq(B.ID))
.join(A)
.on(B.A_ID.eq(A.ID))
.fetchInto(CReportDTO.class);
};
}
My question
In case where all fields are needed I would prefer to have my report DTO contain A, B, C POJOs, without flattening them out:
class CReportDTO2 {
C c;
B b;
A a;
CReportDTO2(C c, B b, A a) {
// ...
}
// ..
}
Is it possible to modify my query to:
include all fields from each table
massage it into CReportDTO2 without too much verbosity

You can use a lesser known feature of jOOQ's DefaultRecordMapper by aliasing your fields using a dot notation that denotes the nesting structure of your DTO:
public List<CReportDTO> getPendingScheduledDeployments() {
return dslContext.select(
// Add these vvvvvvvvvvvvvvvvvvvv
C.ID .as("c.c_id"),
C.FIELD1 .as("c.c_field1"),
C.B_ID .as("c.b_id"),
B.FIELD1 .as("b.b_field1"),
B.A_ID .as("b.a_id")
A.FIELD1 .as("a.a_field1"),
A.FIELD2 .as("a.a_field2")
)
.from(C)
.join(B)
.on(C.B_ID.eq(B.ID))
.join(A)
.on(B.A_ID.eq(A.ID))
.fetchInto(CReportDTO2.class);
}
See Javadoc
If Field.getName() is MY_field.MY_nested_field (case-sensitive!), then this field's value will be considered a nested value MY_nested_field, which is set on a nested POJO
Note that this doesn't work with the constructor you've provided. You'll have to provide a default constructor as well, and make your fields non-final (in case they are).

Related

How to fetch object using the List Association in Hibernate

I have a class A with setters and getters
class A{
private int id;
private List<B> b;
}
I want to fetch the A object based on the B objects. So suppose I have a list of B objects I want to get the A objects that contain the B.
So i decided to use in
Here is the code
Criteria criteria = session.createCriteria(A.class).setCacheable(false);
criteria.add(Restrictions.in("b",{list of B})).list();
This gives an error java.sql.SQLException: No value specified for parameter 1
How can I get object A using B
You should join the B's first:
Criteria criteria = session.createCriteria(A.class)
.setCacheable(false)
.setFetchMode("bList", FetchMode.JOIN)
.createAlias("bList", "b");
and then use an IN for the ids of b:
criteria.add(Restrictions.in("b.id",{list of B})).list();
Set your fetch mode accordingly whether you want the list of B's also apart from A.

Update Entity Relationship via UPDATE .. SET Query

I have two entities:
class A {
#OneToMany(mappedBy = "a")
List<B> bs;
// getter/ setter
}
class B {
#ManyToOne
A a;
// getter/ setter
}
To delete one b, I first need to invalidate that relationship.
"Traditionally" I would do something like that:
A a = em.getReference(A.class, entityIdA)
B b = em.getReference(B.class, entityIdB);
a.getBs().remove(b);
b.setA(null);
em.remove(b);
This is not very performant, if the List of a's is getting large (a few hundreds in my case).
I know I can also use JPQL to create an update query.
something like this:
Query q = em.createQuery("UPDATE B b SET b.a = NULL");
q.executeUpdate();
Question: What would be the corresponding JPQL query to remove one b from a's list of bs?
In short:
How to translate
a.getBs().remove(b);
into a JPQL query?
EDIT: the mentioned update query translates to
UPDATE B SET A_ID = ? WHERE (ID = ?)
bind => [null, 2]
Tables look like this:
A
ID
B
ID A_ID
From the comments and from this question, changing the owning side of the relationship is sufficient.
Therefore, to do
a.getBs().remove(b);
as an jpql query, one can do
"UPDATE B b SET b.a = NULL"
This will release the bidirectional relationship between a and b.
Note that you might need to clear the L2 cache or close the EntitiyManagerFactory for this to take effect.
factory.getCache().evictAll();

Perform join on two tables in HQL

Consider table A and B. Entire column in table A and only one column in table B need to be taken.Previously,a named query is created with which entire column values of table A are taken. Now i need to include one column of table B too.
In DAO class,below is code
X x=null
Query query = getEntityManager().createNamedQuery("AAA").setParameter("serialID",val);
x=(X) query.getSingleResult()
And in DTO class, AAA is part of class X which refers to table A. The query of AAA is below:
select X from A X where COLUMNVAL= serialID;
Now, table B also has COLUMNVAL too and i need to consider value of xyz column from that table.Consider xyz is mapped in class Q.
I tried the below query for which i got classcast exception as java.lang.object cannot be cast to A
select X, Q.xyz from A X, B Q where X.COLUMNVAL = Q.COLUMNVAL AND X.COLUMNVAL=serialID
Please favour how to proceed further. A has huge number of columns so ResultList is not preferred.
I tried the below query for which i got classcast exception as
java.lang.object cannot be cast to A
select X, Q.xyz from A X, B Q where X.COLUMNVAL = Q.COLUMNVAL AND
X.COLUMNVAL=serialID
if you wish to use that query, you'll non get objects of class A, but instead an Array of Object where the first element is an object of class and the second element is the value Q.xyz
so you should use
Object[] x;
x=(Object[]) query.getSingleResult()
Create a ResultDTO class, which extends entity class A thus having all fields from class A and have its own required field(xyz) of the entity class B. Now you can do this to get ResultDTO:
String queryString = "select a, b.xyz from A a join B b on(a.field = b.field)"
+ " where a.serialID = :serialID";
return (List<ResultDTO>) entityManager.createQuery(queryString)
.setParameter("serialID", serialId);
.unwrap(org.hibernate.query.Query.class)
.setResultTransformer(Transformers.aliasToBean(ResultDTO.class))
.getResultList();
Moreover, I would suggest you to read this post The best way to map a projection query to a DTO (Data Transfer Object) with JPA and Hibernate

Cannot create Typed Query for query with more than one return using request result type

I'm trying to bind oracle result list to a summary list. But my summary list has 3 classes defined as entities of DB
I have three entity classes A, B, C
Summary.class
{
#Autowired
private A a;
#Autowired
private B b;
#Autowired
private C c;
//getters and setters
}
#Enity
class A{
Fields 1..n ;
} // same goes for other classes definition
I get the results with following query, but the results cannot be cast to the Summary object
List<Summary> summaryList = entityManager.createQuery("from A a, B b, C c" +
" where a.field1 = b.field1 and a.fValue = :fValue " +
"and b.field3= c.field3", Summary.class)
.setParameter("fValue ", fValue )
.getResultList();
Debugging:
I made sure resultslist is not empty and below query works fine if I dont cast it to an object
List summaryList = entityManager.createQuery("from A a, B b, C c" +
" where a.field1 = b.field1 and a.fValue = :fValue " +
"and b.field3= c.field3")
.setParameter("fValue ", fValue )
.getResultList();
The alternative 1 I see is to iterate through summaryList and assign it them to individual lists like this, which I haven't tested yet but I think it might give a class cast exception since the casting dint work before
for (int i = 0; i < summaryList.size(); i++) {
Summary s= (Summary) summaryList.get(i); // might be class cast Exception
aList.add(s.getA());
bList.add(s.getB());
}
The alternative 2 I'm thinking is to
get only class A fields list from db cast it to A's List, do it 3 times brute force till I get all of them.
Below are some of questions I looked at before creating a new question
Uses a different class to combine multiple entity classes
gets a list back mapped to pojo
Please let me know your thoughts, I'm thinking my main approach is good way to do it if it works.
Your JPQL select statement "from A a, B b, C c" can not be mapped back to Summary entity, JPA does not have enough info to do that.
If in your logic, a summary instance can be composed from A, B, C then you can have a constructor like
public Summary(A a, B b, C c) {
.............
}
and changed your select statment to be
"select new Summary(a, b, c) FROM A a, B b, C c"

How to join one table with different tables with criteria API?

I have following classes:
class A {
private B b;
// getters/setters
}
class B {
private C c;
private D d;
// getters/setters
}
class C {
private boolean outdated;
// getters/setters
}
class D {
// not important fields
// getters/setters
}
Class B is connected to A, C and D with relation 'one-to-one'.
I am trying to join following tables with criteria api.
I have following code:
Root<A> root = query.from(A.class);
root.join(A_.b)
.join(B_.c)
.join(B_.d);
But unfortunately this code will not compile, I will get error on line with ".join(B_.d)", because after joining B with C I cannot use fields of B for joining.
The reason why I want to make such joins is because I need to have condition that entity C is not outdated (so I will add 'on' condition for it).
Does anybody know how to solve this problem?
root.join(A_.b).join(B_.c) represents a C, so there is no way then to join to "B_.d". You would need to do
Root<A> root = query.from(A.class);
Join<A,B> bJoin = root.join(A_.b);
bJoin.join(B_.c);
bJoin.join(B_.d);

Categories

Resources