Identity Map Pattern

This pattern belongs to Object-Relational Behavioral Patterns Catalog and this Catalog belongs to Patterns of Enterprise Application Architecture.

Intent

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.
An Identity Map keeps a record of all objects that have been read from the database in a single business transaction. Whenever you want an object, you check the Identity Map first to see if you already have it.

How It Works

The basic idea behind the Identity Map is to have a series of maps containing objects that have been pulled from the database. In a simple case, with an isomorphic schema, you’ll have one Map per database table. 
When you load an object from the database, you first check the map. If there’s an object in it that corresponds to the one you’re loading, you return it. If not, you go to the database, putting the objects on the map for future reference as you load them.

Choice of Keys

The first thing to consider is the key for the map. The obvious choice is the primary key of the corresponding database table.

Explicit or Generic

You have to choose whether to make the Identity Map explicit or generic. An explicit Identity Map is accessed with distinct methods for each kind of object you need: such as findPerson(1). A generic map uses a single method for all kinds of objects, with perhaps a parameter to indicate which kind of object you need, such as find("Person", 1). The obvious advantage is that you can support a generic map with a generic and reusable object.

How Many

Here the decision varies between one Map per class and one Map for the whole session. A single map for the session works only if you have database-unique keys (see the discussion in Identity Field (216) for the trade-offs on that.) Once you have one Identity Map, the benefit is that you have only one place to go and no awkward decisions about inheritance.

Where to Put Them


Identity Maps need to be somewhere where they’re easy to find. They’re also tied to the process context you’re working in.

When to Use It

  • The idea behind the Identity Map pattern is that every time we read a record from the database, we first check the Identity Map to see if the record has already been retrieved. This allows us to simply return a new reference to the in-memory record rather than creating a new object, maintaining referential integrity.
  • A secondary benefit to the Identity Map is that, since it acts as a cache, it reduces the number of database calls needed to retrieve objects, which yields a performance enhancement.

Sample Code

Let's create Sequence diagram to demonstarte IdentityMap Pattern.

With reference to above Sequence diagram the sequence of steps are 
1. The request first comes to finder method with the primary key.
2. The finder checks whether the PersonMap (IdentityMap) contains person object for given primary key.
3. If person object is found in PersonMap (IdentityMap) then return person object from PersonMap and don't hit the database.
4. If person object not found in PersonMap (IdentityMap) then it fires the SQL to the database and gets the record from the database.
You can see here, it reduces the number of database calls needed to retrieve objects, which yields a performance enhancement.

Let's write the code for above Sequence Diagram.
public class Person {
    private int key;
    private String firstName;
    private String lastName;
    private String noOfDependents;
    
    // getter and setter methods
}

public class PersonDatabase{
    
    public Person finder(int key){
        
        // Check for person object in IdentityMap
        Person person = IdentityMapUtility.getPerson(key);
        if(person == null){
            // get person object from database
            
            // add person object to IdentityMap
            addPerson(person);
        }
        return person;
        
    }
}
public class IdentityMapUtility{
    
    // personMap as IdentityMap
    private Map personMap = new HashMap();
    
    // Add person object to IdentityMap
    public static void addPerson(Person arg) {
        personMap.put(arg.getID(), arg);
    }
    
    // Retrieve person object from personMap
    public static Person getPerson(Long key) {
        return (Person) personMap.get(key);
    }
    
    public static Person getPerson(long key) {
        return getPerson(new Long(key));
    }
}

References

https://www.martinfowler.com/eaaCatalog/identityMap.html

Comments