How to manage multiple references in Business Objects

Tutorial for managing multiple references in the BDM Business Objects.

Introduction

This tutorial explains how to create, update or delete a multiple reference in a Business Object. The tutorial can be used with Bonita Community edition, and uses features that are available in all editions. The example uses an expense report as business object.

Design a task contract and associated operations to update a business object with a multiple reference.

Create the business object model

Create an Expense business object like below.

Expense business object

Then create ExpenseReport business object referencing multiple expenses by composition.

Expense report business object

Design the expense report process

  • Create a new process

  • Add a new report business variable of type ExpenseReport initialized with following groovy script

	def aDummyExpense = expenseDAO.newInstance();
	aDummyExpense.amount = 5
	aDummyExpense.nature = "A dummy expense"
	aDummyExpense.expenseDate = new Date()

	def anotherDummyExpense = expenseDAO.newInstance();
	anotherDummyExpense.amount = 55
	anotherDummyExpense.nature = "A another dummy expense"
	anotherDummyExpense.expenseDate = new Date()

	def report = expenseReportDAO.newInstance()
	report.expenses << aDummyExpense
	report.expenses << anotherDummyExpense
	return report
  • Rename Step1 to Update expense report

Design the task contract to update expenses

  • Select the Update expense report task

  • Go to Execution > Contract property tab

  • Create a contract like below

Contract

newExpenses input is used to gather a list of new expenses to add to the report expensesToDelete input is used to gather a list of Expense id to delete expensesToUpdate input is used to gather a list of existing expenses to update in the report

In the contract we use TEXT input type for persistenceId instead of numeric type to support the whole java.lang.Long range. JSON number type range does not extend to java.lang.Long.MAX_VALUE. A conversion to long will be applied in scripts.

Add operations to update the business data

  • Go to Execution > Operations property tab

  • Add an operation

  • Use the report business variable as left operand.

  • Change the operator and select Use a java method, choose the setExpenses method.

  • In the right operand use the following script to add new expenses in the report:

	def result = []
	result.addAll(report.getExpenses())
	newExpenses.each{
		def exp = expenseDAO.newInstance()
		exp.amount = it.amount
		exp.nature = it.nature
		exp.expenseDate = it.expenseDate
		result << exp
	}
	return result
  • Add another operation

  • Use the report business variable as left operand.

  • Change the operator and select Use a java method, choose the setExpenses method.

  • In the right operand use the following script to delete expenses from the report:

	def result = []
	result.addAll(report.getExpenses())
	result.removeAll(result.findAll{expensesToDelete.contains(it.persistenceId.toString())})
	return result
  • Add another operation

  • Use the report business variable as left operand.

  • Change the operator and select Use a java method, choose the setExpenses method.

  • In the right operand use the following script to update expenses existing in the report:

	import com.company.model.Expense;

	def updatedExpenses = []
	updatedExpenses.addAll(report.getExpenses())
	expensesToUpdate.each{ exp ->
		def Expense expenseToUpdate = updatedExpenses.find{
			it.persistenceId == exp.persistenceId.toLong()
		}
		if(expenseToUpdate){
			expenseToUpdate.amount = exp.amount
			expenseToUpdate.expenseDate = exp.expenseDate
			expenseToUpdate.nature = exp.nature
		}else{
			throw new Exception("Expense with id $exp.persistenceId does not exists.")
		}
	}
	return updatedExpenses

Run the process

You may now run the process and validate the expected behavior using autogenerated forms.

It should display this result according to the initialization of the report business variable.

[
	{
	"persistenceId":1,
	"persistenceId_string":"1",
	"persistenceVersion":0,
	"persistenceVersion_string":"0",
	"amount":5.0,
	"amount_string":"5.0",
	"nature":"A dummy expense",
	"expenseDate":1461748727495
	},
	{
	"persistenceId":2,
	"persistenceId_string":"2",
	"persistenceVersion":0,
	"persistenceVersion_string":"0",
	"amount":55.0,
	"amount_string":"55.0",
	"nature":"A another dummy expense",
	"expenseDate":1461748727495
	}
]
  • Perform the Update expense report task like below

Form 1

Form 2

It should display the following result:

[
	{
	"persistenceId":1,
	"persistenceId_string":"1",
	"persistenceVersion":1,
	"persistenceVersion_string":"1",
	"amount":7.0,
	"amount_string":"7.0",
	"nature":"updated nature",
	"expenseDate":1461801600000
	},
	{
	"persistenceId":3,
	"persistenceId_string":"3",
	"persistenceVersion":0,
	"persistenceVersion_string":"0",
	"amount":10.0,"amount_string":"10.0",
	"nature":"new expense",
	"expenseDate":1462406400000
	}
]

To conclude, when modifying a collection of Business objects in a script you must return new java.util.List instances and not the list returned by an accessor (eg: report.getExpenses()) as it will return an Hibernate specific implementation not compliant with our business objects. Do not forget to use the persistence id (or another unique attribute of the object) in the contract if you need to access existing object (update or delete usecases).