Document Properties
Kbid25255B
Last Modified04-Feb-2020
Added to KB15-Oct-2013
Public AccessEveryone
StatusOnline
Doc TypeGuidelines, Concepts & Cookbooks
Product
  • ICM 7.6
  • ICM 7.7
  • ICM 7.8
  • ICM 7.9
  • ICM 7.10

Concept - Persistent Objects

1 Introduction

The Concept is addressed to developers who want to make use of the IS 7 Persitent Objects Layer (Data Layer) to create new persistent objects or re-use already existing ones .

Before dealing with persistent object development Intershop recommends to be familiar with Enfinity Definition Language, EDL modeling and business object development in common.

1.1 Persistent Objects and Modeling

Functionality encoded by a cartridge typically requires persistent storage of information in the database. If no suitable database tables exist, new tables have to be created. In order to make these database tables accessible to application components such as pipelets, new persistent objects need to be created at the persistence layer. It may also be necessary to implement additional manager classes, which operate on the new persistent objects.

1.2 Glossary

TermDescription
EDLIntershop 7 includes Enfinity Definition Language(EDL), a textual domainspecific language (DSL) for modeling persistent objects for Intershop 7.
 PO persistent object
CAPICartridge API

1.3 References

For a comprehensive introduction to EDL, see Reference - EDL , Concept - EDL Modeling and Cookbook - EDL Modeling .

For more detailed information on business object development, see Concept - Business Objects and Cookbook - Business Objects .

2 Persistent Objects

2.1 Persistent Object Classes

Persistent objects (POs) are Java objects used to store and read data to/from the database. A PO consists of four (optional five) files:

  • A PO class representing the persistent object
    The respective classes carry the suffix PO, such as ProductPO.java.
  • A descriptor file describing the object-relational mapping (OR mapping)
    The file extension of the descriptor file is *.orm, such as ProductPO.orm.
  • A factory class for lifecycle management
    The respective classes carry the suffix POFactory, such as ProductPOFactory.java.
  • A key class for identifying persistent objects
    The respective classes carry the suffix POKey, such as ProductPOKey.java.
  • If the model defines a key an alternate key-class
    The respective classes carry the suffix POAlternateKey, such as Preference DefinitionPOAlternateKey . java.

These classes are usually part of the internal package of a cartridge and not exposed publicly. Apart from this abstract base classes of persistent objects may participate in the public API.

2.2 Object-Relational Mapping

  • Mapping Classes to Tables
    In Intershop 7, single POs are mapped to single tables or database views. Other possibilities like mapping classes to join tables or other structures are not used within Intershop 7. Each instance of the PO corresponds to a row. Attributes of the class correspond to columns. The primary key attribute of the class maps onto the primary key of the table, which is the column (or the columns) used to uniquely identify a row.
  • Mapping and Inheritance Relations
    Intershop 7 uses an approach according to which only leaf classes are mapped onto the database, whereas super classes must be abstract. Tables for leaf classes contain both the attributes inherited from the super class as well as their own attributes.

2.3 Base Classes for Persistent Objects

Many POs in Intershop 7 are derived from the general base classes RegionalSettingsPO or ExtensibleObjectsPO, which are both part of com.intershop.beehive.core.capi.domain. PersistentObjectPO automatically provides a range of attributes, ensuring these attributes are defined in a way compatible with Intershop 7 mechanisms and processes. Attributes automatically provided by  RegionalSettingsPO include:

  • A UUID as primary key
  • An optimistic control attribute ( oca)
  • A domainID attribute
    This is a foreign key attribute used to reference the domain to which the persistent object belongs. Classes derived from  RegionalSettingsPO automatically inherit methods to get and set the domainID, as well as methods to get and set domain instances for a persistent object referenced by the domainID.
Domain getDomain()
void setDomain(Domain aDomain)
String getDomainID()
void setDomainID(String aDomainID)

In addition, ExtensibleObjectPO(a sub-class of RegionalSettingsPO) provides functionality which allows developers to add custom attributes at runtime, as described in Reference - Attributes of Persistent Objects- Extensible Object Attributes.

ExtensibleObjectPO and PersistentObjectPO base class

2.4 Modeling Example of Persistent Object Classes

The following EDL snippet defines a persistent object, including a relation respective a dependency to other objects.

namespace com.intershop.training.bc_warehouse.internal
{
  orm class WarehousePO extends ExtensibleObjectPO implements Warehouse
    {
      index(addressID);
      attribute name : string<256> required;
      attribute location : string<256>;
      attribute capacity : int;
      attribute description : string localized;
      attribute addressID : uuid;
      dependency address : Address handler "com.intershop.beehive.core.capi.profile.ProfileMgr"
      {
        foreign key(addressID);
      }
      relation stockItemPOs : StockPO[0..n] inverse warehousePO implements stockItems;
    }
}

3 Relationships Between Persistent Objects

Typically, your object model consists of a set of POs that do not stand alone, but are connected in various ways. In the object model, these connections between classes are modeled as relations or dependencies.

The following sections describe all aspects of relationships in more detail.

3.1 Relationship Types: Relations and Dependencies

Intershop 7 is based on two basic relationship types:

  • Relations
    A relation expresses a bi-directional semantic connection between classes.
    The following EDL snippets define a relation between the two objects WarehousePO and StockPO. Relation definition at the WarehousePO side:

    relation stockItemPOs : StockPO[0..n]
    inverse warehousePO implements stockItems;

    Relation definition on the StockPO side:

    relation warehousePO : WarehousePO[1..1]
    inverse stockItemPOs implements warehouse
    {
    foreign key(warehouseID) -> (UUID);
    }
  • Dependencies
    A dependency expresses a uni-directional relationship between classes. "Uni-directional" in this context means that the relationship is only navigable in one direction. Dependencies are commonly used to build relationships between persistent classes of different cartridges.
    The following EDL snippet (from WarehousePO) models the dependency connecting WarehousePO and Address. It expresses that we only navigate the relationship from WarehousePO to Address, never the other way around.

    dependency address : Address handler
    "com.intershop.beehive.core. …
    capi.profile.ProfileMgr"
    {
    foreign key(addressID);
    }

Compared to relations, dependencies are internally treated in a very different way. Dependencies are not registered in the ORM deployment descriptor file and they do not require to re-create the referenced class. Therefore, dependencies can be used to link custom PO classes to CAPI objects that Intershop 7 provides (such as Product).

3.2 Multiplicity of Relationships


Depending on the multiplicity types on each side of the relationship, three basic kinds of relationships are commonly distinguished:

  • One-to-one
    Allows for [0..1] to [1..1]; [1..1] to  [0..1] and  [0..1] to [0..1]. A [1..1] to [1..1] relation is prohibited because of the chicken-or-the-egg-dilemma.
  • One-to-many
    Allows for [1..1] to [0..n] and [0..1] to  [0..n]. Wheras [0..1] to [1..n] is not a practical known use case.
  • Many-to-many
    A many-to-many relation always needs a join table for mapping purposes.

For relations the multiplicity is declared in the definition. For dependencies the multiplicity is defined only indirect by the code

3.2.1 One-to-Many Relations

One-to-many relations are common in relational database design, allowing the representation of complex data sets in an economic way. For example, each instance of WarehousePO points to the instances of StockPO that belong to it. Likewise, each StockPO points to its WarehousePO.

To relate warehouse and stock data as described, a special column is needed in the StockPO table, identifying the correct WarehousePO instance for each StockPO instance. This special column acts as the foreign key, and it commonly maps onto the primary key of the related table, in our example, the WarehousePO table.

Returning to the representation of relations, the implication is that you must introduce a special attribute that serves as the foreign key and maps onto the primary key attribute of the associated PO class. In a one-to-many relation, the foreign key is defined within the class on the many-side, e.g., StockPO, and it maps onto the primary key of the class on the one-side, e.g., WarehousePO.

EDL snippet from the many-side, e.g., StockPO:

index(warehouseID);
attribute warehouseID : uuid required readonly;
relation warehousePO : WarehousePO[1..1] inverse stockItemPOs implements warehouse
{
  foreign key(warehouseID) -> (UUID);
}

Note

One-to-one relations are organized in precisely the same way, with the exception that the foreign key can be assigned to either class.

For one-to-many relations, the code generator creates special methods for both classes involved, the class on the many-side and the class on the one-side. Methods differ depending on which class is considered.

  • Many-Side Methods
    The methods generated for the class on the many-side, e.g., StockPO, allow you to access the role representing the class on the one-side, e.g., WarehousePO.

    public WarehousePO getWarehousePO()
  • One-Side Methods
    For the class on the one-side, e.g., WarehousePO, the code generator creates a method to access associated the role representing the class on the many-side, e.g., StockPO:

    public Collection getStockItemPOs() 
    public Iterator createStockItemPOsIterator()

    In addition, the code generator creates relationship wrapper methods. These methods check whether a particular element participates in the relationship:

    public boolean isInStockItemPOs(StockPO anElement)
    public int getStockItemPOsCount()

3.2.2 Dependencies

Dependencies are unidirectional relationships to classes which implement PersistentObject. Unidirectional means that they can be traversed only in one direction: from the source class to the target class. The target class always has a multiplicity of 0..1. In the EDL snippet from WarehousePO below, the dependency expresses that each instance of WarehousePO is associated with exactly one instance of Address. In contrast to normal association relationships, the dependency does not express the opposite statement, namely that each instance of Address can be associated with one or more instances of WarehousePO. The Address has no knowledge about the existence of the WarehousePO and the relationship between both entities. However, if the business logic requires this feature, it would be possible to determine all instances of WarehousePO which are related to a given address by using a query. The result may contain many instances or just one single instance if this is restricted by the implementation. Thus, dependent on the implementation a dependency may be a one-to-many or one-to-one relationship.

dependency address : Address handler "com.intershop.beehive.core.capi.profile.ProfileMgr"
{
  foreign key(addressID);
}

The code that code generator creates for dependencies only affects the source class, not the target class. Therefore, dependencies are particularly useful if you create new POs coupled with existing objects (such as Address) which you are unable to recompile or change.

Another common use case is hiding the relation to the One-Side-PO of a PO located in the same cartridge, because it is not intended to make this relation "public" for this PO. In that case the handler must not be defined and the factory of the PO is called to locate the related instance by a primary key lookup. Another use case is avoiding the creation of bi-directional relations to mass data.

Note

A foreign key is needed inside the source class, e.g., Warehouse, mapping onto the primary key of the target class, e.g., Address. The foreign key enables you to identify the particular target class instance, e.g., a particular product, to which a source class instance , e.g., a particular WarehousePO, is connected.

The following EDL snippet from WarehousePO declares the foreign key attribute addressID:

attribute addressID : uuid; 
index(addressID);

Typically, a dependency connects a custom PO with other persistent objects represented by a CAPI interface.

In our example, the dependency connects the custom PO WarehousePO with the standard Intershop 7 CAPI interface Address. When modeling a dependency like this, you have to provide the symbolic name of the manager which provides access to the object behind the CAPI interface.

The code generator uses the provided manager to create the accessor methods.

Note

The handler class has to provide a resolve<ClassName>FromID() method. Going by example, when constructing a dependency from a custom class to the CAPI interface Address, the respective manager would be the ProfileMgr (which provides a resolveAddressFromID() method).

In addition, the business object behind the interface has to provide a getUUID() method. Otherwise, compile errors may result since the setter method of the foreign key calls getUUID() of the interface. 

The code generator creates a getter method which allows you to access the target class instance to which a particular source class instance is connected and a setter method to assign a source instance to a target instance. For example, for the dependency connecting WarehousePO with Address, the following methods are generated:

public Address getAddress()
{
    if (getAddressIDNull())
    {
        return null;
    }
    ProfileMgr factory = (ProfileMgr) NamingMgr.getInstance().lookupManager(ProfileMgr.REGISTRY_NAME);
    return (Address) factory.resolveAddressFromID(getAddressID());
}

public void setAddress(Address address)
{
    if (address == null)
    {
        setAddressIDNull(true);
    }
    else
    {
        setAddressID(address.getUUID());
    }
}

 

3.2.3 Many-to-Many Relationships

Many-to-many relations are common in Intershop 7 applications. For example, consider the relation between product and supplier: each supplier may deliver more than one product, while each product may be delivered by more than one supplier. Or, product bundles, which combine products to form a new product, e.g., a computer, mouse, and monitor may be bundled into a complete computer package. Here too, a many-to-many relationship exists, because each product bundle can contain many different products, and each product can be part of different product bundles.

Note

This relationship is reflexive, because product bundles are also products. Intershop 7 uses so-called assignment classes to simulate many-to-many relationships.

Consider the figure below  which expresses the relationship between products and product bundles (which again are products). Via the assignment class BundleAssignmentPO, product bundles are paired with products by pairing values for bundleID(a foreign key attribute referencing UUIDs of product bundles) with values of productID(a foreign key attribute referencing products that participate in a bundle). Both foreign keys map to the primary key of Product( UUID).

Assignment classes in the representation of product bundles

Note

The BundleAssignmentPO is a PO but is not derived from ExtensibleObjectPO. BundleAssignmentPO has a natural primary key, composed of the two foreign key attributes.

3.3 CAPI Interfaces and Persistent Objects

There are many use cases where a persistent object implements a CAPI interface. This interface can be modeled as well or imported as an external type. An example of a set of CAPI interfaces modeled on top of custom POs is shown in the following EDL snippets:

import "enfinity:/demo/edl/com/intershop/demo/capi/Warehouse.edl";
namespace com.intershop.demo.internal
{
  orm class WarehousePO extends ExtensibleObjectPO implements Warehouse { ... }
}
import "enfinity:/demo/edl/com/intershop/demo/capi/Stock.edl";
namespace com.intershop.demo.internal
{
  oca orm class StockPO implements Stock { ... }
}
  • The PO WarehousePO implements the interface Warehouse.
  • The PO StockPO implements the interface Stock.
    The interfaces are part of the capi package while the implementation classes are part of the internal package.

Note that POs and interfaces use different base classes:

  • The PO WarehousePO inherits from the abstract class com.intershop.beehive.core.capi.domain.ExtensibleObjectPO.

  • The interface Warehouse extends the interface com.intershop.beehive.core.capi.domain.ExtensibleObject.

Disclaimer

The information provided in the Knowledge Base may not be applicable to all systems and situations. Intershop Communications will not be liable to any party for any direct or indirect damages resulting from the use of the Customer Support section of the Intershop Corporate Web site, including, without limitation, any lost profits, business interruption, loss of programs or other data on your information handling system.

Customer Support
Knowledge Base
Product Resources
Support Tickets