Workaround for HHH-2772 (hibernate querydsl) - java

I'm trying to perform a query to find cars by their foo property. The properties are stored in a different table.
I have two classes
#Embeddable
#Table(name = "PROPERTY")
public class Property {
#Column(name = "type", nullable = false)
private String type;
#Column(name = "string_value", nullable = true)
private String stringValue;
...
}
#Entity
#Table(name = "CAR")
public class Car {
#Id
...
private String id;
#ElementCollection(fetch = FetchType.EAGER)
#Fetch(FetchMode.SUBSELECT)
#CollectionTable(name = "PROPERTY", joinColumns = #JoinColumn(name = "car_ID") )
private Set<Property> properties = new HashSet<Property>();
...
}
I'm trying to perform a query
QueryDsl:
.from(car)
.leftJoin(car.properties, foo)
.on(foo.type.eq("foo"))
.where(predicate)
Resulting HQL:
select
car
from
com....Car car
left join
car.properties as foo with foo.type = :a1
where
...
This doesn't work because of: https://hibernate.atlassian.net/browse/HHH-2772
Before that, it was possible to write HQL:
SELECT cat FROM Cat cat LEFT JOIN cat.kittens as kitten WITH kitten.owner=:owner
Now the HQL is raising an exception:
org.hibernate.hql.ast.InvalidWithClauseException: with clause can only reference columns in the driving table
Workaround is to explicitly use primary key (ownerId):
SELECT cat FROM Cat cat LEFT JOIN cat.kittens as kitten WITH kitten.owner.ownerId=:ownerId
The problem is that I don't have the ownerId, or an owner, since it's an element collection.
If I were to turn the element collection into a #oneToMany #manyToOne, the property could not longer be embeddable and would require an id. This is not an option. (I can't define a composite ID (this is a requirement), and I don't want to add a new column )
What do you recommend?
Is it possible to add the Car or Car Id as a field into an embaddable class?
Can I create the criteria in a different way?
I'm interested in any workaround that doesn't require database changes. (Hibernate changes or ok)
Thank you

Related

QueryDsl extra query, even though I'm using a join

I have Car and Color classes. Car class has both Entity and Id fields of Color.
Here is the Car class code:
public class Car {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#JoinColumn(name = "color_id", insertable = false, updatable = false)
#ManyToOne(targetEntity = CarColor.class, fetch = FetchType.EAGER)
private CarColor color;
#Column(name = "color_id")
private Long colorId;
}
Here is the Color class code:
public class CarColor {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "name")
private String name;
#JsonIgnore
#OneToMany(mappedBy = "color")
List<Car> cars;
}
When I'm fetching a Car i have an extra query to get a color Entity which is causing n+1 problem. So I have tried to use joins.
return query.select(qCar)
.from(qCar)
.innerJoin(qCar.color(), QCarColor.carColor)
.where(predicate)
.fetch();
I'm getting these queries:
select
car0_.id as id1_3_,
car0_.color_id as color_id3_3_,
from
car car0_
inner join
color carcolor2_
on car0_.color_id=carcolor2_.id
Hibernate:
select
carcolor0_.id as id1_6_0_,
carcolor0_.name as name2_6_0_
from
color carcolor0_
where
carcolor0_.id=?
As you can see, at the end I have an extra query to get color even though it has already joined Color entity. That is the main problem. Maybe it somehow connected with my decision to store both Entity and Id.
Queries don't use the association eagerness to add joins arbitrarily to your query. After that the entity loader still notices that this is an eager association and will fetch it separately.
You can however simply add the join yourself. Important here is to add the join as fetch join, so that the join result will be initialised as the value of the association.
return query.select(qCar)
.from(qCar)
.innerJoin(qCar.color(), QCarColor.carColor).fetchJoin()
.where(predicate)
.fetch();

How to ignore some column when join tables in Hibernate?

Hello This is my 2 tables:
record and submission.
In submission, it has 1 composite primary key:(submission_id, question_id). One submission number can have several questions number. For example:
And as for record, it has a composite primary key:(student_id, exam_id). It looks like this:
I want to join these 2 tables like MySQL:
select * from record
left join submission
on record.submission_id = submission.submission_id.
But in hibernate, I have successfully join these 2 tables, but it gives me the following hql:
Hibernate:
select
...all columns...
from
record record0_
inner join
submission submission1_
on record0_.submission_id=submission1_.submission_id
and record0_.question_id=submission1_.question_id
where
1=1
In this case, I will get 0 rows in the result.
I don't want it use "and record0_.question_id=submission1_.question_id" after on clause, because there is no question_id in my record table.
But I have to add all primary keys into the #joinColumns() when I add Submission attribute in Record class, like this:
// Record class
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "record")
public class Record implements java.io.Serializable{
private static final long serialVersionUID = 1L;
// Other columns I don't need to show
#Column(name = "submission_id")
private Integer submissionId;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name = "submission_id", referencedColumnName = "submission_id",insertable=false, updatable=false),
#JoinColumn(name = "question_id", referencedColumnName = "question_id",insertable=false, updatable=false)
})
private Submission submission;
}
My Submission class like this:
#Getter
#Setter
#ToString
#AllArgsConstructor
#NoArgsConstructor
#Entity
#Table(name = "submission")
public class Submission implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id
#Column(name = "submission_id")
private Integer submissionId;
#Id
#Column(name = "question_id")
private Integer questionId;
#OneToOne(fetch = FetchType.LAZY, mappedBy = "submission")
private Record record;
}
Anyone can give me some advice?
-------- How I combine these tables-------
Actually, I join 4 tables and all these joins have the same problem declared above.
Code below is how i combine these 4 tables (record, submission, question, optional)
#Override
public List<RcdSubQuesOpt> getRcdSubQuesOpt(int studentID, int examId) {
Session session = this.getSession();
// RcdSubQuesOpt --> this is a class to store attributes from different tables(classes)
List<RcdSubQuesOpt> results;
Transaction transaction = null;
transaction = session.beginTransaction();
CriteriaBuilder criteriaBuilder = session.getCriteriaBuilder();
CriteriaQuery<RcdSubQuesOpt> criteriaQuery = criteriaBuilder.createQuery(RcdSubQuesOpt.class);
// To combine these tables use join
Root<Record> pRoot = criteriaQuery.from(Record.class);
Join<Record, Submission> rcd2sub = pRoot.join(Record_.submission);
Join<Submission, Question> sub2que = rcd2sub.join(Submission_.question);
Join<Question, Optional> que2opt = sub2que.join(Question_.optional);
// Attributes in RcdSubQuesOpt class
// get these columns from result and assign them to RcdSubQuesOpt class
criteriaQuery.multiselect(
pRoot.get("studentId"),
pRoot.get("examId"),
rcd2sub.get("questionId"),
rcd2sub.get("stuAnswer"),
sub2que.get("content"),
que2opt.get("content"),
que2opt.get("answer"));
// Predicate predicate = pRoot.get("examId").equals(1);
criteriaQuery.where();
results = session.createQuery(criteriaQuery).getResultList();
transaction.commit();
return results;
}
You haven't mentioned how you retrieve that data using hibernate. Have you tried trying to use #Query (select r from Record left join Submission sub on r.submissionId = sub.id where ...") ?
you have defined a #OneToOne relation in your record class. Apparantly thats wrong, since there exists more then one entry in your submission table for one record. So change this to #OneToMany and the respective relation in the submission class to #ManyToOne.
Besides your entities are not well named and mapped. Submission is in fact more of a question or an answer to it, because a line in that table does not represent one submission, which would be the expected meaning.

Hibernate composite pattern with many-to-many composition

I want to create a tree structure where each node can have multiple parents and children. (So actually it is not really a tree but more of a network).
For example, we have an interface to implement the composition, a User class which is the leaf node and a Group class which builds the structure. There would be some check against recursion (adding a group to a group that had the first group as a parent somewhere).
interface GroupMember {
boolean isLeaf();
}
class User implements GroupMember {
private int id;
private String name;
boolean isLeaf() { return true; }
}
class Group implements GroupMember {
private int id;
private Set<GroupMember> members;
boolean isLeaf() { return false; }
public addMember(GroupMember newMember) {
// Some check against recursion
members.add(newMember);
}
}
I see the most efficient way of implementing this in the database would be to have a link table (though this is just a suggestion and not required):
TABLE GROUP_MEMBER
-------------------
PARENT_ID NUMBER
CHILD_TYPE CHAR(1)
CHILD_ID NUMBER
However, I am not sure if Hibernate supports this design. It seems to me that in loading the members set in Group Hibernate would have to consider the discriminator in the GROUP_MEMBER table to decide which class to instantiate.
I have considered having group containing two sets to separately fetch the groups and users, but this seems less than ideal.
May be I'm wrong, but I don't agree with having CHILD_TYPE to be part part of GROUP_MEMBER. I's a CHILD implementation detail and should stay with it. By moving it to the CHILD table, you can use standard ManyToMany JPA mapping, which should make the life simpler.
If desired, CHILD_TYPE can be a discriminator inside the CHILD table.
I always recommend to have a FK. Bugs happen, and orphans in the database are always a huge headache.
Entities:
#Entity
#Inheritance(strategy = InheritanceType.SINGLE_TABLE)
#DiscriminatorColumn(name = "CHILD_TYPE", length = 1)
#Table(name = "MEMBERS", schema = "mtm")
#Data //lombok
#EqualsAndHashCode(onlyExplicitlyIncluded = true) //lombok
public abstract class GroupMember {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Integer id;
#ManyToMany
#JoinTable(name = "GROUP_MEMBER", schema = "mtm",
joinColumns = #JoinColumn(name = "MEMBER_ID", referencedColumnName = "ID"),
inverseJoinColumns = #JoinColumn(name = "PARENT_ID", referencedColumnName = "ID"))
private Set<Group> parents = new HashSet<>();
public abstract boolean isLeaf();
}
#Entity
#DiscriminatorValue("G")
#Data
#EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
class Group extends GroupMember {
#ManyToMany(mappedBy = "parents")
private Set<GroupMember> members = new HashSet<>();
public boolean isLeaf() {
return false;
}
}
#Entity
#DiscriminatorValue("U")
#SecondaryTable(name = "USERS", schema = "mtm")
#Data
#EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true)
class User extends GroupMember {
#EqualsAndHashCode.Include
#Column(table = "USERS")
private String name;
public boolean isLeaf() {
return true;
}
}
Schema:
create schema if not exists MTM;
CREATE TABLE MTM.MEMBERS (
id INT GENERATED BY DEFAULT AS IDENTITY,
CHILD_TYPE CHAR(1)
);
CREATE TABLE MTM.GROUP_MEMBER (
member_id INT,
parent_id INT
);
CREATE TABLE MTM.users (
id INT,
name varchar(255)
);
Notes:
Standard Hibernate MTM and inheritance strategies are implemented
Common data is stored in the MEMBERS table and User specific inside USERS table (implemented using #SecondaryTable)
Group data is stored entirely inside MEMBERS for efficiency (eliminates JOIN), but can be extended in the same way as User
If required, an additional interface can be introduced for the isLeaf() property.
I think you could use a #NamedQuery looking like select g from Group g left join fetch g.members on top of your Group class and use this query with the Hibernate session. Then you would use a query like select g from Group g left join fetch g.members where g.id = :id and get the result then.

How to avoid unwanted queries with Hibernate?

I want to make a query against an entity , called Utilisateur :
#Override
#Transactional
public List<Utilisateur> list() {
String hql = "from Utilisateur where deleted is null or deleted <> 1";
Query query = sessionFactory.getCurrentSession().createQuery(hql);
#SuppressWarnings("unchecked")
List<Utilisateur> listUser = (List<Utilisateur>) query.list();
return listUser;
}
#Entity
#Table(name = "utilisateur")
public class Utilisateur {
#Id
#SequenceGenerator(name="s_utilisateur", sequenceName="s_utilisateur", allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="s_utilisateur")
#Column(name = "user_code")
private Long code;
#Column(name = "user_nom")
private String nom;
#Column(name = "user_prenom")
private String prenom;
#Column(name = "user_login")
private String login;
#ManyToOne
#JoinColumn(name = "struct_code")
private Structure structure;
...
}
As you can see there is the attribute structure of type Structure. So here is the entity Structure :
#Entity
#Table(name = "structure")
public class Structure {
#Id()
#Column(name="struct_code")
private String code;
#ManyToOne
#JoinColumn(name = "str_struct_code")
private Structure parent;
#ManyToOne
#JoinColumn(name="niv_struct_code")
private NiveauStructure niveauStructure;
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "structures")
#JsonBackReference
private Set<Cdmt> programmes = new HashSet<Cdmt>();
#Column(name="struct_lib")
private String lib;
...
}
Again there is the attribute programmes , here is its code :
#Entity
#Table(name = "cdmt")
public class Cdmt {
#Id
#Column(name = "cdmt_code")
private String code;
#ManyToOne
#JoinColumn(name = "class_cdmt_code")
private ClasseCdmt classeCdmt;
#Column(name="cdmt_design")
#Lob
private String lib;
#ManyToOne
#JoinColumn(name = "prog_code")
private Pmo pmo;
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.MERGE)
#JoinTable(name = "programme_structure" , joinColumns = {#JoinColumn(name = "cdmt_code")} , inverseJoinColumns = {#JoinColumn(name = "struct_code")} )
#JsonManagedReference
private Set<Structure> structures = new HashSet<Structure>();
#ManyToOne
#JoinColumn(name = "cdm_cdmt_code")
private Cdmt cdmtParent;
...
}
At runtime there are many queries involved displayed in the console , among those queries there are queries against the Pmo entity although there is no mention about that entity in the query ! So how to avoid this propagations of queries ?
update :
here is query trace in the console :
Hibernate: select programmes0_.struct_code as struct_code2_50_0_, programmes0_.cdmt_code as cdmt_code1_33_0_, cdmt1_.cdmt_code as cdmt_code1_4_1_, cdmt1_.cdm_cdmt_code as cdm_cdmt_code7_4_1_, cdmt1_.cdmt_comment as cdmt_comment2_4_1_, cdmt1_.class_cdmt_code as class_cdmt_code8_4_1_, cdmt1_.cdmt_debut as cdmt_debut3_4_1_, cdmt1_.deleted as deleted4_4_1_, cdmt1_.cdmt_fin as cdmt_fin5_4_1_, cdmt1_.cdmt_design as cdmt_design6_4_1_, cdmt1_.prog_code as prog_code9_4_1_, cdmt2_.cdmt_code as cdmt_code1_4_2_, cdmt2_.cdm_cdmt_code as cdm_cdmt_code7_4_2_, cdmt2_.cdmt_comment as cdmt_comment2_4_2_, cdmt2_.class_cdmt_code as class_cdmt_code8_4_2_, cdmt2_.cdmt_debut as cdmt_debut3_4_2_, cdmt2_.deleted as deleted4_4_2_, cdmt2_.cdmt_fin as cdmt_fin5_4_2_, cdmt2_.cdmt_design as cdmt_design6_4_2_, cdmt2_.prog_code as prog_code9_4_2_, classecdmt3_.class_cdmt_code as class_cdmt_code1_5_3_, classecdmt3_.class_cdmt_comment as class_cdmt_comment2_5_3_, classecdmt3_.class_cdmt_lib as class_cdmt_lib3_5_3_, classecdmt3_.class_cdmt_niveau as class_cdmt_niveau4_5_3_, pmo4_.prog_code as prog_code1_31_4_, pmo4_.class_pmo_code as class_pmo_code6_31_4_, pmo4_.prog_debut as prog_debut2_31_4_, pmo4_.prog_fin as prog_fin3_31_4_, pmo4_.prog_design as prog_design4_31_4_, pmo4_.pmo_prog_code as pmo_prog_code7_31_4_, pmo4_.pnd_code as pnd_code8_31_4_, pmo4_.prog_comment as prog_comment5_31_4_, classepmo5_.class_pmo_code as class_pmo_code1_6_5_, classepmo5_.class_pmo_comment as class_pmo_comment2_6_5_, classepmo5_.class_pmo_lib as class_pmo_lib3_6_5_, classepmo5_.class_pmo_niveau as class_pmo_niveau4_6_5_, pmo6_.prog_code as prog_code1_31_6_, pmo6_.class_pmo_code as class_pmo_code6_31_6_, pmo6_.prog_debut as prog_debut2_31_6_, pmo6_.prog_fin as prog_fin3_31_6_, pmo6_.prog_design as prog_design4_31_6_, pmo6_.pmo_prog_code as pmo_prog_code7_31_6_, pmo6_.pnd_code as pnd_code8_31_6_, pmo6_.prog_comment as prog_comment5_31_6_, pnd7_.pnd_code as pnd_code1_32_7_, pnd7_.creation as creation2_32_7_, pnd7_.pnd_debut as pnd_debut3_32_7_, pnd7_.deleted as deleted4_32_7_, pnd7_.pnd_fin as pnd_fin5_32_7_, pnd7_.pnd_intitule as pnd_intitule6_32_7_, pnd7_.modification as modification7_32_7_, pnd7_.obj_code as obj_code10_32_7_, pnd7_.owner as owner8_32_7_, pnd7_.pnd_comment as pnd_comment9_32_7_, objectif8_.obj_code as obj_code1_25_8_, objectif8_.cdmt_code as cdmt_code8_25_8_, objectif8_.obj_comment as obj_comment2_25_8_, objectif8_.creation as creation3_25_8_, objectif8_.deleted as deleted4_25_8_, objectif8_.obj_intitule as obj_intitule5_25_8_, objectif8_.modification as modification6_25_8_, objectif8_.nat_obj_code as nat_obj_code9_25_8_, objectif8_.obj_obj_code as obj_obj_code10_25_8_, objectif8_.owner as owner7_25_8_, objectif8_.prog_code as prog_code11_25_8_, objectif8_.pta_code as pta_code12_25_8_, cdmt9_.cdmt_code as cdmt_code1_4_9_, cdmt9_.cdm_cdmt_code as cdm_cdmt_code7_4_9_, cdmt9_.cdmt_comment as cdmt_comment2_4_9_, cdmt9_.class_cdmt_code as class_cdmt_code8_4_9_, cdmt9_.cdmt_debut as cdmt_debut3_4_9_, cdmt9_.deleted as deleted4_4_9_, cdmt9_.cdmt_fin as cdmt_fin5_4_9_, cdmt9_.cdmt_design as cdmt_design6_4_9_, cdmt9_.prog_code as prog_code9_4_9_, natureobje10_.nat_obj_code as nat_obj_code1_23_10_, natureobje10_.nat_obj_comment as nat_obj_comment2_23_10_, natureobje10_.nat_obj_lib as nat_obj_lib3_23_10_, objectif11_.obj_code as obj_code1_25_11_, objectif11_.cdmt_code as cdmt_code8_25_11_, objectif11_.obj_comment as obj_comment2_25_11_, objectif11_.creation as creation3_25_11_, objectif11_.deleted as deleted4_25_11_, objectif11_.obj_intitule as obj_intitule5_25_11_, objectif11_.modification as modification6_25_11_, objectif11_.nat_obj_code as nat_obj_code9_25_11_, objectif11_.obj_obj_code as obj_obj_code10_25_11_, objectif11_.owner as owner7_25_11_, objectif11_.prog_code as prog_code11_25_11_, objectif11_.pta_code as pta_code12_25_11_, pmo12_.prog_code as prog_code1_31_12_, pmo12_.class_pmo_code as class_pmo_code6_31_12_, pmo12_.prog_debut as prog_debut2_31_12_, pmo12_.prog_fin as prog_fin3_31_12_, pmo12_.prog_design as prog_design4_31_12_, pmo12_.pmo_prog_code as pmo_prog_code7_31_12_, pmo12_.pnd_code as pnd_code8_31_12_, pmo12_.prog_comment as prog_comment5_31_12_, pta13_.pta_code as pta_code1_34_13_, pta13_.cdmt_code as cdmt_code18_34_13_, pta13_.class_pta_code as class_pta_code19_34_13_, pta13_.creation as creation2_34_13_, pta13_.pta_definitif as pta_definitif3_34_13_, pta13_.deleted as deleted4_34_13_, pta13_.pta_desc as pta_desc5_34_13_, pta13_.exer_code as exer_code20_34_13_, pta13_.pta_intitule as pta_intitule6_34_13_, pta13_.modification as modification7_34_13_, pta13_.owner as owner8_34_13_, pta13_.pta_pta_code as pta_pta_code21_34_13_, pta13_.pta_activite as pta_activite9_34_13_, pta13_.pta_cloture as pta_cloture10_34_13_, pta13_.pta_limite_decaisse as pta_limite_decais11_34_13_, pta13_.pta_num_credit as pta_num_credit12_34_13_, pta13_.pta_signature as pta_signature13_34_13_, pta13_.pta_unite_execution as pta_unite_executi14_34_13_, pta13_.pta_vigueur as pta_vigueur15_34_13_, pta13_.pta_ref as pta_ref16_34_13_, pta13_.pta_resultat_annee as pta_resultat_anne17_34_13_, pta13_.sect_code as sect_code22_34_13_, pta13_.struct_code as struct_code23_34_13_, pta13_.typ_proj_code as typ_proj_code24_34_13_, cdmt14_.cdmt_code as cdmt_code1_4_14_, cdmt14_.cdm_cdmt_code as cdm_cdmt_code7_4_14_, cdmt14_.cdmt_comment as cdmt_comment2_4_14_, cdmt14_.class_cdmt_code as class_cdmt_code8_4_14_, cdmt14_.cdmt_debut as cdmt_debut3_4_14_, cdmt14_.deleted as deleted4_4_14_, cdmt14_.cdmt_fin as cdmt_fin5_4_14_, cdmt14_.cdmt_design as cdmt_design6_4_14_, cdmt14_.prog_code as prog_code9_4_14_, classepta15_.class_pta_code as class_pta_code1_7_15_, classepta15_.class_pta_comment as class_pta_comment2_7_15_, classepta15_.class_pta_lib as class_pta_lib3_7_15_, classepta15_.class_pta_niveau as class_pta_niveau4_7_15_, exer16_.exer_code as exer_code1_15_16_, exer16_.exer_en_cours as exer_en_cours2_15_16_, exer16_.exer_lib as exer_lib3_15_16_, pta17_.pta_code as pta_code1_34_17_, pta17_.cdmt_code as cdmt_code18_34_17_, pta17_.class_pta_code as class_pta_code19_34_17_, pta17_.creation as creation2_34_17_, pta17_.pta_definitif as pta_definitif3_34_17_, pta17_.deleted as deleted4_34_17_, pta17_.pta_desc as pta_desc5_34_17_, pta17_.exer_code as exer_code20_34_17_, pta17_.pta_intitule as pta_intitule6_34_17_, pta17_.modification as modification7_34_17_, pta17_.owner as owner8_34_17_, pta17_.pta_pta_code as pta_pta_code21_34_17_, pta17_.pta_activite as pta_activite9_34_17_, pta17_.pta_cloture as pta_cloture10_34_17_, pta17_.pta_limite_decaisse as pta_limite_decais11_34_17_, pta17_.pta_num_credit as pta_num_credit12_34_17_, pta17_.pta_signature as pta_signature13_34_17_, pta17_.pta_unite_execution as pta_unite_executi14_34_17_, pta17_.pta_vigueur as pta_vigueur15_34_17_, pta17_.pta_ref as pta_ref16_34_17_, pta17_.pta_resultat_annee as pta_resultat_anne17_34_17_, pta17_.sect_code as sect_code22_34_17_, pta17_.struct_code as struct_code23_34_17_, pta17_.typ_proj_code as typ_proj_code24_34_17_, secteur18_.sect_code as sect_code1_48_18_, secteur18_.sect_comm as sect_comm2_48_18_, secteur18_.sect_lib as sect_lib3_48_18_, structure19_.struct_code as struct_code1_50_19_, structure19_.struct_lib as struct_lib2_50_19_, structure19_.niv_struct_code as niv_struct_code15_50_19_, structure19_.str_struct_code as str_struct_code16_50_19_, structure19_.struct_sigle as struct_sigle3_50_19_, structure19_.struct_comment as struct_comment4_50_19_, structure19_.struct_contact as struct_contact5_50_19_, structure19_.struct_interne as struct_interne6_50_19_, structure19_.struct_mission_fonc as struct_mission_fon7_50_19_, structure19_.struct_mission_oper as struct_mission_ope8_50_19_, structure19_.struct_resp_fonc as struct_resp_fonc9_50_19_, structure19_.struct_resp_hiera as struct_resp_hiera10_50_19_, structure19_.struct_resp_oper as struct_resp_oper11_50_19_, structure19_.struct_site as struct_site12_50_19_, structure19_.struct_tache_fonc as struct_tache_fonc13_50_19_, structure19_.struct_tache_oper as struct_tache_oper14_50_19_, niveaustru20_.niv_struct_code as niv_struct_code1_24_20_, niveaustru20_.niv_struct_comment as niv_struct_comment2_24_20_, niveaustru20_.niv_struct_lib as niv_struct_lib3_24_20_, niveaustru20_.niv_struct_ordre as niv_struct_ordre4_24_20_, structure21_.struct_code as struct_code1_50_21_, structure21_.struct_lib as struct_lib2_50_21_, structure21_.niv_struct_code as niv_struct_code15_50_21_, structure21_.str_struct_code as str_struct_code16_50_21_, structure21_.struct_sigle as struct_sigle3_50_21_, structure21_.struct_comment as struct_comment4_50_21_, structure21_.struct_contact as struct_contact5_50_21_, structure21_.struct_interne as struct_interne6_50_21_, structure21_.struct_mission_fonc as struct_mission_fon7_50_21_, structure21_.struct_mission_oper as struct_mission_ope8_50_21_, structure21_.struct_resp_fonc as struct_resp_fonc9_50_21_, structure21_.struct_resp_hiera as struct_resp_hiera10_50_21_, structure21_.struct_resp_oper as struct_resp_oper11_50_21_, structure21_.struct_site as struct_site12_50_21_, structure21_.struct_tache_fonc as struct_tache_fonc13_50_21_, structure21_.struct_tache_oper as struct_tache_oper14_50_21_, typeprojet22_.typ_proj_code as typ_proj_code1_52_22_, typeprojet22_.typ_proj_comment as typ_proj_comment2_52_22_, typeprojet22_.typ_proj_lib as typ_proj_lib3_52_22_ from programme_structure programmes0_ inner join cdmt cdmt1_ on programmes0_.cdmt_code=cdmt1_.cdmt_code left outer join cdmt cdmt2_ on cdmt1_.cdm_cdmt_code=cdmt2_.cdmt_code left outer join classe_cdmt classecdmt3_ on cdmt2_.class_cdmt_code=classecdmt3_.class_cdmt_code left outer join pmo pmo4_ on cdmt2_.prog_code=pmo4_.prog_code left outer join classe_pmo classepmo5_ on pmo4_.class_pmo_code=classepmo5_.class_pmo_code left outer join pmo pmo6_ on pmo4_.pmo_prog_code=pmo6_.prog_code left outer join pnd pnd7_ on pmo6_.pnd_code=pnd7_.pnd_code left outer join objectif objectif8_ on pnd7_.obj_code=objectif8_.obj_code left outer join cdmt cdmt9_ on objectif8_.cdmt_code=cdmt9_.cdmt_code left outer join nature_objectif natureobje10_ on objectif8_.nat_obj_code=natureobje10_.nat_obj_code left outer join objectif objectif11_ on objectif8_.obj_obj_code=objectif11_.obj_code left outer join pmo pmo12_ on objectif11_.prog_code=pmo12_.prog_code left outer join pta pta13_ on objectif11_.pta_code=pta13_.pta_code left outer join cdmt cdmt14_ on pta13_.cdmt_code=cdmt14_.cdmt_code left outer join classe_pta classepta15_ on pta13_.class_pta_code=classepta15_.class_pta_code left outer join exercice exer16_ on pta13_.exer_code=exer16_.exer_code left outer join pta pta17_ on pta13_.pta_pta_code=pta17_.pta_code left outer join secteur secteur18_ on pta17_.sect_code=secteur18_.sect_code left outer join structure structure19_ on pta17_.struct_code=structure19_.struct_code left outer join niveau_structure niveaustru20_ on structure19_.niv_struct_code=niveaustru20_.niv_struct_code left outer join structure structure21_ on structure19_.str_struct_code=structure21_.struct_code left outer join type_projet typeprojet22_ on pta17_.typ_proj_code=typeprojet22_.typ_proj_code where programmes0_.struct_code=?
I had the same problem. I'm using SqlResultSetMapping.
The point of SqlResultSetMapping is to defined custom mapping for NamedNativeQuery.
With SqlResultSetMapping, you can either use :
EntityResult to map the result of a query to an Entity object
ConstructorResult to map the result of a query to "non-entity" object with the contructor
ColumnResult
Example with ConstructorResult
Let's say we have a very complex entity ComplexObject with many relations to other objects.
#Entity
#Table(name = "complex_object")
public class ComplexObject {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id_complex_object")
private Integer id;
#Column(name = "label")
private String label;
// More relations...
}
I want a query to retrieve only the id and the label of this entity.
In ComplexObject, I define a new NamedNativeQuery as following.
#NamedNativeQueries({
#NamedNativeQuery(name = "ComplexObject.getIdAndLabel", query = "SELECT co.id_complex_object, co.label FROM complex_object", resultSetMapping = "SimpleObject")
})
The important part of this NamedNativeQuery is the resultSetMapping = "SimpleObject".
Then I can define a SimpleObject that is not a entity and match my query as following :
public class SimpleObject {
private Integer id;
private String label;
/**
* This constructor is very important !
* Its signature has to match the SqlResultSetMapping defined in the entity class.
* Otherwise, an exception will occur.
*/
public SimpleObject(Integer id, String label) {
this.id = id;
this.label = label;
}
// Getters and setters...
}
Then I can define the SqlResultSetMapping in ComplexObject as following :
#SqlResultSetMappings({
#SqlResultSetMapping(name = "SimpleObject", classes = {
#ConstructorResult(targetClass = SimpleObject.class, columns = {
#ColumnResult(name = "id_complex_object", type = Integer.class),
#ColumnResult(name = "label", type = String.class)
})
})
})
It's done.
The NamedNativeQuery will use SimpleObject SqlResultSetMapping to construct a SimpleObject (throught the constructor) so your query returns a SimpleObject instead of ComplexObject.
Try adding attribute fetch=FetchType.LAZY to the #ManyToOne on Structure object of Utilisateur entity class.
Since ManyToOne relations are eagerly loaded. I think it is trying to load Structure entity in a new SELECT statement. And this is causing entities with in Structure to be loaded further.
So adding fetch=FetchType.LAZY on Structure should prevent the initial SELECT query of Structure and thus the subsequent entities mapped in the Structure entity.
UPDATE
In the above statements, the assumption you don't want to populate Structure object as you fetch Utilisateur object. If that is not the case, then you can move the FetchType.LAZY further down the association chain. Say you want to fetch Structure but not its related entities like parent etc, then you can move FetchType.LAZY to those annotation (ManyToOne etc).
Note that with the HQL you are using from Utilisateur where deleted is null or deleted <> 1 with no joins it will by default fire a new SELECT query to fetch those LAZY associations by default. And if you want to fetch with single (or lesser number of) query that with joins you need to update your HQL to use JOIN FETCH queries.
One common issue we usually encounter is a scenario where we don't want to fetch an association, but somewhere in the toString method we navigate the associations start printing them and resulting in the LazyInitializationException.

Spring Data: findByEntityNot() in a many to many relationship

I have the following many to many relationship:
#Entity
public class Foo implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#JsonIgnore
#ManyToMany
#JoinTable(name = "foo_bar",
joinColumns = {#JoinColumn(name = "foo_id", referencedColumnName = "id")},
inverseJoinColumns = {#JoinColumn(name = "bar_name", referencedColumnName = "name")})
private Set<Bar> bars = new HashSet<>();
}
#Entity
public class Bar implements Serializable {
#Id
private String name;
}
Now I want to query the FooRepository for all Foo's which DO NOT contain a Bar with the name "example". I tried to use the following Spring data method in FooRepository:
findByBars_NameNot(String barName);
But this returns one of every entry of the pivot table foo_bar which doesn't have "example" in its bar_name column. This means it can return duplicate Foo objects as well as Foo object which do actually contain a Bar with name "example" i.e. it's equivalent to the following SQL:
SELECT * FROM myschema.foo_bar WHERE bar_name != "example";
Is there any nice way in Spring data to write a repository method to do what I am trying?
I have found the following native query which does what I need but I am hesitant to use a native query as I feel there is a cleaner way of doing this:
SELECT * FROM myschema.foo WHERE id NOT IN (SELECT foo_id FROM myschema.foo_bar WHERE bar_name = "example")
From Spring Data JPA docs
public interface FooRepository extends JpaRepository<Foo, Long> {
#Query("SELECT f FROM Foo f WHERE NOT EXISTS (SELECT b FROM f.bars b WHERE b.name = ?1)")
Foo findByNotHavingBarName(String name);
}
Unfortunately, there's no support for EXISTS queries in the query creation from method names

Categories

Resources