Hibernate generates ghost entries for new child entities - java

I have a weird (to me anyway) issue with Hibernate which I can't make any sense of.
The code below is my attempt at modeling a ManyToOne relation with an attribute between the entities Case and Suggestion using an additional entity CaseToSuggestion with Case being my aggregate root:
#Entity
#Table(name = "sga_cases")
public class Case {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
// Business key
#Column(name = "caseid", unique = true, nullable = false)
private String caseId;
...
#OneToMany(mappedBy = "associatedCase", orphanRemoval = true, cascade = CascadeType.ALL)
private Set<CaseToSuggestion> associatedSuggestions = new HashSet<CaseToSuggestion>();
...
}
#Entity
#Table(name = "sga_suggestions")
public class Suggestion {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "suggestionid", unique = true, nullable = false)
private String suggestionId;
#Column(name = "localizationkey", nullable = false)
private String localizationKey;
#OneToMany(mappedBy = "associatedSuggestion", orphanRemoval = true, cascade = CascadeType.ALL)
private Set<CaseToSuggestion> caseMapping = new HashSet<CaseToSuggestion>();
...
}
#Entity
#Table(name = "sga_case2suggestion")
public class CaseToSuggestion {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#Column(name = "feedback")
private float feedback;
#ManyToOne
#JoinColumn(name = "caseid")
private Case associatedCase;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "suggestionid")
private Suggestion associatedSuggestion;
...
}
If I create new objects for the above entities, set the appropriate associations and then store them with the EntityManager everything works fine:
Case c = new Case(...)
Suggestion sugg = new Suggestion(...);
CaseToSuggestion case2sugg = new CaseToSuggestion(c, sugg, 1.0f);
c.getAssociatedSuggestions().add(case2sugg);
sugg.getAssociatedCases().add(case2sugg);
Followed by entityManager.persist(c);
If I want to add a new association to a Case that is already in the database I retrieve the Case and Suggestion entities I want to associate from the EntityManager and add them to a new CaseToSuggestion:
CaseToSuggestion c2s = new CaseToSuggestion(c, s, fb);
c.getAssociatedSuggestions().add(c2s);
s.getAssociatedCases().add(c2s);
Followed by entityManager.merge(c);
The the CaseToSuggestion entity is stored in the database but for every entry I get a "ghost entry" with a new generated id and all fields null:
+----+----------+--------+--------------+
| id | feedback | caseid | suggestionid |
+----+----------+--------+--------------+
| 3 | 1 | 3 | 1 |
| 4 | 1 | 4 | 2 |
| 5 | 1 | 5 | 2 |
| 6 | 0 | NULL | NULL |
| 7 | 1 | 6 | 2 |
| 8 | 0 | NULL | NULL |
| 9 | 1 | 7 | 2 |
| 10 | 0 | NULL | NULL |
+----+----------+--------+--------------+
Does anybody have an idea what might cause this?

OK, I got it.
Removing the OneToMany-Mapping in Suggestion altogether solved the problem for me.

Related

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)

JPA - 3 columns table to Entity Map

I'm looking for a way to group the query and put the other values into a map but I can' t find a way with a single table without joining. My scenario is the following:
CREATE TABLE metadata (
container_id INT NOT NULL,
metadata_key VARCHAR(50) NOT NULL,
metadata_value VARCHAR(255) NOT NULL,
PRIMARY KEY (container_id, metadata_key)
);
#container_id | #metadata_key | metadata_value
--------------+-------------------------+--------------------
15 | REPORTING_DATE | 20201231
15 | TYPES | T1,T2,Tx
11 | START_DATE | 20201231
1 | SHUTDOWN_ID | 12345
#Entity
#Table(name = "metadata")
public class Metadata {
#Id
#Column(name = "container_id")
private Integer containerId;
// metadata_key:metadata_value ???
private Map<String, String> metadata = new HashMap<>();
...
}
Is it possible?
You should consider 2 tables :
First table : container
#id | other stuff
--------------+-------------------------
15 | …
11 | …
1 | …
then other table for instance "metadata_key_value" like that :
#container_id | #metadata_key | metadata_value
--------------+-------------------------+--------------------
15 | REPORTING_DATE | 20201231
15 | TYPES | T1,T2,Tx
11 | START_DATE | 20201231
1 | SHUTDOWN_ID | 12345
Where « container_id » is FK of « container » table
Then the mapping should be like that :
#Entity
#Table(name = « container »)
public class Container {
#Id
#Column(name = « id »)
private Integer id;
#CollectionTable(name = "metadata_key_value», joinColumns =#JoinColumn(name = "container_id"))
#MapKeyColumn(name = "metadata_key")
#Column(name = "metadata_value")
private Map<String, String> metadata = new HashMap<>();
...
}

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

JPA Query on #OneToMany Set<Entity>

The GenericVehicle represent a vehicle which may have 0 or more GenericVehicleAccessory.
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public class GenericVehicle extends GenericEntity {
#Id
private Long id;
#Column(nullable=false)
#NotNull(message="{GenericVehicle.vehicleName.notNull")
#Size(max=128, message="{GenericVehicle.vehicleName.size")
private String vehicleName;
#OneToMany(mappedBy = "genericVehicle", cascade = { CascadeType.ALL }, orphanRemoval = true, fetch = FetchType.LAZY)
private Set<GenericVehicleAccessory> accessories;
// ..
}
#Entity
#Table(name = "GENERICVEHICLEACCESSORIES")
public class GenericVehicleAccessory extends GenericEntity {
#Id
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "GENERICVEHICLE_ID")
private GenericVehicle genericVehicle;
private String name;
// ..
}
I would like to perform a query which selects all GenericVehicle(s) which owns a Set (accessories) of a given criteria set.
For example:
+----+-------------------+
| ID | VEHICLENAME |
+----+-------------------+
| 1 | Toyota Auris |
| 2 | Volkswagen Passat |
| 3 | Bentley Arnage |
| 4 | Hyundai Accent |
| 5 | Toyota Auris |
+----+-------------------+
+-------------------+----------------------------------+
| GENERICVEHICLE_ID | NAME |
+-------------------+----------------------------------+
| 1 | Leather seats |
| 1 | Electronic stability control |
| 2 | Power steering |
| 4 | ABS |
| 4 | Airbag |
| 4 | Cruise control |
| 5 | Leather seats |
| 5 | Electronic stability control |
+-------------------+----------------------------------+
A criteria like this:
criteria.setVehicleName="Toyota Auris";
new GenericVehicleAccessory tmp0 = new GenericVehicleAccessory();
tmp0.setName="Leather Seats";
new GenericVehicleAccessory tmp1 = new GenericVehicleAccessory();
tmp1.setName="Electronic stability control";
criteria.addToAccessory(tmp0);
criteria.addToAccessory(tmp1);
would select the GenericVehicle entity with ID=1 (since ID=5 not have Electronic Stability Control).
How can I do that by using JPA Criteria API?

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