Lecture 09The Behavioral Problem
Reading Fowler 3 Mapping to Relational Database
– The Behavioural Problem Fowler 12: Object-Relational Behavioral
Patterns– Unit of work– Identity Map– Lazy Load
Agenda Error handling The Behavioral Problem Object-Relational Behavioral Patterns
– Unit of work– Identity Map– Lazy Load
Object-Relational Mapping
Error Handling
Copyright © 2008 Ólafur Andri Ragnarsson
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
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?
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
Error Flow
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{ ...
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
Unchecked exceptions
Examplepublic 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);}
Example
catch (Throwable t) { String msg = "Unable to access Database: cause: " + t.getMessage(); log.severe(msg); throw new RuDataAccessException(msg, t); } return rows; }
}
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);}
Which of these statements is not true
A) Checked exceptions must be always be handled by callerB) In layered systems, each layer must handle exceptionsC) Unchecked exceptions are never handledD) Checked exceptions require more coding
QUIZ
✔
The Behavioral Problem
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?
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?
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
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
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
Unit of WorkMaintains 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?
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
Unit of Work Caller Registration
– User of the object has to remember to register the object with the Unit of Work for changes
Unit of Work Object Registration
– The object must register itself with the Unit of work
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
Identity MapEnsures 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
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)
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
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
Lazy LoadAn 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
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
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; }
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(); }
Lazy Load Value Holder
– An object with a getValue method– Clients call getValue to get the real object, the
first call triggers the loadClass 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; }
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 fieldsClass Employee... public String Name { get { Load(); return _name; } set { Load(); _name = value; } } String _name;
class Domain Object
protected void Load() { if(IsGhost()) DataSource.load(this); }
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
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) Lazy LoadB) Identity MapC) Unit of WorkD) Data Mapper
QUIZ
✔
Object Relational Mapping
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
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
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
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
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
Hibernate
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
Architecture High-level architecture
Properties file definedata access
Mapping definitionmaps classes to tables
Architecture
Database Properties File
– hibernate.properties
Contains information to access the database– Username and password– URL– Database driver
Hibernate will automatically read the file from the classpath
hibernate.connection.username=andrihibernate.connection.password=abc123hibernate.connection.url=jdbc:jtds:sqlserver://honn.ru.is:1433hibernate.connection.driver_class=net.sourceforge.jtds.jdbc.Driver
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>
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();
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
Example NemandiGateway
public interface NemandiGateway{ public Nemandi findNemandi(String kennitala); public Collection getNemendur(); public void addNemandi(Nemandi nemandi);}
Example NemandiData
– Constructor creates the configuration and the factory
– Variable factory is used when a Session is neededpublic class NemandiData implements NemandiGateway{ SessionFactory factory = null;
public NemandiData() { Configuration cfg = new Configuration(); cfg.addClass(Nemandi.class); factory = cfg.buildSessionFactory(); }
Example NemandiData
– findNemandi
public Nemandi findNemandi(String kennitala){ Session session = factory.openSession(); Nemandi nem = (Nemandi)session.get(Nemandi.class, kennitala); session.close(); return nem;}
Example NemandiData
– getNemendur
– Uses the Hibernate Query Language, HQL
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; }
Example NemandiData
– addNemandi public void addNemandi(Nemandi nemandi) { Session session = factory.openSession(); Transaction tx = session.beginTransaction(); session.save(nemandi); tx.commit(); session.close(); }
Summary The Behavioral Problem
– When objects are used Object-Relational Behavioral Patterns
– Unit of work– Identity Map– Lazy Load
Object-Relational Mapping