I've encountered a strange problem in a simple Spring Boot application, where I update a parent entity, delete all of its children and then create new ones.
After saving, the resulting entity looks fine and has all of the new children, but when I query it again, I find that one of the child entities is lost!
The quick and dirty solution for this would be to split the code into two transactions, but I want to understand the cause the orphan removal to act like this.
Here's the service code:
#Service
public class ParentService {
private final EntityManager em;
public ParentEntity getParent(UUID parentId) {
return em.createQuery(
"SELECT p " +
"from ParentEntity p " +
"JOIN FETCH p.children " +
"WHERE p.id = :parentId", ParentEntity.class)
.setParameter("parentId", parentId)
.getSingleResult();
}
#Transactional
public ParentEntity resetChildren(UUID parentId) {
var parent = getParent(parentId);
parent.getChildren().clear();
addChildren(parent, 2);
em.persist(parent);
return parent;
}
private void addChildren(ParentEntity parent, int childCount) {
for (var i = 0; i < childCount; i++) {
parent.addChildren(new ChildEntity());
}
}
}
The SQL output after resetting children and the fetching the parent again is this:
select parententi0_.id as id1_1_0_, children1_.parent_id as parent_i2_0_1_, children1_.id as id1_0_1_, children1_.id as id1_0_2_, children1_.parent_id as parent_i2_0_2_ from parent parententi0_ left outer join child children1_ on parententi0_.id=children1_.parent_id where parententi0_.id=?
insert into child (parent_id, id) values (?, ?)
insert into child (parent_id, id) values (?, ?)
delete from child where id=?
delete from child where id=?
delete from child where id=? <-- One extra delete
select parententi0_.id as id1_1_0_, children1_.parent_id as parent_i2_0_1_, children1_.id as id1_0_1_, children1_.id as id1_0_2_, children1_.parent_id as parent_i2_0_2_ from parent parententi0_ left outer join child children1_ on parententi0_.id=children1_.parent_id where parententi0_.id=?
The entities look like this:
The parent
#Entity
#Table(name = "parent")
public class ParentEntity {
#Id
#GeneratedValue
private UUID id;
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<ChildEntity> children = new HashSet<>();
...
public void addChildren(ChildEntity child) {
this.children.add(child);
child.setParent(this);
}
...
}
And the child
#Entity
#Table(name = "child")
public class ChildEntity {
#Id
#GeneratedValue
private UUID id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "parent_id")
private ParentEntity parent;
...
}
An important note to this, that in my use case, the id of these entities is a UUID. I can't get it working with any numeric ids
The code repository with a unit test can be found here
An interesting thing happens, if I decide to add 1 child, instead of two or more, the parent itself is deleted! Because of this, it feels like I'm looking at a bug in Hibernate.
The problem was that I had cascade on the child side, which lead to very very weird results. I don't want parent to be deleted on cascade, so I don't need that.
The fix was to change the child to this:
#Entity
#Table(name = "child")
public class ChildEntity {
#Id
#GeneratedValue
private UUID id;
#ManyToOne // <---- No more cascade!
#JoinColumn(name = "parent_id")
private ParentEntity parent;
...
}
Firstly, let me thank to who want to give the answer,
I am a new person using JPA for now, suppose that I have two tables, table A and B, for table A, we have the column
id_A,
id_B,
etc...
For table B, we have
id_B
And we have the entity classes,
#Data
class A {
#Id
#Column(name = "ID_A")
private String id;
#ManyToOne
#JoinColumn(name = "ID_B", referencedColumnName = "ID_B")
private B b;
}
#Data
class B {
#Id
#Column(name = "ID_B")
private String id;
#JsonManagedReference
#OneToMany(mappedBy = "b", cascade = CascadeType.ALL)
#BatchSize(size = BATCH_SIZE)
private List<A> as;
}
Now, in order to only use the id of B, I firstly select all the relevant entity of A by ARepository.findAllByIds().
with the above query, I want to get all the ID_Bs, but when I do it by looping the result of the above query - a.getB, I noticed that it will get the B from Database again.
I know that we can fetch the A and B together by EAGER or EntityGraph, etc...
I am just wondering, is there any a way that I can only get the id_b from the ARepository.findAllByIds()?
To answer your question I think you will have to annotate your findAllByIds method with something like...
#Query(value = "SELECT a, b.id FROM A a LEFT JOIN a.b b where a.id in :ids") then your Bs will be loaded within the same query as the As
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.
Help me Hibernate Guru..
i have 2 relationship class, let's call class A and B
#Entity
#Table(name="A")
public class A extends Serializable{
#Id
#Column(name="a_id")
private int id;
#Column(name="a_name")
private String name;
/*
*.....Setter and Getter
*/
}
#Entity
#Table(name="B")
public class B extends Serializable{
#Id
#Column(name="b_id")
private int id;
#ManyToMany(
fetch= FetchType.EAGER,
targetEntity=package.A.class,
cascade={CascadeType.ALL}
)
#JoinTable(
name="B_A",
joinColumns=#JoinColumn(name="b_id"),
inverseJoinColumns=#JoinColumn(name="a_id")
)
#Fetch(FetchMode.SUBSELECT)
private List<A> list;
/*
*.....Setter and Getter
*/
}
Hibernate will generate 3 Table A, B, and B_A. with table B_A have 2 foreign key, one foreign key for primary key table A and one again foreign key for primary key table B,
i want select data from table A, like query :
select * from A a inner join B_A ba on ba.a_id = a.id inner join B b on b.b_id = ba.b_id where b.id in(?, ?, ?, ?)
so how Criteria code i have to create???? and for expected list result List i want to use Transformer.
Thanks
Try this:
criteria.createCriteria(A.class)
.createCriteria("id", "join_between_a_b")
.add(Restrictions.eq("some_field_of_A", someValue));
Remember, A and B have to have id as their identifiers in order to make them join.
By some_field_of_A I mean anything you like in class A, like name for example. You can have restriction over any properties of these classes.