I have a native query in spring boot like this:
select
a.x,
a.y,
b.m,
b.n
from
table1 a,
table2 b
where
a.x = b.x
order by a.x
here for each entry for table1 there can be multiple matching rows in table2. I.e there is OneToMany mapping.
for this i created an object to hold the data:
#Entity
#Table(name="table1")
#SecondaryTable(name = "table2", pkJoinColumns = {#PrimaryKeyJoinColumn(name = "x", referencedColumnName = "x")})
public class TableData {
#Id
#Column(name = "x")
public String x;
#Column(name = "y")
public String y;
#Column(name = "m", table = "table2")
public String m;
#Column(name = "n", table = "table2")
public String n;
//getters and setters
When i execute the sql in my db i see the results coming fine. I.e i see two different rows with same a.x but with different b.m and b.n
But my json response has two identical rows with same a.x and same b.m and b.n
Sample data:
table1:
ManagerID|ManagerName|Role
'1','John','Manager'
table2:
ManagerID|AssociateName|AssociateId
'1','Peter','2'
'1','Jacob','3'
Expected output
'1','John','Peter','2'
'1','John','Jacob','3'
Output i am seeing right now:
'1','John','Peter','2'
'1','John','Peter','2'
Any guess where i am getting wrong ??
My problem got resolved by using the #IdClass annotation as the resulting data from the query was a join and the uniqueness can be maintained by a combination of fields !!
Related
I am having difficulty writing a HQL query to select ONLY the caseid, title, and caseStatus fields from my Cases entity. The cases returned have to be distinct based on caseid. I do not want the name and userid fields to be included. I also do not want to use Lazy fetching for caseid, title, and caseStatus fields. Note that the caseStatus field is a one-to-many List. Below are the entities. The getters/setters are omitted to save space.
#Entity
#Table(name = "Cases")
public class Cases {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "caseid", nullable = false)
private Integer caseid;
private Integer userid;
private String name;
private String title;
#OrderBy("caseStatusId DESC")
#OneToMany(mappedBy = "cases", fetch = FetchType.EAGER)
private List<CaseStatus> caseStatus;
}
#Entity
#Table(name = "CaseStatus")
public class CaseStatus {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "caseStatusId", nullable = false)
private Integer caseStatusId;
private String info;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "caseid")
private Cases cases;
}
My goal is to retrieve a distinct List<Cases> or List<Object[]> of the Cases entity containing only caseid, title, and a List<CaseStatus>. The List<CaseStatus> will contain CaseStatus objects with all of its fields populated.
public List<Object[]> getCases(String title) {
TypedQuery<Object[]> q = em.createQuery("select distinct c.caseid, c.title, cs "
+ "FROM Cases c join c.caseStatus cs "
+ "where c.title like :title", Object[].class);
q.setParameter("title", "%" + title + "%");
List<Object[]> results = q.getResultList();
return results;
}
The above method is close, but not correct because rather than returning a List<CaseStatus> in one of the indexes, it is only returning a single CaseStatus entity.
For example, if my DB contains a single Case with a List<CaseStatus> having a size of n for example, the results will be similar to the example below:
Example of results I'm getting now. Not correct:
List<Object[]> index 0:
Contains an Object[] where:
Object[0] = {some caseid}
Object[1] = {some title}
Object[2] = {1st CaseStatus}
List<Object[]> index 1:
Contains an Object[] where:
Object[0] = {same caseid as the one found in index 0 above}
Object[1] = {same title as the one found in index 0 above}
Object[2] = {2nd CaseStatus}
...
List<Object[]> index n-1:
Contains an Object[] where:
Object[0] = {same caseid as all the previous}
Object[1] = {same title as all the previous}
Object[2] = {nth CaseStatus}
Example of results I hope to achieve:
List<Object[]> index 0:
Contains an Object[] where:
Object[0] = {unique caseid}
Object[1] = {some title}
Object[2] = List<CaseStatus> with size of n
Updated the question. Instead of name, title, and List<CaseStatus>, the fields I want to retrieve are caseid, title, and List<CaseStatus>. caseid is the primary key of Cases.
I found various threads Select Collections with HQL - hibernate forum and Select collections with HQL - stackoverflow. It's pretty much the problem I ran into. Looks like no one found a solution in these threads.
Hibernates a bit confused about the query; in HQL do your join like this (apologies, I've not been able to test before posting due to wonky computer, but you should get the idea)
select distinct c from Cases c left join fetch c.caseStatus cs where....
the "fetch" makes it eager. Note that this will return an array of type Cases. You where clauses look about right.
In fact HQL is fully object-oriented and uses your classes structure in the Query, so by writing c.caseStatus HQL expects that your Cases class has a caseStatus property, which is wrong because it's a collection.
If you take a look at Hibernate HQL documentation you can see that:
Compared with SQL, however, HQL is fully object-oriented and understands notions like inheritance, polymorphism and association.
I think what you need to do here is to change your query so it matches your classes structures:
Query q = em.createQuery("select distinct c.name, c.title, cs.caseStatus FROM Cases c left join c.caseStatus where "
+ "c.name like :name and "
+ "c.title like :title");
Correct syntax should be
TypedQuery<Object[]> q = em.createQuery("select c.name, c.title, cs FROM Cases c "
+ "join c.caseStatus cs where "
+ "c.name = :name and "
+ "c.title = :title", Object[].class);
Return type will be List<Object[]>, where in first index of Object[] is c.name, second is c.title and third is associated caseStatus entity. It is possible to query for multiple instances (rows).
We need JOIN because relationship between CaseStatus and Case is mapped via collection.
SELECT cs
FROM Case c JOIN c.cases cs;
Why don't you just use
Query q = em.createQuery("select distinct c from Cases c where "
+ "c.name like :name and "
+ "c.title like :title");
Just try this. This may be a naive approach but should be able to solve the problem. You may be getting more fields than you required but the return type would be list of Cases.
I have a database table Communications with type, value and a foreign key as index that maps back to a Person table declared as follows:
#Table(name = 'communication', schema = 'schema')
#org.hibernate.annotations.Table(appliesTo = 'communication', indexes = {
#Index(name = "idx_communication_person_id", columnNames = { "person_id" })
}
)
And the Person object maps to this as:
#OneToMany(fetch = LAZY, cascade = ALL, orphanRemoval = true)
#JoinColumn(name = "person_id")
#OrderColumn
#Index(name = "idx_communication_person_id")
private final List<Communication> communications
Now I want to create a HQL query with Hibernate, that selects based on this index colum, like:
WHERE person.id in ( SELECT c.person_id FROM Communication c WHERE c.type = 3 AND c.value = 'john.doe#server.com' )
That doesn't work, because HQL doesn't know c.person_id at this point, because index columns are in general unknown to HQL.
How do I properly address the index in HQL, or if that is not possible: how do I write the statement to archive the same as the native-like query above?
EDIT: For performance reasons there must not be a JOIN in any form.
I think you need something like this:
SELECT p.* FROM person p
JOIN p.communication c
WHERE c.type = 3 AND c.value = 'john.doe#server.com'
That doesn't work, because HQL doesn't know c.person_id at this point, because index columns are in general unknown to HQL.
This doesn't make much sense to me.
If you want to have an HQL statement that returns a list of identifiers for Person based on some criteria, you can easily do it much like how your SQL statement is written.
SELECT p.id
FROM Communication c JOIN FETCH c.person p
WHERE c.type = :communicationType
AND c.value = :emailAddress
If you actually want persons, just write the query to select c.person rather than p.id in order to hydrate all Persons. In the following, the query allows you to specify a person identifier on the predicate if needed.
SELECT c.person
FROM Communication c JOIN FETCH c.person p
WHERE c.person.id = :personId
AND c.type = :communicationType
AND c.value = :emailAddress
UPDATE
If you don't want to use any joins, then simply expose the personId as a numeric value on your Communication entity without any association mappings.
public class Communication {
#Column(name = "personId", nullable = false, insertable = false, updatable = false)
private Long personId;
}
You should then be able to issue a query such as:
SELECT c.personId
FROM Communcation c
WHERE c.type = :communicationType
AND c.value = :emailAddress
I have two tables, which have a one-to-one relationship and look something like this:
OrderItem
ID data1 data2 data3
1 a b c
2 d e f
3 g h i
DisputedItem
ID data4
1 q
3 r
Is there a way to pull data4 into my Hibernate model for OrderItem without having a separate DisputedItemModel? Preferably using annotations.
You can use Hibernate #Formula annotation, if you have annotation based mappings or <formula> tag if you are mapping by XML.
What formula can do is help you extract a value by a query and map it to a field in your OrderItem domain model. This field can be from any table but the query thats used to map the property should return the indented field type.
You can use following URL for reference Hibernate Formula
Yes, here is an example:
#Table(name = "table")
public class c
{
#Id
#Column(name = "ID")
private int Id;
#Formula("(select t.data from table t where t.ID = ID))")
private String data;
}
I need to make a query inside a DAO using hibernate criteria, but im not sure how.
I have 2 entities, A and B, and an association table, that contains both A and B ID's.
A doesnt know B, and B doesnt know A.
I want to find all A's that are associated with a certain B, using criteria.
I made a diagram, hope it helps to explain.
Example image
As you can see, table A have 3 records, table B have 3 records too and table AssocAB 3 records as well. I want to find all A's that are associated in AssocAB with B1. The query should return A1 and A3.
is it possible?
Here's the classes, and the relationship annotated.
Entity A: No annotation
Entity B: Have a Set of Entity A
#ManyToMany
#JoinTable(name = "Assoc_AB", joinColumns = { #JoinColumn(name = "ID_B")}, inverseJoinColumn = { #JoinColumn(name = "ID_A")})
#ForeignKey(name = "FK_A_B", inverseName = "FK_B_A")
public set<A> getA(){
return this.listOfA;
}
And no class for association class, the mapping on B creates the assoc table.
Thanks in advance.
I tried to write a criteria query with your indications, but it's not easy because you tell that A and B tables have a int ID, but then you put Strings in your rows ("A1", "B1"...). I supposed that your id is a String instead of an int. You should have something like this:
List<A> listOfA = new ArrayList<A>();
Criteria criteria = session.createCriteria(B.class, "b");
criteria.add(Restrictions.eq("b.id", "B1"));
List<B> listOfBs = criteria.list();
for (B b : listOfBs) {
listOfA.addAll(b.getA());
}
If you have a name property in B class, you would have to modify the restriction:
criteria.add(Restrictions.eq("b.name", "B1"));
I hope this code can help you solve your problem.
I have two entities
Class A{
#OneToMany(mappedBy = "a", cascade = CascadeType.REMOVE)
private List<B> b;
private Integer credits;
//other fields
}
Class B{
#ManyToOne
#JoinColumn(name = "a_fk")
private A a;
private Integer reputations;
//other fields
}
Now I want select the B with the highest reputation for each A.
What I am doing currently is
"select a FROM A a ORDER BY a.credits DESC"; //maximum results set to 500
And then finding the highest B in each A.
for (A a: aList) {
bList.add(Collections.max(a.getB(),new bComparator()));
}
But this solution isn't very intuitive.
I tried writing the query below, but doesn't give the desired result.
select b FROM B b WHERE b.reputations = (SELECT MAX(b.reputations) FROM B bInner WHERE bInner.a.id = b.a.id) ORDER BY b.a.credits DESC
Any suggestions for an optimized query?? Thanks in advance.
This should be your query, I've tested it:
select distinct a,b
FROM A a JOIN a.b b
WHERE b.reputations=(SELECT MAX(b2.reputations) FROM B b2 WHERE b2.a=a)