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

[Spring/Hibernate] Lege elementen in List

Pagina: 1
Acties:

  • Mikey NL
  • Registratie: Februari 2004
  • Laatst online: 19-10 09:57
Ik ben sinds kort bezig om een projectje te doen in Spring + Hibernate. Na vele problemen al opgelost te hebben via Google en GOT ben ik nu bij een probleem aangekomen waar ik niet uit kom.

Ik heb twee klassen, Product en Category. Een Category kan meerdere Producten bevatten en een Product moet in een Category zitten.

Ik kan prima een Product of een Category maken, wijzigen en een overzicht opvragen, maar wat ik nu graag zou willen is een overzicht van Producten opvragen welke in een bepaalde Category zitten.

Mijn klassen zien er als volgt uit:

code:
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
public class Category {
    
    private long            id;
    private String          name;
    private List<Product>   products;
    
    public long getId() {
        return id;
    }
    
    public void setId(long id) {
        this.id = id;
    }
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }

    public List<Product> getProducts() {
        return products;
    }

    public void setProducts(List<Product> products) {
        this.products = products;
    }
    
}


code:
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
package treinverzameling.domain;

public class Product {
    
    private long        id;
    private String      name;
    private Category    category;
    
    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public long getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }
    
}


Zoals je kan zien bevat een Product een verwijzing naar Category en bevat Category een verwijzing naar Product in de vorm van een List.

Hierbij heb ik de volgende mappings gemaakt:
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
<hibernate-mapping>
    <class name="package.Category" table="Category">
        <id name="id" column="category_id">
            <generator class="native"/>
        </id>
        <property name="name" />
        <list name="products" inverse="false">
                <key column="category_id"/>
                <index column="product_id"/>
                <one-to-many class="package.Product"/>
        </list>
    </class>
</hibernate-mapping>


code:
1
2
3
4
5
6
7
8
9
<hibernate-mapping>
    <class name="treinverzameling.domain.Product" table="Product">
        <id name="id" column="product_id">
            <generator class="native"/>
        </id>
        <many-to-one name="category" column="category_id" not-null="true"/>
        <property name="name" />
    </class>
</hibernate-mapping>


Meer code is niet nodig denk ik, maar ik kan altijd meer posten.

Probleem
Als ik de lijst met Producten opvraag van een Category zitten er meerdere lege elementen in de List welke ik terug krijg.
Soms lijkt het erop dat de lege elementen welke erin zitten de Producten zijn welke een andere Category hebben, maar soms verschijnt er ook een leeg element aan het begin van de lijst.

Ik kon op google helaas niks vinden hierover, lijkt alsof niemand anders dit probleem heeft... Helaas zou ik bijna zeggen ;-) Ook got bracht me geen uitkomst dit keer via de zoekfuntie.

Verwijderd

En wat staat er in je database...?

  • Mikey NL
  • Registratie: Februari 2004
  • Laatst online: 19-10 09:57
In de database staan geen lege records, als dat is wat je bedoelt...

Verwijderd

Oh ik zie het al, ik had net je mapping niet echt serieus doorgenomen. Maar de fout zit hem in:
code:
1
<index column="product_id"/>


Deze column geeft niet een relatie weer maar de volgorde van de elementen. Dus maak hier of een aparte kolom van of vergeet hem in zijn geheel.

  • tijn
  • Registratie: Februari 2000
  • Laatst online: 02-11 15:59
Nog even voor de volledigheid: zorg ook dat de waarden in de index column aansluitend zijn, anders krijg je alsnog lege entries in je collection (dus 0,1,2,3,4 en niet 0,1,5,6,7).

Cuyahoga .NET website framework


  • Mikey NL
  • Registratie: Februari 2004
  • Laatst online: 19-10 09:57
Ok, ik kan dus niet zomaar die index column weghalen, want dan krijg ik een foutmelding. Een list moet dus altijd een index column hebben, omdat het een geordende lijst is.

Echter, ik vraag me dan af, als het alleen maar een kolom is om de volgorde aan te geven, waarom haalt ie dan toch alle records eruit (maar laat hij sommige leeg), dat klinkt helemaal niet logisch toch?


Nu moet ik dus een aantal dingen gaan aanpassen, bijvoorbeeld een Set gebruiken ofzo, zodat ik ongeordende lijsten eruit krijg?

code:
1
2
3
public List<Category> getCategoryList() {
    return (List<Category>) getHibernateTemplate().loadAll(Category.class);
}


Ik kan alleen niet zo snel vinden hoe ik deze methode dan zou moeten gaan maken, aangezien loadAll toch echt een list gaat teruggeven. Iemand die me hierbij op weg kan helpen?

Edit: OK, zie net dat je natuurlijk gewoon een nieuwe set kan aanmaken met de list als argument voor de constructor...

[ Voor 6% gewijzigd door Mikey NL op 20-05-2008 12:29 ]


  • Ansur
  • Registratie: Januari 2004
  • Laatst online: 29-10 13:35
Gewoon een aparte index kolom aanmaken.

Het truukje zit 'em erin om dynamisch de index te bepalen ipv. gewoon de index zoals opgehaald uit de database te gebruiken.

Maw. ipv. de typische getter:
code:
1
2
3
    public int getIndex() {
        return this.index;
    }

... doe je dit:
code:
1
2
3
    public int getIndex() {
        return this.category.getProducts().indexOf(this);
    }

  • Tubby
  • Registratie: Juni 2001
  • Laatst online: 07:15

Tubby

or not to be

Volgens mij is jouw oplossing niet wat bedoelt wordt, Hibernate heeft die index kolom nodig voor List collections. En die List die wordt ook hard gevuld op index (iets met index 5 komt ook echt op index 5, ongeacht of er andere elementen voor zitten).

De enige manier om null elementen te voorkomen is bij het verwijderen van een element ze ook uit de Collection te verwijderen.

Als je een Category.products hebt en je wilt een product verwijderen moet je dus Session.delete(product) doen en Category.products.remove(product). Dan wordt de List index goed bijgewerkt. Als je cascade=all hebt aanstaan is het verwijderen uit de Collection ook voldoende overigens.

Met terugwerkende krijgt kun je dergelijke fouten fixen door de collection opnieuw te queryen en op te slaan. (from Product as p where p.category=cat order by p.id)

[ Voor 38% gewijzigd door Tubby op 21-05-2008 15:03 ]

tubby.nl - Artes Moriendi - q1 - bf1942 - WoT - pubg - LinkedIN


  • Ansur
  • Registratie: Januari 2004
  • Laatst online: 29-10 13:35
Hetgeen je zegt klopt op zich wel, maar als je uit een collectie gewoon één element uithaalt, dan wordt de (hibernate)index van de opeenvolgende elementen niet geüpdate. Maw. als je daarna de lijst terug uit de database ophaalt, zal je dus niet met opeenvolgende indexen zitten.

Door die 'dynamische' get zullen de andere producten wel geüpdate worden met een correcte index.

  • Standeman
  • Registratie: November 2000
  • Nu online

Standeman

Prutser 1e klasse

Waarom maak je eigenlijk geen gebruik van een Set maar van een List. Het lijkt me dat de volgorde van de elementen er niet zo toe doet.
Waarschijnlijk wil je je lijst wel gesorteerd hebben, maar dat zal vast niet op de key value zijn.

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


  • Ansur
  • Registratie: Januari 2004
  • Laatst online: 29-10 13:35
(Een Set is gewoon een unieke collection, werkt niet met Key-Value)

Er zijn wel wat redenen om gewoon een List te gebruiken
- een set kan nog altijd een null value hebben
- hibernate's API werkt met Lists, dus ook vrij logisch om zelf ook lists te gebruiken (tenzij er echt een specifieke reden is waarom je een andere collection moet gebruiken)
- als hibernate een list lazy-loaded ophaalt en je de elementen gebruikt om in een set te steken, worden de elementen sowieso van de database gehaald, en heb je dus geen nut aan LL

  • Mikey NL
  • Registratie: Februari 2004
  • Laatst online: 19-10 09:57
Ok, ik volg het niet helemaal meer nu. Ik wil graag List gebruiken, omdat de objecten er dan altijd in dezelfde volgorde in komen te staan. Heb het geprobeerd met een Set, maar dan staan de objecten telkens in een andere volgorde, tenzij ik natuurlijk de objecten er zelf uit ga halen aan de hand van een bepaalde index kolom (via HQL) en daarna in een Set zet.

Maar, als ik dus de Producten in een List laat zetten, zoals in mijn eerste code en dan de mapping aanpas naar een kolom, met opeenvolgende waardes, dan werkt het dus nog steeds niet. Als ik dan aan een Category zijn Producten vraag, zitten er nog steeds lege elementen tussen, ook als ik op bovenstaande wijze dynamisch de index ophaal (welke niet zo in de database staan?)

Hoe moeten mijn klassen en mappings eruit zien om het volgende te kunnen doen:

- Een lijst van alle producten opvragen, waarbij de lijst altijd in dezelfde volgorde staat.
- Een lijst met producten van een bepaalde categorie opvragen, waarbij die lijst altijd in dezelfde volgorde staat en waarbij er geen lege elementen in de lijst verschijnen.

  • Standeman
  • Registratie: November 2000
  • Nu online

Standeman

Prutser 1e klasse

Ansur schreef op woensdag 21 mei 2008 @ 16:52:
(Een Set is gewoon een unieke collection, werkt niet met Key-Value)

Er zijn wel wat redenen om gewoon een List te gebruiken
- een set kan nog altijd een null value hebben
Een set kan geen null values bevatten. Een list kan dat echter wel.
- hibernate's API werkt met Lists, dus ook vrij logisch om zelf ook lists te gebruiken (tenzij er echt een specifieke reden is waarom je een andere collection moet gebruiken)
Hibernate api werkt net zo goed met een Set (Sterker nog, afaik is het zelfs default)
- als hibernate een list lazy-loaded ophaalt en je de elementen gebruikt om in een set te steken, worden de elementen sowieso van de database gehaald, en heb je dus geen nut aan LL
Die laatste snap ik niet echt?

Ik zie geen reden om een List te gebruiken. Wat je namelijk heb is een collectie van het type Product. Waarom zou je in je ORM mapping een volgorde willen opslaan? Deze is voor persistance helemaal niet belangrijk of interessant.

Het enige moment dat volgorde belangrijk wordt is wanneer je het laat zien in je GUI. Dan wordt het handig om een List object aan te maken en deze te sorteren met een Comparator.

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


  • Ansur
  • Registratie: Januari 2004
  • Laatst online: 29-10 13:35
Standeman schreef op donderdag 22 mei 2008 @ 16:30:
[...]

Een set kan geen null values bevatten. Een list kan dat echter wel.
Tuurlijk wel :z En om het ineens maar eens te bewijzen. Gegeven volgende code:
code:
1
2
3
4
5
        final Set set = new HashSet();
        set.add(null);
        System.out.println(set);
        System.out.println(set.size());
        System.out.println(set.contains(null));

... geeft volgende output:
code:
1
2
3
[null]
1
true
[...]

Hibernate api werkt net zo goed met een Set (Sterker nog, afaik is het zelfs default)
Uiteraard kan hibernate ook met sets om. Misschien heb ik me niet specifiek genoeg uitgedrukt: wanneer je data ophaalt geeft hibernate een list terug. bv. als je session.createQuery("from Product where ...").list() uitvoert krijg je (uiteraard) een list object terug. En hier gaat het namelijk om, het ophalen van data.

Je kan de data uit een list natuurlijk in een set gooien, maar dan heb je nog altijd een null value er in zitten.


@Mikey NL mocht ik wat tijd hebben vandaag of morgen zal ik die index proberen te illustreren met wat code + voorbeelden

  • Standeman
  • Registratie: November 2000
  • Nu online

Standeman

Prutser 1e klasse

Ansur schreef op donderdag 22 mei 2008 @ 17:21:
[...]

Tuurlijk wel :z En om het ineens maar eens te bewijzen. Gegeven volgende code:
code:
1
2
3
4
5
        final Set set = new HashSet();
        set.add(null);
        System.out.println(set);
        System.out.println(set.size());
        System.out.println(set.contains(null));

... geeft volgende output:
code:
1
2
3
[null]
1
true
Owja.. da's waar natuurlijk. 8)7 :$

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


  • Mikey NL
  • Registratie: Februari 2004
  • Laatst online: 19-10 09:57
Ansur schreef op donderdag 22 mei 2008 @ 17:21:

@Mikey NL mocht ik wat tijd hebben vandaag of morgen zal ik die index proberen te illustreren met wat code + voorbeelden
Als het niet teveel moeite zou zijn, zou ik dat heel erg op prijs stellen. Ik vraag me alleen af, is er niet ergens een goed voorbeeld te vinden op een website. Een site met categoriën en producten lijkt me toch redelijk algemeen?

Verwijderd

Ansur schreef op donderdag 22 mei 2008 @ 17:21:
Uiteraard kan hibernate ook met sets om. Misschien heb ik me niet specifiek genoeg uitgedrukt: wanneer je data ophaalt geeft hibernate een list terug. bv. als je session.createQuery("from Product where ...").list() uitvoert krijg je (uiteraard) een list object terug. En hier gaat het namelijk om, het ophalen van data.
Dat maakt het echter alles behalve logisch om te kiezen voor een List boven een Set. Die keuze staat hier namelijk helemaal los van. Hibernate kan immers enkel garanderen dat er een Set wordt geretourneerd wanneer er gebruik wordt gemaakt van een distinct clausule. Dat zegt echter niets over je domein model waar het meestal veel aannemelijker is om sets te gebruiken.

Verder vind ik het lazy loading argument niet echt noemenswaardig. Dat impliceert namelijk dat je elementen uit je List wil lazy loaden doordat je om een of andere duistere reden de exacte index weet en dat is nou niet echt bepaald aannemelijk.

  • Ansur
  • Registratie: Januari 2004
  • Laatst online: 29-10 13:35
Verwijderd schreef op donderdag 22 mei 2008 @ 22:26:
[...]
Dat zegt echter niets over je domein model waar het meestal veel aannemelijker is om sets te gebruiken.
En daar heb je gelijk in. Maar toch opteer ik zelf nog altijd voor lists.
Uiteindelijk wil je een lijst toch op een gestructureerde manier weergeven. Een 'order by' heeft al geen nut als je de resultaten in een set steekt. Als je dan in je DAO layer eerst al die list naar een set omzet, om dan ergens in je presentation layer ofzo die weer naar een list om te zetten en er een comparator tegen aan te smijten... overbodig werk toch? En uiteindelijk weet je wat er in je collection zit.
Pagina: 1