Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[Java + DB] Refactor van Hibernate naar JDBC

Pagina: 1
Acties:

  • titan_pi8
  • Registratie: Januari 2004
  • Laatst online: 11-11 23:10
Goeiedag,

Ik ben bezig met het ontwerpen van een administratieve applicatie. De details van dit programma zijn niet belangrijk of interessant dus zal ik deze achterwege laten.Het programma moet de input van een gebruiker opvragen, hier eventueel wat berekeningen mee doen en dan opslaan in een database. Het programma moet deze input later uiteraard ook terug kunnen opvragen. Het is een 2-tier applicatie, dus elke client verbindt rechtstreeks met de database.

Oorspronkelijk wou ik dit doen met behulp van Hibernate, zodat ik minder last zou hebben met O/R-mapping. Maar bij nader inzicht blijkt dit een grote ontwerpfout te zijn. Hibernate is namelijk ongeschikt voor 2-tier applicaties. Ik zit nu met het probleem dat ik alle business objects heb ontworpen om te gebruiken met Hibernate, en dat ik daar nu van wil afstappen en gewoon JDBC in de plaats wil gebruiken. De andere mogelijkheid is om er toch een 3-tier applicatie van te maken, maar ik denk dat dat voor nog meer werk zou zorgen.

Ik heb al een tijdje nagedacht over hoe ik de code moet refactoren. Van de meeste zaken zoals hoe/waar/wanneer business objects aan te maken en op te slaan in de database heb ik al een idee. Die dingen zullen wel lukken. Maar ik heb problemen met hoe ik de properties en collections van de business objects moet veranderen. Ik kan dit het beste uitleggen met een voorbeeld.

Veronderstel de volgende situatie:
Ik heb een Employee-object dat een property 'name' heeft en een one-to-many associatie heeft met een aantal Diploma-objecten (een werknemer heeft één of meerdere diploma's). De Employee class zou er dan bijvoorbeeld als volgt uit kunnen zien:

Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class Employee {
    private String name;
    private final Set<Diploma> diplomas = new HashSet<Diploma>(2, 1f);
    
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        if (! isValidName(name))
            throw new IllegalArgumentException("wrong name");
        
        this.name = name;
    }
    
    public void addDiploma(Diploma diploma) {
        if (! isValidDiploma(diploma))
            throw new IllegalArgumentException("bad diploma");
        
        diplomas.add(diploma);
    }

    public Set<Diploma> getDiplomas() {
        return new HashSet<Diploma>(diplomas);
    }
}

Ik heb een aantal methodes zoals isValidName() en removeDiploma() weggelaten omdat die niet nodig zijn voor wat ik wil vragen.

Toen ik Hibernate gebruikte, werkte dit prima. Maar nu zal ik alles manueel d.m.v. JDBC moeten gaan laden en opslaan uit de database. Mijn vraag is nu hoe ik dit het beste aanpak?
  • Moet ik alle wijzigingen aan de properties of collections van een object direct opslaan in de database?
  • Als er een eigenschap of collection opgevraagd wordt, moet ik dan gewoon direct een verbinding maken met de database en een query doen voor de gegevens die er uit wil halen? Of moet ik deze gegevens cachen?
  • Bij het laden van een object A, moet ik dan ook direct alle objecten B_i waar A een associatie mee heeft laden? En dan ook alle objecten C_j waar elk object B_i een associatie mee heeft? Dus bij het laden van een object, moet ik dan de volledige object-graph laden? Dit zou volgens mij te veel overhead geven, omdat niet de volledige object-graph gebruikt moet worden.
Als ik alle gegevens direct wegschrijf naar de database en ook rechtstreeks lees uit de database, dan zou de Employee classe er zo uit zien:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Employee {
    public String getName() {
        String resultaat;
        MaakVerbindingMetDatabase();
        resultaat = VoerSelectQueryUit();
        SluitVerbindingMetDatabase();
        return resultaat;
    }
    
    public void setName(String name) {
        if (! isValidName(name))
            throw new IllegalArgumentException("wrong name");
        
        MaakVerbindingMetDatabase();
        VoerUpdateQueryUit();
        SluitVerbindingMetDatabase();
    }
    
    public void addDiploma(Diploma diploma) {
        if (! isValidDiploma(diploma))
            throw new IllegalArgumentException("bad diploma");
        
        MaakVerbindingMetDatabase();
        VoerCreateQueryUit();
        SluitVerbindingMetDatabase();
    }

    public Set<Diploma> getDiplomas() {
        MaakVerbindingMetDatabase();
        ResultSet result = VoerSelectQueryUit();
        SluitVerbindingMetDatabase();
        
        return ConverteerNaarSet(result);
    }
}

Hier wordt alles dus direct gelezen/geschreven van/naar de database. Er zijn dus ook geen instantievariabelen meer nodig.
  • Is dit een goede aanpak of niet? Is er een betere manier?
  • Hoe wordt dit normaal, meestal gedaan?
  • Moet ik toch instantievariabelen gebruiken (om te cachen)?
Ik hoop dat jullie mij wat meer inzicht kunnen geven in deze situatie.
Alvast bedankt!

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

Alarmnummer

-= Tja =-

Waarom is volgens jou hibernate ongeschikt voor een 2 tier applicatie.

De aanpak die je hebt gekozen is zeker niet aan te bevelen. Je wilt niet ieder update op een veld binnen een transactie doen. Je wilt acties die samen een bepaalde handeling uitvoeren (bv geld overmaken van 1 rekening dan een ander) atomic uitvoeren (dus alles of niets).

Verder gaat het ook voor geen meter performen.

Ps:
ben je dit alleen aan het doen of heb je er nog een senior die je gaat helpen. Zonder professionele hulp gaat dit volgens mij helemaal de mist in.

[ Voor 81% gewijzigd door Alarmnummer op 04-04-2008 15:15 ]


  • Kettrick
  • Registratie: Augustus 2000
  • Laatst online: 13:53

Kettrick

Rantmeister!

Zolang je zaken als caching e.d. goed configureert zou hibernate prima moeten werken lijkt me?

  • Standeman
  • Registratie: November 2000
  • Laatst online: 19:33

Standeman

Prutser 1e klasse

Ik denk dat het makkelijker zal zijn om er een 3-tier hibernate applicatie van te maken. Jouw oplossing ziet er qua database-connecties en onderhoud er zeer intensief uit. Verder denk ik dat de code een hel wordt om te debuggen / testen.

Ik ben altijd een groot voorstander van om domein objecten pojo's te laten zijn en via het DAO pattern alles te persisteren. Daarbij kan je namelijk zelf bedenken wanneer je iets opslaat of niet.

Bedenk trouwens ook hoeveel calls naar de DB je krijgt wanneer je bijv. van een x aantal users de getName() en getDiplomas() methods gaat afroepen. Dat gaat imo gruwelijk uit de klauwen lopen.

The ships hung in the sky in much the same way that bricks don’t.


  • titan_pi8
  • Registratie: Januari 2004
  • Laatst online: 11-11 23:10
Alarmnummer schreef op vrijdag 04 april 2008 @ 15:11:
Waarom is volgens jou hibernate ongeschikt voor een 2 tier applicatie.
RoeLz schreef op vrijdag 04 april 2008 @ 15:16:
Zolang je zaken als caching e.d. goed configureert zou hibernate prima moeten werken lijkt me?
Ik heb tamelijk veel opgezocht hierover, maar deze thread heeft mij overhaald om Hibernate overboord te gooien.

Ik wil gerust nog wel proberen om het met Hibernate aan de slag te krijgen, indien ik een aantal succes-stories over 2-tier applicaties met Hibernate te lezen krijg. Maar tot nu toe heb ik er zo nog geen gevonden...
Alarmnummer schreef op vrijdag 04 april 2008 @ 15:11:
Ps:
ben je dit alleen aan het doen of heb je er nog een senior die je gaat helpen. Zonder professionele hulp gaat dit volgens mij helemaal de mist in.
Ik sta er ongeveer alleen voor. Ik heb wel hulp van iemand anders, maar deze persoon houdt zich niet bezig met Java, maar hij kent wel het een en ander van databases.
Ik heb redelijk wat ervaring met Java, maar niet met de combinatie van OO-ontwerp en databases. Maar dat dit project de mist in gaat gaan neem ik toch niet aan. Je moet altijd ergens beginnen, en tot zo ver is alles erg goed gegaan. We hebben nu wel een redelijke tegenslag gekregen, maar dat is in het verleden nog zo geweest en daar geraken we wel uit.

Het gaat verder ook niet om een applicatie die door 100 mensen tegelijk intensief gebruikt gaat worden. Het programma zal zeker op minder dan 10 pc's staan en er zullen niet vaak meerdere gebruikers het programma tegelijk hebben draaien, laat staan met dezelfde werknemer bezig zijn.
Standeman schreef op vrijdag 04 april 2008 @ 15:17:
Ik denk dat het makkelijker zal zijn om er een 3-tier hibernate applicatie van te maken. Jouw oplossing ziet er qua database-connecties en onderhoud er zeer intensief uit. Verder denk ik dat de code een hel wordt om te debuggen / testen.
Heb je misschien wat meer info hierover? Kan ik mijn huidige business objects dan nog behouden? En zo ja, staan die dan in de client-laag (zoals nu), in de middelste laag, of in beide?
Ik veronderstel dat ik dan met RMI moet werken?
Standeman schreef op vrijdag 04 april 2008 @ 15:17:
Ik ben altijd een groot voorstander van om domein objecten pojo's te laten zijn en via het DAO pattern alles te persisteren. Daarbij kan je namelijk zelf bedenken wanneer je iets opslaat of niet.
Dit gaat volgens mij ook in een 2-tier applicatie. Maar dan heb ik nog altijd de vraag of ik bij het laden van een object (via een DAO) de volledige object-graph moet laden, of op een of andere manier gebruik kan maken van lazy loading?
Standeman schreef op vrijdag 04 april 2008 @ 15:17:
Bedenk trouwens ook hoeveel calls naar de DB je krijgt wanneer je bijv. van een x aantal users de getName() en getDiplomas() methods gaat afroepen. Dat gaat imo gruwelijk uit de klauwen lopen.
Het aantal gebruikers is erg beperkt dus misschien valt het dan nog wel mee? Maar heb je hetzelfde probleem niet bij een 3-tier applicatie, dan is er nog (bijna) evenveel netwerkverkeer. Er zal waarschijnlijk wel minder met de database gecommuniceerd moeten worden, maar ik denk niet dat dat een probleem zou zijn, gezien het beperkt aantal gebruikers.

  • Standeman
  • Registratie: November 2000
  • Laatst online: 19:33

Standeman

Prutser 1e klasse

titan_pi8 schreef op vrijdag 04 april 2008 @ 16:09:


[...]

Heb je misschien wat meer info hierover? Kan ik mijn huidige business objects dan nog behouden? En zo ja, staan die dan in de client-laag (zoals nu), in de middelste laag, of in beide?
Ik veronderstel dat ik dan met RMI moet werken?
Nou stel, op een scherm laat je tien employee namen zien die allemaal tien diploma's hebben. Dan zit je met jouw code al op 30 SQL statements en dan ben je nog niets spannends aan het doen. Zodra je meer attributen krijgt loopt het al snel de spuigaten uit (imo).

Nu moet ik wel eerlijk vertellen dat ik voornamelijk ervaring heb met het bouwen van 3-tier webapplicaties of 2-tier Swing applicaties waarbij alles op de client draait en er vanuit daar een DB connectie wordt gemaakt.
Bij dat laatste moet je wel een beetje hopen dat users elkaars data niet gaan verklooien.
[...]

Dit gaat volgens mij ook in een 2-tier applicatie. Maar dan heb ik nog altijd de vraag of ik bij het laden van een object (via een DAO) de volledige object-graph moet laden, of op een of andere manier gebruik kan maken van lazy loading?
Bij hibernate kan je uiteraard zelf kiezen of je iets lazy wilt loaden of niet. Het ligt een beetje aan an de grootte van je objectgraph, het aantal objecten en van het aantal users. Zolang dat een beetje binnen de perken blijft vind ik het makkelijkst om het eager te doen.
[...]

Het aantal gebruikers is erg beperkt dus misschien valt het dan nog wel mee? Maar heb je hetzelfde probleem niet bij een 3-tier applicatie, dan is er nog (bijna) evenveel netwerkverkeer. Er zal waarschijnlijk wel minder met de database gecommuniceerd moeten worden, maar ik denk niet dat dat een probleem zou zijn, gezien het beperkt aantal gebruikers.
Ik doelde meer op het feit dat je bij elke method call een query op de database doet. Het is wat makkelijker om gelijk alle data van het object te qeuriën dan het stukje bij beetje op te bouwen.

[ Voor 5% gewijzigd door Standeman op 04-04-2008 21:08 ]

The ships hung in the sky in much the same way that bricks don’t.


  • Macros
  • Registratie: Februari 2000
  • Laatst online: 29-10 20:56

Macros

I'm watching...

Als ik jou was zou ik gewoon bij Hibernate blijven. Hibernate is eigenlijk niks meer dan een handige tool om je domein op de database te mappen. Als je JDBC direct gaat gebruiken blijven de problemen die je had met Hibernate hetzelfde, alleen krijg je het mega probleem erbij dat je zelf al je queries mag gaan schrijven en aanroepen in plaats van dat Hibernate die voor je genereerd.

Ik snap ook niks van die link die je als argumentatie opvoert dat Hibernate niet op een 2-tier applicatie kan werken. De vraag wordt gesteld door iemand die duidelijk niet weet waar hij over praat, dus ik heb verder de tread niet gelezen.

Je kan best Hibernate gebruiken, als je dan versioning gebruikt, zodat gebruikers elkaars aanpassingen niet overschrijven is het helemaal makkelijk om in een 2-tier applicatie te gebruiken.

Veel succes met je implementatie mbv Hibernate en vergeet JDBC aub.

"Beauty is the ultimate defence against complexity." David Gelernter


  • whoami
  • Registratie: December 2000
  • Laatst online: 19:48
Ik vind de aanpak die je voorstelt in je TS helemaal niet goed; je gaat je Business Objecten nu verantwoordelijk laten zijn om de DB connectie te openen, te sluiten, en transacties te starten, maar je BO weten toch helemaal niet of die connectie eigenlijk al gesloten moet worden ? Of als je transactie al moet gecommit worden ? Je BO weten toch helemaal niets van de context waarin ze uitgevoerd worden ?
Op die manier ga je idd performance problemen krijgen; je gaat nu nl. voor iedere method waarin je naar de DB gaat, een connectie openen en opnieuw sluiten.

Wat heb je in die thread die je aanhaalt gelezen om je te laten besluiten dat hibernate geen goede optie is ?

https://fgheysels.github.io/


  • titan_pi8
  • Registratie: Januari 2004
  • Laatst online: 11-11 23:10
Macros schreef op zaterdag 05 april 2008 @ 15:04:
Als ik jou was zou ik gewoon bij Hibernate blijven. Hibernate is eigenlijk niks meer dan een handige tool om je domein op de database te mappen. Als je JDBC direct gaat gebruiken blijven de problemen die je had met Hibernate hetzelfde, alleen krijg je het mega probleem erbij dat je zelf al je queries mag gaan schrijven en aanroepen in plaats van dat Hibernate die voor je genereerd.
[...]
Je kan best Hibernate gebruiken, als je dan versioning gebruikt, zodat gebruikers elkaars aanpassingen niet overschrijven is het helemaal makkelijk om in een 2-tier applicatie te gebruiken.
Misschien was het dan toch te voorbarig om Hibernate overboord te gooien. Ik heb nog wat opgezocht over Hibernate + 2-tier applicaties en op sommige sites/fora wordt het session-per-application patroon aangehaald. Hierbij is het, integenstelling tot wat ik eerst dacht, niet zo dat er maar één session is voor een ganse applicatie, maar wel één session per 'groot' venster in de applicatie. Zo kan ik bijvoorbeeld een session maken voor het venster waar de meeste eigenschappen van een employee bepaald kunnen worden. Dit heeft bijvoorbeeld als voordeel dat als er een exception gebeurt, niet heel de applicatie afgesloten moet worden, maar enkel het venster waar die session bij hoort.
Macros schreef op zaterdag 05 april 2008 @ 15:04:
Ik snap ook niks van die link die je als argumentatie opvoert dat Hibernate niet op een 2-tier applicatie kan werken. De vraag wordt gesteld door iemand die duidelijk niet weet waar hij over praat, dus ik heb verder de tread niet gelezen.
whoami schreef op zaterdag 05 april 2008 @ 16:00:
Wat heb je in die thread die je aanhaalt gelezen om je te laten besluiten dat hibernate geen goede optie is ?
In die thread gaat het zeker niet om de startpost, die is inderdaad nogal vaag. Maar er wordt wel daadwerkelijk een goede discussie gevoerd (hoofdzakelijk tussen 2 personen) waarbij hun standpunten, volgens mij, goed geargumenteerd worden. Als je geïnteresseerd bent kan je best de eerste 3 pagina's lezen, daar staat het grootste deel van de argumenten waar ik naar verwijs...
whoami schreef op zaterdag 05 april 2008 @ 16:00:
Ik vind de aanpak die je voorstelt in je TS helemaal niet goed; je gaat je Business Objecten nu verantwoordelijk laten zijn om de DB connectie te openen, te sluiten, en transacties te starten, maar je BO weten toch helemaal niet of die connectie eigenlijk al gesloten moet worden ? Of als je transactie al moet gecommit worden ? Je BO weten toch helemaal niets van de context waarin ze uitgevoerd worden ?
Op die manier ga je idd performance problemen krijgen; je gaat nu nl. voor iedere method waarin je naar de DB gaat, een connectie openen en opnieuw sluiten.
Dit zijn het soort problemen die ik wel had verwacht met de aanpak uit de TS. Maar mijn bedoeling was om te vragen hoe dit dan wel netjes geïmplementeerd zou kunnen worden. Uiteraard verwachtte ik geen kant en klare oplossing, maar ik wou weten of er niet bepaalde, veelgebruikte design-patterns waren die ik zou kunnen gebruiken.

Ik ga voorlopig nog wat wachten met de implementatie van de verbinding met de database en eerst de business objects en eventueel ook de GUI verder afwerken. Als die zaken in orde zijn kan ik mij volledig toeleggen op de database. Ik ga dan toch proberen om met Hibernate te werken, aangezien dat blijkbaar toch fatsoenlijk te doen moet zijn.

  • Macros
  • Registratie: Februari 2000
  • Laatst online: 29-10 20:56

Macros

I'm watching...

Het beste design pattern hiervoor is het dao (Database Access Object) pattern. Dan definieer je al je business objects als POJO's (zoals Hibernate Entities). En heb je een DAO classe per entiteit. Die is dan verantwoordelijk voor het ophalen, zoeken, wegschrijven en dergelijke naar de database voor die entiteit. Al je dao's zullen waarschijnlijk veel code gelijk hebben, dus het handigste is dan om een abstract super dao te maken en die generic maken. Bijvoorbeeld:
Java:
1
2
3
4
5
public class AbstractDAO<T extends Entity> {
    public void save(T entity){ // implementatie }
    public void delete(T entity){ // implementatie }
    public void load(long id){ // implementatie }
}

En dan maak je een specifieke implementatie voor elke entiteit. Als je geen Hibernate gebruikt zal je bijna niets in je abstracte dao kunnen zetten. Misschien alleen load(long id) en delete(T) en zal je alles zelf moeten implementeren. Als je Hibernate gebruikt hoef je alleen maar specifieke zoek implementaties in de concrete dao's te implementeren en kan je meestal met een kale implementatie volstaan.

Ik hoop dat ik een beetje duidelijk ben geweest. Met het dao design pattern kan je de db mapping implementatie helemaal los weken van de rest van je applicatie. Je zult dan wel iets voor je transacties en sessies moeten bedenken. De transacties zijn dan lastiger dan de sessies. Misschien kan je iets van een transaction manager maken die een transactie start aan het begin van een belangrijke operatie en hem commit aan het einde daarvan. In een 3-tier applicatie kan je dat door de applicatie server laten doen, maar nu moet de client dat zelf regelen. Misschien is het een idee om wel 3 tiers te schrijven, maar de presentatie en de business tier beide op de client te draaien in dezelfde vm. De database kan makkelijk meerdere clients tegelijk aan. Je moet dan alleen op de versioning letten, wat ik al zei, en bedenken wat je doet als clients stale data hebben en daar komen ze achter...

"Beauty is the ultimate defence against complexity." David Gelernter


  • JKVA
  • Registratie: Januari 2004
  • Niet online

JKVA

Design-by-buzzword fanatic

De mannen van Hibernate geven zelf al een voorbeeld hoe je dit zou kunnen implementeren. Ik vind het zelf vrij aardig werken.
http://www.hibernate.org/328.html

Fat Pizza's pizza, they are big and they are cheezy


  • Confusion
  • Registratie: April 2001
  • Laatst online: 01-03-2024

Confusion

Fallen from grace

De JBoss Eclipse Hibernate tools plugin genereert ook hele aardige standaard DAO's. Moeten even aangepast worden voor gebruikt met Spring, maar dat is niet zo veel werk.

Wie trösten wir uns, die Mörder aller Mörder?


  • titan_pi8
  • Registratie: Januari 2004
  • Laatst online: 11-11 23:10
Ik volg het DAO pattern eigenlijk al. Ik werk op deze manier: http://www.devx.com/Java/Article/30396/0/page/1

Het zijn andere zaken waar Hibernate lastig over kan doen. Bijvoorbeeld lazy loading: als ik een object laad met een bepaalde session, en ik sluit die session daarna, dan kan het zijn dat ik een exception i.v.m. lazy loading krijg. Dit komt omdat als ik een eigenschap van dat object wil opvragen, dat nog niet geladen was (wegens lazy loading), dat dan de session waarmee dat object geladen was nog open moet zijn. Maar dit probleem kom ik normaal niet tegen als ik het session-per-application-window pattern (zoals in mijn vorige post uitgelegd) gebruik. Als laatste mogelijkheid kan ik lazy loading natuurlijk ook gewoon uit zetten.

Voor de rest moet ik nog opletten op StaleObjectExceptions. Ik moet nog afwachten of ik hier last van ga krijgen en wat ik er dan aan zou kunnen doen.

Misschien zijn er nog problemen die ik ga tegenkomen, maar dit zijn de problemen die ik het vaakst ben tegenkomen op andere fora.

  • Macros
  • Registratie: Februari 2000
  • Laatst online: 29-10 20:56

Macros

I'm watching...

Denk je dat je die problemen met JDBC niet krijgt dan? In je DAO's kan je bepalen hoeveel je van een entiteit laadt of op je entiteiten zelf als dat mogelijk is. StaleObjectExceptions heb je met JDBC natuurlijk ook, alleen dan ipv een exception schrijf je zonder dat iemand er iets van ziet iemand zijn wijzigingen over.

Hibernate helpt je juist zoveel mogelijk in deze situaties, met JDBC moet je dat allemaal zelf coden of negeren, met alle gevolgen van dien.

"Beauty is the ultimate defence against complexity." David Gelernter

Pagina: 1