1. Trang chủ
  2. » Công Nghệ Thông Tin

Patterns in JavaTM, Volume 3 Java Enterprise Java Enterprise Design Patterns phần 9 docx

50 206 0

Đ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

Thông tin cơ bản

Định dạng
Số trang 50
Dung lượng 305,5 KB

Nội dung

FIGURE 9.2 Persistence Layer pattern. 392 BusinessClass1 BusinessClass2 «interface» PersistableIF persistenceLayer «interface» BusinessClass1PersisterIF persists «interface» BusinessClass2PersisterIF persists BusinessClass1PersisterImpl BusinessClass2PersisterImpl TransactionManagerImpl PersistenceManager «interface» TransactionManagerIF commit( ) abort( ) Creates-and- manages- reuse-of Creates-and- manages- reuse-of Creates-and- manages- reuse-of Coordinates Coordinates Coordinates ▲ ▲ ▲ ▲ ▲ ▲ this one-to-one relationship. Alternatively, there may be one or a small number of classes in the role that implement multiple inter- faces and so are responsible for managing the persistence of instances of multiple classes. You usually see this organization when the code that implements the persistence logic is generated at runtime by a code generator. Using a code generator to gener- ate the persistence code can be a big savings in programmer time. However, automatically generated code tends not to be as well optimized as code written by a skilled programmer. TransactionManagerIF. Interfaces in this role declare methods that are used to commit or abort transactions. TransactionManagerImpl. Classes in this role implement the TransactionManagerIF interface. PersistenceManager. Classes in this role define methods that are responsible for creating and returning objects that implement the BusinessClass1PersisterImpl, BusinessClass2Persister- Impl, , and TransactionManagerIF interfaces. If more than one type of persistent storage is supported, then the classes’ methods are also responsible for ensuring that the objects they return are appropriate for the type of persistent storage being used. CONSEQUENCES ⁄ If the underlying technology used to persist an application changes, then only the persistence layer needs to change. For example, an application that is initially developed to use a relational database may be changed to use an object-oriented database or a data cube. ⁄ The underlying persistence schema can be changed without modify- ing any part of an application outside of its persistence layer. ⁄ The logical complexities of working with the underlying database are hidden by the persistence layer from the rest of the application. For example, most databases require a different call or command to store a new object than to update the contents of an existing object. Ÿ Some operations that are easy to do in SQL, OQL, or another query language may be difficult to do through a persistence layer. For exam- ple, determining the number of customers that live in a particular zip code may be a simple query in SQL. However, working through a per- sistence layer that does not have a method specifically for that query generally involves writing a loop with procedural code to get each customer’s zip code, and if it matches the zip code in question, incre- ment a count. Database Patterns ■ 393 Ÿ If a persistence layer is not carefully tuned for a specific application, it will generally be difficult or impossible to optimize access to the application’s persistent store at the application level. IMPLEMENTATION Transactions The Persistence Layer pattern is usually implemented on top of one or more databases or external persistent storage managers. Each of these databases will have a unique set of strategies for managing locks on behalf of transactions. Two transactions may run concurrently on top of one data- base. The same two transactions may be forced to run serially or even deadlock when run on top of another database. For example, suppose that you have two transactions that are run- ning on top of a relational database manager. One transaction fetches rows from a table. The other transaction updates individual rows in the same table. Under one database manager, this works perfectly well, because the database manager is sophisticated enough to do row-level locking and a shadow write* to the row being updated. A less sophisticated database manager sees that the fetch of the rows will involve most of the rows in the table and so it tries to be efficient by doing a tablelock instead of a rowlock. The transaction to update a row is now unable to get its rowlock, because the whole table is locked and the database manager does not do shadow writes. Because these are being used in a loop where the fetched rows are being used to drive the updates, the loop hangs since the first update waits forever to get its lock. † For this reason, to remain as independent of the underlying storage manager as possible, the application should organize all related actions into the same transaction. The persistence layer cannot enforce such an organization, but it can be designed to facilitate it: • The persistence layer should be designed to allow any sequence of oper- ations on the persistent store to be included in the same transaction. • The persistence layer should be designed so that every operation in the persistent store is part of an explicit transaction. If an operation 394 ■ CHAPTER NINE * A shadow write is a write that is visible only to the transaction that wrote it and to subse- quent transactions. † This actually happened to the author with two different database managers. The names of the database managers are not stated because this behavior is not unique to these database managers. is not part of an explicit transaction, database managers and the like will treat it as being part of its own implicit transaction. • To ensure consistent behavior across different persistent stores, the persistence layer should either prohibit nested transactions* or simu- late the feature when it runs over a persistent storage manager that does not support it. The first item, allowing any sequence of operations on the persistent store to be included in the same transaction, is generally just a matter of avoiding anything in the design that prevents it. The second item is almost as simple. To ensure that every operation is part of an explicit transaction simply requires a way of putting operations in the context of an explicit transaction and the appropriate checks being made to ensure that each operation is in the context of a transaction. The third item is complicated. The simplest way to resolve it is to prohibit nested transactions. The reason to resolve the issue this way is that some popular database engines, such as Oracle, do not support nested transactions and simulating nested transactions at the persistence layer is complicated. However, support for nested transactions has the benefit of increasing reuse of code, which also results in less maintenance effort. If nested transactions are supported, method A can call method B without having to be concerned whether method B will perform its own transac- tion. If a persistence layer does not support nested transactions, then spe- cial arrangements will have to be made for method B to be aware of its caller’s transaction and to use it. Clearly, support for nested transactions is desirable. The problem is that some persistent stores do not support nested transactions. In some cases, it may be possible for a persistence layer to simulate support for nested transactions. However, with some database managers, it is impossi- ble to run an application that relies on nested transactions. Complex Objects Retrieving a complex object may involve retrieving a number of related objects. If the related objects are not always used, defer loading them by using the Lazy Retrieval pattern. This means that the methods of the com- plex object’s class need to assume that links to the appropriate related Database Patterns ■ 395 * If transactions are allowed to nest, that means that within the same thread, a shorter trans- action can begin and end while a longer transaction is pending and the following will be true: If the shorter transaction is committed, it only commits changes that occur after the shorter transaction started. If the longer transaction is aborted, the changes made during the shorter transactions are undone, even if the short transaction was committed. objects may be null and to call the persistence layer to retrieve those objects if they are needed. Caching For performance reasons, it is often advantageous for classes in the BusinessClassPersisterImpl role to cache objects they retrieve from the database. Using a cache benefits performance in two ways. • Caching saves time. If a requested object is in the cache, there is no need to spend the time it takes to retrieve the object from the data- base. • Caching may save memory. Some complex objects may share the same related objects. Using a cache may allow them to share the same copy of the object by avoiding a situation where a different copy of the related object is loaded for each complex object that refers to it. The technique of object caching is discussed in detail in the discus- sion of the Cache Management pattern in Volume 1. There are some constraints on the use of caching in a persistence layer. The problem is that objects in the cache cease to be identical to the objects in the database if another database client updates those objects. Many database engines do not have a way for the database engine to notify its clients in real time when an object is modified. Even if a database engine does allow real-time notifications, if the database has many clients, notifying all of them may introduce an unacceptable performance prob- lem. This difficulty does not prevent the use of caching in all cases. There are two situations in which caching is a useful optimization. Caching works well for objects that are not expected to always be up- to-date. For example, objects that summarize real-time data, such as a business’s gross sales for the current day, may be satisfactory if they are guaranteed not to be more than ten minutes behind reality. Some objects have no specific requirement for being up-to-date. For example, in an air- line reservation system, there is no expectation that just because a particu- lar seat appears to be available it will actually be available when someone tries to assign the seat to a passenger. Management of cached objects that may not be up-to-date is described in more detail by the Cache Consistency pattern. The other situation in which caching is a good optimization is when you can be sure that the state of a persisted object will never change while a copy of it is in a cache. There are two common cases of this. One case is if the object in question will never change. An example of this is an object that describes an event that happened in the past. The other case is when 396 ■ CHAPTER NINE you have a lock on a persisted object. This will generally be the case while you are updating its contents. To update a persisted object, you will tell a persistence layer to retrieve the object for update. The persistence layer should then ensure that you have a lock on the object at least until you update it or the current transaction ends. While there is a lock on a persisted object retrieved for update, it is safe to cache the object. Caching the object in this circumstance is gener- ally not an effective optimization technique, since applications will gener- ally not retrieve the object again while they have a lock on it. However, it does allow the persistence layer to detect a relatively common sort of bug. Sometimes an application will try to update the contents of an object using an old version of the object. This can result in some of the object’s contents unintentionally reverting to old values. This is discussed in more detail in the Stale Object pattern. Serialization If there will be no need to do ad hoc queries on a kind of object, it may be possible to simplify the details of its persistence by using serialization. Single Instances There is generally no need to have more than one instance of the PersistenceManager class or each BusinessClass1PersisterImpl class. Having only one instance of each BusinessClass1PersisterImpl class makes updates easier by simplifying the implementation of the Stale Object pattern. Managing classes so that they have only one instance is done using the Singleton pattern, described in Volume 1. KNOWN USES The author has seen many applications that are designed with a persis- tence layer. There are also a number of commercial tools, such as CoCoBase, to help create one. In addition, entity beans, a form of Enterprise JavaBean, provide a limited implementation of the Persistence Layer pattern. The Enterprise JavaBean specification* allows entity beans to have container managed per- sistence, which relieves the programmer of the burden of manually gener- ating persistence code. However, this mechanism only works well for Database Patterns ■ 397 * At the time of this writing, the current version of the Enterprise JavaBean specification is 1.1. mapping rows of a table into an object. It is not very helpful for managing the persistence of complex objects that may be stored in multiple tables, such as a customer object that may have multiple addresses, phone num- bers, purchase history, and demographic information associated with it. It is particularly inappropriate for complex objects that have a one-to-many relationship with some of their associated objects. Object-oriented databases such as GemStone provide a persistence layer. DESIGN EXAMPLE The design example for the Persistence Layer pattern is a persistence framework that is part of a larger open source framework called ClickBlocks.* This framework comes with classes that support object per- sistence in relational databases through the JDBC API. Because this exam- ple is relatively complex, the class diagrams showing its organization are split into multiple figures. To help in understanding the persistence frame- work, Appendix A contains an introduction to the use of the persistence framework. The source code is on the CD distributed with this book. Figure 9.3 shows some interfaces that are used by the persistence framework. The rest the persistence framework is shown in Figure 9.4. Here are descriptions of the interfaces shown in Figure 9.3: PersistableIF. Every class whose instances are to be persisted must implement this interface. The interface is a convenient way for the persistence package to declare references to 398 ■ CHAPTER NINE FIGURE 9.3 Interfaces. * The current version of the persistence package should be available at www.clickblocks.org as the package org.clickblocks.persistence. «interface» CachableIF getIdObject:Object «interface» PersistableIF «interface» PersistenceIF objects it persists. The interface also defines a method named getPersistenceInterface that is useful to development environments and tools that are aware of the persistence framework. The getPersistenceInterface method returns a Class object that encapsulates an interface. The interface it encapsu- lates is the interface responsible for managing the persistence of instances of the class that implements the PersistableIF inter- face. For example, consider a class named Foo that implements the PersistableIF interface. If the Foo class’s implementation of the getPersistenceInterface method returns a Class object that encapsulates an interface named FooPersisterIF, then any class responsible for managing the persistence of Foo objects must implement the FooPersisterIF interface. CachableIF. This interface extends the PersistableIF interface. This interface must be implemented by classes whose instances the persistence layer will be expected to cache. The CachableIF interface defines a method named getIdObject, which is expected to return the object’s unique object ID encapsulated in an object. The object it returns is used as a key in a HashMap, so the object’s class must have implementa- tions of the hashCode and equals methods that reflect the value of the object ID it encapsulates. The motivations for this are dis- cussed under the “Implementation” heading of the CRUD pattern. PersistenceIF. Every class that is responsible for persisting objects must implement this interface. Figure 9.4 shows the static organization of most of the rest of the per- sistence package. The complete persistence framework is on the CD that accompanies this book. Here are descriptions of the classes and interfaces that appear in Figure 9.4: PersistenceManagerFactory. Instances of classes in the PersistenceManager role are responsible for creating objects responsible for managing the persistence of other objects. In this example, all such classes must implement the PersistencemanagerIF interface. Each concrete class that implements the PersistencemanagerIF interface creates PersistenceManager objects that manage the persistence of objects using one particular kind of database. The PersistenceManagerFactory class allows the persistence framework to support multiple types of databases. The PersistenceManagerFactory class is responsible for Database Patterns ■ 399 FIGURE 9.4 ClickBlocks persistence package. 400 Persister #putInCache:void #getFromCache:CachableIF #removeFromCache:void #checkStale «interface» PersistenceManagerIF +registerInterface(interface:Class, class:Class):void +getPersister(interface:Class):PersistenceIF +getNewTransaction:TransactionIF +getCurrentTransaction:TransactionIF +execute(:Runnable) +execute(:Runnable, :TransactionIF) PersistenceManagerFactory +getInstance:PersistenceManagerFactory( ) +getPersistenceManager:PersistenceManagerIF( ) +registerInitializer(:PersistenceInitializerIF):void Creates-objects-that-implement 1 1 AbstractPersistenceManager JDBCPersistenceManager getConnection( ):Connection JDBCPersister #getManager:JDBCPersisterManager #createException #updateException #deleteException «interface» TransactionIF +commit( ):void +abort( ):void +isDone( ):boolean +addTransactionCommittedListener(:TransactionIF):void +removeTransactionCommittedListener(:TransactionIF):void Uses Gets-connection-from JDBCTransaction +getConnection( ):Connection +commit( ):void +abort( ):void +isDone( ):boolean +addTransactionCommittedListener(:TransactionIF):void +removeTransactionCommittedListener(:TransactionIF):void 1 1 * 1 ▲ ▲ ▲ TEAMFLY Team-Fly ® Database Patterns ■ 401 creating instances of a class that implements the PersistencemanagerIF interface and supports the type of database being used. Here are descriptions of the PersistenceManagerFactory class’s methods: getInstance. This method is static and returns the single instance of the PersistenceManagerFactory class. getPersistenceManager. This method returns the PersistenceManagerIF object that will be responsible for creating objects that know how to persist objects to the desired type of persistent store. registerInitializer. This persistence package does not contain any classes that know how to persist a specific business class. The application that uses this persistence package is expected to provide those classes. The application is also expected to register those classes with the persistence package. The application arranges to register its classes to per- sist business objects by passing a PersistenceInitial- izerIF object to this method before its first call to the getPersistenceManager method. During the first call to the getPersistenceManager method, it passes the freshly created PersistenceManagerIF object to the initialize method of every PersistenceInitializerIF object that was passed to this method. Those initialize methods are expect to register classes to persist business objects at that time by calling the PersistenceManagerIF object’s registerInterface method. PersistenceManagerIF. Classes in the PersistenceManager role must implement the PersistenceManagerIF interface. The PersistenceManagerIF interface defines methods to get/create objects to persist business objects and to support transaction management. Classes that implement this interface are usually specific to one kind of database. Here are descriptions of its methods. getPersister. This method returns a PersisterIF object that implements a given subinterface of the PersisterIF inter- face. The argument should be a Class object that encapsu- lates the interface responsible for the persistence of a particular class of object. The object this method returns is an instance of a class that knows how to persist objects to the database manager being used. [...]... buf.append(dt.getDate()).append('\''); return buf.toString(); } // format(Date) /** * Format a String object by enclosing it in single quotes and * doubling any internal single quotes */ public static String format(String s) { if (s==null) { return " NULL "; } // if int slength = s.length(); // Size the new StringBuffer for external single quotes // and two doubled internal single quotes StringBuffer buf = new StringBuffer(slength+4);... the EngineSpecificBusinessClassPersisterImpl classes for the persistence engine should make use of those methods • • 416 ■ C HAPTER N INE The way that an EngineSpecificBusinessClassPersisterImp1 object makes use of an EngineSpecificTransaction object’s checkStale and putInCacheForUpdate methods is shown in Figure 9. 9 Here are descriptions of the interactions shown in Figure 9. 9: Call the EngineSpecificBusinessClassPersisterImp1... current the objects in a cache are Singleton The Persistence Layer pattern uses the Singleton pattern (described in Volume 1) to manage instances of classes Marker Interface The Persistence Layer pattern uses the Marker Interface pattern, described in Volume 1, to recognize instances of classes that it may persist Database Patterns ■ 407 CRUD This pattern was previously described in [Yoder98] SYNOPSIS Organize... public Object getIdObject() ; } // interface CachableIF RELATED PATTERNS Persistence Layer The Stale Object design pattern is used in designing the implementation of the Persistence Layer pattern CRUD The Stale Object pattern is used with the CRUD pattern in designing classes responsible for persisting specific kinds of objects for an application Database Patterns ■ 4 23 Type Conversion This pattern is... put in the EngineSpecificTransaction object’s cache by a call to the putInCacheForUpdate method EngineSpecificBusinessClassPersisterImpl Classes in this role implement methods to create, retrieve, update, and delete instances of a particular class in a database Classes in this role are specific to a particular persistence engine If the EngineSpecificTransaction class for a persistence engine has putInCacheForUpdate... transactions from simple operations in a straightforward way ⁄ Organizing operations to persist a class of objects into a single interface results in a highly cohesive design Ÿ Managing the persistence of objects using only simple operations may place a greater burden on the programmer writing complex transactions than using more complex and specialized operations would Ÿ Composing complex persistence operations... date in ANSI format */ public static String format(Date dt) { if (dt==null) { return "NULL"; } // if long time = dt.getTime(); if (time==Long.MIN_VALUE) { return "DATE'1000-1-1'"; Database Patterns } // if MIN_VALUE if (time==Long.MAX_VALUE) { return "DATE '99 99- 12 -31 '"; } // if StringBuffer buf = new StringBuffer(16); buf.append("DATE'").append(dt.getYear()+ 190 0); buf.append('-').append(dt.getMonth()+1).append('-');... object is in the cache, then the checkStale method just returns 1 1: b := retrieve(1 234 , true) 2: update(b) b:BusinessClass :EngineSpecificBusinessClassPersisterImpl 1.1: tx := getCurrentTransaction( ) 2.1: tx := getCurrentTransaction( ) 1.2: putInCacheForUpdate(b) 2.2 checkStale( ) :PersisterManager FIGURE 9. 9 Interactions for stale object checking tx: EngineSpecificTransaction Database Patterns ■... changes that were made to an in- memory object after it was retrieved for update The Snapshot pattern described in Volume 1 describes how to do this by saving the necessary information If you are using this technique, it may be convenient for the update cache to contain the information needed to restore an object’s internal state in addition to the object itself 418 ■ C HAPTER N INE KNOWN USES The ClickBlocks... developed proprietary applications DESIGN EXAMPLE Given a class named Organization that is responsible for representing organizations, the design for an interface to persist instances of Organization might look like the one shown in Figure 9. 7 RELATED PATTERNS Object Identifier Implementations of CRUD interfaces use the Object Identifier pattern to identify objects in memory and in databases Persistence Layer . FIGURE 9. 2 Persistence Layer pattern. 39 2 BusinessClass1 BusinessClass2 «interface» PersistableIF persistenceLayer «interface» BusinessClass1PersisterIF persists «interface» BusinessClass2PersisterIF persists BusinessClass1PersisterImpl BusinessClass2PersisterImpl TransactionManagerImpl PersistenceManager «interface» TransactionManagerIF commit(. is shown in Figure 9. 4. Here are descriptions of the interfaces shown in Figure 9 .3: PersistableIF. Every class whose instances are to be persisted must implement this interface. The interface. opera- tions in a straightforward way. ⁄ Organizing operations to persist a class of objects into a single inter- face results in a highly cohesive design. Ÿ Managing the persistence of objects using

Ngày đăng: 14/08/2014, 02:20