Unit of Work

Situace

V trvalém úložišti dat jsou uloženy entity. Program může tyto entity načítat, vytvářet, upravovat či mazat v paměti. Provedené změny se ukládají zpět do trvalého úložiště.

Problém

Množina entit v paměti se může během spuštění programu měnit. Některé entity jsou upraveny, jiné smazány a mohou přibýt i entity nové. Pokud odpovídající změny nebudou provedeny i v trvalém úložišti, budou po odstranění entit z paměti ztraceny.

Někdy je také žádoucí, aby změny v trvalém úložišti proběhly najednou, například v jedné transakci. Toto se špatně zajišťuje především tehdy, když jsou změny roztroušeny v různých částech kódu nebo v čase.

Řešení

Bude vytvořena třída, která bude zaznamenávat provedené změny a teprve po potvrzení tyto změny najednou provede i v trvalém úložišti. Změny nemusí provádět přímo, ale delegovat je na jiné třídy, které mají změny daného typu entity na starosti.

Implementace

Implementace se mohou lišit v pravidlech, jakými se řídí označování instancí. Někdy je například nepřípustné, aby byla nová instance označena jako změněná, jindy to přípustné je. Proto je nutné tato pravidla vždy pečlivě uvážit a implementovat.

Třídu Unit of Work je možné implementovat i jako mapu, která každé instanci přiřazuje nejvýše jednu hodnotu z množiny NEW, DIRTY, REMOVED.

kód v jazyce Java - Zobrazit

  1. import java.util.HashSet;
  2. import java.util.Set;
  3.  
  4. /**
  5.  * Unit of Work.
  6.  *
  7.  * @author Vojtěch Hordějčuk
  8.  * @param <V>
  9.  * třída entity
  10.  */
  11. public class UnitOfWork<V>
  12. {
  13.   /**
  14.    * množina nových instancí
  15.    */
  16.   private final Set<V> setOfNew;
  17.   /**
  18.    * množina změněných instancí
  19.    */
  20.   private final Set<V> setOfDirty;
  21.   /**
  22.    * množina odstraněných instancí
  23.    */
  24.   private final Set<V> setOfRemoved;
  25.   /**
  26.    * DAO pro načítání entit z trvalého úložiště
  27.    */
  28.   private final DAO<V> dao;
  29.  
  30.   /**
  31.    * Vytvoří novou instanci.
  32.    *
  33.    * @param dao
  34.    * DAO pro zadaný typ entit
  35.    */
  36.   public UnitOfWork(final DAO<V> dao)
  37.   {
  38.     this.setOfNew = new HashSet<V>();
  39.     this.setOfDirty = new HashSet<V>();
  40.     this.setOfRemoved = new HashSet<V>();
  41.  
  42.     this.dao = dao;
  43.   }
  44.  
  45.   /**
  46.    * Označí instanci jako novou.
  47.    *
  48.    * @param entity
  49.    * instance entity
  50.    */
  51.   public void markNew(final V entity)
  52.   {
  53.     if (this.setOfDirty.contains(entity))
  54.     {
  55.       // nová instance nemohla být změněna, pokud neexistovala
  56.  
  57.       throw new IllegalStateException("Already registered as dirty.");
  58.     }
  59.  
  60.     if (this.setOfRemoved.contains(entity))
  61.     {
  62.       // nová instance nemohla být odstraněna, pokud neexistovala
  63.  
  64.       throw new IllegalStateException("Already registered as removed.");
  65.     }
  66.  
  67.     this.setOfNew.add(entity);
  68.   }
  69.  
  70.   /**
  71.    * Označí instanci jako změněnou.
  72.    *
  73.    * @param entity
  74.    * instance entity
  75.    */
  76.   public void markDirty(final V entity)
  77.   {
  78.     if (this.setOfNew.contains(entity))
  79.     {
  80.       // pokud je instance entity nová, není ještě v úložišti
  81.       // proto by nebylo co upravovat
  82.  
  83.       return;
  84.     }
  85.  
  86.     this.setOfDirty.add(entity);
  87.   }
  88.  
  89.   /**
  90.    * Označí instanci jako smazanou.
  91.    *
  92.    * @param entity
  93.    * instance entity
  94.    */
  95.   public void markRemoved(final V entity)
  96.   {
  97.     if (!this.setOfNew.contains(entity))
  98.     {
  99.       if (this.setOfDirty.contains(entity))
  100.       {
  101.         // instance byla dřív označena jako změněná
  102.         // tato informace již není platná a musí se smazat
  103.  
  104.         this.setOfDirty.remove(entity);
  105.       }
  106.  
  107.       this.setOfRemoved.add(entity);
  108.     }
  109.     else
  110.     {
  111.       // instance byla nová, v úložišti tedy není
  112.       // proto ji lze odebrat jen tak
  113.  
  114.       this.setOfNew.remove(entity);
  115.     }
  116.   }
  117.  
  118.   /**
  119.    * Vymaže záznamy o změnách zadané entity.
  120.    *
  121.    * @param entity
  122.    * instance entity
  123.    */
  124.   public void markClean(final V entity)
  125.   {
  126.     this.setOfNew.remove(entity);
  127.     this.setOfDirty.remove(entity);
  128.     this.setOfRemoved.remove(entity);
  129.   }
  130.  
  131.   /**
  132.    * Provede změny v trvalém úložišti.
  133.    */
  134.   public void commit()
  135.   {
  136.     // vytvořit nové
  137.  
  138.     for (final V newEntity : this.setOfNew)
  139.     {
  140.       this.dao.insert(newEntity);
  141.     }
  142.  
  143.     this.setOfNew.clear();
  144.  
  145.     // uložit změněné
  146.  
  147.     for (final V dirtyEntity : this.setOfDirty)
  148.     {
  149.       this.dao.update(dirtyEntity);
  150.     }
  151.  
  152.     this.setOfDirty.clear();
  153.  
  154.     // smazat odstraněné
  155.  
  156.     for (final V removedEntity : this.setOfRemoved)
  157.     {
  158.       this.dao.remove(this.setOfRemoved);
  159.     }
  160.  
  161.     this.setOfRemoved.clear();
  162.   }
  163.  
  164.   /**
  165.    * Vymaže všechny záznamy o změnách.
  166.    */
  167.   public void rollback()
  168.   {
  169.     this.setOfNew.clear();
  170.     this.setOfDirty.clear();
  171.     this.setOfRemoved.clear();
  172.   }
  173. }

Reference