Hibernate Criteria for Join Table - java

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.

Related

One-to-many join on composite primary key in jpa

I have search whole day but couldn't find the solution.
I have 2 entities Table A and Table B. Table A has 1 primary key and Table B has composite key. There is oneToMany mapping between Table A and Table B
I have created Table A Like below
#Entity
public class TableA {
#Id
private Integer sId;
private Integer roleNo;
private String studentName;
#OneToMany
#JoinColumn(name="roleNo")
private List<TableB> tableb;
}
Table B looks like below
#Entity
public class TableB {
#EmbededId
private CKStudent student;
private String activities;
}
here is my composite class
#Embeddable
public class CKStudent {
private Integer roleNo;
private Integer ActivityId;
}
I want to create query like this
select *
from tableA a
left join tableB on a.roleNo = b.roleNo
where a.sId = 1 and b.activityId = 3
I have written jpa method for it
List<TableA> findBySIdAndtablebStudentActivityId(Integer id,Integer activityId);
but I am not getting the required result,
I am getting result for all the activityId, and not the activityId which I am passing through parameter.
Any help would be appreciated.
UPDATE
This JPA is creating 2 queries
Query #1
select
tbla0_.sId as s_id1_8_, tbla0_.roleNo as rol2_8_,
tbla0_.studentName as student_name3_8
from tableA tbla0_
left outer join tableB tbl1_ on tbla0_.role_id = tbl1_.role_id
where tbla0_.sId = ?
and tbl1_.activityId = ?
which is the correct query
Query #2
select
tblB1_.roleNo as roleNo_21_0_, tblB1_.activityId as activityId2_21_0_,
tblB1_.activities as act1_21_1_,
from tableB tblB1_
where tblB1_.roleNo = ?
which is wrong and returning wrong results.

hibernate createAlias with clause generates wrong query

I have the following tables:
A: id
B: id, text
AB: aID, bID
I want to joib A and B where B.text contains the word 'cat'.
This is the hibernate query I do:
Criteria c = session.createCriteria(TableA.class, "A");
c.createAlias("A.bs", "B", JoinType.INNER_JOIN, Restrictions.like("b.text", "%cat%"));
c.setProjection(Projections.property("id"));
The generated query is:
Select id
FROM A a
INNER JOIN AB ab ON a.id=ab.aID AND (b.text like ?)
INNER JOIN B b ON b.id=ab.bID AND (b.text like ?)
For some reason AND (b.text like ?) appears in both inner joins. I far as I understand its supposed to be only in the second on.
This causes the following exception:
java.sql.SQLException: No value specified for parameter 2
I guess it's happening because it has only one parameters and two '?'.
What am I missing?
EDIT:
Adding the persistent classes:
#Entity
#Table(name="A")
Class A {
#Id
#Column(name="id", length=255)
protected String id;
#OneToMany
#JoinTable(name="AB", joinColumns = #JoinColumn( name="aID"), inverseJoinColumns = #JoinColumn( name="bID"))
#LazyCollection(LazyCollectionOption.FALSE)
protected List<B> bs;
}
#Entity
#Table(name="B")
Class B {
#Id
#Column(name="id", length=255)
protected String id;
#Column(name="text", length=255)
protected String text;
}
I think you need to create an alias
c.createAlias("A.bs", "b", JoinType.INNER_JOIN, Restrictions.like("b.text", "%cat%"));
Updated
I think It is a Hibernate bug. You can fix it by using a restriction in the where clause
c.createAlias("A.bs", "b", JoinType.INNER_JOIN);
c.add(Restrictions.like("b.text", "%cat%"));
or don't use join table and do association by foreign key in B.

Setting max fetch depth in hibernate jpa

I have 3 classes named A,B,C as follows :
Class A :
#Entity
public class A implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#ManyToOne(cascade = CascadeType.PERSIST)
private B b;
//Getters and Setters
}
Class B :
#Entity
public class B {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#ManyToOne(cascade = CascadeType.PERSIST)
private C c;
//Getters and Setters
}
Class C:
#Entity
public class C implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
//Getters and Settters
}
when i use method :
em.find(A.class, a.getId());
hibernate generates following sql query :
Hibernate :
select
a0_.id as id1_0_0_,
a0_.b_id as b_id2_0_0_,
b1_.id as id1_1_1_,
b1_.c_id as c_id2_1_1_,
c2_.id as id1_2_2_
from
A a0_
left outer join
B b1_
on a0_.b_id=b1_.id
left outer join
C c2_
on b1_.c_id=c2_.id
where
a0_.id=?
and loads all three objects but i don't want hibernate to goes that far.
i tried to set a max fetch dept for hibernate by setting
<property name="hibernate.max_fetch_depth" value="0"/>
so that hibernate just goes for one object of class A, but the generated sql query became this one :
Hibernate:
select
a0_.id as id1_0_0_,
a0_.b_id as b_id2_0_0_
from
A a0_
where
a0_.id=?
Hibernate:
select
b0_.id as id1_1_0_,
b0_.c_id as b_id2_1_0_
from
B b0_
where
b0_.id=?
Hibernate:
select
c0_.id as id1_2_0_,
from
C c0_
where
c0_.id=?
as it's clear, hibernate fetched all three objects again.
as the documentation says :
max_fetch_depth :
Sets a maximum "depth" for the outer join fetch tree for single-ended associations (one-to-one, many-to-one). A 0 disables default outer join fetching.
it just impacts the outer join not the max fetch depth generally.
is there any way that when try to fetch objects of A, hibernate just brings 1 level of association? ( i mean objects of A have b but b doesn't have c )

Map<String, Entity> with composite key in Entity

I've got the following two tables:
TABLE A:
INT id
VARCHAR x
PRIMARY KEY (id)
TABLE B:
INT a_id
VARCHAR locale
VARCHAR z
PRIMARY KEY (a_id, locale)
It's basically a simple OneToMany relation. Table B contains the the id (a_id) of the referenced row in Table A plus a locale. This means: Every entry in A can have 0..* entries in Table B, each one with a distinct locale value.
I have the following two classes, which should represent those tables:
#Entity
#Table(name="A")
class A {
#Id
#Column(name="id")
int id;
#Column(name="x")
String x;
#OneToMany(mappedBy="a") // ???
#MapKey... // ???
Map<String, B> bMap;
}
#Entity
#Table(name="B")
class B {
#ManyToOne
#JoinColumn(name="a_id")
A a;
#Column(name="locale")
String locale;
#Column(name="z")
String z;
}
Now two things are missing:
The Annotations for Map<String, B> bMap. I just don't know if I should use a #MapKey or #MapKeyColumn and how to map to that composite key. And if I should/have to use #OneToMany?
The B class of course needs a composite key. Should I use an #EmbeddedId or #IdClass?
Could you provide some example code for this scenario?
Thank you!
Working solution at the bottom
I think, I've now managed to put things together. At least the generated SQL Tables look right, though I still have to figure out how to get Cascaded Saving done...
#Entity
#Table(name="A")
public class A {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
long id;
#Column(name="x")
String x;
#OneToMany(mappedBy="id.a", cascade=CascadeType.ALL, orphanRemoval=true)
#MapKey(name="id.locale")
Map<String, B> bMap = new HashMap<String, B>();
}
#Entity
#Table(name="B")
public class B {
#EmbeddedId
BPK id;
#Column(name="z")
String z;
}
#Embeddable
public class BPK implements Serializable {
#ManyToOne
#JoinColumn(name="a_id")
A a;
#Column(name="locale")
String locale;
// equals + hashcode
}
When calling aRepository.findById(...) Hibernates gives:
Hibernate: select * from A a where a.id=?
which is correct.
But if I call aEntity.getBMap() it always fetches the whole map, even if I just want to use aEntity.getBMap().put("EN", someBObject) and don't want to read any data from it. But that's okay for now.
Now I've just to figure out how to get Cascaded Saving to work. When doing aEntity.getBMap().put("EN", someBObject); aRepository.save(eEntity); I get
org.hibernate.id.IdentifierGenerationException: null id generated for:class B
I think I'm just missing some setters for the #EmbeddedId or it's fields.
FINALLY SOLVED:
Cascaded saving somehow didn't work with #EmbeddedId composite key. So I thought about it and figured out, that I could instead use an #ElementCollection! :).
So here's what I finally did:
#Entity
#Table(name="A")
public class A {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
long id;
#Column(name="x")
String x;
#ElementCollection
#CollectionTable(name="B", joinColumns=#JoinColumn(name="a_id"))
#MapKeyColumn(name="locale")
Map<String, B> bMap = new HashMap<String, B>();
}
#Embeddable
public class B {
#Column(name="z")
String z;
}
Hibernate outputs:
A a = aRepository.findById(...)
Hibernate: select * from A where id=?
a.getBMap().put("EN", someBObject)
Hibernate: select * from B where a_id=?
aRepository.save(a)
Hibernate: insert into B (a_id, locale, z) values (?, ?, ?)

Hibernate: delete orphan from Join Table

I have two persistent classes. They are are in many-to-many relationship. One class have a set of objects of another class.
#Entity class A {
#Id #Generated int id;
#ManyToMany(cascade = CascadeType.ALL)
Set<B> myset = new HashSet<B>();
}
and
#Entity class B {
#Id #Generated int id;
}
Hibernate makes table: A_B with columns A_id and B_id. If i delete some A or B object, any entrie in A_B table makes no sense anymore. Can it be deleted automaticly?
When you delete A, you should clear its set of Bs before.
When you delete B, you should remove it from the set of Bs of every A. This os of course easier if you make the relationship bidirectional.

Categories

Resources