Lazy-Fetchig und Session-Problem mit Hibernate

Paddius

Mitglied
Hallo,

ich versuche momentan meine ersten Schritte in Hibernate zumachen und hab mir zu diesem Zweck mal ein 3-teiliges Videotutorial zu Hibernate angeschaut. War alles in allem echt nett gemacht und erweckte in mir den Eindruck, dass ich mit dem Gerlernten recht schnell den Dreh raushaben sollte. Leider lief mir heute ein Problem über den Weg, welches ich bisher nicht so wirklich beheben konnte.

Hier mal meine Entitys:

Java:
@Entity
public class User implements Serializable {
    
    @Id
    @Column(name="User_ID")
    @GeneratedValue
    private Long Id;
    private String name;
    private String pw;
    private String email;
    @OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
    private Collection<Driver> drivers = new ArrayList<Driver>();

    public User() {
    }

    public User(String name, String pw, String email) {
        this.name = name;
        this.pw = pw;
        this.email = email;
    }

    public Long getId() {
        return Id;
    }

    public void setId(Long Id) {
        this.Id = Id;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getName() {
        return name;
    }

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

    public String getPw() {
        return pw;
    }

    public void setPw(String pw) {
        this.pw = pw;
    }

    public Collection<Driver> getDrivers() {
        return drivers;
    }

    public void setDrivers(Collection<Driver> drivers) {
        this.drivers = drivers;
    }
}


@Entity
public class Driver implements Serializable {
    
    @Id
    @GeneratedValue
    private Long Id;
    private Team team;
    private Championship cs;

    public Driver() {
    }

    public Driver(Team team, Championship cs) {
        this.team = team;
        this.cs = cs;
    }

    public Long getId() {
        return Id;
    }

    public void setId(Long Id) {
        this.Id = Id;
    }

    public Championship getCs() {
        return cs;
    }

    public void setCs(Championship cs) {
        this.cs = cs;
    }

    public Team getTeam() {
        return team;
    }

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

Und hier meine DAOs:

Java:
public class UserDAO {

    public void addUser(User u) {
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction transaction = session.beginTransaction();
        session.save(u);
        transaction.commit();
    }

    public void updatePassword(User u, String newPW) {
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction transaction = session.beginTransaction();
        u.setPw(newPW);
        session.saveOrUpdate(u);
        transaction.commit();
    }

    /*
     * returns either the user with the given name or null if no user with this
     * name exists
     */
    public User getUserByName(String name) {
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction transaction = session.beginTransaction();
        Query query = session.createQuery("from User where name = :name");
        query.setParameter("name", name);

        List resultList = query.list();
        if (resultList.size() > 0) {
            transaction.commit();
            return (User) resultList.listIterator().next();
        } else {
            transaction.commit();
            return null;
        }

    }

    public User getUserById(Long Id) {
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction transaction = session.beginTransaction();
        User u = (User) session.get(User.class, Id);
        transaction.commit();
        return u;
    }

    public void deleteUserById(Long Id) {
        User delUser = getUserById(Id);
        if (delUser != null) {
            Session session = HibernateUtil.getSessionFactory().getCurrentSession();
            Transaction transaction = session.beginTransaction();
            session.delete(delUser);
            transaction.commit();
        }

    }
    
    public List<User> getUsers() {
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction transaction = session.beginTransaction();        
        Query query = session.createQuery("from User");
        return query.list();
    }
    
    public void addDriver(User u, Driver d) {
        DriverDAO driverDAO = new DriverDAO();
        driverDAO.addDriver(d);
        
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction transaction = session.beginTransaction();        
        u.getDrivers().add(d);
        transaction.commit();
    }
    
    public List<Driver> getDrivers(User u) {
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();        
        session.load(User.class, u.getId());
        Transaction transaction = session.beginTransaction();
        List<Driver> drivers = (List<Driver>) u.getDrivers();
        return drivers;
    }
}

public class DriverDAO {
    
    public void addDriver(Driver d) {
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction transaction = session.beginTransaction();        
        session.save(d);
        transaction.commit();
    }
}

Nun habe ich mir mal ne Klasse zum Testen meiner Funktionen gemacht und folgendes probiert:

Java:
public static void main(String[] args) {
        User patrick = new User("Patrick", "test123", "thepadde86@googlemail.com");
        UserDAO userDAO = new UserDAO();
        
        userDAO.addUser(patrick);
        userDAO.addUser(dieter);
        userDAO.addUser(falko);
        
        userDAO.addDriver(patrick, new Driver(new Team(), new Championship()));
        userDAO.addDriver(patrick, new Driver(new Team(), new Championship()));
        userDAO.addDriver(patrick, new Driver(new Team(), new Championship()));
        userDAO.addDriver(patrick, new Driver(new Team(), new Championship()));
        User temp = userDAO.getUserByName("Patrick");
        List<Driver> listOfDrivers =  userDAO.getDrivers(temp);

        for (Driver driver : listOfDrivers) {
            System.out.println("Driver: " + driver.getId());
        }
    }

Leider bekomme ich dann folgende Exception beim Ausführen der Zeile
Code:
List<Driver> listOfDrivers =  userDAO.getDrivers(temp);
.

Code:
Schwerwiegend: failed to lazily initialize a collection of role: de.racegatherer.classes.User.drivers, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: de.racegatherer.classes.User.drivers, no session or session was closed
	at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:358)
	at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:350)
	at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:343)
	at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
	at org.hibernate.collection.PersistentBag.iterator(PersistentBag.java:249)
	at de.racegatherer.RaceGathererDB.main(RaceGathererDB.java:41)

Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: de.racegatherer.classes.User.drivers, no session or session was closed
	at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:358)
	at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:350)
	at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:343)
	at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
	at org.hibernate.collection.PersistentBag.iterator(PersistentBag.java:249)
	at de.racegatherer.RaceGathererDB.main(RaceGathererDB.java:41)

Mir ist schon klar, dass das wohl daran liegt, dass er beim Lazy-Fetchen der Liste nicht mehr die gleiche Session nutzt, die er beim eigentlichen Holen des User-Objekts genutzt hat. Beim Googlen bin ich dann darauf gestoßen, dass man die Session wohl mit session.load() wiederbekommt, wie man in der Funktion
Code:
public List<Driver> getDrivers(User u)
in der UserDAO sehen kann. Funktioniert nur leider nicht wie erhofft. Auch das Umstellen des FetchTypes auf Eager hat nicht zum gewünschten Ergebnis geführt und ich denke es muss ja auch mit dem Performance-schonenden Lazy-Loading gehen.

Ist mein genereller Ansatz mit den Sessions falsch? Wenn ja, wie löst man das besser?

Schöne Grüße,
Patrick
 
N

nillehammer

Gast
Dein Denkfehler ist, dass Du zwar mit dem korrekten User arbeitest, diesen aber nicht an die Session reatachest. Dein load geht zwar in die richtige Richtung, ist aber nicht ganz korrekt.
Der Fehler steckt hier:
[JAVA=75]
public List<Driver> getDrivers(User u) {
Session session = HibernateUtil.getSessionFactory().getCurrentSession();
session.load(User.class, u.getId());
Transaction transaction = session.beginTransaction();
List<Driver> drivers = (List<Driver>) u.getDrivers();
return drivers;
}

[/code]
Ändere es zu dem hier:
Java:
    public List<Driver> getDrivers(User u) {
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();    
// Du musst den return value von load/merge weiter benutzen. DAS ist nämlich
// das reatachte Objekt, bei dem die Lazy-Loading Probleme nicht auftreten.   
        u = (User)session.load(User.class, u.getId());
        Transaction transaction = session.beginTransaction();
        List<Driver> drivers = (List<Driver>) u.getDrivers();
        return drivers;
    }
 

Paddius

Mitglied
Cool, danke!

Nun hat sich aber ein weiteres Problem ergeben: Die Daten werden nicht in die Datenbank geschrieben. User sind korrekt in der Tabelle 'User' hinterlegt, die neu angelegten Fahrer sind auch in der Tabelle 'Driver' angelegt. Nur in der Tabelle 'User_Driver' (welche Hibernate wohl zum referenzieren der User und Driver zueinander benutzt?) sind absolut keine Daten vorhanden.

Ich denke den Fehler gefunden zu haben:

Java:
    public void addDriver(User u, Driver d) {
        DriverDAO driverDAO = new DriverDAO();
        driverDAO.addDriver(d);
        
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction transaction = session.beginTransaction();        
        u.getDrivers().add(d);
        transaction.commit();
    }

Diese Funktion habe ich wie folgt erweitert:

Java:
    public void addDriver(User u, Driver d) {
        DriverDAO driverDAO = new DriverDAO();
        driverDAO.addDriver(d);
        
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction transaction = session.beginTransaction();        
        u.getDrivers().add(d);
        session.saveOrUpdate(u); // hinzugefügt
        transaction.commit();
    }

Ist das so gedacht oder bekomme ich damit auf kurz oder lang auch wieder Probleme?
 
N

nillehammer

Gast
Die angepasste Version ist korrekt, aber etwas umständlich. Mit dem Mapping in User:
[JAVA=11]
@OneToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
private Collection<Driver> drivers = new ArrayList<Driver>();
[/code]
Sagst Du Hibernate zwei Dinge:
1. Der User ist der "Owner" der Relation drivers. D.h. Änderungen müssen immer vom User aus gemacht werden (also add auf drivers und den User speichern). Das machst Du ja jetzt. Insofern nur mal als Hintergrundinfo, warum Deine erste Version nicht funktioniert hat.
2. Du Cascades alle Operationen, also auch Persist

Deswegen kannst Du in Deiner Methode einige Zeilen weglassen:
Java:
    public void addDriver(User u, Driver d) {
       
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction transaction = session.beginTransaction();        
        u.getDrivers().add(d);
        session.saveOrUpdate(u); // hinzugefügt
        transaction.commit();
    }
Abgesehen davon brauchst Du das adden des Drivers auch nicht im Dao zu machen. Mache es außerhalb und speichere im Dao einfach den User:
Java:
    // Hier ist das User-Objekt mit dem bereits geaddeten Driver
    public void saveUser(User u) {
       
        Session session = HibernateUtil.getSessionFactory().getCurrentSession();
        Transaction transaction = session.beginTransaction();        
        session.saveOrUpdate(u);
        transaction.commit();
    }
 
Zuletzt bearbeitet von einem Moderator:

Neue Themen


Oben