DESIGNING A PERSISTENCE FRAMEWORK WITH PATTERNS The Problem
DESIGNING A PERSISTENCE FRAMEWORK WITH PATTERNS
The Problem: Persistent Objects persistent object l An object that can survive the process or thread that created it. A persistent object exists until it is explicitly deleted Product. Description Storage Mechanisms and Persistent Objects Object databases l Relational databases l others l
The Solution: A Persistence Service from a Persistence Framework The framework should provide functions such as: store and retrieve objects in a persistent storage mechanism l commit and rollback transactions l
persistence framework l general-purpose, reusable, and extendable set of types that provides functionality to support persistent objects framework l A set of collaborating abstract and concrete classes that may be used as a template to solve a related family of problems. It is usually extended via subclassing for application-specific behavior
Key Ideas Mapping Object identity Database mapper Materialization and dematerialization Caches Transaction state of object Transaction operations Lazy materialization Virtual proxies
The Representing Objects as Tables pattern How do you map an object to a record or relational database schema? The Representing Objects as Tables pattern
UML Data Modeling Profile aggregate signifies a referential constraint: a Product. Description row can't exist without a related Manufacturer row «Table» Manufacturer «PK» OID : char(16) Name : varchar(100) City : varchar(50) * 1 PK - primary key FK - foreign key «Table» Product. Description «PK» OID : char(16) Description : varchar(100). . . «FK» Manu_OID : char(16)
Method 1
Method 2 a
Method 2 b
object identifier (OID)pattern
Accessing a Persistence Service with a Facade
Mapping Objects: Database Mapper or Database Broker Pattern Who should be responsible for materialization and dematerialization of objects (for example, a Product. Description) from a persistent store?
The Persistence. Facade—as true of all facades—does not do the work itself, but delegates requests to subsystem objects. l direct mapping l l persistent object class itself indirect mapping Database Broker pattern l Database Mapper pattern l
Metadata-Based Mappers class Persistence. Facade { //. . . public Object get( OID oid, Class persistence. Class ) { // an IMapper is keyed by the Class of the persistent object IMapper mapper = (IMapper) mappers. get( persistence. Class ); // delegate return mapper. get( oid ); } //. . . } usage: (Manufacturer) Persistence. Facade. get. Instance(). get( manu. OID, Manufacturer. class) );
UML notation : This is a qualified assocation . It means : 1. There is a 1 -M association from Persistence. Facade to IMapper objects. 2. With a key of type Class , an IMapper is found (e. g. , via a Hash. Map lookup ) 1 Persistence. Facade «interface» IMapper 1 get. Instance Class () : Persistence. Facade get ( OID , Class ) : Object put ( OID , Object ). . . note that the Class as a parameter is no longer needed in this version of get , as the class is "hardwired " for a particular persistent type get (OID ) : Object put ( OID , Object ). . . Product. Specification RDBMapper Product. Specification Flat. File. Mapper Manufacturer RDBMapper . . get ( OID ) : Object put ( OID , Object ). . . each mapper gets and puts objects in its own unique way depending on the kind of data store and format ,
Template Method Pattern
Framework Design with the Template Method Pattern l l l if (object in cache) return it else create the object from its representation in storage save object in cache return it
«interface» IMapper get (OID ) : Object put ( OID , Object ). . . // template method public final Object get { obj : = cached. Objects ( OID oid ) . get (oid ); if (obj == null ) { Abstract Persistence. Mapper // hook method obj = get. Object. From. Storage cached. Objects } return obj } ; ( oid ); . put ( oid , obj ); TEMPLATE + get ( OID ) : Object # get. Object. From. Storage. . . {leaf } (OID ) : Object {abstract } HOOK
How to use the Framework
Further factoring out the varying and unvarying parts of the algorithm.
Final Framework
Next. Gen Persistence Product. Description RDBMapper Product. Description File. With. XMLMapper + Product. Description. RDBMapper (table. Name ) # get. Object. From. Record (OID , DBRecord ) : Object Sale RDBMapper. . . # get. Object. From. Record # get. Object. From. Storage (OID ) : Object Product. Description In. Memory. Test. Data. Mapper (OID , DBRecord ) : Object # get. Object. From. Storage (OID ) : Object Persistence 1 + Persistence. Facade get. Instance () : Persistence. Facade 1 Class get ( OID , Class ) : Object put ( OID , Object ). . . Abstract RDBMapper + Abstract. RDBMapper (table. Name ) # get. Object. From. Storage (OID ) : Object {leaf } # get. Object. From. Record (OID , DBRecord ) : Object - get. DBRecord (OID ) : DBRecord «interface» IMapper get (OID ) : Object put ( OID , Object ). . . Abstract Persistence. Mapper + get ( OID ) : Object {leaf } # get. Object. From. Storage (OID ) : Object. . .
Configuring Mappers class Mapper. Factory { public IMapper get. Product. Specification. Mapper(){. . . } public IMapper get. Sale. Mapper() {. . . } }
class Mapper. Factory{ public Map get. All. Mappers( ) {. . . } } class Persistence. Facade{ private java. util. Map mappers = Mapper. Factory. getlnstance( ). get. All. Mappers( ); }
class Product. Specification. RDBMapper extends …{ // hook method override protected Object get. Object. From. Storage( OID oid ) { String key = oid. to. String(); db. Rec = SQL execution result of: "Select * from PROD_SPEC where key =" + key Product. Specification ps = new Product. Specification(); ps. set. OID( oid ); ps. set. Price( db. Rec. get. Column("PRICE") ); ps. set. Item. ID( db. Rec. get. Column("ITEM_ID") ); ps. set. Descrip( db. Rec. get. Column("DESC") ); return ps; } }
class RDBOperations { public Result. Set get. Product. Description. Data( OID oid ) {. . . } public Result. Set get. Sale. Data( OID oid ) {. . . } class Product. Description. RDBMapper extends Abstract. Persistence. Mapper{ protected Object get. Object. From. Storage( OID oid ) { Result. Set rs = RDBOperations. get. Instance(). get. Product. Description. Data( oid ); Product. Description ps = new Product. Description(); ps. set. Price( rs. get. Double( "PRICE" ) ); ps. set. OID( oid ); return ps; }
Pattern: Cache Management to maintain materialized objects in a local cache to improve performance (materialization is relatively slow) and support transaction management operations such as a commit. When objects are materialized, they are placed in the cache, with their OID as the key. Subsequent requests to the mapper for an object will cause the mapper to first search the cache, thus avoiding unnecessary materialization
Transactional States and the State Pattern Persistent objects can be inserted, deleted, or modified. Operating on a persistent object (for example, modifying it) does not cause an immediate database update; rather, an explicit commit operation must be performed.
[ from DB ] State chart : Persistent. Object [new (not from DB )] New commit / insert Old. Clean save rollback / reload commit / update Old. Dirty delete Legend : New --newly created ; not in DB Old --retrieved from DB Clean --unmodified Dirty --modified delete Old. Delete rollback / reload Deleted commit / delete
Go. F State pattern Context/Problem An object's behavior is dependent on its state, and its methods contain case logic reflecting conditional statedependent actions. Is there an alternative to conditional logic? Solution Create state classes for each state, implementing a common interface. Delegate state-dependent operations from the context object to its current state object. Ensure the context object always points to a state object reflecting its current state.
Designing a Transaction with the Command Pattern
Ordering the database tasks Table A: case. No Student. No Health Table B: Student. No Student. Name Inseart a record (“ 05001”, ”wang”) to B update A ("001", "05001"),
Command Context/Problem How to handle requests or tasks that need functions such as sorting (prioritizing), queueing, delaying, logging, or undoing? Solution Make each task a class that implements a common interface
actions become objects, and thus can be sorted, logged, queued, and so forth.
{ sort() for each ICommand cmd. execute() } Transaction commands : List commit() add. Delete(obj: Persistent. Object ) add. Insert( obj: Persistent. Object ) add. Update( obj: Persistent. Object ) sort(). . . use Sort. Strategy objects to allow different sort algorithms to order the Commands «interface» ICommand execute( ) undo() DBCommand Persistent. Object object : Persistent. Object commands. add( new DBUpdate. Command (obj) ); perhaps simply object. commit() but each Command can perform its own unique actions 1. . * undo is a no -op fo this example , but more complex solution adds a polymorphic undo to each subclass which uniquely knows how to und an operation DBUpdate. Command execute() {abstract} undo() {leaf} DBInsert. Command execute() 1 commit(). . . DBDelete. Command execute()
Lazy Materialization with a Virtual Proxy
actually references an instance of Manufacturer. Proxy Persistent. Object oid. . . Product. Specification 1 manufacturer : IManufacturer. . . 1 get. Address(). . . get. Manufacturer. Address() : Address { return manufacturer. get. Address() } 1 «interface» IManufacturer Proxy Manufacturer real. Subject : IManufacturer - get. Real. Subject() : IManufacturer + get. Address(). . . { if ( real. Subject == null ) real. Subject = Persistence. Facade. get(oid, Manufacturer. class); return real. Subject; } 3 1 Proxy-for real. Subject address get. Address(). . . { return get. Real. Subject(). get. Address() } 2
// EAGER MATERIALIZATION OF MANUFACTURER class Product. Specification. RDBMapper extends Abstract. Persistence. Mapper{ protected Object get. Object. From. Storage( OID oid ){ Result. Set rs = RDBOperations. getlnstance(). get. Product. Specification. Data( oid ); Product. Specification ps = new Product. Specification(); ps. set. Price( rs. get. Double( "PRICE" ) ); // here's the essence of it String manufacturer. Foreign. Key = rs. get. String( "MANU_OID" ); OID manu. OID = new OID( manufacturer. Foreign. Key ); ps. set. Manufacturer( (Manufacturer) Persistence. Facade. get. Instance(). get(manu. OID, Manufacturer. class) ); // or LAZY MATERIALIZATION OF MANUFACTURER ps. set. Manufacturer( new Manufacturer. Proxy( manu. OID ) );
the Representing Object Relationships as Tables one-to-one associations Place an OID foreign key in one or both tables representing the objects in relationship. l Or, create an associative table that records the OIDs of each object in relationship. l
one-to-many associations, such as a collection many-to-many associations l Create an associative table that records the OIDs of each object in relationship.
Unresolved Issues • dematerializing objects l Briefly, the mappers must define put. Object. To. Storage. methods. l Dematerializing composition hierarchies requires collaboration between multiple mappers and the maintenance of associative tables (if an RDB is used). • materialization and dematerialization of collections • queries for groups of objects • thorough transaction handling • error handling when a database operation fails • multiuser access and locking strategies • security—controlling access to the database
- Slides: 64