Hibernate query - select one Object which have only one Set element which meets criteria -
i have 3 classes:
public class a{ private string name; } public class b{ private aobj; } public class c{ private set<b> bobj; } and following criteria:
session session = opensession(); criteria c1 = session.createcriteria(c.class); criteria c2 = c1.createcriteria("bobj"); criteria c3 = c2.createcriteria("aobj"); c2.add(restrictions.eq("name",name)); which work , c1.uniqueresult() expected 1 (name unique)
and question: way c object 1 element in set contain bobj meets criteria c2? (supposing set bobj have more 1 element)
update 1: actual result (c.class json)
{ bobj : [ 1: { aobj : { name : name1}}, 2: { aobj : { name : name2}}, 3: { aobj : { name : name3}} ]} expected result name = name1 :
{ bobj : [ 1: { aobj : { name : name1}} ] } so after query bobj have 1 element in list meets criteria name = name1
there many ways that, maybe can use 1 of following fits needs best.
e.g. if have example instance of b handy, use criteriabuilder. pass example object (bobj) expect member of association , specify associations size:
final root<c> selection = createquery.from(c.class); createquery.select(selection).where( criteriabuilder.ismember(bobj, selection.<set<b>>get("bobj")), criteriabuilder.equal(criteriabuilder.size(selection.<set<b>>get("bobj")), 1)); if criteria more, define (implicit) join behavior selection , add restrictions:
final criteria criteria = entitymanager.unwrap(session.class).createcriteria(c.class, "c"); criteria.createalias("c.bobj", "b"); criteria.createalias("b.aobj", "a"); criteria.add(restrictions.sizeeq("c.bobj", 1)); criteria.add(restrictions.eq("a.name", "test")); or if jpql, use like:
final typedquery<c> query = entitymanager.createquery( "select c c c join c.bobj b join b.aobj c.bobj.size = 1 , a.name = :name ", c.class); query.setparameter("name", "test"); if need play with, here test try out. change name ("test") or uncomment second association member test restrictions:
import static org.hamcrest.corematchers.is; import static org.junit.assert.assertthat; import java.util.list; import java.util.set; import javax.persistence.entitymanager; import javax.persistence.persistencecontext; import javax.persistence.typedquery; import javax.persistence.criteria.criteriabuilder; import javax.persistence.criteria.criteriaquery; import javax.persistence.criteria.root; import org.hibernate.criteria; import org.hibernate.session; import org.hibernate.criterion.restrictions; import org.junit.test; import org.junit.runner.runwith; import org.springframework.boot.test.context.springboottest; import org.springframework.test.context.junit4.springrunner; import org.springframework.transaction.annotation.transactional; @runwith(springrunner.class) @springboottest @transactional public class listtests { @persistencecontext private entitymanager entitymanager; @test public void criteriabuildertest() { final aobj = new a(); aobj.setname("test"); entitymanager.persist(aobj); final b bobj = new b(); bobj.setaobj(aobj); entitymanager.persist(bobj); final b bobj2 = new b(); bobj2.setaobj(aobj); entitymanager.persist(bobj2); final c cobj = new c(); cobj.getbobj().add(bobj); // cobj.getbobj().add(bobj2); entitymanager.persist(cobj); final criteriabuilder criteriabuilder = entitymanager.getcriteriabuilder(); final criteriaquery<c> createquery = criteriabuilder.createquery(c.class); final root<c> selection = createquery.from(c.class); createquery.select(selection).where(criteriabuilder.ismember(bobj, selection.<set<b>>get("bobj")), criteriabuilder.equal(criteriabuilder.size(selection.<set<b>>get("bobj")), 1)); final list<c> resultlist = entitymanager.createquery(createquery).getresultlist(); assertthat("should 1 result!", resultlist.size(), is(1)); } @test public void criteriatest() { final aobj = new a(); aobj.setname("test"); entitymanager.persist(aobj); final b bobj = new b(); bobj.setaobj(aobj); entitymanager.persist(bobj); final b bobj2 = new b(); bobj2.setaobj(aobj); entitymanager.persist(bobj2); final c cobj = new c(); cobj.getbobj().add(bobj); // cobj.getbobj().add(bobj2); entitymanager.persist(cobj); final criteria criteria = entitymanager.unwrap(session.class).createcriteria(c.class, "c"); criteria.createalias("c.bobj", "b"); criteria.createalias("b.aobj", "a"); criteria.add(restrictions.sizeeq("c.bobj", 1)); criteria.add(restrictions.eq("a.name", "test")); final list<c> resultlist = criteria.list(); assertthat("should 1 result!", resultlist.size(), is(1)); } @test public void jpqltest() { final aobj = new a(); aobj.setname("test"); entitymanager.persist(aobj); final b bobj = new b(); bobj.setaobj(aobj); entitymanager.persist(bobj); final b bobj2 = new b(); bobj2.setaobj(aobj); entitymanager.persist(bobj2); final c cobj = new c(); cobj.getbobj().add(bobj); // cobj.getbobj().add(bobj2); entitymanager.persist(cobj); final typedquery<c> query = entitymanager.createquery( "select c c c join c.bobj b join b.aobj c.bobj.size = 1 , a.name = :name ", c.class); query.setparameter("name", "test"); final list<c> resultlist = query.getresultlist(); assertthat("should 1 result!", resultlist.size(), is(1)); } } the mapping test like:
@entity public class { @id @generatedvalue private long id; private string name; public string getname() { return name; } public void setname(string name) { this.name = name; } } @entity public class b { @id @generatedvalue private long id; @onetoone private aobj; public void setaobj(a aobj) { this.aobj = aobj; } } @entity public class c { @id @generatedvalue private long id; @onetomany private set<b> bobj = new hashset<>(); public set<b> getbobj() { return bobj; } public void setbobj(set<b> bobj) { this.bobj = bobj; } } i tend typesafe solution, maybe first or last one. or think using metamodel generator.
update:
after nailing down problem in comments add test make problem more clear (and keep origin answer history purposes).
accessing unfiltered association give results (either eager on query time or lazy on accessing it). 1 solution filter before access , convert projection or something. maybe can play around test find way that:
@test public void jpqltestwithdelayedfilterquery() { final string filterfieldname = "name"; final string filterfieldvalue = "test"; // 1 want bs final aobj = new a(); aobj.setname(filterfieldvalue); entitymanager.persist(aobj); // b should pass filter final b bobj = new b(); bobj.setaobj(aobj); entitymanager.persist(bobj); // not matching filter final aobj2 = new a(); aobj2.setname("testxxx"); entitymanager.persist(aobj2); // don't want b here final b bobj2 = new b(); bobj2.setaobj(aobj2); entitymanager.persist(bobj2); // b test first query final b bobj3 = new b(); bobj3.setaobj(aobj2); entitymanager.persist(bobj3); // 1 returned first query final c cobj = new c(); cobj.getbobj().add(bobj); cobj.getbobj().add(bobj2); entitymanager.persist(cobj); // c test first query final c cobj2 = new c(); cobj2.getbobj().add(bobj3); entitymanager.persist(cobj2); // let's cs need final session session = entitymanager.unwrap(session.class); final query cquery = session.createquery( "select c c c inner join c.bobj b inner join b.aobj a.name = :name "); cquery.setparameter(filterfieldname, filterfieldvalue); final list cresults = cquery.list(); assertthat("should 1 c result!", cresults.size(), is(1)); // sadly b collection initialized fully, @ latest on accessing it, 2 bs here :/ final c cresult = (c)cresults.iterator().next(); assertthat("should 2 initialized b results here!", cresult.getbobj().size(), is(2)); // way getting our needed bs filter (imagine did not use before) final query query = session.createfilter(cresult.getbobj(), "where this.aobj.name = :name"); query.setparameter(filterfieldname, filterfieldvalue); final list bresult = query.list(); assertthat("should 1 filtered b result here!", bresult.size(), is(1)); }
Comments
Post a Comment