Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống
1
/ 47 trang
THÔNG TIN TÀI LIỆU
Thông tin cơ bản
Định dạng
Số trang
47
Dung lượng
317,21 KB
Nội dung
CODE EXAMPLE The code example for the Lazy Retrieval pattern is a class named Organization that is from a framework that uses the class to model real- world business organizations. public class Organization extends PhysicalEntity implements TaxExemptIDOwner, PersistableIF, TransactionCommittedListener { /** * The entity that is responsible for the accounts, items, * measurements, etc. that this organization uses. */ private ResponsibleEntity responsibleEntity; This Organization class has an attribute called responsible- Entity . Its responsibleEntity attribute is managed using the Lazy Retrieval pattern. When objects responsible for the persistence of Or- ganization objects retrieve an Organization object, they do not set its responsibleEntity attribute. Instead, they set an Organization object’s responsibleEntityId attribute to the object identifier of the object that is the value of the Organization object’s responsibleEntity attribute. /** * If responsibleEntity is null and idIsSet is true, then * use this id to fetch the ResponsibleEntity object that * will be its value. */ private long responsibleEntityId; If an attribute of an object is another object, then the natural way to indicate that the value of the attribute has not been set is for its value to be null. Because responsibleEntityId is a long, there is no natural way to indicate that the value of responsibleEntityId has not been set. One way of indicating that the value of responsibleEntityId has not been set is to reserve a value, such as −1, for that purpose. If you have to assume that every possible value of responsibleEntityId may be used, then reserving a value is not an acceptable solution. The Organization class uses a separate boolean variable called responsibleIdIsSet to indi- cate that the value of responsibleEntityId has not been set. /** * true when an ID is passed to the setResponsibleEntity * method. */ private boolean responsibleIdIsSet = false; 442 ■ CHAPTER NINE /** * Return the responsible organization that governs this * entity or null if there is none. */ public ResponsibleEntity getResponsibleEntity() { if (responsibleEntity!=null) { return responsibleEntity; } // if responsibleEntity if (responsibleIdIsSet) { } // if return responsibleEntity; } // getResponsibleEntity() } // class Organization RELATED PATTERNS isDirty. The isDirty pattern is often used with the Lazy Retrieval pattern to avoid unnecessary update operations for a complex object’s associated objects. Lazy Initialization. The Lazy Retrieval pattern is a specialized ver- sion of the Lazy initialization pattern described in Volume 2. Object Identifier. An implementation of the Lazy Retrieval pattern may use the Object Identifier pattern to identify an object that is associated with a complex object but not yet retrieved from the database. Database Patterns ■ 443 APPENDIX Persistence Framework 445 The persistence framework is in the package com.clickblocks.persistence. The classes it contains provide support for persisting objects in a reusable way. It also supports transactions. It can be extended to work with most persistent stores that support or are compatible with ACID transaction management. This version only comes with support for persistence through the JDBC API. The persistence framework relies on some classes in the com .clickblocks.util package. The following sections describe the steps to follow to implement persistence for instances of a given class. The remainder of this section is a brief description of the classes and interfaces that you will need to be aware of in the persistence framework. This is provided to give the interested reader a big picture of what the steps that follow accom- plish. If you are not interested, you can skip this section. All of the classes listed in Table A.1 can be found in the com.clickblocks .persistence package. A 446 ■ APPENDIX A TABLE A.1 Classes Found in the com.clickblocks.persistence Package PersistenceManagerFactory This class is responsible for creating an instance of a class that is responsible for creating objects that manage the persistence of other objects. This implementation always creates an instance of the same class. The class it instantiates will be specific to a single type of persistent store. The class that this release instantiates is specific to JDBC-based persistence. Future releases may relax this restriction. PersistenceManagerIF The classes that PersistenceManagerFactory instantiates must implement this interface. Classes that implement this interface have two responsibilities. Given an interface for classes responsible for persisting a kind of object a PersistenceManagerIF must provide an object that implements that interface for the appropriate persistent store. Persistence- ManagerIF objects are also responsible for pro- viding TransactionIF objects to manage multioperation transactions against the persis- tent store the object works with. PersistenceIF For each class whose instances you want to per- sist, there must be a corresponding interface. This interface should extend PersistenceIF. The name of the interface for persisting instances of a class should have the form Classname- PersisterIF. For example, if the name of the class whose instances are to be persisted is Role, then the name of the interface for classes that will be responsible for persisting Role objects should be RolePersisterIF. Such interfaces should be part of the same package as the class they are respon- sible for persisting. PersistableIF Classes should implement PersistableIF or CachableIF if their instances are to be persisted. Classes whose instances do not have a unique object ID should implement PersistableIF. CachableIF Classes should implement PersistableIF or CachableIF if their instances are to be persisted. Classes whose instances do have a unique object ID should implement CachableIF. The name of this class comes from the principle that if a per- sisted object has a unique object ID, then it should be represented in a JVM by at most one object. That is achieved by caching CachableIF objects. Define the Persister Interface The first step in extending the persistence framework to persist instances of a particular class is to define a public interface that extends PersistenceIF. The purpose of this interface is to declare the methods that all classes responsible for persisting the class in question must implement. This interface should be in the same package as the class to be persisted. The name of the interface for persisting instances of a class should have the form ClassnamePersisterIF. For example, if the name of the class APPENDIX A ■ 447 JDBCPersister Classes that implement the PersistableIF or CachableIF interface by persisting objects through the JDBC API must extend this abstract class. TransactionIF TransactionIF objects are responsible for encap- sulating transactions on behalf of an underlying persistent store. PersistenceManagerIF objects are responsible for creating TransactionIF objects. Methods of PersistableIF interfaces that perform operations in the context of a transac- tion take an argument of this type. TransactionIF classes have methods for committing or aborting the transaction that they encapsulate. TransactionCommittedListener Some objects are complex in the sense that TransactionCommittedEvent fully representing them in a persistent store requires the persisting of multiple related objects. To avoid wasted effort when updating the persisted form of an object, classes of com- plex objects will generally have one or more dirty attributes to determine if all or some parts of a complex object may not match the contents of the persistent store. When such an object is involved in an insert or update operation that is part of a transaction, its dirty flags should not be cleared until the transaction is committed. For that reason, classes of complex objects should implement the TransactionCommittedListener interface. Objects that implement the TransactionCommittedListener interface can be registered with a TransactionIF object by the code that implements create and update opera- tions. When a TransactionIF object commits its underlying transaction, it sends a Transaction- CommittedEvent to all objects that have regis- tered to receive the event. When an object receives a TransactionCommittedEvent, it should clear all of its dirty flags. whose instances are to be persisted is Role, then the name of the interface for classes that will be responsible for persisting Role objects should be RolePersisterIF. The methods defined in the interface will typically have the names create, retrieve, update, and delete, with varying signatures. More detailed descriptions of these methods follow. create The purpose of methods named create is to create a persisted version of an object. All create methods should take at least one parameter which is the object to be persisted. Most persister interfaces will have a create method that takes one argument which is the object to be persisted. /** * Persist the given item description. * * @param theItem * The ItemDescription object to be persisted. * @exception DuplicateException * If an ItemDescription with the same object * ID as the given ItemDescription has already * been persisted. * @exception CBSystemException * If there is a problem that this method * cannot otherwise classify, it wraps the * exception in a CBSystemException. * @exception BusinessRuleException * If the operation violates a constraint in the * database that appears to reflect a business * rule. */ public void create(ItemDescription theItem) throws DuplicateException, CBSystemException, BusinessRuleException ; retrieve The purpose of methods named retrieve is to retrieve persisted objects and create in-memory Java versions of the persisted objects. The return type of retrieve methods should either be the class of the objects it will retrieve or Iterator. There is no specific pattern for the parameters of retrieve methods. If the return type of a retrieve method is the class of the objects it retrieves, 448 ■ APPENDIX A then the method’s parameters should be sufficient to select a single object. If the objects in question have a unique object identifier, the interface should include a retrieve method that takes an object identifier as a parameter. If the objects being retrieved may be retrieved for the purpose of updating their contents, then the retrieve method should have a boolean parameter. This parameter is typically called forUpdate. If forUpdate is true, then the transaction that the retrieve operation is associated with should get a lock on the object in anticipation of its being updated or deleted. Transactions are discussed later on. Here is an example of the sort of retrieve method we have been dis- cussing: /** * Return the persisted version of the ItemDescription * object with the given id. * * @param itemId * The persisted ItemDescription object. * @param forUpdate * If this is true, then the persisted * itemDescription information is write locked in * the expectation that it will be updated by a * future operation with the same transaction. * @exception NotFoundException * If there is no persisted ItemDescription * object with the given id. * @exception CBSystemException * If there is a problem that this method cannot * otherwise classify, it wraps the exception in a * CBSystemException. */ public ItemDescription retrieve(long itemId , boolean forUpdate) throws CBSystemException, NotFoundException; If the return type of a retrieve method is Iterator, then the method’s parameters need only be sufficient to identify the set of objects to return through the iterator. /** * Return an Iterator over all the persisted * Organization objects. * * @exception CBSystemException * If there is a problem that this method * cannot otherwise classify, it wraps the * exception in a CBSystemException. */ public Iterator retrieve() throws CBSystemException; APPENDIX A ■ 449 update The purpose of the update method is to make the state of the persisted version of an object match the state of a given in-memory version of the same object. update methods generally return no result and take one parameter, which is the in-memory object whose contents will be used to update the corresponding persisted object. /** * Update the persisted version of the given * ItemDescription. * @param item * The ItemDescription object to be updated. * @exception StaleObjectException * if the ItemDescription object is stale. For * any given object id, the retrieve method or * any iterators that it may create normally * return the same object for any given object * ID. However when an object is retrieved for * update, the object returned may be a * different object than returned previously. * When that happens, objects previously * returned with the same object id are * considered stale. * @exception NotFoundException * If there is no persisted ItemDescription * object with the same object ID as the given * object. * @exception CBSystemException * If there is a problem that this method * cannot otherwise classify, it wraps the * exception in a CBSystemException. */ public void update(ItemDescription org) throws NotFoundException, CBSystemException, BusinessRuleException, StaleObjectException; delete The purpose of the delete method is to delete the persisted version of an object from the persistent store. delete methods generally return no result and take one parameter which is an in-memory object that corre- sponds to the persisted object to be deleted. /** * Delete the persited form of the given * ItemDescription object. * * @param theItem 450 ■ APPENDIX A TEAMFLY Team-Fly ® * The ItemDescription object to be deleted. * @exception BusinessRuleException * If the given ItemDescription object cannot * be deleted because it is referenced by * another object. * @exception CBSystemException * If there is a problem that this method * cannot otherwise classify, it wraps the * exception in a CBSystemException. */ public void delete(ItemDescription theItem) throws BusinessRuleException, CBSystemException; Modify the Class to Cooperate with the Framework To persist an object’s entire state, it may be necessary to access a portion of the object’s state information that is not public. To facilitate that, you should add package private methods to the object’s class that will allow persister classes to get and set the object’s nonpublic state. Classes that are to be persisted should implement either the PersistableIF interface or the CachableIF interface. Classes that do not have a unique object identifier for their instances should implement the PersistableIF interface. The PersistableIF interface declares one method called getPersistence- Interface. The purpose of this method is so that, given an arbitrary object that implements the PersistableIF interface, you can determine the inter- face you will need to use to persist it. public interface PersistableIF { /** * Return a class object that represents the sub-interface * of PersistenceIF that should be used to manage the * persistence of this class. */ public Class getPersistenceInterface(); } // interface PersistableIF A typical implementation of getPersistenceInterface looks like this; /** * Return a class object that represents the * sub-interface of PersistenceIF that should be used to * manage the persistence of this class. */ public Class getPersistenceInterface(){ return ItemDescriptionPersisterIF.class; } // getPersistenceInterface() APPENDIX A ■ 451 [...]... if String name = rs.getString(2); String cuisine = rs.getString (3) ; int rating = rs.getInt(6); Integer starRating = (rating>=1 ? new Integer(rating): null); boolean deliveryAvailable = "T".equals(rs.getString(7)); // Create the Restaurant object 462 ■ A PPENDIX A thisRestaurant = new Restaurant(id); thisRestaurant.setCuisine(cuisine); thisRestaurant.setMinPrice(dollarAmount(minAmt));... versions.getMatching(myInterval); ResponsibleEntity myResponsibleEntity = myVersion.getResponsibleEntity(); String responsibleEntityString = myResponsibleEntity.toString(); Date startDate = myInterval.getStartDate(); String startDateString; startDateString = JDBCUtil.format(startDate); Date endDate = myInterval.getEndDate(); String endDateString; endDateString = JDBCUtil.format(endDate); String nameString = JDBCUtil.format(myVersion.getName());... thisRestaurant.setStarRating(starRating); putInCache(thisRestaurant); return thisRestaurant; } catch (SQLException e) { String msg = "Error occurred retrieving restaurant data"; throw new CBInternalException(toString(), msg, e); } // try } // instantiate(ResultSet) This instantiate method contains the logic for looking for the object in the cache and adding any object it instantiates to the cache An instantiate... (described in vol 1), 406 and Persistence Layer pattern, 406 ACID properties, 39 –50, 58–62 ACID transaction, 34 5 ACID Transaction pattern, 34 , 37 – 53, 63, 74, 79 and Composite Transaction pattern, 63 and Lock File pattern, 290 and Optimistic Concurrency pattern, 30 2 and Static Locking Order pattern, 295 Active object, 26 Actor, 31 Adapter pattern (described in vol 1), 63 and Composite pattern, 63 Additional... JDBCUtil.format(myVersion.getName()); String description = myVersion.getTextualDescription(); String descriptionString = JDBCUtil.format(description); String unit = myVersion.getMeasurementUnit().getName(); String unitString = JDBCUtil.format(unit); String dimension = myVersion.getMeasurementDimension() getName(); String dimensionString; dimensionString = JDBCUtil.format(dimension); String inventoryFlagString = JDBCUtil.format(myVersion.isInventory());... ftp://ftp.irmc.com/irmc.com/papers/configurable_prototype .pdf [Yoder98] Joseph W Yoder, Ralph E Johnson, Quince D Wilson “Connecting Business Objects to Relational Database” http://jerry.cs.uiuc.edu/~plop/plop98/final_submissions/P51 .pdf [Yoder-Barcalow98] Joseph W Yoder, Jeffery Barcalow “Architectural Patterns for Enabling Application Security” http://www.joeyoder.com/papers /patterns/ Security/appsec .pdf I N D E X A Abort, 35 Abstract Factory... null; String query = null; try { myStatement = myConnection.createStatement(); long restaurantID = restaurant.getId(); String cuisineString = JDBCUtil.format(restaurant.getCuisine()); String govID = restaurant.getGovtIdentifier(); String minPrice = format(restaurant.getMinPrice()); String maxPrice = format (restaurant.getMaxPrice()); query = "UPDATE lfx_restaurant_tb" A PPENDIX A ■ 465 + " SET cuisine="... JDBCUtil.format(myVersion.isInventory()); query = "INSERT INTO itm_item_version_tb" + "(item_id," + "responsible_organization_id," + "effective_begin," + "effective_end, item_name," + "item_description," ■ 455 456 ■ A PPENDIX A + "measurement_unit," + "dimension_name, inventory_item)" + "VALUES ("+ itemId + "," + responsibleEntityString+"," + startDateString + "," + endDateString + "," + nameString + "," + descriptionString... of Patterns John Wiley & Sons, Baffins Lane, Chichester, West Sussex, England, 1996 [CEF98] Andy Carlson, Sharon Estepp, Martin Fowler “Temporal Patterns Paper presented at PLOP’98 http://jerry.cs.uiuc.edu/~plop/plop98/final_submissions/P09 .pdf [Date94] Chris J Date An Introduction to Database Systems AddisonWesley, Reading, MA, 1994 [Doeringer90] Willibald Doeringer, Doug Dykeman, Matthias Kaiserswerth,... Reading, MA, 1995 475 476 ■ B IBLIOGRAPHY [Gray-Reuter 93] Jim Gray, Andreas Reuter Transaction Processing: Concepts and Techniques Morgan Kaufman Publishers, San Mateo, California, 19 93 [Heaney99] Mathew Heaney “Static Locking Order”, http://www.acm.org/archives/wa.cgi?A2=ind9904&L =patterns& F= &S=&P=878 [Keller98] Wolfgang Keller, Jens Coldewey: Accessing Relational Databases: A Pattern Language, in . thisRestaurant; } // if String name = rs.getString(2); String cuisine = rs.getString (3) ; int rating = rs.getInt(6); Integer starRating = (rating>=1 ? new Integer(rating): null); boolean deliveryAvailable =. myVersion.getMeasurementDimension() .getName(); String dimensionString; dimensionString = JDBCUtil.format(dimension); String inventoryFlagString = JDBCUtil.format(myVersion.isInventory()); query = "INSERT INTO itm_item_version_tb" +. myVersion.getResponsibleEntity(); String responsibleEntityString = myResponsibleEntity.toString(); Date startDate = myInterval.getStartDate(); String startDateString; startDateString = JDBCUtil.format(startDate); Date