[Hibernate] wat voor type queries te gebruiken

Pagina: 1
Acties:
  • 150 views sinds 30-01-2008
  • Reageer

  • Swinnio
  • Registratie: Maart 2001
  • Laatst online: 20-02 07:52
Ben nu een tijdje met Hibernate aan de slag en tot nu toe bevalt het best goed. Begin nu langzaamaan wat moeilijkere queries nodig te hebben, die me aan het denken hebben gezet welke type queries nu het verstandigste te gebruiken zijn. In principe zijn er drie mogelijkheden:
  • Native SQL: lijkt me in normale gevallen af te raden, aangezien het je onafhankelijkheid van de DB verpest
  • Criteria queries: lijken voor de meeste queries redelijk simpel te implementeren.
  • HQL
Heb tot nu toe vrijwel alleen met criteria gewerkt maar lijk nu ook HQL nodig te hebben. Dit brengt me op de vraag: wat is (volgens Hibernate of volgens jullie) de "default" werkwijze bij het opzetten van nieuwe queries: HQL of Criteria. En wat zijn de voor- en nadelen van beide, want dat wordt me uit de docuementatie helaas niet helemaal duidelijk.

If the world wouldn't suck, we'd all fall off


Verwijderd

Ik laat het per situatie afhangen. Dus wat het me het minste moeite kost.

  • zneek
  • Registratie: Augustus 2001
  • Laatst online: 08-02-2025
Swinnio schreef op dinsdag 16 mei 2006 @ 11:00:
Ben nu een tijdje met Hibernate aan de slag en tot nu toe bevalt het best goed. Begin nu langzaamaan wat moeilijkere queries nodig te hebben, die me aan het denken hebben gezet welke type queries nu het verstandigste te gebruiken zijn. In principe zijn er drie mogelijkheden:
  • Native SQL: lijkt me in normale gevallen af te raden, aangezien het je onafhankelijkheid van de DB verpest
  • Criteria queries: lijken voor de meeste queries redelijk simpel te implementeren.
  • HQL
Heb tot nu toe vrijwel alleen met criteria gewerkt maar lijk nu ook HQL nodig te hebben. Dit brengt me op de vraag: wat is (volgens Hibernate of volgens jullie) de "default" werkwijze bij het opzetten van nieuwe queries: HQL of Criteria. En wat zijn de voor- en nadelen van beide, want dat wordt me uit de docuementatie helaas niet helemaal duidelijk.
Ik durf het niet met 100% zekerheid te zeggen, maar volgens mij profiteer je het meest van o.a. (session)cache als je Criteria gebruikt. Ook heb ik het gevoel dat de optimalisatie van Criteria vele malen beter is dan HQL. Ik heb een aantal tests gedaan in diverse systemen en telkens leken de aantallen queries grondig lager uit te vallen bij gebruik van Criteria ipv HQL. Zeker als je met langere sessions(per request bijv.) werkt wil je niet dat elke query alles weer uit de db haalt.

Verwijderd

Kijk of het mogelijk is om views te gebruiken met je database en roep dan de view aan als het nodig is.

  • momania
  • Registratie: Mei 2000
  • Laatst online: 08:47

momania

iPhone 30! Bam!

Ik gebruik alleen nog maar Criteria queries. Zelfs redelijk complexe queries kan je daar toch goed mee maken.

Heb je een bepaalde query redelijk vaak nodig, kan je hem altijd nog als named hql query maken, of natuurlijk een view in de database.

Nadeel van de criteria queries is alleen dat het soms redelijk veel code kan kosten in verhouding tot een hql query. En voor de opbouw van je query moet je net even op een ander manier denken dan bij een 'normale' query.

Verder komt je icm goede cache en configuratie daarvan (dus ook goed de hashcode en equals geimplementeerd in je classes) op een goede hit rate imo.
Ik gebruik het nu zelf icm JBoss cache en dat werkt heerlijk eigenlijk. :)

Neem je whisky mee, is het te weinig... *zucht*


  • -FoX-
  • Registratie: Januari 2002
  • Niet online

-FoX-

Carpe Diem!

Ik denk dat je Criteria queries best als basis queries kan zien. Hiermee bedoel ik dan dat je het best eerst probeert om je query te verwezenlijken adhv Criteria's. Als je er dan nog absoluut niet uit geraakt, kan je verder kijken naar HQL of SQL.

Op zich is er geen probleem om op bepaalde plaatsen pure SQL te gebruiken, maar in 95% van de gevallen kan dat toch vermeden worden. Ik sluit dus absoluut niet uit dat het in bepaalde gevallen krachtiger is om effectief gebruik te maken van je onderliggende rdbms.

Verder heb ik vergelijkende testen ook eens gedaan, zoals zneek ze omschrijft, en daar kwam ik ook tot de conclusie dat de Criteria queries gerichter hun werk doen en dus ook beter performen.
momania schreef op dinsdag 16 mei 2006 @ 11:44:
Verder komt je icm goede cache en configuratie daarvan (dus ook goed de hashcode en equals geimplementeerd in je classes) op een goede hit rate imo.
Ik gebruik het nu zelf icm JBoss cache en dat werkt heerlijk eigenlijk. :)
Wat bedoel je met goed equals/hashcode implementeren in je classes?

Verder lijkt het me wel interessant om je cache configuratie even toe te lichten, als het niet teveel werk is.

  • Swinnio
  • Registratie: Maart 2001
  • Laatst online: 20-02 07:52
Ik vind een bijkomend voordeel van Criteria, dat het wat abstracter werkt. Je hebt meer het gevoel queries op objecten te doen en niet op een databank en dat maakt het vaak wat intuitiever.
Probleem is dat ik nu een query met een IN clause en twee subqueries heb, die ik met geen mogelijkheid zo geformuleerd krijg dat Hibernate er iets van snapt :)

If the world wouldn't suck, we'd all fall off


  • momania
  • Registratie: Mei 2000
  • Laatst online: 08:47

momania

iPhone 30! Bam!

-FoX- schreef op dinsdag 16 mei 2006 @ 12:02:
Wat bedoel je met goed equals/hashcode implementeren in je classes?
Nou, de meesten configureren wel een cache, maar vergeten de hashcode en equals methods te implementeren op hun domein objecten. Dan werkt je cache sowieso al niet goed. Voor zover ik weet maakt de cache gebruik van deze vergelijkings methodes. :)
Verder lijkt het me wel interessant om je cache configuratie even toe te lichten, als het niet teveel werk is.
Ik gebruik op het werk een JBoss server met daarin JBoss cache. Voordeel van deze cache is dat hij werkt in clusters en ook dus transactional cache ondersteund. De configuratie heb ik hier nu even niet bij de hand, maar als ik morgen op m'n werk ben zal ik hier proberen even wat meer toe te lichten. :)

Het is op zich heel erg simpel te implementeren. Zeker als je al JBoss server draait. Wij gebruiken 'm ook niet in een cluster, maar gewoon als local cache, dus dat maakt het helemaal simpel. In een cluster hadden we ook al wel kort getest met onze lokale jboss installaties en ook dat werkt op het eerste gezicht erg simpel. Cluster naam invullen en de rest gaat vanzelf :P

Enige wat niet standaard geconfigureerd is, is een evection policy. Die ruimt na een bepaalde time-out objecten weer op uit de cache.

Anyway, zal ik morgen eens de rest proberen toe te lichten. Wij gebruiken nml ook nog de hibernate deployer in JBoss etc... werkt ook wel aardig :)
Swinnio schreef op dinsdag 16 mei 2006 @ 12:42:
Ik vind een bijkomend voordeel van Criteria, dat het wat abstracter werkt. Je hebt meer het gevoel queries op objecten te doen en niet op een databank en dat maakt het vaak wat intuitiever.
Probleem is dat ik nu een query met een IN clause en twee subqueries heb, die ik met geen mogelijkheid zo geformuleerd krijg dat Hibernate er iets van snapt :)
IN clauses moet je sowieso proberen te voorkomen :P
Subqueries is een verhaal apart idd. Dat viel mij aan het begin ook even tegen om dat met criteria queries te doen, maar is uitenidelijk toch ook redelijk simpel gelukt.
Post anders even een voorbeeld waar je niet uit komt. Dan zal ik het morgen even met m'n eigen subquery criteria queries vergelijken :)

[ Voor 22% gewijzigd door momania op 16-05-2006 14:13 ]

Neem je whisky mee, is het te weinig... *zucht*


  • Swinnio
  • Registratie: Maart 2001
  • Laatst online: 20-02 07:52
momania schreef op dinsdag 16 mei 2006 @ 14:11:
[...]
IN clauses moet je sowieso proberen te voorkomen :P
Subqueries is een verhaal apart idd. Dat viel mij aan het begin ook even tegen om dat met criteria queries te doen, maar is uitenidelijk toch ook redelijk simpel gelukt.
Post anders even een voorbeeld waar je niet uit komt. Dan zal ik het morgen even met m'n eigen subquery criteria queries vergelijken :)
Ok, de SQL-query luidt als volgt (en werkt :-))
SQL:
1
2
3
4
5
6
7
8
9
10
11
12
Select * 
from Endpoint 
where Endpoint.Application_ID 
IN (
   Select Application_ID 
   from Application 
   where Application.node_ID = (
   Select node_ID 
   from Node 
   where IEEE_Address = 1)
) 
and Endpoint_Number = 1 

De structuur is alsvolgt: ik heb een Endpoint tabel met een nummer een FK naar Application (Application_ID). Application heeft op zijn beurt weer een FK naar Node (node_ID). Node heeft naast een ID ook een IEEE adress. De drie tabellen (Endpoint, Applicaiton en Node) hebben zelf ook een ID (primary key). Helaas heeft die dezelfde naam als een FK die ernaar wijst (Node_ID, Application_ID en Endpoint_ID).
De query moet dus voor een bepaald endpoint-nummer het endpoint die verwijst naar de node met een bepaald IEEE-adres geven. De 'IN' clause is nodig (denk ik) omdat de subquery die er op volgt meerdere resultaten terug kan leveren.
Welnu, in Hibernate heb ik alsvolgt geprobeerd dit na te bouwen:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
DetachedCriteria nodeCrit = DetachedCriteria.forClass(Node.class).add(
        Restrictions.eq(NodeDAO.IEEE_ADDRESS, 1));

DetachedCriteria appCrit = DetachedCriteria.forClass(Application.class)
        .add(Restrictions.eq(ApplicationDAO.NODE_ID, nodeCrit));

List appList = session.createCriteria(ApplicationDAO.class).add(
        Restrictions.eq(ApplicationDAO.NODE_ID, appCrit)).list();

session.createCriteria(Endpoint.class).add(
        Restrictions.in(APP_ID, appList)).add(
        Restrictions.eq(NUMBER, 1));

Twee problemen zie ik er zelf al in:
  • De detached criteria leveren steeds de complete tabel terug i.p.v. enkel zijn ID, zoals in de SQL-query.
  • Het ophalen van de appList lijkt me overbodig, maar om Restrictions.in te kunnen gebruiken heb ik een collection nodig.
/edit: layout

[ Voor 11% gewijzigd door Swinnio op 16-05-2006 15:27 ]

If the world wouldn't suck, we'd all fall off


  • momania
  • Registratie: Mei 2000
  • Laatst online: 08:47

momania

iPhone 30! Bam!

Om alleen een ID op te halen ipv een collectie met objecten, moet je een projection gebruiken :)

bv:
Java:
1
2
3
DetachedCriteria nodeCrit = DetachedCriteria.forClass(Node.class).add( 
        Restrictions.eq(NodeDAO.IEEE_ADDRESS, 1)); 
nodeCrit.setProjection(Projections.property(NodeDao.ID));

Dit geeft dan als het goed is alleen een lijst met de node id's terug.

Ik snap alleen je model niet helemaal zo.. query je nou tegen model objecten aan, of tegen je dao classes :?

Ook snap ik de subqueries en de IN clause in dit geval niet echt. Volgens mij moet dit met een simpele join ook gewoon te doen zijn. :)

Java:
1
2
3
4
5
Criteria endPointCrit = session.createCriteria(Endpoint.class);
endPointCrit.add(Restrictions.eq(NUMBER, 1));

Criteria nodeCrit = endPointCrit.createCriteria("applications").createCriteria("nodes");
nodeCrit.add(Restrictions.eq(NodeDAO.IEEE_ADDRESS, 1));


Je maakt dus de eerste criteria aan voor de tabel/class waar je het resultaat van wilt hebben. Doe je vanaf die criteria weer een createCriteria, dan maak je in princiepe een join. Zo join je tot het niveau waar je nog een WHERE clause eraan wilt hangen. (in dit geval de Node dus) :)

Neem je whisky mee, is het te weinig... *zucht*


  • Swinnio
  • Registratie: Maart 2001
  • Laatst online: 20-02 07:52
momania schreef op dinsdag 16 mei 2006 @ 16:59:
Om alleen een ID op te halen ipv een collectie met objecten, moet je een projection gebruiken :)

bv:
Java:
1
2
3
DetachedCriteria nodeCrit = DetachedCriteria.forClass(Node.class).add( 
        Restrictions.eq(NodeDAO.IEEE_ADDRESS, 1)); 
nodeCrit.setProjection(Projections.property(NodeDao.ID));

Dit geeft dan als het goed is alleen een lijst met de node id's terug.
Bedankt! Wist dat dat fout ging maar ik zag het nergens in de voorbeelden m.b.t Criteria staan. Of heb ik ergens overheen gekeken?
Ik snap alleen je model niet helemaal zo.. query je nou tegen model objecten aan, of tegen je dao classes :?
Ja, sorry is ook verwarrend. Ik heb m.b.v. static final string in mijn dao classes de verschillende properties uit de .hbm.xml opgenomen, zodat ik daar geen spelfouten in kan maken.
Ook snap ik de subqueries en de IN clause in dit geval niet echt. Volgens mij moet dit met een simpele join ook gewoon te doen zijn. :)

Java:
1
2
3
4
5
Criteria endPointCrit = session.createCriteria(Endpoint.class);
endPointCrit.add(Restrictions.eq(NUMBER, 1));

Criteria nodeCrit = endPointCrit.createCriteria("applications").createCriteria("nodes");
nodeCrit.add(Restrictions.eq(NodeDAO.IEEE_ADDRESS, 1));


Je maakt dus de eerste criteria aan voor de tabel/class waar je het resultaat van wilt hebben. Doe je vanaf die criteria weer een createCriteria, dan maak je in princiepe een join. Zo join je tot het niveau waar je nog een WHERE clause eraan wilt hangen. (in dit geval de Node dus) :)
Ok, ga het morgen meteen proberen.

[ Voor 4% gewijzigd door Swinnio op 16-05-2006 19:43 ]

If the world wouldn't suck, we'd all fall off


  • momania
  • Registratie: Mei 2000
  • Laatst online: 08:47

momania

iPhone 30! Bam!

Swinnio schreef op dinsdag 16 mei 2006 @ 19:37:
[...]
Ja, sorry is ook verwarrend. Ik heb m.b.v. static final string in mijn dao classes de verschillende properties uit de .hbm.xml opgenomen, zodat ik daar geen spelfouten in kan maken.
Waarom doe je dat niet gewoon in de domein classes dan ipv in je dao classes? :)
Dat zou het al een stuk overzichtelijker maken.

Daarnaast maak je in je voorbeeld een criteria aan voor een dao class ipv de domein class. Dat zal dan of een foutje zijn denk ik, of ik snap het niet :)

Neem je whisky mee, is het te weinig... *zucht*


  • Swinnio
  • Registratie: Maart 2001
  • Laatst online: 20-02 07:52
momania schreef op dinsdag 16 mei 2006 @ 19:40:
[...]

Waarom doe je dat niet gewoon in de domein classes dan ipv in je dao classes? :)
Dat zou het al een stuk overzichtelijker maken.

Daarnaast maak je in je voorbeeld een criteria aan voor een dao class ipv de domein class. Dat zal dan of een foutje zijn denk ik, of ik snap het niet :)
Hmm, misschien heb ik het ook wel verkeerd begrepen :) De DOA classes zijn door HibernateSynchronizer gegenereerd en in eerste instantie leeg. Daar heb ik methoden als createAndStore en findByID opgenomen. Deze geven echter wel Hibernate Objecten terug. Is dat geen goede methode? Is wat ik uit dit topic heb geleerd.

If the world wouldn't suck, we'd all fall off


  • momania
  • Registratie: Mei 2000
  • Laatst online: 08:47

momania

iPhone 30! Bam!

Dat is wel de juiste methode, maar als je final statics wilt gebruiken om de properties te indentificeren van je domein classes, moet je dat gewoon in je domein classes doen. :)

Neem je whisky mee, is het te weinig... *zucht*


  • Swinnio
  • Registratie: Maart 2001
  • Laatst online: 20-02 07:52
momania schreef op dinsdag 16 mei 2006 @ 20:29:
Dat is wel de juiste methode, maar als je final statics wilt gebruiken om de properties te indentificeren van je domein classes, moet je dat gewoon in je domein classes doen. :)
Misschien een stomme vraag, maar wat zijn dan de domein classes in de structuur die HibernateSynchronizer aanmaakt?
Heb nu bij een mapping file Node.hbm.xml een
  • package hibernate.base: BaseNode
  • package hibernate: Node extends BaseNode, BaseNodeDAO en Node.hbm.xml
  • package hibernate.doa: NodeDAO extends BaseNodeDAO
Aangezien de laatste niet overschreven wordt bij opnieuw synchroniseren (na een verandering in de mapping), heb ik ze daar ondergebracht.

If the world wouldn't suck, we'd all fall off


  • momania
  • Registratie: Mei 2000
  • Laatst online: 08:47

momania

iPhone 30! Bam!

Alle classes waar Base voor staat wordt door de synchronizer opnieuw gegenereerd.
In princiepe zou je dan op alles wat daarvan afgeleid is, je eigen implementatie kunnen zetten.

Voor jou zijn classes als Node dan een domein class en NodeDAO een dao :)
In Node kan je dus je static final's kwijt.
Je kan volgens mij met de synchronizer ook nog aangeven dat je je afgeleide domein classes ook in een aparte package wil hebben. Dat is wel zo makkelijk weer voor het overzicht.

Toch zou ik je willen aanraden om eens goed te kijken of je de sychronizer op deze manier wel echt nodig hebt. Ik heb 'm zelf ook in eerste instantie gebruikt, maar dingen als die base classes voor je domein classes vond ik een beetje overkill.

Daarbij: als je zelf je xml mappings opzet en je classes daarbij, leer je er ook wat meer van :)

Neem je whisky mee, is het te weinig... *zucht*


  • momania
  • Registratie: Mei 2000
  • Laatst online: 08:47

momania

iPhone 30! Bam!

Ok, hier nog even de update over JBoss Cache.

Hier is te lezen hoe je deze onder JBoss kan opzetten.
http://www.jboss.org/deve...jboss/tc5-clustering.html
Hier wordt een setup gedaan voor tomcat clustering, waarbij JBoss cache nodig is. Je kan later natuurlijk gewoon alleen maar de cache voor Hibernate gebruiken, maar als je je aplpicaties zelf ook nog 'distributable' wilt maken, staat dat daar ook uitgelegd :)

De tc5-cluster-service.xml die daar besproken wordt, heeft standaard het volgende:
XML:
1
<attribute name="CacheMode">REPL_ASYNC</attribute> 

Dit hebben wij naar LOCAL veranderd:
XML:
1
<attribute name="CacheMode">LOCAL</attribute> 

Nu blijft de cache gewoon lekker op zich zelf draaien.

Ik had zelf een iets uitgebreidere xml gevonden, maar de details zal ik verder besparen.
Wel wil ik nog even dit uitlichten (iets wat er meestal standaard niet in staat):
XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--  Name of the eviction policy class. --> 
<attribute name="EvictionPolicyClass">org.jboss.cache.eviction.LRUPolicy</attribute> 

<!--  Specific eviction policy configurations. This is LRU --> 
<attribute name="EvictionPolicyConfig">
  <config>
    <attribute name="wakeUpIntervalSeconds">5</attribute> 
    <!--  Cache wide default --> 
    <region name="/_default_">
      <attribute name="maxNodes">5000</attribute> 
      <attribute name="timeToLiveSeconds">300</attribute> 
      <!-- Maximum time an object is kept in cache regardless of idle time -->
      <attribute name="maxAgeSeconds">120</attribute>
    </region>

    <region name="/net/tweakers/examplepackage">
      <attribute name="maxNodes">5000</attribute> 
      <attribute name="timeToLiveSeconds">300</attribute> 
    </region>
  </config>
</attribute>

Dit zorgt er dus voor dat je cache ook weer opgeruimd wordt :)

Om de cache nu binnen Hibernate te kunnen gebruiken, moeten we nog wat aan de Hibernate configuratie veranderen. Wij gebruiken zelf de hibernate deployer in Jboss, dus hebben geen echte hibernate.conf.xml, maar meer iets dat lijk op een jboss-service.xml (voor je jboss services (sar)).

XML:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<server>
  <mbean code="org.jboss.hibernate.jmx.Hibernate" name="jboss.har:service=HibernateService">
    <attribute name="DatasourceName">java:/AnyDS</attribute>
    <attribute name="SessionFactoryName">java:/hibernate/AnySessionFactory</attribute>
    <attribute name="Dialect">org.hibernate.dialect.Oracle9Dialect</attribute>

    <attribute name="CacheProviderClass">org.jboss.hibernate.cache.DeployedTreeCacheProvider</attribute>
    <attribute name="DeployedTreeCacheObjectName">jboss.cache:service=TomcatClusteringCache</attribute>

    <attribute name="ShowSqlEnabled">false</attribute>
    <attribute name="SqlCommentsEnabled">false</attribute>
    <attribute name="StatGenerationEnabled">true</attribute>
    <attribute name="MaxFetchDepth">1</attribute>
  </mbean>
</server>

Nu hebben we Hibernate verteld dat hij de TreeCache moet gebruiken en waar hij deze kan vinden. :)

Bij elk domein object dat je dan in de cache wilt hebben voeg je dan de cache tag toe, maar nu met 'transactional' als value ipv de veel gebruikte 'read-only'
XML:
1
2
3
<class name="net.tweakers.examplepackage.ExampleClass" table="EXAMPLE_TABLE">
  <cache usage="transactional"/>
</class>


En dan ben je (zo goed als) klaar :Y)

Hibernate houd zelf mooi stats bij (en meer dan de stats die hij dumpt in de console als je er om vraagt), dus daar kan je zelf zien hoe je 2nd level cache het er vanaf brengt :)

Neem je whisky mee, is het te weinig... *zucht*


  • Swinnio
  • Registratie: Maart 2001
  • Laatst online: 20-02 07:52
momania schreef op dinsdag 16 mei 2006 @ 21:28:
Alle classes waar Base voor staat wordt door de synchronizer opnieuw gegenereerd.
In princiepe zou je dan op alles wat daarvan afgeleid is, je eigen implementatie kunnen zetten.

Voor jou zijn classes als Node dan een domein class en NodeDAO een dao :)
In Node kan je dus je static final's kwijt.
Ok, dan ga ik dat eens proberen. Zie alleen nog niet zo in wat daar het grote voordeel van is, buiten het feit dat het wellicht wat logischer is.
Je kan volgens mij met de synchronizer ook nog aangeven dat je je afgeleide domein classes ook in een aparte package wil hebben. Dat is wel zo makkelijk weer voor het overzicht.
Geloof dat dat pas kan vanaf versie 3.1.1. (die ik nu gebruik) en daarmee kreeg ik om een of andere reden geen code meer gegenereerd die compileert.
Toch zou ik je willen aanraden om eens goed te kijken of je de sychronizer op deze manier wel echt nodig hebt. Ik heb 'm zelf ook in eerste instantie gebruikt, maar dingen als die base classes voor je domein classes vond ik een beetje overkill.

Daarbij: als je zelf je xml mappings opzet en je classes daarbij, leer je er ook wat meer van :)
Ben het met je eens dat al die base-classes wellicht wat overkill zijn. Aan de andere kant vind ik dat niet zo'n probleem als het een duidelijke structuur is waar goed mee te werken valt. Uiteindelijk gaat het me er alleen om dat ik snel kan ik er tijd mee kan besparen. Bovendien vind ik dat ik al genoeg heb geleerd, aangezien dit het eerste project is waar ik überhaupt Hibernate gebruik ;)

[ Voor 2% gewijzigd door Swinnio op 17-05-2006 08:49 . Reden: tiepvoud ]

If the world wouldn't suck, we'd all fall off


  • -FoX-
  • Registratie: Januari 2002
  • Niet online

-FoX-

Carpe Diem!

momania schreef op dinsdag 16 mei 2006 @ 14:11:
Nou, de meesten configureren wel een cache, maar vergeten de hashcode en equals methods te implementeren op hun domein objecten. Dan werkt je cache sowieso al niet goed. Voor zover ik weet maakt de cache gebruik van deze vergelijkings methodes. :)
Op welke manier implementeer jij je equals/hashcode dan op je domein objecten? Eerst deed ik dit aan de hand van de PK, maar later realiseerde ik me dat dit nogal wat problemen met zich mee kan brengen. Hibernate geeft je namelijk de garantie dat er binnen een Session geen 2 verschillende objecten dezelfde entity representeren. Meer info over dit probleem: Blog: Hibernate equals and hashcode

De meeste problemen met caching liggen in het feit dat deze (binnen Hibernate) niet goed geconfigureerd zijn, lijkt me. Ik heb het dan over de 2nd level cache, waarin ik vaak zie dat Hibernate gebruikers het voldoende achten om <cache usage="read-only"/> op te nemen in hun mapping; maar niet definiëren op het niveau van de associaties. Hierdoor worden deze dus ook niet gecached en vervalt eigenlijk het nut van de caching een beetje.

  • momania
  • Registratie: Mei 2000
  • Laatst online: 08:47

momania

iPhone 30! Bam!

-FoX- schreef op woensdag 17 mei 2006 @ 10:32:
[...]
Op welke manier implementeer jij je equals/hashcode dan op je domein objecten? Eerst deed ik dit aan de hand van de PK, maar later realiseerde ik me dat dit nogal wat problemen met zich mee kan brengen. Hibernate geeft je namelijk de garantie dat er binnen een Session geen 2 verschillende objecten dezelfde entity representeren. Meer info over dit probleem: Blog: Hibernate equals and hashcode
Maar daar staat wel:
This solution should work for most typical webapplications where all entities are created in the context of a single Hibernate Session and are destroyed as soon as that session is destroyed. If you use the “Open Session in View” the same session is available for the duration of the request (even the rendering of the jsp).
Dus dat geld alleen maar binnen 1 Hibernate sessie. Daarbij heeft hibernate de equals en hashcode niet nodig, maar ik vind het zelf ook wel makkelijk. Als je wat detached opbjecten in een collectie kwijt wilt bijvoorbeeld. En volgens mij gebruikt de 2nd level cache het ook wel op een slimme manier, en die staat natuurlijk los van je sessie :)
De meeste problemen met caching liggen in het feit dat deze (binnen Hibernate) niet goed geconfigureerd zijn, lijkt me. Ik heb het dan over de 2nd level cache, waarin ik vaak zie dat Hibernate gebruikers het voldoende achten om <cache usage="read-only"/> op te nemen in hun mapping; maar niet definiëren op het niveau van de associaties. Hierdoor worden deze dus ook niet gecached en vervalt eigenlijk het nut van de caching een beetje.
Configuratie is vaak het grootste probleem idd. Ik heb het idee dat de meesten alleen maar wat dingen aanzetten en het niet testen. Het gebruik van de JBoss Cache zoals ik het doe kan waarschijnlijk ook nog wel 10 keer beter, maar het werkt nu iig al beter dan die smerige ehCache :P

Neem je whisky mee, is het te weinig... *zucht*


  • -FoX-
  • Registratie: Januari 2002
  • Niet online

-FoX-

Carpe Diem!

momania schreef op woensdag 17 mei 2006 @ 10:42:
Dus dat geld alleen maar binnen 1 Hibernate sessie. Daarbij heeft hibernate de equals en hashcode niet nodig, maar ik vind het zelf ook wel makkelijk. Als je wat detached opbjecten in een collectie kwijt wilt bijvoorbeeld. En volgens mij gebruikt de 2nd level cache het ook wel op een slimme manier, en die staat natuurlijk los van je sessie :)
En je implementeert die equals/hashcode ook gewoon op basis van de primary key, of welke best practice volg je?

Verwijderd

momania schreef op woensdag 17 mei 2006 @ 10:42:
maar het werkt nu iig al beter dan die smerige ehCache :P
Wat vind jij smerig aan ehCache?

  • momania
  • Registratie: Mei 2000
  • Laatst online: 08:47

momania

iPhone 30! Bam!

-FoX- schreef op woensdag 17 mei 2006 @ 10:53:
[...]
En je implementeert die equals/hashcode ook gewoon op basis van de primary key, of welke best practice volg je?
Equals reference en anders op primary key idd. bv:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public boolean equals(Object o) {
  if (this == o) {
    return true;
  }
  if (o == null) {
    return false;
  }
  if (!(o instanceof User)) {
    return false;
  }
        
  final User a = (User)o;
  if( this.userId!=a.userId && (this.userId==null || !this.userId.equals(a.userId)) ) {
    return false;
  }
  return true;
}

En hashcode op alle not-null properties:
Java:
1
2
3
4
5
6
public int hashCode() {
  int hashCode = 1;
  hashCode = 31 * (this.userId == null ? 0 : this.userId.hashCode());
  hashCode = 31 * (this.userName == null ? 0 : this.userName.hashCode());
  return hashCode;
}
Verwijderd schreef op woensdag 17 mei 2006 @ 10:57:
[...]

Wat vind jij smerig aan ehCache?
Geen idee eigenlijk, heb het nooit echt goed werkend gekregen. De hitrate die ik met JBoss Cache haal is echt veel beter. Nu heb ik nooit echt vergeleken of dat nou aan de verschillend cache implementaties lag of aan de configuratie daarvan (of andere bijzaken). Ik ben op een gegeven moment overgestapt naar JBoss Cache en dat vind ik wel goed werken verder :)

Neem je whisky mee, is het te weinig... *zucht*


Verwijderd

Okee, nooit echt naar ehCache gekeken dus :)

code:
1
2
3
4
if( this.userId!=a.userId && (this.userId==null || !this.userId.equals(a.userId)) ) {
    return false;
  }
  return true;

code:
1
return this.userId==a.userId || (this.userId!=null && this.userId.equals(a.userId));

Ik vind dit persoonlijk wat prettiger lezen...

  • Alarmnummer
  • Registratie: Juli 2001
  • Laatst online: 09-07-2024

Alarmnummer

-= Tja =-

Het grote probleem aan jouw aanpak is dat de hashcode en de equals dus kunnen veranderen en dit kan gevolgen hebben. Stel dat jij een entity in een hash structuur zet en later besluit je het object te saven. Dan zal de hashcode van het object na het saven anders zijn dan voor het saven -> en daardoor kan je objecten niet meer terug vinden in de set. In jouw geval kan je dit dus krijgen:

code:
1
2
3
4
5
6
Persoon p = new Persoon();
Set<Persoon> personen = new HashSet<Persoon>();
personen.add(persoon);
persoonDao.save(p);//hierdoor zou de id gezet kunnen worden
personen.add(persoon);
int size = personen.size();

De size van de set is nu 2, en niet 1 zoals je zou verwachten. Vooral voor complexere object structuren gaat dit gegarandeerd een keer fout. En dit zijn geen leuke errors om op te moeten sporen.

Verder is het gebruik van entities buiten sessies meestal niet aan te bevelen aangezien je dan gevoelig bent voor allerlei isolation issues: non repeatable reads, phantom reads (meestal niet zo`n probleem) en lost updates. Dit probleem kan je voor de repeatable reads/lost updates trouwens wel eenvoudig fixen door optimistic locking in hibernate te activeren.

[ Voor 21% gewijzigd door Alarmnummer op 18-05-2006 12:36 ]


  • momania
  • Registratie: Mei 2000
  • Laatst online: 08:47

momania

iPhone 30! Bam!

Alarmnummer schreef op donderdag 18 mei 2006 @ 12:23:
[...]

Dit probleem kan je voor de repeatable reads/lost updates trouwens wel eenvoudig fixen door optimistic locking in hibernate te activeren.
Daar ben ik toevallig nu net naar aan het kijken. We hadden wat locking problemen en ik kwam er idd achter dat de treecache nog pessimistic locking gebruikte (versie 1.2.4) en er vanaf 1.3 ook optimistic mogenlijk is.

Neem je whisky mee, is het te weinig... *zucht*


  • misfire
  • Registratie: Maart 2001
  • Laatst online: 12-10-2024
Ik gebruik zelf meestal HQL named queries. Die vind ik voor statische queries wel een boel duidelijker dan Criteria, en je kunt ze lekker bij elkaar zetten. De named queries worden ook tijdens het starten geparsed en gevalideerd wat ook handig kan zijn. Criteria gebruik ik alleen bij dynamische queries. Query caching en de second-level cache werkt exact hetzelfde voor HQL als voor de Criteria API dus (onder water is HQL ook gewoon een Criteria).
momania schreef op dinsdag 16 mei 2006 @ 11:44:
Verder komt je icm goede cache en configuratie daarvan (dus ook goed de hashcode en equals geimplementeerd in je classes) op een goede hit rate imo.
Ik gebruik het nu zelf icm JBoss cache en dat werkt heerlijk eigenlijk. :)
De hashcode en equals methode hoef je helemaal niet te overriden voor de second-level cache. Hibernate cachet geen objecten maar alleen de waardes. Deze waardes worden gevonden in de second-level cache op basis van de primary key. Overriden van equals en hashcode is bijzonder foutgevoelig en in de meeste gevallen ook niet nodig. Mocht je toch equals/hashcode moeten overriden bij Hibernate gebruik dan zou ik eerst 10 keer nadenken, want waarschijnlijk doe je dan ook al andere dingen die bij Hibernate eigenlijk niet door de beugel kunnen.

  • Gert
  • Registratie: Juni 1999
  • Laatst online: 05-12-2025
Ja hele foute dingen.
You have to override the equals() and hashCode() methods if you
* intend to put instances of persistent classes in a Set (the recommended way to represent many-valued associations) and
* intend to use reattachment of detached instances

  • misfire
  • Registratie: Maart 2001
  • Laatst online: 12-10-2024
Netjes als je ook even de bron erbij zet. Selectief quoten/lezen is ook een kunst, zie wat er onder staat in dezelfde alinea:
Hibernate guarantees equivalence of persistent identity (database row) and Java identity only inside a particular session scope. So as soon as we mix instances retrieved in different sessions, we must implement equals() and hashCode() if we wish to have meaningful semantics for Sets.
De punten die genoemd worden gelden dus alleen als je persistent entities gaat mixen tussen sessies, wat op zichzelf al iets is waar je toch even heel stevig over zou na moeten denken. Dan nog is het alleen van toepassing als je dat doet zonder dat je gebruik maakt van de merge() functie om te attachen, als je merge() gebruikt dan kun je op die manier namelijk je persistent identity garanderen. Zelfs zonder merge() kun je in meeste gevallen prima met de object identity uit de voeten, afhankelijk van welk punt je aan het attachen/detachen bent.
Pagina: 1