Spring Boot custom query order by aliased aggregate column - java

I have a table like this called my_objects:
| code | description | open | closed |
+ ---- + ----------- + ---- + ------ +
| 1 | first | 0 | 1 |
| 1 | first | 1 | 0 |
| 2 | second | 1 | 0 |
| 2 | second | 1 | 0 |
I'm returning a JSON object like this:
{
"totalItems": 2
"myObjs": [
{
"code": 1,
"description": "first",
"openCount": 1,
"closedCount": 1
},
{
"code": 2,
"description": "second",
"openCount": 2,
"closedCount": 0
}
],
"totalPages": 1,
"curentPage": 0
}
My query in my repository (MyObjsRepository.java) looks like this:
#Query(
value = "SELECT new myObjs(code, description, "
+ "COUNT(CASE open WHEN 1 THEN 1 ELSE null END) as openCount "
+ "COUNT(CASE closed WHEN 1 THEN 1 ELSE null END) as closedCount) "
+ "FROM MyObjs "
+ "GROUP BY (code, description)"
)
Page<MyObjs> findMyObjs(Pageable pageable);
This works, but I run into an issue when trying to sort by my aggregated columns. When I try to sort by openCount, the Pageable object will contain a org.springframework.data.domain.Sort with an Order with the property openCount. The log for my application shows what's going wrong (formatted for readability):
select
myObjs0_.code as col_0_0_,
myObjs0_.description as col_1_0_,
count(case myObjs0_.open when 1 then 1 else null end) as col_2_0_,
count(case myObjs0_.closed when 1 then 1 else null end) as col_3_0_
from my_objects myObjs0_
group by (myObjs0_.code, myObjs0_.description)
order by myObjs0_.openCount asc limit ?
The aliases aren't preserved, so I get the following error:
Caused by: org.postgresql.util.PSQLException: ERROR: column myObjs0_.openCount does not exist
I've tried renaming the sorting parameters, adding columns with the aliased names to my entity, and adding open and closed to the group by clause. I think I may be able to solve this with a native query, but I'd really rather not do that. Is there a way to resolve this issue without a native query?
Edit:
The MyObjs entity looks like this:
#Entity
#Table(schema = "my_schmea", name = "my_objects")
public class MyObjs {
#Column(name = "code")
private Integer code;
#Column(name = "description")
private String description;
#Column(name = "open")
private Integer open;
#Column(name = "closed")
private Integer closed;
/* getters, setters, and constructor */
}
The MyObjsDto looks like this:
#JsonAutoDetect(getterVisibility = JsonAutoDetect.Visibility.PUBLIC_ONLY)
public class MyObjsDto {
#JsonProperty(value = "code")
private String code;
#JsonProperty(value = "description")
private String description;
#JsonProperty(value = "openCount")
private String open;
#JsonProperty(value = "closedCount")
private String closed;
/* getters, setters, and constructor */
}

Sort uses column which is present in the table. Here you are calculating it.
I would suggest you to explore and use #Formula annotation to perform the same action.
#Formula("COUNT(CASE open WHEN 1 THEN 1 ELSE null END)")
private Integer open;
#Formula("COUNT(CASE closed WHEN 1 THEN 1 ELSE null END)")
private Integer closed;
and use this attributes to apply the sorting.

Related

Java Stream: Merging multiple results read from separate CSV files?

I am reading City and Country data from 2 CSV files and need to merge the result using Java Stream (I need to keep the same order as the first result). I thought using parallel stream or ComletableFuture, but as I need the result of first fetch for passing as parameter to the second fetch, I am not sure if they are suitable for this scenario.
So, in order to read data from the first query and pass the result of this query to the second one and obtain result, what should I do in Java Stream?
Here are the related entities. I have to relate them using country code values.
Assume that I just need the country names for the following cities. Please keep in mind that, I need to keep the same order as the first result. For example, if the result is [Berlin, Kopenhag, Paris] then the second result should be the same order as [Germany, Denmark, France].
City:
id | name | countryCode |
------------------------------
1 | Berlin | DE |
2 | Munich | DE |
3 | Köln | DE |
4 | Paris | FR |
5 | Kopenhag | DK |
...
Country:
id | name | code |
----------------------------------
100 | Germany | DE |
105 | France | FR |
108 | Denmark | DK |
...
Here are the related classes:
public class City{
#CsvBindByPosition(position = 0)
private Integer id;
#CsvBindByPosition(position = 1)
private String name;
#CsvBindByPosition(position = 2)
private String countryCode;
// setters, getters, etc.
}
public class Country {
#CsvBindByPosition(position = 0)
private Integer id;
#CsvBindByPosition(position = 1)
private String name;
#CsvBindByPosition(position = 2)
private String code;
// setters, getters, etc.
}
You can merge your data with stream, for example add an countryName in City :
List<Country> countries = // Your CSV Country lines
List<City> cities = // Your CSV City lines
cities.forEach(city -> city.setCountryName(countries.stream()
.filter(country -> country.getCode().equals(city.getCountryCode()))
.map(Country::getName).findAny().orElse(null)));

org.hibernate.QueryException: illegal attempt to dereference collection in order by clause

I am trying to implement a simple HQL query of all objects of type A ordered by the following predicate :
a.getListB().get(0).getC().getLastname()
I tried the following HQL query :
select a_ from A a_ order by a_.listB.c.lastname
But I am getting the following exception :
org.hibernate.QueryException: illegal attempt to dereference collection
I have tried the following SQL query but I am getting inconsistent results :
select a.* from A a
left outer join B b on b.a_id=a.id
left outer join C c on b.uploaded_from=c.id
order by c.lastname=(select c_.lastname from A a_
left outer join B b_ on b_.a_id=a_.id
left outer join C c_ on b_.uploaded_from=c_.id
where a.id=a_.id limit 1) asc;
Code snipet :
#Entity
#Table(name = "A")
pubic class A {
private int id;
private List<B> listB;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
#OrderBy(clause = "id")
#OneToMany(fetch = FetchType.LAZY, mappedBy = "a")
public List<B> getListB() {
return this.listB;
}
}
#Entity
#Table(name = "B")
pubic class B {
private int id;
private A a;
private C c;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "a_id", nullable = false)
public A getA() {
return this.a;
}
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "uploaded_from", nullable = false)
public C getC() {
return this.c;
}
}
#Entity
#Table(name = "C")
pubic class C {
private int id;
private String lastname;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "id", unique = true, nullable = false)
public Integer getId() {
return this.id;
}
#Column(name = "lastname")
public String getLastname() {
return this.lastname;
}
}
Any hints how can I work around this problem please either in HQL, Criteria or even native SQL?
I used the following tables to test one possible solution for your problem further below
create table A (
id UUID
);
create table B (
id UUID,
id_a UUID,
id_c UUID
);
create table C (
id UUID,
lastname varchar(63)
);
insert into A values
('aaaaaaaa-aaaa-aaaa-aaaa-000000000000'),
('aaaaaaaa-aaaa-aaaa-aaaa-000000000001'),
('aaaaaaaa-aaaa-aaaa-aaaa-000000000002'),
('aaaaaaaa-aaaa-aaaa-aaaa-000000000003'),
('aaaaaaaa-aaaa-aaaa-aaaa-000000000004');
insert into C values
('cccccccc-cccc-cccc-cccc-000000000000', 'C zero'),
('cccccccc-cccc-cccc-cccc-000000000001', 'C one'),
('cccccccc-cccc-cccc-cccc-000000000002', 'C two'),
('cccccccc-cccc-cccc-cccc-000000000003', 'C three'),
('cccccccc-cccc-cccc-cccc-000000000004', 'C four'),
('cccccccc-cccc-cccc-cccc-000000000005', 'C five'),
('cccccccc-cccc-cccc-cccc-000000000006', 'C six');
insert into B values
('bbbbbbbb-bbbb-bbbb-bbbb-000000000000', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000000', 'cccccccc-cccc-cccc-cccc-000000000000'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000001', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000000', 'cccccccc-cccc-cccc-cccc-000000000001'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000002', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000000', 'cccccccc-cccc-cccc-cccc-000000000002'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000003', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000001', 'cccccccc-cccc-cccc-cccc-000000000003'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000004', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000001', 'cccccccc-cccc-cccc-cccc-000000000004'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000005', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000001', 'cccccccc-cccc-cccc-cccc-000000000005'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000006', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000002', 'cccccccc-cccc-cccc-cccc-000000000006'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000007', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000002', 'cccccccc-cccc-cccc-cccc-000000000000'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000008', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000002', 'cccccccc-cccc-cccc-cccc-000000000001'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000009', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000003', 'cccccccc-cccc-cccc-cccc-000000000002'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000010', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000003', 'cccccccc-cccc-cccc-cccc-000000000003'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000011', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000003', 'cccccccc-cccc-cccc-cccc-000000000004'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000012', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000004', 'cccccccc-cccc-cccc-cccc-000000000005'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000013', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000004', 'cccccccc-cccc-cccc-cccc-000000000006'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000014', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000004', 'cccccccc-cccc-cccc-cccc-000000000000'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000015', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000000', 'cccccccc-cccc-cccc-cccc-000000000001'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000016', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000001', 'cccccccc-cccc-cccc-cccc-000000000002'),
('bbbbbbbb-bbbb-bbbb-bbbb-000000000017', 'aaaaaaaa-aaaa-aaaa-aaaa-000000000002', 'cccccccc-cccc-cccc-cccc-000000000003');
Unique id_a entries are numbered via this intermediate select:
select id,id_a,id_c,ROW_NUMBER()
over (partition by id_a order by id_a) as rowNumber
from B as aggregate;
Result:
id | id_a | id_c | rownumber
--------------------------------------+--------------------------------------+--------------------------------------+-----------
bbbbbbbb-bbbb-bbbb-bbbb-000000000000 | aaaaaaaa-aaaa-aaaa-aaaa-000000000000 | cccccccc-cccc-cccc-cccc-000000000000 | 1
bbbbbbbb-bbbb-bbbb-bbbb-000000000015 | aaaaaaaa-aaaa-aaaa-aaaa-000000000000 | cccccccc-cccc-cccc-cccc-000000000001 | 2
bbbbbbbb-bbbb-bbbb-bbbb-000000000001 | aaaaaaaa-aaaa-aaaa-aaaa-000000000000 | cccccccc-cccc-cccc-cccc-000000000001 | 3
bbbbbbbb-bbbb-bbbb-bbbb-000000000002 | aaaaaaaa-aaaa-aaaa-aaaa-000000000000 | cccccccc-cccc-cccc-cccc-000000000002 | 4
bbbbbbbb-bbbb-bbbb-bbbb-000000000004 | aaaaaaaa-aaaa-aaaa-aaaa-000000000001 | cccccccc-cccc-cccc-cccc-000000000004 | 1
bbbbbbbb-bbbb-bbbb-bbbb-000000000016 | aaaaaaaa-aaaa-aaaa-aaaa-000000000001 | cccccccc-cccc-cccc-cccc-000000000002 | 2
bbbbbbbb-bbbb-bbbb-bbbb-000000000003 | aaaaaaaa-aaaa-aaaa-aaaa-000000000001 | cccccccc-cccc-cccc-cccc-000000000003 | 3
bbbbbbbb-bbbb-bbbb-bbbb-000000000005 | aaaaaaaa-aaaa-aaaa-aaaa-000000000001 | cccccccc-cccc-cccc-cccc-000000000005 | 4
bbbbbbbb-bbbb-bbbb-bbbb-000000000017 | aaaaaaaa-aaaa-aaaa-aaaa-000000000002 | cccccccc-cccc-cccc-cccc-000000000003 | 1
bbbbbbbb-bbbb-bbbb-bbbb-000000000006 | aaaaaaaa-aaaa-aaaa-aaaa-000000000002 | cccccccc-cccc-cccc-cccc-000000000006 | 2
bbbbbbbb-bbbb-bbbb-bbbb-000000000007 | aaaaaaaa-aaaa-aaaa-aaaa-000000000002 | cccccccc-cccc-cccc-cccc-000000000000 | 3
bbbbbbbb-bbbb-bbbb-bbbb-000000000008 | aaaaaaaa-aaaa-aaaa-aaaa-000000000002 | cccccccc-cccc-cccc-cccc-000000000001 | 4
bbbbbbbb-bbbb-bbbb-bbbb-000000000011 | aaaaaaaa-aaaa-aaaa-aaaa-000000000003 | cccccccc-cccc-cccc-cccc-000000000004 | 1
bbbbbbbb-bbbb-bbbb-bbbb-000000000010 | aaaaaaaa-aaaa-aaaa-aaaa-000000000003 | cccccccc-cccc-cccc-cccc-000000000003 | 2
bbbbbbbb-bbbb-bbbb-bbbb-000000000009 | aaaaaaaa-aaaa-aaaa-aaaa-000000000003 | cccccccc-cccc-cccc-cccc-000000000002 | 3
bbbbbbbb-bbbb-bbbb-bbbb-000000000012 | aaaaaaaa-aaaa-aaaa-aaaa-000000000004 | cccccccc-cccc-cccc-cccc-000000000005 | 1
bbbbbbbb-bbbb-bbbb-bbbb-000000000013 | aaaaaaaa-aaaa-aaaa-aaaa-000000000004 | cccccccc-cccc-cccc-cccc-000000000006 | 2
bbbbbbbb-bbbb-bbbb-bbbb-000000000014 | aaaaaaaa-aaaa-aaaa-aaaa-000000000004 | cccccccc-cccc-cccc-cccc-000000000000 | 3
(18 rows)
We can now obtain only the first C entry associated to a A entry by selecting only entries with rowNumber=1:
select *
from (
select id,id_a,id_c,ROW_NUMBER()
over (partition by id_a order by id_a) as rowNumber from B)
as aggregate
where aggregate.rowNumber=1;
Result:
id | id_a | id_c | rownumber
--------------------------------------+--------------------------------------+--------------------------------------+-----------
bbbbbbbb-bbbb-bbbb-bbbb-000000000000 | aaaaaaaa-aaaa-aaaa-aaaa-000000000000 | cccccccc-cccc-cccc-cccc-000000000000 | 1
bbbbbbbb-bbbb-bbbb-bbbb-000000000004 | aaaaaaaa-aaaa-aaaa-aaaa-000000000001 | cccccccc-cccc-cccc-cccc-000000000004 | 1
bbbbbbbb-bbbb-bbbb-bbbb-000000000017 | aaaaaaaa-aaaa-aaaa-aaaa-000000000002 | cccccccc-cccc-cccc-cccc-000000000003 | 1
bbbbbbbb-bbbb-bbbb-bbbb-000000000011 | aaaaaaaa-aaaa-aaaa-aaaa-000000000003 | cccccccc-cccc-cccc-cccc-000000000004 | 1
bbbbbbbb-bbbb-bbbb-bbbb-000000000012 | aaaaaaaa-aaaa-aaaa-aaaa-000000000004 | cccccccc-cccc-cccc-cccc-000000000005 | 1
(5 rows)
By joining C it is now possible to sort by lastname:
select aggregate.id_a,c.lastname
from (
select id,id_a,id_c,ROW_NUMBER()
over (partition by id_a order by id_a) as rowNumber from B)
as aggregate
join C as c on aggregate.id_c=c.id where aggregate.rowNumber=1
order by c.lastname;
Result:
id_a | lastname
--------------------------------------+----------
aaaaaaaa-aaaa-aaaa-aaaa-000000000004 | C five
aaaaaaaa-aaaa-aaaa-aaaa-000000000003 | C four
aaaaaaaa-aaaa-aaaa-aaaa-000000000001 | C four
aaaaaaaa-aaaa-aaaa-aaaa-000000000002 | C three
aaaaaaaa-aaaa-aaaa-aaaa-000000000000 | C zero
(5 rows)
(Tested with Postgres 14.3)

Groovy createCriteria issue with joined table

I have a domain class Coach which has a has many relationship to another domain class CoachProperty.
Hibernate/Grails is creating a third joined table in the database.
In the example below I am trying to fetch the coaches which both have foo AND bar for their text value. I have tried different solutions with 'or' and 'and' in Grails which either returns an empty list or a list with BOTH foo and bar.
Coach:
class Coach {
static hasMany = [ coachProperties : CoachProperty ]
CoachProperty:
class CoachProperty {
String text
boolean active = true
static constraints = {
text(unique: true, nullable: false, blank: false)
}
}
Joined table which is being auto-created and I populated with some data, in this example I am trying to fetch coach 372 since that coach has both 1 and 2 i.e foo and bar:
+---------------------------+-------------------+
| coach_coach_properties_id | coach_property_id |
+---------------------------+-------------------+
| 150 | 2 |
| 372 | 1 |
| 372 | 2 |
| 40 | 3 |
+---------------------------+-------------------+
Inside Coach.createCriteria().list() among with other filters. This should return coach 372 but return empty:
def tempList = ["foo", "bar"]
coachProperties{
for(String temp: tempList){
and {
log.info "temp = " + temp
ilike("text",temp)
}
}
}
I seem to remember this error. Was something about not being able to use both nullable & blank at the same time.Try with just 'nullable:true'
I had to create a workaround with executeQuery where ids is the list containing the id's of the coachproperties i was trying to fetch.
def coaches = Coach.executeQuery '''
select coach from Coach as coach
join coach.coachProperties as props
where props.id in :ids
group by coach
having count(coach) = :count''', [ids: ids.collect { it.toLong()
}, count: ids.size().toLong()]
or{
coaches.each{
eq("id", it.id)
}
}

QueryDSL intersection with Spring Boot Data JPA

I am using QueryDSL within a Spring Boot, Spring Data JPA project.
I have the following schema for a table called test:
| id | key | value |
|----|------|-------|
| 1 | test | hello |
| 1 | test | world |
| 2 | test | hello |
| 2 | foo | bar |
| 3 | test | hello |
| 3 | test | world |
Now I want to write the following SQL in QueryDSL:
select id from test where key = 'test' and value = 'hello'
INTERSECT
select id from test where key = 'test' and value = 'world'
Which would give me all ids where key is 'test' and values are 'hello' and 'world'.
I did not find any way of declaring this kind of SQL in QueryDSL yet. I am able to write the two select statements but then I am stuck at combining them with an INTERSECT.
JPAQueryFactory queryFactory = new JPAQueryFactory(em); // em is an EntityManager
QTestEntity qTestEntity = QTestEntity.testEntity;
var q1 = queryFactory.query().from(qTestEntity).select(qTestEntity.id).where(qTestEntity.key("test").and(qTestEntity.value.eq("hello")));
var q2 = queryFactory.query().from(qTestEntity).select(qTestEntity.id).where(qTestEntity.key("test").and(qTestEntity.value.eq("world")));;
In the end I want to retrieve a list of ids which match the given query. In general the amount of intersects can be something around 20 or 30, depending on the number of key/value-pairs I want to search for.
Does anyone know a way how to do something like this with QueryDSL ?
EDIT:
Assume the following schema now, with two tables: test and 'user':
test:
| userId | key | value |
|---------|------|-------|
| 1 | test | hello |
| 1 | test | world |
| 2 | test | hello |
| 2 | foo | bar |
| 3 | test | hello |
| 3 | test | world |
user:
| id | name |
|----|----------|
| 1 | John |
| 2 | Anna |
| 3 | Felicita |
The correspond java classes look like this. TestEntity has a composite key consisting of all of its properties.
#Entity
public class TestEntity {
#Id
#Column(name = "userId", nullable = false)
private String pubmedId;
#Id
#Column(name = "value", nullable = false)
private String value;
#Id
#Column(name = "key", nullable = false)
private String key;
}
#Entity
class User {
#Id
private int id;
private String name;
#ElementCollection
private Set<TestEntity> keyValues;
}
How can I map the test table to the keyValues properties within the User class?
Your TestEntity is not really an Entity, since it's id is not a primary key, it's the foreign key to the user table.
If it's only identifiable by using all its properties, it's an #Embeddable, and doesn't have any #Id properties.
You can map a collection of Embeddables as an #ElementCollection part of another entity which has the id as primary key. The id column in your case is not a property of the Embeddable, it's just the foreign key to the main table, so you map it as a #JoinColumn:
#Embeddable
public class TestEmbeddable {
#Column(name = "value", nullable = false)
private String value;
#Column(name = "key", nullable = false)
private String key;
}
#Entity
class User {
#Id
private int id;
#ElementCollection
#CollectionTable(
name="test",
joinColumns=#JoinColumn(name="id")
)
private Set<TestEmbeddable> keyValues;
}
In this case, the QueryDSL becomes something like this (don't know the exact api):
user.keyValues.any().in(new TestEmbeddable("test", "hello"))
.and(user.keyValues.keyValues.any().in(new TestEmbeddable("test", "world"))
In this case I'd probably just use an OR expression:
queryFactory
.query()
.from(qTestEntity) .select(qTestEntity.id)
.where(qTestEntity.key("test").and(
qTestEntity.value.eq("hello")
.or(qTestEntity.value.eq("world")));
However, you specifically mention wanting to use a set operation. I by the way think you want to perform an UNION operation instead of an INSERSECT operation, because the latter one would be empty with the example given.
JPA doesn't support set operations such as defined in ANSI SQL. However, Blaze-Persistence is an extension that integrates with most JPA implementations and does extend JPQL with set operations. I have recently written a QueryDSL extension for Blaze-Persistence. Using that extension, you can do:
List<Document> documents = new BlazeJPAQuery<Document>(entityManager, cbf)
.union(
select(document).from(document).where(document.id.eq(41L)),
select(document).from(document).where(document.id.eq(42L))
).fetch();
For more information about the integration and how to set it up, the documentation is available at https://persistence.blazebit.com/documentation/1.5/core/manual/en_US/index.html#querydsl-integration

Hibernate: Select Parent with Children Conditions

I have the following tables:
HEADER
---------------------------------
ID | STATUS
---------------------------------
1 | A
---------------------------------
2 | B
---------------------------------
DATA
--------------------------------------------------
ID | HEADER_ID | DKEY | DVALUE
--------------------------------------------------
1 | 1 | Age | 90
--------------------------------------------------
2 | 1 | Gender | M
--------------------------------------------------
3 | 1 | Score | 1000
--------------------------------------------------
1 | 2 | Age | 8
--------------------------------------------------
2 | 2 | Gender | M
--------------------------------------------------
3 | 2 | Score | 0
--------------------------------------------------
My JPA classes are:
**#Entity
#Table (name="HEADER")
public class Header {
#Id
#GeneratedValue
private int id;
#Column (name="STATUS")
private String status;
#OneToMany (cascade=CascadeType.ALL, orphanRemoval=true, mappedBy="header")
#LazyCollection(LazyCollectionOption.FALSE)
private Set<Data> dataList;
--- getters and Setters ---
}
#Entity
#Table (name="DATA")
publi class Data {
#Id
#GeneratedValue
private int id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "HEADER_ID", nullable = false)
private Header header;
#Column (name="DKEY")
private String key;
#Column (name="DVALUE")
private String value;
}**
Now, my problem is, I want to select a Header with Data that has "Age" equals to "90" and "Gender" equals to "M" using hibernate.
I tried the below approach:
select distinct h from Header h left join h.dataList dl where (dl.key = 'Age' and dl.value = '90') and (dl.key = 'Gender' and dl.value = 'M')
This returns me nothing because the condition "(dl.key = 'Age' and dl.value = '90') and (dl.key = 'Gender' and dl.value = 'M')" is executed in one "DATA" record which will always yield to false.
If i put or, "(dl.key = 'Age' and dl.value = '90') or (dl.key = 'Gender' and dl.value = 'M')" the result is wrong since I wanted to get those Header that Data satisfies Age and Gender conditions.
I had hours of headache because of this issue and I am out of solutions to try anymore. Hope someone could point me to the right direction/solution.
Thank yo so much.
You may try rewrite it this way:
select distinct h from Header as h
where h.id in
(select dl.header.id from Data as dl where dl.key = 'Age' and dl.value = '90')
and h.id in
(select dl.header.id from Data as dl where dl.key = 'Gender' and dl.value = 'M')

Categories