Вы находитесь на странице: 1из 49

Designing a Persistence Framework

with Patterns

Persistent Objects
Storage mechanisms:
Object databases
Relational databases
Other flat files, XML structures, hierarchical
databases, etc.

Object-Relational (O-R) mapping service:


maps an object to a relational record.

Persistence Frameworks
Persistence framework: general-purpose,
reusable, extendable set of types that
provides functionality to support persistent
objects.
Persistence service: subsystem created by
persistence framework.
In technical services layer.
Called an O-R mapping service for RDBs.

Materialization
Transforming a non-object representation
of data from a persistent store into objects.
Lazy materialization: an instance is only
materialized on demand, when needed.
Implemented by a Virtual Proxy

Caching: materialized objects are typically


cached for performance.

Dematerialization (passivation):
The reverse transformation.

Representing Objects as Tables


Pattern: define a table in an RDB for each
persistent object class.
Primitive object attributes map to columns.
Attributes that refer to other complex objects
is more complicated.

Key Ideas

Mapping between a class and its


persistent store and between object
attributes and the fields in a record
Object identity to easily relate records
to objects and ensure there are no
inappropriate duplicates, records and
objects have a unique identifier
Database mapper pure fabrication
object responsible for materialization and
dematerialization
6

Key Ideas

Caches persistence services cache


materialized for performance
Transaction state of object State of
objects in relation to the current
transaction. Which objects are dirty?
Transaction operations Commit and
Rollback

Key Ideas

Lazy materialization not all objects are


materialized at once; materialization on
demand
Virtual proxies Lazy materialization can
be implemented using a smart reference
known as a virtual proxy

Mapping

Between a class and its persistent store


Between object attributes and fields in a record

Mapping objects and tables

Manufacturer
name
city
...
...

MANUFACTURER TABLE
: Manufacturer
name = Now&Zen
city = Mumbai

name

city

Now&Zen

Mumbai

Celestial
Shortening

San Ramon

Object Identifier

Object Identifier Pattern


Assign an object identifier (OID) to each
record and object (or proxy of an object).
Relates records to objects.
Avoids duplicates.
Usually alphanumeric value.
In object-land, represented by an OID
interface or class.
In RDB, usually a fixed-length char value.
Every table has an OID as primary key.
Maps every object to some row in some table.

Fig. 37.3 OIDs link objects and records

MANUFACTURER TABLE
Manufacturer
city
name
oid : OID
...

: Manufacturer
city = Mumbai
name = Now&Zen
oid = xyz123

OID

name

city

xyz123

Now&Zen

Mumbai

abc345

Celestial
Shortening

San Ramon

...
This is a simplified design.
In reality, the OID may be
placed in a Proxy class.

primary key

Accessing a Persistence Service with a Faade

Faade provides a unified interface to a


subsystem.
Operation to retrieve an object given OID.
Type of object to materialize is also needed.

Operation to store an object (dematerialize).

Faade delegates requests to subsystem


objects.

Fig. 37.4 The Persistence Faade

: DBProductsAdapter

: PersistenceFacade

PersistenceFacade
...
getInstance() : PersistenceFacade

pd = get(...)

get( OID, Class ) : Object


put( OID, Object )
...
// example use of the facade
OID oid = new OID("XYZ123");
ProductDescription pd = (ProductDescription) PersistenceFacade.getInstance().get( oid, ProductDescription.class );

Database Mapper

Database Mapper Pattern


Direct mapping: persistent object class
defines the code to save itself in a DB.
Strong coupling to persistent storage.
Reduced cohesion: technical services mixed
with application logic.

Indirect mapping: create a class that is


responsible for materialization,
dematerialization and object caching.
Database Mapper or Database Broker.
Different mapper class for each persistent
object class.

Fig. 37.5 Database Mappers


UML notation: This is a qualified assocation. It means:
1. There is a 1-M association from PersistenceFacade to IMapper objects.
2. With a key of type Class, an IMapper is found (e.g., via a HashMap lookup)

1
PersistenceFacade
interface
IMapper

1
Class

getInstance() : PersistenceFacade
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

ProductSpecification
RDBMapper

get(OID) : Object
put( OID, Object )
...

ProductSpecification
FlatFileMapper

Manufacturer
RDBMapper

...

...

...

get( OID) : Object


put( OID, Object )
...

get( OID) : Object


put( OID, Object )
...

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 (GoF)


Define a method in a superclass that
defines the skeleton of an algorithm, with
its varying and unvarying parts.
Invokes other methods (hook methods),
some of which may be overridden in a
subclass to provide unique behavior at points
of variability.
Template method is typically public, hook
methods protected.

Fig. 37.7 Template Method for mapper objects

interface
IMapper
get(OID) : Object
put( OID, Object )
...
// template method
public final Object get( OID oid )
{
obj := cachedObjects.get(oid);
if (obj == null )
{
// hook method
obj = getObjectFromStorage( oid );
cachedObjects.put( oid, obj );
}
return obj;
}

Abstract
PersistenceMapper
TEMPLATE
+ get( OID) : Object {leaf}
# getObjectFromStorage(OID) : Object {abstract}
...

HOOK

Fig. 37.8 Overriding the hook method


// template method
public final Object get( OID oid )
{
obj := cachedObjects.get(oid);
if (obj == null )
{
// hook method
obj = getObjectFromStorage( oid );
cachedObjects.put( oid, obj )

IMapper

Abstract
PersistenceMapper

+ get( OID) : Object {leaf}

}
return obj
}

# getObjectFromStorage(OID) : Object {abstract}


...

// hook method override


protected Object getObjectFromStorage( OID oid )
{
String key = oid.toString();
dbRec = SQL execution result of:
"Select * from PROD_DESC where key =" + key

ProductDescription
RDBMapper

ProductDescription pd = new ProductDescription();


pd.setOID( oid );
pd.setPrice( dbRec.getColumn("PRICE") );
pd.setItemID( dbRec.getColumn("ITEM_ID") );
pd.setDescrip( dbRec.getColumn("DESC")
);
return pd;
}

# getObjectFromStorage(OID) : Object

Fig. 37.9 Template Method twice to factor common code for RDB
IMapper
Abstract
PersistenceMapper

+ get( OID) : Object {leaf}


protected final Object
getObjectFromStorage( OID oid )
{
dbRec = getDBRecord( oid );
// hook method
return getObjectFromRecord( oid, dbRec );
}

# getObjectFromStorage(OID) : Object {abstract}


...

Abstract
RDBMapper
tableName : String

private DBRecord getDBRecord OID oid )


{
String key = oid.toString();
dbRec = SQL execution result of:
"Select * from "+ tableName + " where key =" + key
return dbRec;
}

+ constructor AbstractRDBMapper( tableName )


# getObjectFromStorage(OID) : Object {leaf}
# getObjectFromRecord(OID, DBRecord) : Object {abstract}
- getDBRecord(OID) : DBRecord

// hook method override


protected Object
getObjectFromRecord( OID oid, DBRecord dbRec )
{
ProductDescription pd = new ProductDescription();
pd.setOID( oid );
pd.setPrice( dbRec.getColumn("PRICE") );
pd.setItemID( dbRec.getColumn("ITEM_ID") );
pd.setDescrip( dbRec.getColumn("DESC")
);
return pd;
}

ProductDescription
RDBMapper

+ constructor ProductDescriptionRDBMapper(tabName)
# getObjectFromRecord(OID, DBRecord) : Object

Persistence Framework for NextGen POS

NextGen-specific classes are in a different


package from general technical services
Persistence package.
IMapper, AbstractPersistenceMapper, and
AbstractRDBMapper are part of framework.
ProductDescriptionRDBMapper is a subclass
added by application programmer.
ProductDescriptionInMemoryTestDataMapper
produces hard-coded objects for testing
without accessing an external DB.

Fig. 37.10 The persistence framework


NextGen Persistence
ProductDescription
RDBMapper

ProductDescription
FileWithXMLMapper

+ ProductDescriptionRDBMapper(tableName)
# getObjectFromRecord(OID, DBRecord) : Object

# getObjectFromStorage(OID) : Object

Sale
RDBMapper

ProductDescription
InMemoryTestDataMapper

...
# getObjectFromRecord(OID, DBRecord) : Object

# getObjectFromStorage(OID) : Object

Persistence
1
+ PersistenceFacade
getInstance() : PersistenceFacade

1
Class

get( OID, Class ) : Object


put( OID, Object )
...

Abstract
RDBMapper
+ AbstractRDBMapper(tableName)
# getObjectFromStorage(OID) : Object {leaf}
# getObjectFromRecord(OID, DBRecord) : Object
- getDBRecord(OID) : DBRecord

interface
IMapper
get(OID) : Object
put( OID, Object )
...

Abstract
PersistenceMapper
+ get( OID) : Object {leaf}
# getObjectFromStorage(OID) : Object
...

Cache

Cache Management
Pattern: Make the Database Mappers
responsible for maintaining a local cache
of materialized objects to improve
performance.
When objects are materialized, they are
cached, with their OID as key.
Subsequent requests to the mapper for an
object will be satisfied from the cache if the
object has already been materialized.

A Class for SQL Statements

Consolidate all SQL operations in a Pure


Fabrication singleton.
SQL operations: SELECT, INSERT, . . .
RDB mapper classes collaborate with SQLstatements class to obtain a DB record or
ResultSet (Java).
This is preferable to hard-coding SQL
statements into the different RDB mapper
classes.

Example of a SQL-statements Class

Interface:
class RDBOperations
{
public ResultSet getProductDescriptionData(OID oid){}
Public ResultSet getSaleData( OID oid ) { }
}

Mapper:
class ProductDescriptionRDBMapper
extends AbstractPersistenceMapper
{ protected Object getObjectFromStorage( OID oid )
{ ResultSet rs = RDBOperations.getInstance().
getProductDescriptionData(oid);
ProductDescription ps = new ProductDescription();
ps.setPrice( rs.getDouble( PRICE ) );
ps.setOID( oid );
return ps;
}

Transaction state of Object

Transactional States and the State Pattern

Assume:
Persistent objects can be inserted, deleted, or
modified.
Modifying a persistent object does not cause
an immediate DB update an explicit commit
operation must be performed.
The response of an operation depends on the
transactional state of the object:
An old dirty object was modified after retrieval.
An old clean object need not be updated in DB.
Delete or save causes state change, not commit.

Fig. 37.12 Statechart for PersistentObject

State chart: PersistentObject


[new (not from DB)]

New

commit / insert

[ from DB]

OldClean

save
rollback / reload
commit / update

OldDirty

delete
Legend:
New--newly created; not in DB
Old--retrieved from DB
Clean--unmodified
Dirty--modified

delete
OldDelete

rollback / reload
Deleted

commit / delete

Fig. 37.13 Persistent object classes extend PersistentObject

Domain
ProductDescription
...

Persistence
PersistentObject
oid : OID
timeStamp:
DateTime
commit()
delete()
rollback()
save()
...

PersistentObject provides common technical


services for persistence

The State Pattern (GoF)


Problem: An objects behavior is
dependent on its state, and its methods
contain case logic reflecting conditional
state-dependent actions.
Solution: Create state classes for each
state, implementing a common interface.
Delegate state-depended operations from
the context object to its current state obj.
Ensure the context object always points to a
state object reflecting its current state.

Fig. 37.14 Applying the State pattern


{ state.delete( this ) }

{ state.rollback( this ) }

{ state.commit( this ) }
{ state.save( this ) }

PersistentObject
PObjectState

oid : OID
state : PObjectState

commit()
delete()
rollback()
save()
setState(PObjectState)
...

commit(obj : PersistentObject)
delete(obj : PersistentObject)
rollback(obj : PersistentObject)
save(obj : PersistentObject)

OldDirty
State
Product
Specification

// default no-op
// bodies for
// each method

OldClean
State

New
State

OldDelete
State

Sale
...

...
...
...

commit(...)
delete(...)
rollback(...)

delete(...)
save(...)

commit(...)

commit(...)
rollback(...)

{ // commit
PersistenceFacade.getInstance().update( obj )
obj.setState( OldCleanState.getInstance() ) }
{ // rollback
PersistenceFacade.getInstance().reload( obj )
obj.setState( OldCleanState.getInstance() ) }
{ // delete
obj.setState( OldDeleteState.getInstance() ) }
{ // save
obj.setState( OldDirtyState.getInstance() ) }

{ // commit
PersistenceFacade.getInstance().insert( obj )
obj.setState( OldCleanState.getInstance() ) }
{ // commit
PersistenceFacade.getInstance().delete( obj )
obj.setState( DeletedState.getInstance() ) }

Transaction Operations

Database Transactions
Transaction: a unit of work whose tasks
must all complete successfully or none
must be completed.
Completion is atomic.
Represent the set of tasks to be done with a
Transaction class.
The order of database tasks within a
transaction can affect its success and
performance.
Ordering database tasks can help: generally,
inserts first, then updates, then deletes.

The Command Pattern (GoF)


Problem: How to handle requests or tasks
that need functions such as prioritizing,
queueing, delaying, logging, or undoing.
Solution: Make each task a class that
implements a common interface.
Actions become objects that can be sorted,
logged, queued, etc.
Each has a polymorphic execute method.
E.g., command objects can be kept in a
history stack to enable undo.

Fig. 37.15 Commands for database operations


{
sort()
for each ICommand cmd
cmd.execute()
}

Transaction
commands : List
commit()
addDelete(obj:PersistentObject)
addInsert( obj:PersistentObject)
addUpdate( obj:PersistentObject)
sort()
...
use SortStrategy objects to allow
different sort algorithms to order the
Commands

interface
ICommand
execute( )
undo()

DBCommand
PersistentObject
object : PersistentObject

{
commands.add( new DBUpdateCommand(obj) );
}

perhaps simply
object.commit()
but each Command can
perform its own unique
actions

1..*

undo is a no-op for


this example, but a
more complex
solution adds a
polymorphic undo
to each subclass
which uniquely
knows how to undo
an operation

DBUpdateCommand
execute()

execute() {abstract}
undo() {leaf}

DBInsertCommand
execute()

commit()
...

DBDeleteCommand
execute()

Lazy Materialization with a Virtual


Proxy

Lazy Materialization with a Virtual Proxy

Virtual Proxy (GoF): a proxy for another


object (the real subject) that materializes
the real subject when it is first referenced.
A lightweight object that stands for the real
object that may or may not be materialized.
E.g., a Manufacturer object is rarely used, but a
ProductDescription has attribute visibility to an
IManufacturer instance.
When ProductDescription sends a getAddress
message to the ManufacturerProxy, it materializes
the real Manufacturer, using its OID.

Fig. 37.16 Manufacturer Virtual Proxy


actually references an
instance of
ManufacturerProxy

PersistentObject
oid
...

ProductSpecification
1

manufacturer : IManufacturer
...

getAddress()
...

getManufacturerAddress() : Address

{
return manufacturer.getAddress()
}

interface
IManufacturer

Manufacturer
Proxy

Manufacturer

realSubject : IManufacturer
- getRealSubject() : IManufacturer
+ getAddress()
...

{
if ( realSubject == null )
realSubject = PersistenceFacade.get(oid, Manufacturer.class);
return realSubject;
}

1
Proxy-for
realSubject

address
getAddress()
...

{
return getRealSubject().getAddress()
}

Representing Relationships in Tables

Representing Object Relationships as


Tables pattern:
One-to-one associations
Place an OID foreign key in one or both tables
representing the objects in a relationship, or treat
as a one-to-many association, as below.

One-to-many associations (a collection) or


many-to-many associations:
Create an associative table that records the OIDs
of each object in relationship.

Architectural Analysis

Architectural Analysis

Focus on requirements that strongly


influence the architecture.
e.g Secure system.

Focus on Non-Functional requirements.


Identify variation points(e.g third party
services) and evolution points(in future).

Architectural Analysis

Steps in Architectural Analysis.


Identify and analyze the non-functional
requirements
Analyze alternatives
Remove requirement, Hire an expert etc

Architectural design principles


High cohesion
Low coupling
Protected variations

Logical architecture refinement

Logical architecture refinement

Interlayer collaboration
(UI,DOMAIN,TECHNICAL SERVICE
layer)
Apply faade, observer and controller
pattern.

Package design

Package design

Organize packages to reduce the impact


of changes.
Relational Cohesion(RC)
RC=

numberof int ernalrelat ion


Numberofty pes

Low RC=>package contain unrelated thing

Вам также может понравиться