Guide‎ > ‎Logic Design Patterns‎ > ‎

Insert Into From


Context

It is a very common requirement to copy one or more source rows to target rows.  A corollary requirement is that the created target rows are usually children of the source rows, and require proper initialization of their attributes.  The general concept is loosely analogous to the SQL Insert Into Select From command.
  • Auditing
This is simply a copy of the current row, under the proper conditions.  We work a common example below.
  • Deep Copy
It is a common requirement to "clone" a Business Object, meaning a "master" and its related objects.  For example, a customer might like to create a new order by copying an old one.

  • Bill of Materials explosion
This is widely regarded as a particularly tough pattern.  We shall see it is, in fact, a variant of a deep copy, and can be solved with just a few rules.


Solution: Insert Into From

We can provide such services with ad hoc Java code, either in our application or (better, for re-use!) in our Business Logic Components.  But this pattern is so prevalent that this is simply reinventing the wheel.  

We can save cost and increase agility with re-usable Insert Into From service for copying.  We can then invoke these from our Action rules under the appropriate conditions.  This class provides a set of similar services, which we review in the sub-sections below:

 Example  Description Service
Auditing This is simply a copy of the current row, with provisions for audit conditions and target attribute initialization. insertChildFrom

Deep Copy It is a common requirement to "clone" a Business Object, meaning a "master" and its related objects.  For example, a customer might like to create a new order by copying an old one. copyMyAttributesFrom
Bill of Materials Explosion A variation of deep copy insertChildrenFrom 


Examples


Auditing

The most common case of auditing follows the pattern:

When a source object is altered meeting an audit condition,
copy the source object to a child target object,
initializing appropriate target attributes

The Audit Salary Changes example follows this pattern:

When a source object Employee is altered meeting an audit condition of salary changes,
copy the source object to a child target object EmployeeAudit,
initializing appropriate target attributes to record the old salary

As shown in the logicdoc for this requirement, we simply supply an Action Rule in the EmployeeLogic Business Logic Component like this:

if (employee.baseSalary != employee_old.baseSalary)
BusinessLogic.insertChildFrom(EmployeeAudit.class, logicContext)

So, from our pattern:
  • the source object Employee is the origin of the rule (EmployeeLogic), and the second parameter
  • the child target object EmployeeAudit is the first parameter
  • the target attribute initialization is described below
Common to all variations of this pattern is the initialization of created objects, described in the sub-section below.


Target Object Attribute Initialization

All variations of this pattern create one or more target objects.  Clearly, a mechanism is required to initialize the attributes of such created objects:
  • set foreign key
Insert Into always creates 1 or more children of the source object, so we need to set the foreign key of the created child.  In our example, we link the target child EmployeeAudit to the source parent Employee.

The system uses the heuristic to introspect the target (child) for an entity-type method that returns the type of the source (parent).  (Note: an exception is raised in the rare case where more than one such method is found).

  • copy like-named attributes
The system next copies like named attributes from the source to the target.  In the example above, this will set the Salary attribute.

(Note: the source for this extension is delivered, so it would be trivial to expand this heuristic, or to provide a call-back closure).

  • target object derivation rules
target objects are always saved after they are created, which invokes their business logic.  So, you can define formulas etc on these objects.



Deep Copy

The copy operation often includes not just (scalar) attributes, but also related data.  For example, if we want to create a new Purchaseorder from an existing one, we need to clone both the Purchaseorder and the Lineitems.

Business logic is initiated only when performing transactions (inserts, updates and deletes) to the database.  The sections below illustrate you can trigger this logic from either the source (copied) object, or from the target (inserted) object.  You can also build services which expose the functionality more explicitly.  Unlike manually coded services, such services can be thin, since they can invoke the domain-encapsulated business logic.

Copy from Target

See clonePurchaseorder example described here, and utilizes the InsertIntoFrom#copyMyAttributesFrom service. This particular example provides an interface  where the client inserts a Purchaseorder, setting an Entity attribute (clonedFrom) as the source of the deep copy.  As above, the functionality is invoked via an action rule:

/**
 * Deep copy of clonedFrom PO (if non-null) to this new PO.
 */
@Action
public void actionClonedFromPurchaseorder() {
if (logicContext.verb == Verb.INSERT && logicContext.logicNestLevel == 0 &&
                                             purchaseorder.clonedFrom != null)    {
String[] deepCopy = ["lineitems", "lineitemUsages"]
InsertIntoFrom.copyMyAttributesFrom (
              logicContext,              // indicates current object (copy target)
              purchaseorder.clonedFrom,  // copy source
              deepCopy)                  // collections to copy
}
}


Copy from Source

Another design approach is to update an existing order, whose logic create a copy of itself.  That example is described here.

Alert: using transient attributes

You may be tempted to set an transient attribute to trigger the copy operations.  This is actually an excellent idea, but will fail since Hibernate detects updates which involve only transient attributes, and prunes the update.  This of course prevents logic execution.



Bill of Materials Explosion

When ordering a Product that is a kit, we want to reduce inventory for each of the components of the kit.

The key requirement is to populate the SubItems for a kit-based Lineitem (see the database structure), by copying the ProductBillofMaterials to the subItems.  You can do this with the following explodeBillofMaterials Action rule on Lineitem:

if (logicContext.getVerb() == Verb.INSERT && lineitem.product.countComponents > 0)

    InsertIntoFrom.insertChildrenFrom( // if product is a kit, copy components -> subItems
           Lineitem.class,
           lineitem.product.components,
           logicContext);


The remaining logic (4 rules) is simply to properly arrange for quantities (i.e., if you buy 2 planes, you need 8 engines).  The Bill Of Materials Explosion example is an advanced example used in the Training.


Insert Into Operation

While there varying formulations of InsertIntoFrom, we can illustrate their operation by describing the example above.  The basic idea is to create a series of (target) Lineitem objects, 1 for each (source) component object.

There are 3 key players:
  1. Parent - the object that issues the InsertIntoFrom, here a Lineitem

  2. Source - the objects being copies, here a list: lineitem.product.components

  3. Target - the class in which rows are inserted - here, also Lineitem (the created sub items)
The system iterates over the Source list, creating a target instance that is initialized as follows:
  • copyAttributesFromTo copies like named attributes from the Parent to the Target
In our example, the Target SubItem is linked to the Order
  • link the Target to the Parent (there is a presumed one-to-many relationship from the Parent to the Target)
This links the Target SubItem to the Parent Lineitem (role name: kitItem)
  • link the Target to the Source (there is an optional one-to-many relationship from the Parent to the Target, the caller specifies whether this linkage exists with the aLinkToSource parameter
In our example, this linkage does not occur - the helper used sets the aLinkToSource = false
  • copyAttributesFromTo copies like named attributes from the Source to the Target
In our example, this copies ProductBillofMaterials.kitNumberRequired to the Target (SubItem) kitNumberRequired
  • after the copy operations, Target logic is invoked
In our example, this computes qtyOrdered as

lineitem.kitNumberRequired * lineitem.kitItem.qtyOrdered


The copyAttributesFromTo copies like-named attributes, as noted above; note that:
  • Primary key attributes are not copied

  • Non-null values are over-written, so, for example, copied Source values override copied Parent values

For internals information on the clone operation, please see Insert Into Operation.

Subpages (1): Insert Into Operation
Comments