Hibernate.org Community Documentation

- Zurück
- Weiter

## Kapitel 11. Das Arbeiten mit Objekten

Hibernate is a full object/relational mapping solution that not only shields the developer from the details of the underlying database management system, but also offers state management of objects. This is, contrary to the management of SQL `statements` in common JDBC/SQL persistence layers, a natural object-oriented view of persistence in Java applications.

Mit anderen Worten - Hibernate Anwendungsentwickler sollten sich stets über den Status Ihrer Objekte Gedanken machen und nicht unbedingt über die Ausführung von SQL-Anweisungen. Dieser Teil wird von Hibernate übernommen und ist nur dann für den Anwendungsentwickler von Bedeutung, wenn die Performance des Systems eingestellt wird.

## 11.1. Statusarten von Hibernate Objekten

Hibernate definiert und unterstützt die folgenden Arten des Objektstatus:

- Transient - an object is transient if it has just been instantiated using the `new` operator, and it is not associated with a Hibernate `Session`. It has no persistent representation in the database and no identifier value has been assigned. Transient instances will be destroyed by the garbage collector if the application does not hold a reference anymore. Use the Hibernate `Session` to make an object persistent (and let Hibernate take care of the SQL statements that need to be executed for this transition).

- Persistent - a persistent instance has a representation in the database and an identifier value. It might just have been saved or loaded, however, it is by definition in the scope of a `Session`. Hibernate will detect any changes made to an object in persistent state and synchronize the state with the database when the unit of work completes. Developers do not execute manual `UPDATE` statements, or `DELETE` statements when an object should be made transient.

- Detached - a detached instance is an object that has been persistent, but its `Session` has been closed. The reference to the object is still valid, of course, and the detached instance might even be modified in this state. A detached instance can be reattached to a new `Session` at a later point in time, making it (and all the modifications) persistent again. This feature enables a programming model for long running units of work that require user think-time. We call them application transactions, i.e., a unit of work from the point of view of the user.

We will now discuss the states and state transitions (and the Hibernate methods that trigger a transition) in more detail.

## 11.2. Objekte persistent machen

Newly instantiated instances of a persistent class are considered transient by Hibernate. We can make a transient instance persistent by associating it with a session:

    DomesticCat fritz = new DomesticCat();

    fritz.setColor(Color.GINGER);

    fritz.setSex('M');

    fritz.setName("Fritz");

    Long generatedId = (Long) sess.save(fritz);

If `Cat` has a generated identifier, the identifier is generated and assigned to the `cat` when `save()` is called. If `Cat` has an `assigned` identifier, or a composite key, the identifier should be assigned to the `cat` instance before calling `save()`. You can also use `persist()` instead of `save()`, with the semantics defined in the EJB3 early draft.

- `persist()` makes a transient instance persistent. However, it does not guarantee that the identifier value will be assigned to the persistent instance immediately, the assignment might happen at flush time. `persist()` also guarantees that it will not execute an `INSERT` statement if it is called outside of transaction boundaries. This is useful in long-running conversations with an extended Session/persistence context.

- `save()` does guarantee to return an identifier. If an INSERT has to be executed to get the identifier ( e.g. "identity" generator, not "sequence"), this INSERT happens immediately, no matter if you are inside or outside of a transaction. This is problematic in a long-running conversation with an extended Session/persistence context.

Alternatively, you can assign the identifier using an overloaded version of `save()`.

    DomesticCat pk = new DomesticCat();

    pk.setColor(Color.TABBY);

    pk.setSex('F');

    pk.setName("PK");

    pk.setKittens( new HashSet() );

    pk.addKitten(fritz);

    sess.save( pk, new Long(1234) );

If the object you make persistent has associated objects (e.g. the `kittens` collection in the previous example), these objects can be made persistent in any order you like unless you have a `NOT NULL` constraint upon a foreign key column. There is never a risk of violating foreign key constraints. However, you might violate a `NOT NULL` constraint if you `save()` the objects in the wrong order.

Usually you do not bother with this detail, as you will normally use Hibernate's transitive persistence feature to save the associated objects automatically. Then, even `NOT NULL` constraint violations do not occur - Hibernate will take care of everything. Transitive persistence is discussed later in this chapter.

## 11.3. Das Laden eines Objekts

The `load()` methods of `Session` provide a way of retrieving a persistent instance if you know its identifier. `load()` takes a class object and loads the state into a newly instantiated instance of that class in a persistent state.

    Cat fritz = (Cat) sess.load(Cat.class, generatedId);

    // you need to wrap primitive identifiers

    long id = 1234;

    DomesticCat pk = (DomesticCat) sess.load( DomesticCat.class, new Long(id) );

Alternativ können Sie den Status in eine beliebige Instanz laden:

    Cat cat = new DomesticCat();

    // load pk's state into cat

    sess.load( cat, new Long(pkId) );

    Set kittens = cat.getKittens();

Be aware that `load()` will throw an unrecoverable exception if there is no matching database row. If the class is mapped with a proxy, `load()` just returns an uninitialized proxy and does not actually hit the database until you invoke a method of the proxy. This is useful if you wish to create an association to an object without actually loading it from the database. It also allows multiple instances to be loaded as a batch if `batch-size` is defined for the class mapping.

If you are not certain that a matching row exists, you should use the `get()` method which hits the database immediately and returns null if there is no matching row.

    Cat cat = (Cat) sess.get(Cat.class, id);

    if (cat==null) {

        cat = new Cat();

        sess.save(cat, id);

    }

    return cat;

You can even load an object using an SQL `SELECT ... FOR UPDATE`, using a `LockMode`. See the API documentation for more information.

    Cat cat = (Cat) sess.get(Cat.class, id, LockMode.UPGRADE);

Any associated instances or contained collections will not be selected `FOR UPDATE`, unless you decide to specify `lock` or `all` as a cascade style for the association.

Es ist mittels der `refresh()`-Methode jederzeit möglich, ein Objekt und alle seine Collections erneut zu laden. Dies ist insbesondere dann von Nutzen, wenn Datenbank-Trigger zur Initialisierung der Objekt-Properties verwendet werden.

    sess.save(cat);

    sess.flush(); //force the SQL INSERT

    sess.refresh(cat); //re-read the state (after the trigger executes)

How much does Hibernate load from the database and how many SQL `SELECT`s will it use? This depends on the fetching strategy. This is explained in Abschnitt 21.1, „Abrufstrategien“.

## 11.4. Anfragen

If you do not know the identifiers of the objects you are looking for, you need a query. Hibernate supports an easy-to-use but powerful object oriented query language (HQL). For programmatic query creation, Hibernate supports a sophisticated Criteria and Example query feature (QBC and QBE). You can also express your query in the native SQL of your database, with optional support from Hibernate for result set conversion into objects.

### 11.4.1. Ausführen von Anfragen

HQL und native SQL-Anfragen werden durch eine Instanz von `org.hibernate.Query` repräsentiert. Dieses Interface bietet Methoden zur Parameter-Bindung, Handhabung von Ergebnissätzen (sog. "result sets") und für das Ausführen der tatsächlichen Anfrage. Sie können mittels der `Session` immer eine `Query` erhalten:

    List cats = session.createQuery(

        "from Cat as cat where cat.birthdate < ?")

        .setDate(0, date)

        .list();

    List mothers = session.createQuery(

        "select mother from Cat as cat join cat.mother as mother where cat.name = ?")

        .setString(0, name)

        .list();

    List kittens = session.createQuery(

        "from Cat as cat where cat.mother = ?")

        .setEntity(0, pk)

        .list();

    Cat mother = (Cat) session.createQuery(

        "select cat.mother from Cat as cat where cat = ?")

        .setEntity(0, izi)

        .uniqueResult();]]

    Query mothersWithKittens = (Cat) session.createQuery(

        "select mother from Cat as mother left join fetch mother.kittens");

    Set uniqueMothers = new HashSet(mothersWithKittens.list());

A query is usually executed by invoking `list()`. The result of the query will be loaded completely into a collection in memory. Entity instances retrieved by a query are in a persistent state. The `uniqueResult()` method offers a shortcut if you know your query will only return a single object. Queries that make use of eager fetching of collections usually return duplicates of the root objects, but with their collections initialized. You can filter these duplicates through a `Set`.

#### 11.4.1.1. Iterationsergebnisse

Occasionally, you might be able to achieve better performance by executing the query using the `iterate()` method. This will usually be the case if you expect that the actual entity instances returned by the query will already be in the session or second-level cache. If they are not already cached, `iterate()` will be slower than `list()` and might require many database hits for a simple query, usually 1 for the initial select which only returns identifiers, and n additional selects to initialize the actual instances.

    // fetch ids

    Iterator iter = sess.createQuery("from eg.Qux q order by q.likeliness").iterate();

    while ( iter.hasNext() ) {

        Qux qux = (Qux) iter.next();  // fetch the object

        // something we couldnt express in the query

        if ( qux.calculateComplicatedAlgorithm() ) {

            // delete the current instance

            iter.remove();

            // dont need to process the rest

            break;

        }

    }

#### 11.4.1.2. Anfragen, die mit Tupeln reagieren

Hibernate queries sometimes return tuples of objects. Each tuple is returned as an array:

    Iterator kittensAndMothers = sess.createQuery(

                "select kitten, mother from Cat kitten join kitten.mother mother")

                .list()

                .iterator();

    while ( kittensAndMothers.hasNext() ) {

        Object[] tuple = (Object[]) kittensAndMothers.next();

        Cat kitten = (Cat) tuple[0];

        Cat mother = (Cat) tuple[1];

        ....

    }

#### 11.4.1.3. Skalare Ergebnisse

Queries can specify a property of a class in the `select` clause. They can even call SQL aggregate functions. Properties or aggregates are considered "scalar" results and not entities in persistent state.

    Iterator results = sess.createQuery(

            "select cat.color, min(cat.birthdate), count(cat) from Cat cat " +

            "group by cat.color")

            .list()

            .iterator();

    while ( results.hasNext() ) {

        Object[] row = (Object[]) results.next();

        Color type = (Color) row[0];

        Date oldest = (Date) row[1];

        Integer count = (Integer) row[2];

        .....

    }

#### 11.4.1.4. Bind-Parameter

Methods on `Query` are provided for binding values to named parameters or JDBC-style `?` parameters. Contrary to JDBC, Hibernate numbers parameters from zero. Named parameters are identifiers of the form `:name` in the query string. The advantages of named parameters are as follows:

- benannte Parameter sind unempfindlich im Bezug auf die Reihenfolge, in der sie im Anfragenstring erscheinen

- they can occur multiple times in the same query

- sie dokumentieren sich selbst

    //named parameter (preferred)

    Query q = sess.createQuery("from DomesticCat cat where cat.name = :name");

    q.setString("name", "Fritz");

    Iterator cats = q.iterate();

    //positional parameter

    Query q = sess.createQuery("from DomesticCat cat where cat.name = ?");

    q.setString(0, "Izi");

    Iterator cats = q.iterate();

    //named parameter list

    List names = new ArrayList();

    names.add("Izi");

    names.add("Fritz");

    Query q = sess.createQuery("from DomesticCat cat where cat.name in (:namesList)");

    q.setParameterList("namesList", names);

    List cats = q.list();

#### 11.4.1.5. Nummerierung

If you need to specify bounds upon your result set, that is, the maximum number of rows you want to retrieve and/or the first row you want to retrieve, you can use methods of the `Query` interface:

    Query q = sess.createQuery("from DomesticCat cat");

    q.setFirstResult(20);

    q.setMaxResults(10);

    List cats = q.list();

Hibernate weiß, wie die Grenzanfrage in die native SQL Ihres DBMS zu übersetzen ist.

#### 11.4.1.6. Scrollbare Iteration

If your JDBC driver supports scrollable `ResultSet`s, the `Query` interface can be used to obtain a `ScrollableResults` object that allows flexible navigation of the query results.

    Query q = sess.createQuery("select cat.name, cat from DomesticCat cat " +

                                "order by cat.name");

    ScrollableResults cats = q.scroll();

    if ( cats.first() ) {

        // find the first name on each page of an alphabetical list of cats by name

        firstNamesOfPages = new ArrayList();

        do {

            String name = cats.getString(0);

            firstNamesOfPages.add(name);

        }

        while ( cats.scroll(PAGE_SIZE) );

        // Now get the first page of cats

        pageOfCats = new ArrayList();

        cats.beforeFirst();

        int i=0;

        while( ( PAGE_SIZE > i++ ) && cats.next() ) pageOfCats.add( cats.get(1) );

    }

    cats.close()

Note that an open database connection and cursor is required for this functionality. Use `setMaxResult()`/`setFirstResult()` if you need offline pagination functionality.

#### 11.4.1.7. Externalisierung benannter Anfragen

Queries can also be configured as so called named queries using annotations or Hibernate mapping documents. `@NamedQuery` and `@NamedQueries` can be defined at the class level as seen in Beispiel 11.1, „Defining a named query using @NamedQuery“ . However their definitions are global to the session factory/entity manager factory scope. A named query is defined by its name and the actual query string.

Beispiel 11.1. Defining a named query using `@NamedQuery`

    @Entity

    @NamedQuery(name="night.moreRecentThan", query="select n from Night n where n.date >= :date")

    public class Night {

        ...

    }

    public class MyDao {

        doStuff() {

            Query q = s.getNamedQuery("night.moreRecentThan");

            q.setDate( "date", aMonthAgo );

            List results = q.list();

            ...

        }

        ...

    }

Using a mapping document can be configured using the `<query>` node. Remember to use a `CDATA` section if your query contains characters that could be interpreted as markup.

Beispiel 11.2. Defining a named query using `<query>`

    <query name="ByNameAndMaximumWeight"><![CDATA[

        from eg.DomesticCat as cat

            where cat.name = ?

            and cat.weight > ?

    ] ]></query>

Parameter binding and executing is done programatically as seen in Beispiel 11.3, „Parameter binding of a named query“.

Beispiel 11.3. Parameter binding of a named query

    Query q = sess.getNamedQuery("ByNameAndMaximumWeight");

    q.setString(0, name);

    q.setInt(1, minWeight);

    List cats = q.list();

The actual program code is independent of the query language that is used. You can also define native SQL queries in metadata, or migrate existing queries to Hibernate by placing them in mapping files.

Also note that a query declaration inside a `<hibernate-mapping>` element requires a global unique name for the query, while a query declaration inside a `<class>` element is made unique automatically by prepending the fully qualified name of the class. For example `eg.Cat.ByNameAndMaximumWeight`.

### 11.4.2. Das Filtern von Collections

A collection filter is a special type of query that can be applied to a persistent collection or array. The query string can refer to `this`, meaning the current collection element.

    Collection blackKittens = session.createFilter(

        pk.getKittens(),

        "where this.color = ?")

        .setParameter( Color.BLACK, Hibernate.custom(ColorUserType.class) )

        .list()

    );

The returned collection is considered a bag that is a copy of the given collection. The original collection is not modified. This is contrary to the implication of the name "filter", but consistent with expected behavior.

Observe that filters do not require a `from` clause, although they can have one if required. Filters are not limited to returning the collection elements themselves.

    Collection blackKittenMates = session.createFilter(

        pk.getKittens(),

        "select this.mate where this.color = eg.Color.BLACK.intValue")

        .list();

Even an empty filter query is useful, e.g. to load a subset of elements in a large collection:

    Collection tenKittens = session.createFilter(

        mother.getKittens(), "")

        .setFirstResult(0).setMaxResults(10)

        .list();

### 11.4.3. Kriterienanfragen

HQL is extremely powerful, but some developers prefer to build queries dynamically using an object-oriented API, rather than building query strings. Hibernate provides an intuitive `Criteria` query API for these cases:

    Criteria crit = session.createCriteria(Cat.class);

    crit.add( Restrictions.eq( "color", eg.Color.BLACK ) );

    crit.setMaxResults(10);

    List cats = crit.list();

The `Criteria` and the associated `Example` API are discussed in more detail in Kapitel 17, "Criteria Queries".

### 11.4.4. Anfragen in nativer SQL

You can express a query in SQL, using `createSQLQuery()` and let Hibernate manage the mapping from result sets to objects. You can at any time call `session.connection()` and use the JDBC `Connection` directly. If you choose to use the Hibernate API, you must enclose SQL aliases in braces:

    List cats = session.createSQLQuery("SELECT {cat.*} FROM CAT {cat} WHERE ROWNUM<10")

        .addEntity("cat", Cat.class)

    .list();

    List cats = session.createSQLQuery(

        "SELECT {cat}.ID AS {cat.id}, {cat}.SEX AS {cat.sex}, " +

               "{cat}.MATE AS {cat.mate}, {cat}.SUBCLASS AS {cat.class}, ... " +

        "FROM CAT {cat} WHERE ROWNUM<10")

        .addEntity("cat", Cat.class)

    .list()

SQL queries can contain named and positional parameters, just like Hibernate queries. More information about native SQL queries in Hibernate can be found in Kapitel 18, Native SQL.

## 11.5. Änderungen an persistenten Objekten vornehmen

Transactional persistent instances (i.e. objects loaded, saved, created or queried by the `Session`) can be manipulated by the application, and any changes to persistent state will be persisted when the `Session` is flushed. This is discussed later in this chapter. There is no need to call a particular method (like `update()`, which has a different purpose) to make your modifications persistent. The most straightforward way to update the state of an object is to `load()` it and then manipulate it directly while the `Session` is open:

    DomesticCat cat = (DomesticCat) sess.load( Cat.class, new Long(69) );

    cat.setName("PK");

    sess.flush();  // changes to cat are automatically detected and persisted

Sometimes this programming model is inefficient, as it requires in the same session both an SQL `SELECT` to load an object and an SQL `UPDATE` to persist its updated state. Hibernate offers an alternate approach by using detached instances.

## 11.6. Änderungen an abgesetzten Objekten

Zahlreiche Anwendungen müssen ein Objekt in einer Transaktion abrufen, dieses für Modifizierungen an die UI-Schicht schicken und die Änderungen anschließend in einer neuen Transaktion speichern. Anwendungen, die diese Herangehensweise in einer Umgebung mit hoher Nebenläufigkeit (d.h. häufigem gleichzeitigen Zugriff) benutzen, verwenden in der Regel versionierte Daten, um die Isolation der "langen" Arbeitseinheit zu gewährleisten.

Hibernate unterstützt dieses Modell, indem es mittels der `Session.update()` oder `Session.merge()`-Methoden die Möglichkeit der Wiederanbindung abgesetzter Instanzen bietet:

    // in the first session

    Cat cat = (Cat) firstSession.load(Cat.class, catId);

    Cat potentialMate = new Cat();

    firstSession.save(potentialMate);

    // in a higher layer of the application

    cat.setMate(potentialMate);

    // later, in a new session

    secondSession.update(cat);  // update cat

    secondSession.update(mate); // update mate

Wäre `Cat` mit Bezeichner `catId` bereits durch `secondSession` geladen worden, wenn die Anwendung die Wiederanbindung durchzuführen versucht hätte, so wäre eine Ausnahme gemeldet worden.

Use `update()` if you are certain that the session does not contain an already persistent instance with the same identifier. Use `merge()` if you want to merge your modifications at any time without consideration of the state of the session. In other words, `update()` is usually the first method you would call in a fresh session, ensuring that the reattachment of your detached instances is the first operation that is executed.

The application should individually `update()` detached instances that are reachable from the given detached instance only if it wants their state to be updated. This can be automated using transitive persistence. See Abschnitt 11.11, „Transitive Persistenz“ for more information.

The `lock()` method also allows an application to reassociate an object with a new session. However, the detached instance has to be unmodified.

    //just reassociate:

    sess.lock(fritz, LockMode.NONE);

    //do a version check, then reassociate:

    sess.lock(izi, LockMode.READ);

    //do a version check, using SELECT ... FOR UPDATE, then reassociate:

    sess.lock(pk, LockMode.UPGRADE);

Note that `lock()` can be used with various `LockMode`s. See the API documentation and the chapter on transaction handling for more information. Reattachment is not the only usecase for `lock()`.

Other models for long units of work are discussed in Abschnitt 13.3, „Optimistische Nebenläufigkeitskontrolle“.

## 11.7. Automatische Statuserkennung

Benutzer von Hibernate haben den Wunsch nach einer allgemeinen Methode geäußert, die entweder eine transiente Instanz durch Generierung eines neuen Bezeichners speichert oder die dem aktuellen Bezeichner zugehörigen abgesetzten Instanzen aktualisiert/erneut hinzufügt. Die `saveOrUpdate()`-Methode implementiert diese Funktionalität.

    // in the first session

    Cat cat = (Cat) firstSession.load(Cat.class, catID);

    // in a higher tier of the application

    Cat mate = new Cat();

    cat.setMate(mate);

    // later, in a new session

    secondSession.saveOrUpdate(cat);   // update existing state (cat has a non-null id)

    secondSession.saveOrUpdate(mate);  // save the new instance (mate has a null id)

Gebrauch und Semantik von `saveOrUpdate()` scheinen neue Benutzer manchmal zu überfordern. So lange Sie nicht versuchen, diese Instanzen von einer Session in einer neuen Session zu verwenden, sollten Sie `update()`, `saveOrUpdate()` oder `merge()` ohnehin nicht benutzen müssen. Manchmal kommen ganze Anwendungen ohne irgendeine dieser Methoden aus.

In der Regel kommen `update()` oder `saveOrUpdate()` in folgenden Situationen zum Einsatz:

- die Anwendung lädt ein Objekt in der ersten Session

- das Objekt wird an den UI-"Tier" weitergegeben

- am Objekt werden einige Modifikationen vorgenommen

- das Objekt wird zurück an den "Business-Logic-Tier" geleitet

- die Anwendung persistiert diese Modifikationen durch Aufruf von `update()` in einer zweiten Session

`saveOrUpdate()` tut folgendes:

- falls das Objekt in dieser Session bereits persistent ist, geschieht nichts

- falls ein anderes mit der Session assoziiertes Objekt denselben Bezeichner besitzt, wird eine Ausnahme gemeldet

- falls das Objekt keine Bezeichner-Property besitzt, speichern Sie es mittels `save()`

- falls der Bezeichner des Objekts einen ihm zugeordneten Wert am neu instantiierten Objekt besitzt, speichern Sie mittels `save()`

- if the object is versioned by a `<version>` or `<timestamp>`, and the version property value is the same value assigned to a newly instantiated object, `save()` it

- andernfalls aktualisieren Sie das Objekt mittels `update()`

und `merge()` ist völlig anders:

- falls eine persistente Instanz mit demselben Bezeichner zum gegenwärtigen Zeitpunkt mit der Session assoziiert ist, so kopieren Sie den Status des vorgegebenen Objekts in die persistente Instanz

- falls keine persistente Instanz zum gegenwärtigen Zeitpunkt mit der Session assoziiert wird, so versuchen Sie sie aus der Datenbank zu laden oder erstellen Sie eine neue persistente Instanz

- die persistente Instanz wird zurückgeschickt

- die vorgegebene Instanz wird nicht mit der Session assoziiert, sie bleibt abgesetzt

## 11.8. Das Löschen persistenter Objekte

`Session.delete()` will remove an object's state from the database. Your application, however, can still hold a reference to a deleted object. It is best to think of `delete()` as making a persistent instance, transient.

    sess.delete(cat);

You can delete objects in any order, without risk of foreign key constraint violations. It is still possible to violate a `NOT NULL` constraint on a foreign key column by deleting objects in the wrong order, e.g. if you delete the parent, but forget to delete the children.

## 11.9. Objektreplikation zwischen zwei verschiedenen Datenspeichern

It is sometimes useful to be able to take a graph of persistent instances and make them persistent in a different datastore, without regenerating identifier values.

    //retrieve a cat from one database

    Session session1 = factory1.openSession();

    Transaction tx1 = session1.beginTransaction();

    Cat cat = session1.get(Cat.class, catId);

    tx1.commit();

    session1.close();

    //reconcile with a second database

    Session session2 = factory2.openSession();

    Transaction tx2 = session2.beginTransaction();

    session2.replicate(cat, ReplicationMode.LATEST_VERSION);

    tx2.commit();

    session2.close();

The `ReplicationMode` determines how `replicate()` will deal with conflicts with existing rows in the database:

- `ReplicationMode.IGNORE`: ignores the object when there is an existing database row with the same identifier

- `ReplicationMode.OVERWRITE`: overwrites any existing database row with the same identifier

- `ReplicationMode.EXCEPTION`: throws an exception if there is an existing database row with the same identifier

- `ReplicationMode.LATEST_VERSION`: overwrites the row if its version number is earlier than the version number of the object, or ignore the object otherwise

Anwendungsfälle dieses Features beinhalten die Abstimmung von in verschiedenen Datenbankinstanzen eingegebenen Daten, das Upgrade von Systemkonfigurationsinformationen während Produkt-Upgrades, die Wiederholung von während nicht-ACID Transaktionen gemachten Änderungen und mehr.

## 11.10. Das Räumen der Session

Sometimes the `Session` will execute the SQL statements needed to synchronize the JDBC connection's state with the state of objects held in memory. This process, called flush, occurs by default at the following points:

- vor dem Ausführen einiger Anfragen

- von `org.hibernate.Transaction.commit()`

- von `Session.flush()`

The SQL statements are issued in the following order:

1. all entity insertions in the same order the corresponding objects were saved using `Session.save()`

2. alle Entity-Aktualisierungen

3. alle Collection-Löschungen

4. alle Löschungen, Aktualisierungen und Einfügungen von Elementen der Collection

5. alle Einfügungen in Collections

6. all entity deletions in the same order the corresponding objects were deleted using `Session.delete()`

An exception is that objects using `native` ID generation are inserted when they are saved.

Except when you explicitly `flush()`, there are absolutely no guarantees about when the `Session` executes the JDBC calls, only the order in which they are executed. However, Hibernate does guarantee that the `Query.list(..)` will never return stale or incorrect data.

It is possible to change the default behavior so that flush occurs less frequently. The `FlushMode` class defines three different modes: only flush at commit time when the Hibernate `Transaction` API is used, flush automatically using the explained routine, or never flush unless `flush()` is called explicitly. The last mode is useful for long running units of work, where a `Session` is kept open and disconnected for a long time (see Abschnitt 13.3.2, „Erweiterte Session und automatische Versionierung“).

    sess = sf.openSession();

    Transaction tx = sess.beginTransaction();

    sess.setFlushMode(FlushMode.COMMIT); // allow queries to return stale state

    Cat izi = (Cat) sess.load(Cat.class, id);

    izi.setName(iznizi);

    // might return stale data

    sess.find("from Cat as cat left outer join cat.kittens kitten");

    // change to izi is not flushed!

    ...

    tx.commit(); // flush occurs

    sess.close();

During flush, an exception might occur (e.g. if a DML operation violates a constraint). Since handling exceptions involves some understanding of Hibernate's transactional behavior, we discuss it in Kapitel 13, Transactions and Concurrency.

## 11.11. Transitive Persistenz

Es ist recht mühselig, einzelne Objekte zu speichern, zu löschen und erneut hinzuzufügen, insbesondere dann, wenn man es mit einem Diagramm assoziierter Objekte zu tun hat. Ein gängiger Fall ist die Beziehung zwischen übergeordneten und untergeordneten Objekten (sog. "parent/child"-Beziehung). Sehen Sie sich das folgende Beispiel an:

If the children in a parent/child relationship would be value typed (e.g. a collection of addresses or strings), their life cycle would depend on the parent and no further action would be required for convenient "cascading" of state changes. When the parent is saved, the value-typed child objects are saved and when the parent is deleted, the children will be deleted, etc. This works for operations such as the removal of a child from the collection. Since value-typed objects cannot have shared references, Hibernate will detect this and delete the child from the database.

Now consider the same scenario with parent and child objects being entities, not value-types (e.g. categories and items, or parent and child cats). Entities have their own life cycle and support shared references. Removing an entity from the collection does not mean it can be deleted), and there is by default no cascading of state from one entity to any other associated entities. Hibernate does not implement persistence by reachability by default.

Für jeden Grundvorgang der Hibernate Session - einschließlich `persist(), merge(), saveOrUpdate(), delete(), lock(), refresh(), evict(), replicate()` - gibt es eine entsprechende Art der Weitergabe. Die Arten sind dem entsprechend `create, merge, save-update, delete, lock, refresh, evict, replicate` benannt. Falls Sie möchten, dass ein Vorgang entlang einer Assoziation weitergegeben wird, so müssen Sie dass im Mapping-Dokument angeben. Zum Beispiel wie folgt aussehen:

    <one-to-one name="person" cascade="persist"/>

Die Arten der Weitergabe (sog. "Cascade Styles") können kombiniert werden:

    <one-to-one name="person" cascade="persist,delete,lock"/>

You can even use `cascade="all"` to specify that all operations should be cascaded along the association. The default `cascade="none"` specifies that no operations are to be cascaded.

In case you are using annotatons you probably have noticed the `cascade` attribute taking an array of `CascadeType` as a value. The cascade concept in JPA is very is similar to the transitive persistence and cascading of operations as described above, but with slightly different semantics and cascading types:

- `CascadeType.PERSIST`: cascades the persist (create) operation to associated entities persist() is called or if the entity is managed

- `CascadeType.MERGE`: cascades the merge operation to associated entities if merge() is called or if the entity is managed

- `CascadeType.REMOVE`: cascades the remove operation to associated entities if delete() is called

- `CascadeType.REFRESH:` cascades the refresh operation to associated entities if refresh() is called

- `CascadeType.DETACH:` cascades the detach operation to associated entities if detach() is called

- `CascadeType.ALL`: all of the above

## Anmerkung

CascadeType.ALL also covers Hibernate specific operations like save-update, lock etc...

A special cascade style, `delete-orphan`, applies only to one-to-many associations, and indicates that the `delete()` operation should be applied to any child object that is removed from the association. Using annotations there is no `CascadeType.DELETE-ORPHAN` equivalent. Instead you can use the attribute `orphanRemoval as seen in` Beispiel 11.4, „@OneToMany with orphanRemoval“. If an entity is removed from a `@OneToMany` collection or an associated entity is dereferenced from a `@OneToOne` association, this associated entity can be marked for deletion if `orphanRemoval` is set to true.

Beispiel 11.4. `@OneToMany` with `orphanRemoval`

    @Entity

    public class Customer {

       private Set<Order> orders;

       @OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)

       public Set<Order> getOrders() { return orders; }

       public void setOrders(Set<Order> orders) { this.orders = orders; }

       [...]

    }

    @Entity

    public class Order { ... }

    Customer customer = em.find(Customer.class, 1l);

    Order order = em.find(Order.class, 1l);

    customer.getOrders().remove(order); //order will be deleted by cascade

Empfehlungen:

- It does not usually make sense to enable cascade on a many-to-one or many-to-many association. In fact the `@ManyToOne` and `@ManyToMany` don't even offer a `orphanRemoval` attribute. Cascading is often useful for one-to-one and one-to-many associations.

- If the child object's lifespan is bounded by the lifespan of the parent object, make it a life cycle object by specifying `cascade="all,delete-orphan"(`@OneToMany(cascade=CascadeType.ALL, orphanRemoval=true)`)`.

- Andernfalls wird keine Weitergabe benötigt. Wenn Sie jedoch glauben, dass Sie oft mit über- und untergeordneten Objekten in derselben Transaktion arbeiten werden und Sie sich etwas Tipparbeit sparen möchten, so können Sie `cascade="persist,merge,save-update"` verwenden.

Das Mappen einer Assoziation (entweder einer einwertigen Assoziation oder einer Collection) unter Verwendung von `cascade="all"` kennzeichnet die Assoziation als zum Parent/Child-Beziehungstyp gehörig, bei dem Speichern/Aktualisieren/Löschen des übergeordneten Objekts zum Speichern/Aktualisieren/Löschen des untergeordneten Objekts (oder Objekte) führt.

Furthermore, a mere reference to a child from a persistent parent will result in save/update of the child. This metaphor is incomplete, however. A child which becomes unreferenced by its parent is not automatically deleted, except in the case of a one-to-many association mapped with `cascade="delete-orphan"`. The precise semantics of cascading operations for a parent/child relationship are as follows:

- Falls für einen "Parent" `persist()` gilt, so gilt für sämtliche "Children" ebenfalls `persist()`

- Falls für einen "Parent" `merge()` gilt, so gilt für sämtliche "Children" ebenfalls `merge()`

- Falls für einen "Parent" `save()`, `update()` oder `saveOrUpdate()` gilt, so gilt für sämtliche "Children" ebenfalls `saveOrUpdate()`

- Falls auf ein transientes oder abgesetztes "Child" durch einen persistenten "Parent" verwiesen wird, so gilt dafür `saveOrUpdate()`

- Falls ein "Parent" gelöscht wird, so gilt für alle "Children" `delete()`

- Falls der Verweis auf ein "Child" von einem persistenten "Parent" entfällt, passiert nicht besonderes - die Anwendung sollte das "Child" explizit löschen falls nötig - außer es gilt `cascade="delete-orphan"`, in welchem Fall das "verwaiste" Child gelöscht wird.

Finally, note that cascading of operations can be applied to an object graph at call time or at flush time. All operations, if enabled, are cascaded to associated entities reachable when the operation is executed. However, `save-update` and `delete-orphan` are transitive for all associated entities reachable during flush of the `Session`.

## 11.12. Die Verwendung von Metadata

Hibernate requires a rich meta-level model of all entity and value types. This model can be useful to the application itself. For example, the application might use Hibernate's metadata to implement a "smart" deep-copy algorithm that understands which objects should be copied (eg. mutable value types) and which objects that should not (e.g. immutable value types and, possibly, associated entities).

Hibernate exposes metadata via the `ClassMetadata` and `CollectionMetadata` interfaces and the `Type` hierarchy. Instances of the metadata interfaces can be obtained from the `SessionFactory`.

    Cat fritz = ......;

    ClassMetadata catMeta = sessionfactory.getClassMetadata(Cat.class);

    Object[] propertyValues = catMeta.getPropertyValues(fritz);

    String[] propertyNames = catMeta.getPropertyNames();

    Type[] propertyTypes = catMeta.getPropertyTypes();

    // get a Map of all properties which are not collections or associations

    Map namedValues = new HashMap();

    for ( int i=0; i<propertyNames.length; i++ ) {

        if ( !propertyTypes[i].isEntityType() && !propertyTypes[i].isCollectionType() ) {

            namedValues.put( propertyNames[i], propertyValues[i] );

        }

    }

Copyright © 2004 Red Hat, Inc.

- ZurückKapitel 10. Inheritance mapping
- Nach oben
- Zum Anfang
- WeiterKapitel 12. Read-only entities