Java Persistence with Hibernate phần 6 pps

87 501 0
Java Persistence with Hibernate 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

402 CHAPTER 9 Working with objects 9.3.1 Storing and loading objects In a Hibernate application, you store and load objects by essentially changing their state. You do this in units of work. A single unit of work is a set of operations considered an atomic group. If you’re guessing now that this is closely related to transactions, you’re right. But, it isn’t necessarily the same thing. We have to approach this step by step; for now, consider a unit of work a particular sequence of state changes to your objects that you’d group together. First you have to begin a unit of work. Beginning a unit of work At the beginning of a unit of work, an application obtains an instance of Session from the application’s SessionFactory : Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); At this point, a new persistence context is also initialized for you, and it will man- age all the objects you work with in that Session . The application may have multi- ple SessionFactory s if it accesses several databases. How the SessionFactory is created and how you get access to it in your application code depends on your deployment environment and configuration—you should have the simple Hiber- nateUtil startup helper class ready if you followed the setup in “Handling the SessionFactory” in chapter 2, section 2.1.3. You should never create a new SessionFactory just to service a particular request. Creation of a SessionFactory is extremely expensive. On the other hand, Session creation is extremely inexpensive. The Session doesn’t even obtain a JDBC Connection until a connection is required. The second line in the previous code begins a Transaction on another Hibernate interface. All operations you execute inside a unit of work occur inside a transaction, no matter if you read or write data. However, the Hibernate API is optional, and you may begin a transaction in any way you like—we’ll explore these options in the next chapter. If you use the Hibernate Transaction API, your code works in all environments, so you’ll do this for all examples in the following sections. After opening a new Session and persistence context, you use it to load and save objects. Making an object persistent The first thing you want to do with a Session is make a new transient object per- sistent with the save() method (listing 9.2). The Hibernate interfaces 403 Item item = new Item(); item.setName("Playstation3 incl. all accessories"); item.setEndDate( ); Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Serializable itemId = session.save(item); tx.commit(); session.close(); A new transient object item is instantiated as usual B. Of course, you may also instantiate it after opening a Session ; they aren’t related yet. A new Session is opened using the SessionFactory C. You start a new transaction. A call to save() D makes the transient instance of Item persistent. It’s now associated with the current Session and its persistence context. The changes made to persistent objects have to be synchronized with the data- base at some point. This happens when you commit() the Hibernate Transaction E. We say a flush occurs (you can also call flush() manually; more about this later). To synchronize the persistence context, Hibernate obtains a JDBC connec- tion and issues a single SQL INSERT statement. Note that this isn’t always true for insertion: Hibernate guarantees that the item object has an assigned database identifier after it has been saved, so an earlier INSERT may be necessary, depend- ing on the identifier generator you have enabled in your mapping. The save() operation also returns the database identifier of the persistent instance. The Session can finally be closed F, and the persistence context ends. The reference item is now a reference to an object in detached state. You can see the same unit of work and how the object changes state in figure 9.4. It’s better (but not required) to fully initialize the Item instance before manag- ing it with a Session . The SQL INSERT statement contains the values that were Listing 9.2 Making a transient instance persistent B C D E F Figure 9.4 Making an object persistent in a unit of work 404 CHAPTER 9 Working with objects held by the object at the point when save() was called. You can modify the object after calling save() , and your changes will be propagated to the database as an (additional) SQL UPDATE . Everything between session.beginTransaction() and tx.commit() occurs in one transaction. For now, keep in mind that all database operations in transac- tion scope either completely succeed or completely fail. If one of the UPDATE or INSERT statements made during flushing on tx.commit() fails, all changes made to persistent objects in this transaction are rolled back at the database level. How- ever, Hibernate doesn’t roll back in-memory changes to persistent objects. This is reasonable because a failure of a transaction is normally nonrecoverable, and you have to discard the failed Session immediately. We’ll discuss exception handling later in the next chapter. Retrieving a persistent object The Session is also used to query the database and retrieve existing persistent objects. Hibernate is especially powerful in this area, as you’ll see later in the book. Two special methods are provided for the simplest kind of query: retrieval by identifier. The get() and load() methods are demonstrated in listing 9.3. Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Item item = (Item) session.load(Item.class, new Long(1234)); // Item item = (Item) session.get(Item.class, new Long(1234)); tx.commit(); session.close(); You can see the same unit of work in figure 9.5. The retrieved object item is in persistent state and as soon as the persistence context is closed, in detached state. Listing 9.3 Retrieval of a Item by identifier Figure 9.5 Retrieving a persistent object by identifier The Hibernate interfaces 405 The one difference between get() and load() is how they indicate that the instance could not be found. If no row with the given identifier value exists in the database, get() returns null . The load() method throws an ObjectNotFound- Exception . It’s your choice what error-handling you prefer. More important, the load() method may return a proxy, a placeholder, without hitting the database. A consequence of this is that you may get an ObjectNotFoun- dException later, as soon as you try to access the returned placeholder and force its initialization (this is also called lazy loading; we discuss load optimization in later chapters.) The load() method always tries to return a proxy, and only returns an initialized object instance if it’s already managed by the current persistence con- text. In the example shown earlier, no database hit occurs at all! The get() method on the other hand never returns a proxy, it always hits the database. You may ask why this option is useful—after all, you retrieve an object to access it. It’s common to obtain a persistent instance to assign it as a reference to another instance. For example, imagine that you need the item only for a single purpose: to set an association with a Comment : aComment.setForAuction(item) . If this is all you plan to do with the item , a proxy will do fine; there is no need to hit the database. In other words, when the Comment is saved, you need the foreign key value of an item inserted into the COMMENT table. The proxy of an Item pro- vides just that: an identifier value wrapped in a placeholder that looks like the real thing. Modifying a persistent object Any persistent object returned by get() , load() , or any entity queried is already associated with the current Session and persistence context. It can be modified, and its state is synchronized with the database (see listing 9.4). Figure 9.6 shows this unit of work and the object transitions. Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); Item item = (Item) session.get(Item.class, new Long(1234)); item.setDescription("This Playstation is as good as new!"); tx.commit(); session.close(); Listing 9.4 Modifying a persistent instance 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() [...]... 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. refers 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. annotated with @Tran- sient or the transient Java keyword). In this code example, the next synchroni- zation with the database occurs when the resource-local transaction is committed. The Java Persistence

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

            • Beginning a unit of work

            • Making an object persistent

            • Retrieving a persistent object

            • Modifying a persistent object

            • Making a persistent object transient

            • 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

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

Tài liệu liên quan