Avri Blog

Blog à l'avricot - Lord Of Castle, java, nosql, Utomia, AvriChat, javascript, Json, Css, Mootools, ajax, php...

Aller au contenu | Aller au menu | Aller à la recherche

Stop Bubble | Start

dimanche, juillet 10 2011

hibernate : paginationwith collection and the criteria api

Let's say you have an Object TheList, which has a list of Company object: companies, and you want to paginate the companies of a specific list, using the criteria API. A company is composed of an Id, and a List of urls (String).

The first solution I think about is the following :

final Criteria rootCriteria = session.createCriteria(TheList.class, "t");
	    final Criteria companiesCriteria = rootCriteria.createCriteria("t.companies");
	    companiesCriteria.setFirstResult(firstResult).setMaxResults(maxResult);
	    rootCriteria.add(Restrictions.eq("t.id", listId)).add(Restrictions.eq("t.owner.id", ownerId));
	    return rootCriteria.list();

This will return a list of "maxResult" of TheList objects.
Unfortunately, you can't get just the list of companies, hibernate's criteria api can't handle this next query:

final Criteria rootCriteria = session.createCriteria(TheList.class, "t");
	    final Criteria companiesCriteria = rootCriteria.createCriteria("t.companies");
	    companiesCriteria.setFirstResult(firstResult).setMaxResults(maxResult);
	    rootCriteria.add(Restrictions.eq("t.id", listId)).add(Restrictions.eq("t.owner.id", ownerId));
	    rootCriteria.setProjection(Projections.property("t.companies"));
	    return rootCriteria.list();

you will have a nice error :

java.lang.ArrayIndexOutOfBoundsException: 0
	at org.hibernate.loader.criteria.CriteriaLoader.getResultColumnOrRow(CriteriaLoader.java:148)
...

So we can stick to the first solution, and add a built-in transformer:

...
rootCriteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);

But this doesn't fit our need, since the limit is applied on the TheList entity, and not on the companies. Useless...

The only solution I found was to create a subQuery to select the companies ids, and another main request where we select the companies, based on the id of the first request.
This looks like as following:

final Criteria rootCriteria = session.createCriteria(Company.class, "c").setMaxResults(maxResult).setFirstResult(firstResult) ;
	    final DetachedCriteria subQuery = DetachedCriteria.forClass(Target.class, "t") //
		    .add(Restrictions.eq("t.id", targetId)).add(Restrictions.eq("t.owner.id", ownerId)) //
		    .createAlias("t.companies", "cp") //
		    .setProjection(Projections.property("cp.id"));
	    final List<Company> companies = rootCriteria.add(Subqueries.propertyIn("c.id", subQuery)).list(); //"exists" may be a better solution than "in".

The main concern about this solution is that you can't set the setMaxResult/setFirstResult on a DetachedCriteria, and a Subqueries needs to be a DetachedCriteria. Stupid: the subquery will return all the companies, and we only need a few of them...
We could also make 2 separated queries : get the ids on the first one, and the companies on the second one.

Now that you have a list of company, you will probably need to fetch the list of urls contained in each company, because you want to display them on your web page.

final Criteria rootCriteria = session.createCriteria(Company.class, "c").setMaxResults(maxResult).setFirstResult(firstResult) //
		    .setFetchMode("c.urls", FetchMode.JOIN) ;
	    final DetachedCriteria subQuery = DetachedCriteria.forClass(Target.class, "t") //
		    .add(Restrictions.eq("t.id", targetId)).add(Restrictions.eq("t.owner.id", ownerId)) //
		    .createAlias("t.companies", "cp") //
		    .setProjection(Projections.property("cp.id"));
	    final List<Company> companies = rootCriteria.add(Subqueries.propertyIn("c.id", subQuery)).list(); //"exists" may be a better solution than "in".

Damn ! this doesn't work either ! Not yet implemented with hibernate + criteria, only hql... (fell free to send a patch...) https://hibernate.onjira.com//browse/HHH-869

org.hibernate.MappingException: collection was not an association: com.model.company.Company.urls
...


So the only thing we can do is to create an extra query to fetch all the urls, and map the urls to the companies.
Adding urls to a company in the session will result to an extra update, so I decided to evict the companies from my session (I'm in read-only)
Enjoy !

final List<Long> ids = new ArrayList<Long>();
	    for (final Company c : companies) {
		ids.add(c.getId());
		session.evict(c);
	    }
	    final List<Company> cs = session.createQuery("select c from Company c left join fetch c.urls where id IN (:companiesId)").setParameterList("companiesId", ids).list();

Ok, that's just too painfull.
Let's first get the ids, and then use hql to do the fetch join I need. I'll use a StringBuilder to build dynamically the extra criteria of my request...

Conclusion: Criteria api has some features missing...

samedi, avril 17 2010

a full html5 space mmorts using websocket and webgl througt gwt

Google web toolkit rps model is amazing.

Combined with the very last technologies (webgl and websocket), we should be able to develop a wonderful game !

We already have tested Canvas and Comet technologies with :

  • AvriServer, my full-java server using a very nice javascript/java remote procedure
  • AvriTank, a full javascript - canvas game causing Avriserver.
  • The last version of LordOfCastle, using html4 with an awesome dynamic map (Age Of Empire like)
  • The multiplayer diablo-like

but html4 is too limited...

Comet (not only long pulling, even iframe forever, hrx multipart and callback procedure) is really good, but not suficient enougth for a real time game ! Server can push a lot of data, (even if we regulary need to flush the data input), but ajax limitation for massiv send is too important, even with the last specification (8 asynchron ajax call at the same time).

But HTML5 comes out and gives us new opportunities !

Using a websocket module for wgt (websocket + google's rpc) : gwt-websocket , a webgl gwt port and jetty websocket protocol, I think we can start this new project !

mercredi, septembre 30 2009

Avricot, Projets passés et à venir

Cela fait longtemps que je voulais le faire, voici un regroupement de tous les projets que j'ai réalisés.
Les sources quand elles sont présentes sont ouvertes à tous !

Projets personnels

Lord Of Castle

De loin le plus important des projets !
Principalement codé en Javascript et en PHP. La nouvelle version en cours de developpement est completement tournée vers le javascript/ajax/json.
Elle devrait utiliser l'Avrichat qui a été developpé pour pouvoir fonctionner avec du javascript et permettre des actions en temps reel (avec COMET)
Lord Of Castle



En cours...