Skip to end of metadata
Go to start of metadata

Table of Contents


Icon

It may be useful to start with the database table information pertaining to business objects before diving into the Business Object documentation below.

Defining a Business Object implementation

Business objects are java classes that implement the org.kuali.core.bo.BusinessObject interface. However, a majority of business objects extend org.kuali.core.bo.PersistableBusinessObjectBase, which implements org.kuali.core.bo.PersistableBusinessObject and org.kuali.core.bo.BusinessObject.

Icon

BOs extending org.kuali.core.bo.PersistableBusinessObjectBase inherit getters and setters for the object ID and version number columns, so there is no need to implement them in subclasses.

Icon

In each application, all simple class names (i.e. ignoring the package) should be unique. If multiple packages contain the same class name, the data dictionary may not load the duplicated classes properly.

Business objects need to implement getter and setters for each field that is mapped between java business objects and the database table (the mapping is described here). Therefore, if, in java, the ACCOUNT_NM DB column is named "accountName", then the getter method should be called getAccountName and the setter should be setAccountName (i.e. like POJO getters and setters).

Objects that extend org.kuali.core.bo.BusinessObjectBase must also implement the toStringMapper method, which returns a map of the BO's fields to be used in toString.

Icon

The reader should consult the org.kuali.core.bo.PersistableBusinessObjectinterface to determine whether there are additional methods that should be implemented/overridden. The interface defines numerous methods that customize the behavior of the business object upon persistence and retrieval of the business object, and how reference objects of the business object are refreshed, as well as other methods.

Reference objects

A reference object is a member variable of a business object that also implements BusinessObject. It refers to the database row referenced by the values in a foreign key relationship. For example, the account BO/table has a column for a chart of accounts ("FIN_COA_CD") code. Therefore, the account BO may have a referenced Chart object, which represents the chart row referred to by the account's chart of accounts code.

A collection reference is a member variable of a business object that implements java.util.Collection, with each element in the collection being a BusinessObject. A collection reference would be appropriate to model the sub accounts of the account business object.

A reference object or collection is defined in two steps:

  1. A field in a business object is created for either the reference object or collection reference
  2. A relationship is mapped within either OJB or the data dictionary. See below for more details.

To refresh (or retrieve) a reference object is to reload the referenced row from the DB, in case the foreign key field values or referenced data have changed.

For references mapped within the data dictionary, the framework does not have the logic to enable refreshing of a reference. The code must both implement the logic to refresh a data dictionary defined reference and the logic to invoke refreshing. This will be explained in the next session.

Refreshing reference objects mapped in OJB

For references mapped within OJB, the framework automatically takes care of the logic to enable refreshing of a reference. Under certain circumstances, it's able to automatically refresh references upon retrieval of the main BO from the database, and refreshing can also be invoked manually.

Note that this means that if the value of a foreign key field is changed, the corresponding reference object is not refreshed automatically. In the account BO implementation (see below), there's both a String for the chart of accounts code, as well as a Chart (i.e. the BO) reference object with the same chart of accounts code. If the code alters the account's chart code, the framework will not automatically retrieve the Chart reference object corresponding to the account's newly set chart code. To refresh the account's chart reference object with the new chart code, refresh/retrieve must be manually called (see below).

Refreshing reference objects not mapped in OJB

For references with relationships that are not mapped in OJB, code will need to be written to accommodate refreshing. A common example of this is UniversalUser references, because as noted here, institutions may decide to use another datasource for Universal User data (e.g. LDAP).

Although there are alternative strategies for accommodating refreshing, getter methods of these non-OJB mapped reference objects include the code that retrieves the reference object from the underlying datasource.

In contrast to OJB-mapped references, note that this strategy allows for the automatic refreshing of reference objects when a foreign key field value has been changed. In the account BO implementation (see below), after the accountFiscalOfficerSystemIdentifier value (a String) is changed, calling the getAccountFiscalOfficerUser() to retrieve the UniversalUser reference object (i.e. a BO) corresponding to the newly set identifier value will automatically retrieve the UniversalUser object corresponding to the new user ID.

Initializing collection references

Business objects fall into two broad (mostly mutually exclusive) categories: those that are edited by maintenance documents and those that are not. This section refers to business objects that are edited by maintenance documents that have updatable collections.

When constructing this type of BusinessObjects, initialize each of the updatable collection references to an instance of TypedArrayList. TypedArrayList is a subclass of ArrayList that takes in a java.lang.Class object in its constructor. All elements of this list must be of that type, and when the get(int) method is called, if necessary, this list will automatically construct items of the type to avoid an IndexOutOfBoundsException. This class allows us to not worry about checking array bounds and constructing items, which allows us to simplify framework code. For example, the SummaryAccount BO contains a updatable reference to a list of PurApSummaryItem objects.

When a collection is non-updatable (i.e. read only from the database), it is not necessary to initialize the collection. OJB will take care of list construction and population.

Example Account BO Implementation

Simplified Account Implementation

Note that calling setChartOfAccountsCode on an account object will not affect the chartOfAccounts reference object (OJB mapped). Therefore, without refreshing, calling account.getChartOfAccounts().getChartOfAccountsCode() may not equal account.getChartOfAccountsCode().

However, note that calling setAccountFiscalOfficerSystemIdentifier on an account object may affect the accountFiscalOfficerUser reference object (data dictionary mapped) when its getter method is called. This is equivalent to auto-refresh upon changing of the foreign key field.

Creating a data dictionary entry

Consult this page for more information about how to create a data dictionary file for a business object.

Creating BO-related services

Services such as the BusinessObjectService and PersistenceService are able to retrieve/persist BOs from a database. However, their functionality is limited because they are primarily able only to retrieve BOs based only on "equals" criteria.

To work around the limitation, Services and Data Access Objects (DAOs) should be created. For this section, a simple service will be created that updates all accounts in the system with a given account fiscal officer ID.

About Services and Data Access Objects

A Data Access Object serves as an abstraction layer between the persistence system (e.g. the database) and the business logic layer, which is implemented by services. See the KFS architecture page.

All code specific to the underlying persistence layer (e.g. OJB, JDBC/direct SQL, web service calls, etc.) should be placed in the DAO. The Service layer should contain all of the business logic.

Implementing a Data Access Object

Step 1: Defining a DAO interface

The first step to creating a DAO is to define its interface. Client code will call methods using the interface type, so all methods callable by the other code must be specified in the interface.

Step 2: Implementing the DAO

Currently, there are two primary database access mechanisms for implementing DAOs: JDBC and OJB. This section will provide implementations for the DAO using both mechanisms. Unless performance is a concern, OJB should be used.

JDBC implementation

Note the following items about the above code:

  • The class is located in the "jdbc" sub-package of the interface because it's an JDBC based implementation of the DAO.
  • The class and method are annotated with @RawSQL, which allows developers to quickly note that a class and method contain SQL.
  • The class extends PlatformAwareDaoBaseJdbc, which extends org.springframework.jdbc.core.simple.SimpleJdbcDaoSupport, which allows calling SQL statements using the getSimpleJdbcTemplate() method. The "platform aware" component of the name refers to the ability to access a dBPlatform property. This property is of type KualiDBPlatform, which provides methods to support platform specific SQL code.
    Icon

    JDBC DAO's are usually better suited towards operations that involve the manipulation of a large amount of data that can be expressed in SQL. JDBC DAO's are not well suited for returning BO's, because there is a lot of logic involved converting a database row into a BO.

OJB Implementation

Note the following items about the above code:

  • The class is located in the "ojb" sub-package of the interface because it's an OJB based implementation of the DAO.
  • The class extends PlatformAwareDaoBaseOjb, which extends org.springmodules.orm.ojb.support.PersistenceBrokerDaoSupport, which provides a wrapper to OJB's [persistence broker|http://db.apache.org/ojb/docu/tutorials/pb-tutorial.html} by calling getPersistenceBrokerTemplate(). The "platform aware" component of the name refers to the ability to access a dBPlatform property. This property is of type KualiDBPlatform, which provides methods to support platform specific SQL code.
  • Using an Iterator to retrieve results from the database eliminates the need to prefetch all of the accounts in the database into a large list in memory, thus saving time and memory.

Defining DAO Spring beans

Once the implementation class has been defined, a Spring bean needs to be defined for the DAO implementation.

Icon

The Spring bean name should be named similarly to the interface and not reflect the underlying mechanism used (i.e. OJB or JDBC).

JDBC DAO Spring Bean definition

Note the following things about the Spring bean definition:

  • It's named "accountDao", which does not imply that JDBC was used to implement the bean.
  • The parent bean is "platformAwareDaoJdbc". All JDBC-based DAOs must use this parent bean.
OJB DAO Spring Bean definition

Note the following things about the Spring bean definition:

  • It's named "accountDao", which does not imply that OJB was used to implement the bean.
  • The parent bean is "platformAwareDao". All OJB-based DAOs must use this parent bean.

Implementing a Service

There are several steps to create a service.

Step 1: Defining a service interface

The first step to creating a service is to define its interface.  Client code will call methods using the interface type, so all methods callable by the client must be specified in the interface.

Step 2: Implementing the service

Services are created as Spring beans, so properties should be injected.

Note that this service implementation directly calls a DAO method, but more sophisticated logic should be implemented in a service.

Transactional Services

Icon

Service implementations that use Iterator's and other data structures that are lazily loaded from the underlying data store should be annotated with @Transactional. The first method from a class annotated @Transactional in the call stack will start a transaction. When that method terminates (ends, returns, or throws exception), then the transaction will be committed or rolled back. If a transactional method directly or indirectly invokes another transactional method, only one transaction is started. There are ways to start a second transaction, consult the @Transactional javadocs for more details.

Step 3: Defining the Spring bean

The following is the Spring bean definition for the AccountService implementation. Note that an AccountDao instance is injected as a property of the service.

Unable to render {include} The included page could not be found.
  • No labels