The Data Source Layer patterns introduced in lecture L08 are structural patterns. They deal with moving data to and from the database. They try to solve the impedance mismatch problem. However, they do not have any logic as to when and under what circumstances data should be loaded to from the database and when written. Clearly accessing the database is slow, and doing so needlessly hurts performance.
In this lecture we look at the behavioural problem. How to solve issues like writing only changed record back into the database, loading only the needed data once, and how to load the data model partially.
We introduce three new design patterns: Unit of Work, Identity Map and Lazy Load.
Vist the course home page: http://www.olafurandri.com/?page_id=2573
2. Reading
Fowler 3 Mapping to Relational Database
– The Behavioural Problem
Fowler 12: Object-Relational Behavioral
Patterns
– Unit of work
– Identity Map
– Lazy Load
3. Agenda
Error handling
The Behavioral Problem
Object-Relational Behavioral Patterns
– Unit of work
– Identity Map
– Lazy Load
Object-Relational Mapping
5. Error handling
Important aspect of programming
– Programming the best case is usually easy
– Making programs robust is another thing
Empty catch-blocks are usually not acceptable
– Can be worse since the error gets lost
– system.out.println is usually not practical
How to handle exception
– Log the exception
– Create a new exception and throw
– Ignore and have upper layers handle the exception
6. Some guidelines
If you cannot handle an exception, don’t catch it
If you catch an exception, don’t eat it
If you need to handle an exception
– Log with useful information
– Catch it where you can do something with it
Use domain specific exception
– Removes dependences
– Example: Should SQLException be handled in the
web layer if there is duplicate row in the database?
7. Exception Handling
Exceptions flow through layers
– Catch exception at the source and throw a domain specific
exception
– Upper layers will handle the error
Example: Add User
– Table Data Gateway add method catches a duplicate
database exception
– Throw domain specific exception
– Each layer will ignore the exception, just pass it through
– Web layer decides to display message to user saying the
username chosen is already taken
9. Types of Exceptions
Unchecked
– Can occur at any time
– For example
• OutOfMemoryError, NullPointerException
Checked
– Part of declaration, must he handled or specifically
handed to the caller
public static String readFirstLine(String filename)
throwsIOException
{ ...
10. Unexpected Exceptions
Problem with checked exceptions
–
–
–
–
Too much code – unnessary try-catch blocks
Hard-to-read code – difficult to see the real code
The real error can get lost
Dependencies
Guidelines
– Use checked exception if caller must deal with the
problem, the exception has direct consequences to the
computation
– In layered systems, if the calling layer will not be able to do
anything, log and throw unchecked exception
– Layer controlling the flow will handle
12. Example
public class UserInserter extends SqlUpdate
{
...
public int insert(User user)
{
int rows = 0;
try {
rows = update(new Object[] {
user.getUsername(), user.getName(),
user.getEmail(), user.getPassword(), });
}
catch (DataIntegrityViolationException divex)
{
String msg = "User '" + user.getUsername() +
"' is already registered.";
log.info(msg);
throw new RuDuplicateDataException(msg, divex);
}
13. Example
catch (Throwable t)
{
String msg = "Unable to access Database: cause: " +
t.getMessage();
log.severe(msg);
throw new RuDataAccessException(msg, t);
}
return rows;
}
}
14. UserDataGateway
Do not need to handle the exception
public class UserData extends RuData implements UserDataGateway
{
UserInserteruserInserter = ...
public void addUser(User user)
{
userInserter.insert(user);
}
...
}
public interface UserDataGateway extends RuDataGateway
{
User findUser(int id);
Collection findByName(String name);
void addUser(User user);
void updateUser(User user);
void deteleUser(int id);
}
15. QUIZ
Which of these statements is not true
A)
B)
✔ C)
D)
Checked exceptions must be always be handled by caller
In layered systems, each layer must handle exceptions
Unchecked exceptions are never handled
Checked exceptions require more coding
17. The Behavioral Problem
Object-Relational Mapping
– How you relate tables to objects
The Data Source Layer patterns are architectural
patterns – the focus on structure
–
–
–
–
Row Data Gateway,
Table Data Gateway,
Active Record, and
Data Mapper
They simply tell you how to load and save objects to
tables
– What if you maintain these objects in-memory?
18. The Behavioral Problem
How to get various object to load and save
themselves to the database
– With objects in memory, how can we keep track of
modified objects?
– What if we have two of the same object in memory
and both are changed?
– How can we maintain consistency and data integrity?
– What if you need object that is already in memory?
19. Keeping track of changed Objects
Simple way is to have an object that keeps track
of other objects
– Unit of Work
The idea is this
– When object is loaded it is registered as “clean” in the
UoW
– If modified, it is marked “dirty”
– When writing all objects back, just write the dirty ones
20. Keeping track of loaded Objects
What if you need an object from the database –
is it already loaded? And changed?
– Identity Map
The idea is this
– Keep all objects in a map and check get them from
the map
– If they are not in the map, load them from the
database
21. Loading Objects
For rich data models, what about loading object
hierarchies?
– Do we need to load all linked objects?
– Lazy Load
The idea is this
– We load part of the objects but maintain a placeholder
that we use when the rest of the object is needed
22. Unit of Work
Maintains a list of objects affected by a business
transaction and coordinates the writing out of
changes and the resolution of concurrency
problems
Keeps track of objects that are moved in and out
of the database
– What has changed?
23. Unit of Work
How It Works
– Unit of Work is an object that tracks all changes to the
database
– As soon as something affects the database, tell the Unit of
Work
– The Unit of Work must know the state of objects
• Upon committing the Unit of Work decides what to do
• Application programmers don’t have know what to write to the
database
Two methods
– Caller registration
– Object registration
24. Unit of Work
Caller Registration
– User of the object has to remember to register the
object with the Unit of Work for changes
25. Unit of Work
Object Registration
– The object must register itself with the Unit of work
26. Unit of Work
When to Use It
– When you have in-memory objects you need to
synchronize with the database
– When you have many updates to objects and you
want to avoid unneeded calls to the database to save
the object
Benefits
– Keeps the state of object in one place
27. Identity Map
Ensures that each object gets loaded only once by
keeping every loaded object in a map. Looks up
objects using the map when referring to them
Keeps a record of all the objects that have been
read
28. Identity Map
How It Works
– Contains a map of all loaded objects
– Provides method to get the objects
Choice of Key
– Usually the primary key
Explicit or Generic
– Explicit Identity Maps have method of the type of the
object
• Person findPerson (1)
– Generic Identity Maps have generic objects and keys
• Object find(“person”, 1)
29. Identity Map
How Many
– One map per class or per session
– Session maps works for database-unique keys
– For multiple maps, maintain one per class or per table
Where to put them
– Identity maps need to be somewhere
– Can be part of Unit of work
– Can be in a Registry
Identity Maps can be used as cache
– Works well if objects are read-only
30. Identity Map
When to Use It
– When you need to load objects to memory and you
don’t want them duplicated
– Main benefit of Identity Map is avoiding problems
when object is updated in-memory
– For immutable object, such as value object, Identity
Map is not needed – object may be duplicated
Performance
– When you need caching of objects for performance
31. Lazy Load
An object that doesn’t contain all of the data you
need but knows how to get it
Load only the data that is needed
– Load the rest when it is needed
32. Lazy Load
How It Works
– Object can contain other objects and associations
– Loading all the data might be too much
– Lazy Load delays loading until the objects are
needed
Four ways to implement Lazy Load
–
–
–
–
Lazy Initialization
Virtual Proxy
Value Holder
A ghost
33. Lazy Load
Lazy Initialization
– Uses a special marker value (usually null) to indicate
a field isn't loaded
– Every access to the field checks the field for the
marker value and if unloaded, loads it
Class Supplier...
public List getProducts() {
if (products == null)
products = Product.findSupplier(getId());
return products;
}
34. Lazy Load
Virtual Proxy
– An object with the same interface as the real object
– The first time one of its methods are called it loads the
real the object and then delegates.
Class VirtualList...
private List source;
private VirtualListLoader loader;
public VirtualList(VirtualListLoader loader) {
this.loader = loader;
}
private List getSource() {
if(source == null) source = loader.load();
return source();
}
public int size() {
return getSource().size();
}
35. Lazy Load
Value Holder
– An object with a getValue method
– Clients call getValue to get the real object, the first
call triggers the load
Class SupplierVH...
private ValueHolder products;
public List getProducts() {
return (List)products.getValue();
}
Class ValueHolder…
private Object Value;
...
public Object getValue() {
if (value==null) value = loader.load();
return value;
}
36. Lazy Load
A ghost
– The real object without any data
– The first time you call a method the ghost loads the
full data into its fields
class Domain Object
protected void Load() {
if(IsGhost())
DataSource.load(this);
}
Class Employee...
public String Name {
get {
Load();
return _name;
}
set {
Load();
_name = value;
}
}
String _name;
37. Lazy Load
When to Use It
– When you have complex objects with associations
with other objects
– Need to decide how much to get on a hit and how
many hits we want
– Rule might be to bring in everything you need in one
call
• The overhead of taking extra fields in the table is not that
high
– The best time to use Lazy Load is when it involves an
extra call and the data you’re calling isn’t used when
the main object is used
38. QUIZ
We are writing a business application which is using fairly large
data set. We only need to update few objects. Writing them all
back to database is too expensive. What pattern can we use?
A)
B)
✔ C)
D)
Lazy Load
Identity Map
Unit of Work
Data Mapper
40. Object Relational Mapping (ORM)
Use a mapping layer to map between objects
and tables
– Mapping a data representation from an object model
to a relational data model with a SQL-based schema
Mapping requires
metadata
– XML
Authoring and
maintaining
metadata is less work than maintaining SQL
41. Advantages of ORM
Can radically reduce the amount of code you
need to write
– 30% compared to JDBC for server side application
More Productivity
Applications are easier to maintain
Fosters thinking about an OO domain model
42. Disadvantages of ORM
Some loss of control over the persistence
process
May be more difficult to tune queries
Performance characteristics of the tool may
affect your application’s performance
43. When to use ORM?
Well-suited to ORM
– Read-modify-write lifecycle
– Little requirement for stored procedures
Poorly suited to ORM
– “Window on data” application
– Significant use of stored procedures
– Write centric apps, where data is seldom read
44. When to use ORM?
Typical server-side applications are fairly well
suited for ORM
– 90%-95% of applications
– But there are always some special cases
– Mix and match as needed
46. Hibernate
Object/relational mapping tool
– A persistence service that stores Java objects in
relational databases
– Provides an object oriented view of existing relational
data
Uses reflection and XML mapping files to
persist POJOs
– No changes to business domain objects
– The goal is to relieve the developer from a significant
amount of common data persistence-related
programming tasks
49. Database Properties
File
– hibernate.properties
hibernate.connection.username=andri
hibernate.connection.password=abc123
hibernate.connection.url=jdbc:jtds:sqlserver://honn.ru.is:1433
hibernate.connection.driver_class=net.sourceforge.jtds.jdbc.Driver
Contains information to access the database
– Username and password
– URL
– Database driver
Hibernate will automatically read the file from the
classpath
50. Mapping File
File
– hibernate-mapping
– In the same package as Nemandi class
<hibernate-mapping>
<class name="org.ru.honn.domain.Nemandi" table="NEMENDUR">
<id name="kennitala" column="kennitala" type="string">
</id>
<property name="nafn" column="nafn" type="string"
length="64" not-null="false"/>
<property name="netfang" column="netfang" type="string"
length="64" not-null="false"/>
<property name="hopur" column="hopur" type="string"
length="32" not-null="false" />
</class>
</hibernate-mapping>
51. Using Hibernate
Usually an application will
– Create a single Configuration
– Build a single instance of SessionFactory
– Then instantiate Session objects
Configuration cfg = new Configuration();
cfg.addClass(theClass);
SessionFactory factory = cfg.buildSessionFactory();
Session session = factory.openSession();
52. Using Hibernate
Configuration
– Allows the application to specify properties and
mapping documents to be used when creating a
SessionFactor
SessionFactory
– Factory class to create Session objects
Session
– Interface that represents a transaction
– The main function is to offer create, read and delete
operations for instances of mapped entity classes
53. Example
NemandiGateway
public interface NemandiGateway
{
public Nemandi findNemandi(String kennitala);
public Collection getNemendur();
public void addNemandi(Nemandi nemandi);
}
54. Example
NemandiData
– Constructor creates the configuration and the factory
– Variable factory is used when a Session is needed
public class NemandiData implements NemandiGateway
{
SessionFactory factory = null;
public NemandiData()
{
Configuration cfg = new Configuration();
cfg.addClass(Nemandi.class);
factory = cfg.buildSessionFactory();
}
55. Example
NemandiData
– findNemandi
public Nemandi findNemandi(String kennitala)
{
Session session = factory.openSession();
Nemandi nem = (Nemandi)session.get(Nemandi.class, kennitala);
session.close();
return nem;
}
56. Example
NemandiData
– getNemendur
public Collection getNemendur()
{
Session session = factory.openSession();
List l = session.createQuery(
"SELECT n FROM is.ru.honn.domain.Nemandi AS n").list();
session.close();
return l;
}
– Uses the Hibernate Query Language, HQL
58. Summary
The Behavioral Problem
– When objects are used
Object-Relational Behavioral Patterns
– Unit of work
– Identity Map
– Lazy Load
Object-Relational Mapping