Java Persistence with Hibernate 2nd phần 6 pps

86 510 1
Java Persistence with Hibernate 2nd phần 6 pps

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

406 CHAPTER 9 Working with objects First, you retrieve the object from the database with the given identifier. You mod- ify the object, and these modifications are propagated to the database during flush when tx.commit() is called. This mechanism is called automatic dirty checking—that means Hibernate tracks and saves the changes you make to an object in persistent state. As soon as you close the Session , the instance is consid- ered detached. Making a persistent object transient You can easily make a persistent object transient, removing its persistent state from the database, with the delete() method (see listing 9.5). Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Item item = (Item) session.load(Item.class, new Long(1234)); session.delete(item); tx.commit(); session.close(); Look at figure 9.7. The item object is in removed state after you call delete() ; you shouldn’t con- tinue working with it, and, in most cases, you should make sure any reference to it Listing 9.5 Making a persistent object transient using delete() Figure 9.6 Modifying a persistent instance Figure 9.7 Making a persistent object transient The Hibernate interfaces 407 in your application is removed. The SQL DELETE is executed only when the Ses- sion ’s persistence context is synchronized with the database at the end of the unit of work. After the Session is closed, the item object is considered an ordinary transient instance. The transient instance is destroyed by the garbage collector if it’s no longer referenced by any other object. Both the in-memory object instance and the persistent database row will have been removed. FAQ Do I have to load an object to delete it? Yes, an object has to be loaded into the persistence context; an instance has to be in persistent state to be removed (note that a proxy is good enough). The reason is simple: You may have Hibernate interceptors enabled, and the object must be passed through these interceptors to complete its lifecycle. If you delete rows in the database directly, the interceptor won’t run. Having said that, Hiber- nate (and Java Persistence) offer bulk operations that translate into direct SQL DELETE statements; we’ll discuss these operations in chapter 12, section 12.2, “Bulk and batch operations.” Hibernate can also roll back the identifier of any entity that has been deleted, if you enable the hibernate.use_identifier_rollback configuration option. In the previous example, Hibernate sets the database identifier property of the deleted item to null after deletion and flushing, if the option is enabled. It’s then a clean transient instance that you can reuse in a future unit of work. Replicating objects The operations on the Session we have shown you so far are all common; you need them in every Hibernate application. But Hibernate can help you with some special use cases—for example, when you need to retrieve objects from one data- base and store them in another. This is called replication of objects. Replication takes detached objects loaded in one Session and makes them persistent in another Session . These Session s are usually opened from two dif- ferent SessionFactory s that have been configured with a mapping for the same persistent class. Here is an example: Session session = sessionFactory1.openSession(); Transaction tx = session.beginTransaction(); Item item = (Item) session.get(Item.class, new Long(1234)); tx.commit(); session.close(); Session session2 = sessionFactory2.openSession(); Transaction tx2 = session2.beginTransaction(); session2.replicate(item, ReplicationMode.LATEST_VERSION); tx2.commit(); session2.close(); 408 CHAPTER 9 Working with objects The ReplicationMode controls the details of the replication procedure: ■ ReplicationMode.IGNORE —Ignores the object when there is an existing database row with the same identifier in the target database. ■ ReplicationMode.OVERWRITE —Overwrites any existing database row with the same identifier in the target database. ■ ReplicationMode.EXCEPTION —Throws an exception if there is an existing database row with the same identifier in the target database. ■ ReplicationMode.LATEST_VERSION —Overwrites the row in the target database if its version is earlier than the version of the object, or ignores the object otherwise. Requires enabled Hibernate optimistic concurrency control. You may need replication when you reconcile data entered into different data- bases, when you’re upgrading system configuration information during product upgrades (which often involves a migration to a new database instance), or when you need to roll back changes made during non- ACID transactions. You now know the persistence lifecycle and the basic operations of the persis- tence manager. Using these together with the persistent class mappings we dis- cussed in earlier chapters, you may now create your own small Hibernate application. Map some simple entity classes and components, and then store and load objects in a stand-alone application. You don’t need a web container or appli- cation server: Write a main() method, and call the Session as we discussed in the previous section. In the next sections, we cover the detached object state and the methods to reat- tach and merge detached objects between persistence contexts. This is the foun- dation knowledge you need to implement long units of work—conversations. We assume that you’re familiar with the scope of object identity as explained earlier in this chapter. 9.3.2 Working with detached objects Modifying the item after the Session is closed has no effect on its persistent rep- resentation in the database. As soon as the persistence context is closed, item becomes a detached instance. If you want to save modifications you made to a detached object, you have to either reattach or merge it. The Hibernate interfaces 409 Reattaching a modified detached instance A detached instance may be reattached to a new Session (and managed by this new persistence context) by calling update() on the detached object. In our experience, it may be easier for you to understand the following code if you rename the update() method in your mind to reattach() —however, there is a good reason it’s called updating. The update() method forces an update to the persistent state of the object in the database, always scheduling an SQL UPDATE . See listing 9.6 for an example of detached object handling. item.setDescription( ); // Loaded in previous Session Session sessionTwo = sessionFactory.openSession(); Transaction tx = sessionTwo.beginTransaction(); sessionTwo.update(item); item.setEndDate( ); tx.commit(); sessionTwo.close(); It doesn’t matter if the item object is modified before or after it’s passed to update() . The important thing here is that the call to update() is reattaching the detached instance to the new Session (and persistence context). Hibernate always treats the object as dirty and schedules an SQL UPDATE ., which will be exe- cuted during flush. You can see the same unit of work in figure 9.8. You may be surprised and probably hoped that Hibernate could know that you modified the detached item ’s description (or that Hibernate should know you did not modify anything). However, the new Session and its fresh persistence context don’t have this information. Neither does the detached object contain some inter- nal list of all the modifications you’ve made. Hibernate has to assume that an Listing 9.6 Updating a detached instance Figure 9.8 Reattaching a detached object 410 CHAPTER 9 Working with objects UDPATE in the database is needed. One way to avoid this UDPATE statement is to configure the class mapping of Item with the select-before-update="true" attribute. Hibernate then determines whether the object is dirty by executing a SELECT statement and comparing the object’s current state to the current data- base state. If you’re sure you haven’t modified the detached instance, you may prefer another method of reattachment that doesn’t always schedule an update of the database. Reattaching an unmodified detached instance A call to lock() associates the object with the Session and its persistence context without forcing an update, as shown in listing 9.7. Session sessionTwo = sessionFactory.openSession(); Transaction tx = sessionTwo.beginTransaction(); sessionTwo.lock(item, LockMode.NONE); item.setDescription( ); item.setEndDate( ); tx.commit(); sessionTwo.close(); In this case, it does matter whether changes are made before or after the object has been reattached. Changes made before the call to lock() aren’t propagated to the database, you use it only if you’re sure the detached instance hasn’t been modified. This method only guarantees that the object’s state changes from detached to persistent and that Hibernate will manage the persistent object again. Of course, any modifications you make to the object once it’s in managed persis- tent state require updating of the database. We discuss Hibernate lock modes in the next chapter. By specifying Lock- Mode.NONE here, you tell Hibernate not to perform a version check or obtain any database-level locks when reassociating the object with the Session . If you speci- fied LockMode.READ , or LockMode.UPGRADE , Hibernate would execute a SELECT statement in order to perform a version check (and to lock the row(s) in the data- base for updating). Listing 9.7 Reattaching a detached instance with lock() The Hibernate interfaces 411 Making a detached object transient Finally, you can make a detached instance transient, deleting its persistent state from the database, as in listing 9.8. Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); session.delete(item); tx.commit(); session.close(); This means you don’t have to reattach (with update() or lock() ) a detached instance to delete it from the database. In this case, the call to delete() does two things: It reattaches the object to the Session and then schedules the object for deletion, executed on tx.commit() . The state of the object after the delete() call is removed. Reattachment of detached objects is only one possible way to transport data between several Session s. You can use another option to synchronize modifica- tions to a detached instance with the database, through merging of its state. Merging the state of a detached object Merging of a detached object is an alternative approach. It can be complementary to or can replace reattachment. Merging was first introduced in Hibernate to deal with a particular case where reattachment was no longer sufficient (the old name for the merge() method in Hibernate 2.x was saveOrUpdateCopy() ). Look at the following code, which tries to reattach a detached object: item.getId(); // The database identity is "1234" item.setDescription( ); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Item item2 = (Item) session.get(Item.class, new Long(1234)); session.update(item); // Throws exception! tx.commit(); session.close(); Given is a detached item object with the database identity 1234. After modifying it, you try to reattach it to a new Session . However, before reattachment, another instance that represents the same database row has already been loaded into the Listing 9.8 Making a detached object transient using delete() 412 CHAPTER 9 Working with objects persistence context of that Session . Obviously, the reattachment through update() clashes with this already persistent instance, and a NonUniqueObjectEx- ception is thrown. The error message of the exception is A persistent instance with the same database identifier is already associated with the Session! Hibernate can’t decide which object represents the current state. You can resolve this situation by reattaching the item first; then, because the object is in persistent state, the retrieval of item2 is unnecessary. This is straight- forward in a simple piece of code such as the example, but it may be impossible to refactor in a more sophisticated application. After all, a client sent the detached object to the persistence layer to have it managed, and the client may not (and shouldn’t) be aware of the managed instances already in the persistence context. You can let Hibernate merge item and item2 automatically: item.getId() // The database identity is "1234" item.setDescription( ); Session session= sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Item item2 = (Item) session.get(Item.class, new Long(1234)); Item item3 = (Item) session.merge(item); (item == item2) // False (item == item3) // False (item2 == item3) // True return item3; tx.commit(); session.close(); Look at this unit of work in figure 9.9. C D Figure 9.9 Merging a detached instance into a persistent instance The Hibernate interfaces 413 The merge(item) call D results in several actions. First, Hibernate checks whether a persistent instance in the persistence context has the same database identifier as the detached instance you’re merging. In this case, this is true: item and item2 , which were loaded with get() C, have the same primary key value. If there is an equal persistent instance in the persistence context, Hibernate copies the state of the detached instance onto the persistent instance E. In other words, the new description that has been set on the detached item is also set on the persistent item2 . If there is no equal persistent instance in the persistence context, Hibernate loads it from the database (effectively executing the same retrieval by identifier as you did with get() ) and then merges the detached state with the retrieved object’s state. This is shown in figure 9.10. If there is no equal persistent instance in the persistence context, and a lookup in the database yields no result, a new persistent instance is created, and the state of the merged instance is copied onto the new instance. This new object is then scheduled for insertion into the database and returned by the merge() operation. An insertion also occurs if the instance you passed into merge() was a transient instance, not a detached object. The following questions are likely on your mind: ■ What exactly is copied from item to item2 ? Merging includes all value-typed properties and all additions and removals of elements to any collection. ■ What state is item in? Any detached object you merge with a persistent instance stays detached. It doesn’t change state; it’s unaffected by the merge operation. Therefore, item and the other two references aren’t the same in Hibernate’s identity scope. (The first two identity checks in the last Figure 9.10 Merging a detached instance into an implicitly loaded persistent instance 414 CHAPTER 9 Working with objects example.) However, item2 and item3 are identical references to the same persistent in-memory instance. ■ Why is item3 returned from the merge() operation? The merge() opera- tion always returns a handle to the persistent instance it has merged the state into. This is convenient for the client that called merge() , because it can now either continue working with the detached item object and merge it again when needed, or discard this reference and continue working with item3 . The difference is significant: If, before the Session completes, sub- sequent modifications are made to item2 or item3 after merging, the client is completely unaware of these modifications. The client has a handle only to the detached item object, which is now getting stale. However, if the cli- ent decides to throw away item after merging and continue with the returned item3 , it has a new handle on up-to-date state. Both item and item2 should be considered obsolete after merging. Merging of state is slightly more complex than reattachment. We consider it an essential operation you’ll likely have to use at some point if you design your appli- cation logic around detached objects. You can use this strategy as an alternative for reattachment and merge every time instead of reattaching. You can also use it to make any transient instance persistent. As you’ll see later in this chapter, this is the standardized model of Java Persistence; reattachment isn’t supported. We haven’t paid much attention so far to the persistence context and how it manages persistent objects. 9.3.3 Managing the persistence context The persistence context does many things for you: automatic dirty checking, guar- anteed scope of object identity, and so on. It’s equally important that you know some of the details of its management, and that you sometimes influence what goes on behind the scenes. Controlling the persistence context cache The persistence context is a cache of persistent objects. Every object in persistent state is known to the persistence context, and a duplicate, a snapshot of each per- sistent instance, is held in the cache. This snapshot is used internally for dirty checking, to detect any modifications you made to your persistent objects. Many Hibernate users who ignore this simple fact run into an OutOfMemory- Exception . This is typically the case when you load thousands of objects in a Ses- sion but never intend to modify them. Hibernate still has to create a snapshot of The Hibernate interfaces 415 each object in the persistence context cache and keep a reference to the managed object, which can lead to memory exhaustion. (Obviously, you should execute a bulk data operation if you modify thousands of objects—we’ll get back to this kind of unit of work in chapter 12, section 12.2, “Bulk and batch operations.”) The persistence context cache never shrinks automatically. To reduce or regain the memory consumed by the persistence context in a particular unit of work, you have to do the following: ■ Keep the size of your persistence context to the necessary minimum. Often, many persistent instances in your Session are there by accident— for example, because you needed only a few but queried for many. Make objects persistent only if you absolutely need them in this state; extremely large graphs can have a serious performance impact and require signifi- cant memory for state snapshots. Check that your queries return only objects you need. As you’ll see later in the book, you can also execute a query in Hibernate that returns objects in read-only state, without creating a persistence context snapshot. ■ You can call session.evict(object) to detach a persistent instance manu- ally from the persistence context cache. You can call session.clear() to detach all persistent instances from the persistence context. Detached objects aren’t checked for dirty state; they aren’t managed. ■ With session.setReadOnly(object, true) , you can disable dirty checking for a particular instance. The persistence context will no longer maintain the snapshot if it’s read-only. With session.setReadOnly(object, false) , you can re-enable dirty checking for an instance and force the recreation of a snapshot. Note that these operations don’t change the object’s state. At the end of a unit of work, all the modifications you made have to be synchro- nized with the database through SQL DML statements. This process is called flush- ing of the persistence context. Flushing the persistence context The Hibernate Session implements write-behind. Changes to persistent objects made in the scope of a persistence context aren’t immediately propagated to the database. This allows Hibernate to coalesce many changes into a minimal number of database requests, helping minimize the impact of network latency. Another excellent side-effect of executing DML as late as possible, toward the end of the transaction, is shorter lock durations inside the database. [...]... have to know how you’d work with detached objects in Hibernate, but we’ll refer you to earlier sections if a strategy with Java Persistence is the same as in native Hibernate First, let’s see again how entity instances become detached in a Java Persistence application JPA persistence context scope You’ve used Java Persistence in a Java SE environment, with application-managed persistence contexts and... you plan to work only with Hibernate APIs, you can skip the next section and go directly to the next chapter and read about transactions If you want to work on your objects with Java Persistence and/or EJB 3.0 components, read on 9.4 The Java Persistence API We now store and load objects with the Java Persistence API This is the API you use either in a Java SE application or with EJB 3.0 components,... Java Persistence project,” and map some classes to your database schema with annotations Write a main() method that uses an EntityManager and an EntityTransaction; we think you’ll soon see how easy it is to use Java Persistence even without EJB 3.0 managed components or an application server Let’s discuss how you work with detached entity instances The Java Persistence API 9.4.2 423 Working with detached... to the possibility to create a persistence layer with Java Persistence that runs and works without any special runtime environment You can use JPA without an application server, outside of any runtime container, in a 418 CHAPTER 9 Working with objects plain Java SE application This can be a servlet application (the web container doesn’t provide anything you’d need for persistence) or a simple main()... native Hibernate code with a Session, the persistence context begins with createEntityManager() and ends with close() Closing the persistence context isn’t the only way to detach an entity instance 424 CHAPTER 9 Working with objects Manual detachment of entity instances An entity instance becomes detached when it leaves the persistence context A method on the EntityManager allows you to clear the persistence. .. your Java SE application and experiment with the persistence context and detached objects in Java Persistence Instead of only storing and loading entity instances in a single unit of work, try to use several and try to merge modifications of detached objects Don’t forget to watch your SQL log to see what’s going on behind the scenes Once you’ve mastered basic Java Persistence operations with Java SE,... technique with automatic injection 9 .6 Summary We’ve covered a lot of ground in this chapter You now know that the basic interfaces in Java Persistence aren’t much different from those provided by Hibernate Loading and storing objects is almost the same The scope of the persistence context is slightly different, though; in Hibernate, it’s by default the same as the Session scope In Java Persistence, ... programmatically However, the Hibernate Transaction interface, as you may have guessed, also works on top of JTA We’ll show you all these options and discuss portability concerns in more detail Programmatic transaction demarcation with Java Persistence also has to work inside and outside of a Java EE application server Outside of an application server, with plain Java SE, you’re dealing with resource-local transactions;... they can’t participate in the same system transaction If you write EJBs with Java Persistence, the choice is clear: You want the EntityManager with the right persistence context injected into your managed components by the container An alternative you’ll rarely use is the lookup of a container-managed EntityManager Using Java Persistence in EJB components 9.5.2 429 Looking up an EntityManager Instead...4 16 CHAPTER 9 Working with objects For example, if a single property of an object is changed twice in the same persistence context, Hibernate needs to execute only one SQL UPDATE Another example of the usefulness of write-behind is that Hibernate is able to take advantage of the JDBC batch API when executing multiple UPDATE, INSERT, or DELETE statements The synchronization of a persistence context with . objects with Java Persistence and/or EJB 3.0 components, read on. 9.4 The Java Persistence API We now store and load objects with the Java Persistence API. This is the API you use either in a Java. instances become detached in a Java Persistence application. JPA persistence context scope You’ve used Java Persistence in a Java SE environment, with application-managed persistence contexts and. have to know how you’d work with detached objects in Hibernate, but we’ll refer you to earlier sec- tions if a strategy with Java Persistence is the same as in native Hibernate. First, let’s

Ngày đăng: 12/08/2014, 19:21

Mục lục

  • Java Persistence with Hibernate

    • Part 3 Conversational object processing

      • Chapter 9 Working with objects

        • 9.3 The Hibernate interfaces

          • 9.3.1 Storing and loading objects

            • Making a persistent object transient

            • Replicating objects

            • 9.3.2 Working with detached objects

              • Reattaching a modified detached instance

              • Reattaching an unmodified detached instance

              • Making a detached object transient

              • Merging the state of a detached object

              • 9.3.3 Managing the persistence context

                • Controlling the persistence context cache

                • Flushing the persistence context

                • 9.4 The Java Persistence API

                  • 9.4.1 Storing and loading objects

                    • Beginning a unit of work in Java SE

                    • Making an entity instance persistent

                    • Retrieving an entity instance

                    • Modifying a persistent entity instance

                    • Making a persistent entity instance transient

                    • Flushing the persistence context

                    • 9.4.2 Working with detached entity instances

                      • JPA persistence context scope

                      • Manual detachment of entity instances

                      • Merging detached entity instances

                      • 9.5 Using Java Persistence in EJB components

                        • 9.5.1 Injecting an EntityManager

                        • 9.5.2 Looking up an EntityManager

                        • 9.5.3 Accessing an EntityManagerFactory

Tài liệu cùng người dùng

Tài liệu liên quan