Hibernate List Problem

e.motion

Mitglied
Hallo,
hier meine Java-Beans:

Code:
class Team
{
	private int id;
	private List players = new ArrayList();
	private String name;

	public Team()
	{
	}

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

	public int getId()
	{
		return id;
	}

	public List getPlayers()
	{
		return players;
	}
	
	public void setPlayers(List players)
	{
		this.players = players;
	}
	
	public void addPlayer(Player player)
	{
		players.add(player);
	}
	
	public void removePlayer(Player player)
	{
		players.remove(player);
	}
	
	public void clearPlayers()
	{
		players.clear();
	}

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

class Player
{
	private int id;
	private Team team;
	private String name;

	public Player()
	{
	}

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

	public int getId()
	{
		return id;
	} 

	public void setTeam(Team team)
	{
		this.team = team;
	}

	public int getTeam()
	{
		return team;
	} 
	
	public String getName()
	{
		return name;
	}
	
	public void setName(String name)
	{
		this.name = name;
	}
}

Und hier das dazugehörigen Hibernate-Mappings:

Code:
<class name="Team" table="team">
	<id name="id" type="integer" unsaved-value="null" column="team_id">
		<generator class="increment"/>
	</id>
	<list name="players" lazy="true">
		<key column="team_fid"/>
		<index column="player_id"/>	
		<one-to-many class="Player"/>
	</list>
	<property name="name"/>
</class>

<class name="Player" table="player">
	<id name="id" type="integer" unsaved-value="null" column="player_id">
		<generator class="increment"/>
	</id>
	<many-to-one name="team" class="Team" column="team_fid"/>
	<property name="name"/>
</class>

Nun hab ich folgende 2 Probleme:

  • Wenn ich neue Objekte der Datenbank hinzufüge funktioniert dies folgendermaßen einwandfrei:
Code:
Team team = new Team();
team.setName("Team1");

Player player = new Player();
player.setName("Player1");
player.setTeam(team);

session.save(team);
session.save(player);
Aber sobalt ich es folgendermaßen versuche wird der team_fid Fremdschlüssel nicht in die player Tabellen eingetragen.
Code:
Player player = new Player();
player.setName("Player1");

Team team = new Team();
team.setName("Team1");
team.addPalyer(player);

session.save(team);
session.save(player);
  • Wenn ich die Objekte aus der Datenbank lesen möchte funktioniert dies folgendermaßen auch einwandfrei:
Code:
Query query = session.createQuery("select p from Player as p");
for (Iterator it = query.iterate(); it.hasNext();)
{
	Player p = (Player)it.next();
	System.out.println("Player: " + p.getName());
	System.out.println("PacketCaregory: " + p.getTeam().getName());
}

Aber sobalt ich folgerndermaßen versuche die Daten auszulesen werden die Player nicht mit ausgelesen:
Code:
Query query = session.createQuery("select t from Team as t");
for (Iterator it = query.iterate(); it.hasNext();)
{
	Team t = (Team)it.next();
	System.out.println("Team: " + t.getName());
	Hibernate.initialize(t.getPlayers());
	for (Iterator it2 = t.getPlayers().iterator(); it2.hasNext();)
	{
		Player p = (Player)it2.next();
		System.out.println("Player: " + p.getName());
	}
}


Ich vermute sehr stark, dass diese beiden Probleme zusammenhängen.
Hat jemand ne Ahnung was ich Falsch machen?
Vielleicht in den Hibernate-Mappings?

Gruß e.motion
 
Hallo!

Spiel doch hiermit mal ein wenig rum... ;-)

Datenmodell:

create table player(id int, team_id int, name varchar(32) unique);

create table team(id int, name varchar(32) unique);

create table hilo(next_value int);

insert into hilo values(1);

commit;

Beispiel1:

Code:
/*
 * Created on 15.01.2005@21:04:30
 *
 * TODO Licence info
 */
package de.tutorials.core;

import java.io.File;
import java.util.Iterator;

import net.sf.hibernate.Criteria;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.Transaction;
import net.sf.hibernate.cfg.Configuration;
import net.sf.hibernate.expression.Expression;

import org.apache.log4j.BasicConfigurator;

import de.tutorials.core.domain.Player;
import de.tutorials.core.domain.Team;

/**
 * @author Administrator
 *
 * TODO Explain me
 */
public class Test {

    public static void main(String[] args) {
        BasicConfigurator.configure();

        try {
            Configuration cfg = new Configuration();
            cfg.configure(new File("conf/hibernate/hibernate.cfg.xml"));

            SessionFactory sf = cfg.buildSessionFactory();

            Session s = sf.openSession();

            Transaction tx = s.beginTransaction();

            //Beispiel 1:
            Team t = new Team("tutorials.de");
            Player player0 = new Player(t, "Thomas");
            Player player1 = new Player(t, "Richard");
            s.save(t);

            //Beispiel 2:            
            //Criteria c = s.createCriteria(Team.class);
            //c.add(Expression.like("name", "tutorials.de"));
            //for (Iterator iter = c.list().iterator(); iter.hasNext();) {
              //  Team team = (Team) iter.next();
              //  System.out.println(team);
              //  for (Iterator iter2 = team.getPlayers().iterator(); iter2
              //          .hasNext();) {
              //      Player p = (Player) iter2.next();
              //      System.out.println(p);
              //  }
          //  }

            tx.commit();

            s.close();

            sf.close();
        } catch (HibernateException e) {
            e.printStackTrace();
        }
    }
}

Daten:

Code:
mysql> select * from player;
+-------+---------+---------+
| id    | team_id | name    |
+-------+---------+---------+
| 98305 |   65537 | Thomas  |
| 98306 |   65537 | Richard |
+-------+---------+---------+
2 rows in set (0.00 sec)

mysql> select * from team;
+-------+--------------+
| id    | name         |
+-------+--------------+
| 65537 | tutorials.de |
+-------+--------------+
1 row in set (0.00 sec)


Die Klasse Player:
Code:
/*
 * Created on 08.02.2005@21:01:52
 *
 * TODO Licence info
 */
package de.tutorials.core.domain;

import java.io.Serializable;

/**
 * @author Administrator
 *
 * TODO Explain me
 * 
 * @hibernate.class table = "player"
 */
public class Player implements Serializable {
    private Long id;

    private Team team;

    private String name;

    public Player() {
    }

    /**
     * @param name
     */
    public Player(String name) {
        this(null, name);
    }

    /**
     * @param team
     * @param name
     */
    public Player(Team team, String name) {
        this.team = team;
        this.name = name;
        team.addPlayer(this);
    }

    private void setId(Long id) {
        this.id = id;
    }

    /**
     * @hibernate.id generator-class = "hilo"
     * @hibernate.generator-param name = "table" value = "hilo"
     * @hibernate.generator-param name = "column" value = "next_value"
     * @return
     */
    public Long getId() {
        return id;
    }

    public void setTeam(Team team) {
        this.team = team;
    }

    /**
     * @hibernate.many-to-one column = "team_id"
     * @return
     */
    public Team getTeam() {
        return team;
    }

    /**
     * @hibernate.property column = "name"
     * @return
     */
    public String getName() {
        return name;
    }

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

    public boolean equals(Object o) {

        if (!(o instanceof Player))
            return false;

        Player other = (Player) o;
        return other.name.equals(this.name);
    }

    public int hashCode() {
        return this.name.hashCode();
    }

    public String toString() {
        return "Name: " + this.name + " ID: " + this.id;
    }
}

Player.hbm.xml:
Code:
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 2.0//EN" 
    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping
>
    <class
        name="de.tutorials.core.domain.Player"
        table="player"
        dynamic-update="false"
        dynamic-insert="false"
        select-before-update="false"
        optimistic-lock="version"
    >

        <id
            name="id"
            column="id"
            type="java.lang.Long"
        >
            <generator class="hilo">
                <param name="table">hilo</param>
                <param name="column">next_value</param>
              <!--  
                  To add non XDoclet generator parameters, create a file named 
                  hibernate-generator-params-Player.xml 
                  containing the additional parameters and place it in your merge dir. 
              --> 
            </generator>
        </id>

        <many-to-one
            name="team"
            class="de.tutorials.core.domain.Team"
            cascade="none"
            outer-join="auto"
            update="true"
            insert="true"
            access="property"
            column="team_id"
        />

        <property
            name="name"
            type="java.lang.String"
            update="true"
            insert="true"
            access="property"
            column="name"
        />

        <!--
            To add non XDoclet property mappings, create a file named
                hibernate-properties-Player.xml
            containing the additional properties and place it in your merge dir.
        -->

    </class>

</hibernate-mapping>

Die Klasse Team:

Code:
/*
 * Created on 08.02.2005@21:03:25
 *
 * TODO Licence info
 */
package de.tutorials.core.domain;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author Administrator
 *
 * TODO Explain me
 * 
 * @hibernate.class table = "team"
 *  
 */
public class Team implements Serializable {

    private Long id;

    private Set players;

    private String name;

    public Team() {
    }

    /**
     * @param name
     */
    public Team(String name) {
        this.name = name;
    }

    private void setId(Long id) {
        this.id = id;
    }

    /**
     * @hibernate.id generator-class = "hilo"
     * @hibernate.generator-param name = "table" value = "hilo"
     * @hibernate.generator-param name = "column" value = "next_value"
     * @return
     */
    public Long getId() {
        return id;
    }

    /**
     * @hibernate.set table = "player" inverse = "true" cascade = "all"
     * @hibernate.collection-one-to-many class = "de.tutorials.core.domain.Player"
     * @hibernate.collection-key column = "team_id"
     */
    public Set getPlayers() {
        if (this.players == null) {
            players = new HashSet();
        }
        return players;
    }

    public void setPlayers(Set players) {
        this.players = players;
    }

    public void addPlayer(Player player) {
        getPlayers().add(player);
    }

    public void removePlayer(Player player) {
        players.remove(player);
    }

    public void clearPlayers() {
        players.clear();
    }

    /**
     * @hibernate.property column = "name"
     * @return
     */
    public String getName() {
        return name;
    }

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

    public boolean equals(Object o) {
        if (!(o instanceof Team))
            return false;
        Team other = (Team) o;
        return other.name.equals(this.name);
    }

    public int hashCode() {
        return this.name.hashCode();
    }

    public String toString() {
        return "Name: " + this.name + " ID:" + this.id;
    }
}


Team.hbm.xml:
Code:
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 2.0//EN" 
    "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping
>
    <class
        name="de.tutorials.core.domain.Team"
        table="team"
        dynamic-update="false"
        dynamic-insert="false"
        select-before-update="false"
        optimistic-lock="version"
    >

        <id
            name="id"
            column="id"
            type="int"
        >
            <generator class="hilo">
                <param name="table">hilo</param>
                <param name="column">next_value</param>
              <!--  
                  To add non XDoclet generator parameters, create a file named 
                  hibernate-generator-params-Team.xml 
                  containing the additional parameters and place it in your merge dir. 
              --> 
            </generator>
        </id>

        <set
            name="players"
            table="player"
            lazy="false"
            inverse="true"
            cascade="all"
            sort="unsorted"
        >

              <key
                  column="team_id"
              >
              </key>

              <one-to-many
                  class="de.tutorials.core.domain.Player"
              />

        </set>

        <property
            name="name"
            type="java.lang.String"
            update="true"
            insert="true"
            access="property"
            column="name"
        />

        <!--
            To add non XDoclet property mappings, create a file named
                hibernate-properties-Team.xml
            containing the additional properties and place it in your merge dir.
        -->

    </class>

</hibernate-mapping>


Gruß Tom
 
Zuletzt bearbeitet:
Hi, vielen Dank für deine Hilfe.
Hab jetzt bei mir die Collection auch mal zum testen als Set definiert und es hat auch sofort funktioniert.

Doch wenn ich sie als List definiere funktioniert es leider immer noch nicht.
Bei manchen Collections spielt bei mir aber die Reihenfolge der Elemente eine Rolle :(

Gruß
 
Zuletzt bearbeitet:
Hallo!

Welche "Reihenfolge" meinst du denn? Die "natürliche", d.h. wie sie in der Datenbank liegen oder geordnet nach einem bestimmten Kriterium?

Gruß Tom
 
Hallo
Manche Collections sollen nach einem Kriterium geordnet werden.

Bei manchen soll auch nur die Reihenfolge beibehalten werden, wie die Objekte hinzugefügt wurden.
Also wenn ich zum Beispiel ein Team anlege und dem Team die Player hinzufüge. Dann soll dieses Team nicht gleich in die Datenbank geschrieben werden, sondern soll erst noch über das Request-Objekt an eine andere Seite weitergeleitet werden, von der aus das Team dann in die Datenbank geschrieben wird. Hier soll die Reihenfolge der Player wie sie auf der voherigen Seite hinzugefügt wurden auch weiterhin bestehen.

Gruß
 
Hallo!

Denke das du dich nicht auf die natürliche Ordnung verlassen solltest. Wenn du "Orndung" brauchst solltest du diese vom Datenbank System bzw. von Hibernate gewährleisten lassen. Das lässt sich in den Hibernate Mappings-Files zum einen über ein Order Konstrukt (Ordnet dann auf Datenbankseite) und über ein Sort -Konstrukt (Hibernate-Ordnet dann in Memory).
Du solltest weiterhin um eine Ordnugn auf POJO (Bean) Ebene zu erreichen solltest du auch in deinen Bean Klassen das Comparable Interface implementieren. Dann könntest du statt einer normalen Set Implementierung wie das HashSet eine SortedSet Implementierung wie etwa TreeSet verwenden.

Gruß Tom
 
Hey, falls dein Problem noch nicht gelöst ist hab ich eventuell einen Vorschlag:

Wenn Objekte eines Typs bei dir immer nach einer bestimmten Ordnung sortiert werden sollen und die Objekte nur in einer Java-Applikation sichtbar sein sollen, dann spielt die Datenbank-Ebene keine Rolle und daher könntest du einfach die Reihenfolge beim Auslesen folgendermaßen festlegen (benutze ich relativ häufig):

- definiere Deine Objekte als "Comparable"
- dann müssen deine Klassen/Beans die Methode "int compareTo(Object o)" implementieren, die du so schreiben kannst, dass sie die gewünschte Ordnung beschreiben...
- erstelle ein SortedSet über "new TreeSet()"
- sobald du nun Objekte von vergleichbaren (Comparable) Beans/Klassen in dieses SortedSet hinzufügst werden sie automatisch sortiert
- danach kannst du aus dem SortedSte ein ArrayList oder ähnliches machen und die Ordnung bleibt bestehen...


Kleine Anmerkung beim Umgang mit Hibernate:

Von meiner Seite aus ist nicht anzuraten die Beans mit Hibernate arbeiten zu lassen! Ich benutze daher eigenständige Implementationen, die kaum Logik enthalten und wesentlich weniger Zugriffe erlauben von außen als Beans (nur Getter). Dazu gibt es bei mir immer folgendes (Beispiel Objekt "Auto"):

- Interface "Auto extends Comparable" (in Verzeichnis "/model")
- Bean "AutoBean implements Auto" (in Verzeichnis "/model")
- Impl "AutoImpl implements Auto" (in Verzeichnis "/impl")
- Helper-Klasse "AutoHelper" (in Verzeichnis "/helper")

Hierbei setze ich folgende Idee und damit Sicherheitskonzept um:

1.) Helper-Klassen sind die Verbindung der Beans, Impl's und Hibernate und erledigen auch die Verbindungen zu anderen Objekten
2.) Impl's sind die Implementierung für Hibernate mit xDoclet-Hibernate-Tags
3.) Impl's benötigen kaum Logik
4.) Beans sind nur temporär vorhanden, man kann mit ihnen rummachen wie man will, gespeichert wird deswegen noch nichts!
5.) Beans enthalten Verweise zu Validierungen (wenn noch nicht in Web-Applikationen mittels Struts erfolgt)
 
Hallo!

- Interface "Auto extends Comparable" (in Verzeichnis "/model")
- Bean "AutoBean implements Auto" (in Verzeichnis "/model")
- Impl "AutoImpl implements Auto" (in Verzeichnis "/impl")
- Helper-Klasse "AutoHelper" (in Verzeichnis "/helper")

Bei so einem "komplexen" Modell kann man ja gleich wieder mit CMP Entity-Beans anfangen... ;-)
Ist es denn nicht gerade Sinn der Sache, dass die Anwender bei der Benutzung von Hibernate nur mit ihren POJO's herum hantieren und sich nicht mit irgendwelchen Interface/Proxy-geraffel beschäftigen müssen. Ich denke schon.
Das Konzept für DAO's zur nochmaligen Abstraktion der Persistenzschicht ist ja okay, aber das was du da machst ist in meinen Augen echt übertrieben.

Gruß Tom
 
OK, also bei kleineren Applikationen ist das sicherlich viel zu viel des guten...

...allerdings finde ich (da ich mittlerweile in ziemlich komplexen Web-Applikationen arbeite) diese Lösung ziemlich gut, weil immer klar ist, wo was ist und wer worauf was wie tut. Es existiert meistens auch nicht immer eine Helper-Klasse zu einem Interface. Oftmals werden mehrere Objekte in einer Helper-Klasse zusammengeführt, je nachdem wie sehr sie unabhängig von anderen sind oder nicht. Ich tue auch nicht jede einzelne Eigenschaft von einer Bean in das Impl per Hand übertragen sondern habe mir (wie an vielen Stellen) generische Methoden geschrieben, die das erledigen.

Die Trennung der Beans von Hibernate liegt auch darin begründet, dass ich innerhalb von Web-Applikationen mit etlichen Formularen hantiere und in den Servlets die Daten sofort in die Beans geschrieben werden. Dort werden sie dann erst validiert oder schon vorher durch Struts. Somit ist es letztendlich so gut wie unmöglich falsche Daten in der Datenbank zu erzeugen. Alles wird vorher abgefangen.

Außerdem ist es oft ein Ziel Teile einer Applikation auch später wiederverwenden zu können in anderen Applikationen. Das Konzept der Trennung erleichtert sowas erheblich, wie ich finde. Aber das ist ja auch eine Frage von Meinung, Geschmack und Überzeugung.

In kleinen Applikationen würde ich diese Vorgehensweise sicherlich nicht für angebracht halten, es sei denn man hat vor, sie enorm zu Vergrößern, aber für eine mandantenfähige Service-Management-Lösung wie ich sie gerade baue ist das Modell zwar nicht ganz perfekt, aber die beste Lösung, gerade wenn mal mehrere dran arbeiten, die sonst den Code noch nie gesehen haben...


Gruß, CHaoSlayeR

P.S.: vielleicht sollten wir mal einen Thread aufmachen für Diskussionen über solche Dinge wie wir hier gerade ansprechen á la "Die perfekte Abstraktion für deine Applikation..." ;-)
 
Hab mich für die TreeSets entschieden ist echt gut!
Hab garnet gewusst dass es sowas wie Comparable gibt :)

Solche Diskussionen über Patterns und Anwendungsdesign find ich überaus interessant sollten vielleicht echt mal ein extra Thread aufmachen :)

Gruß
 
Zurück