Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Passing Java Arrays in Oracle Stored Procedure From Mule ESB Flow

DZone's Guide to

Passing Java Arrays in Oracle Stored Procedure From Mule ESB Flow

A tutorial on how to use Java Arrays with Oracle database and Mule ESB.

· Java Zone
Free Resource

Just released, a free O’Reilly book on Reactive Microsystems: The Evolution of Microservices at Scale. Brought to you in partnership with Lightbend.

Objective

The objective of this tutorial is to demonstrate the workaround needed to invoke an Oracle Stored Procedure from Mule ESB flow by passing Java arrays as parameters.

The use case for this tutorial is a simple one such as inserting student records from a CSV file into an Oracle database table through a stored procedure, where one of the parameters is an array holding the student’s marks.

Image title

Pre-requisites

  • Oracle XE 11.2g: http://www.oracle.com/technetwork/database/database-technologies/express-edition/downloads/index.html
  • Java SE 8: http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html
  • Mule Anypoint Studio 5.4 with 3.7.3 Runtime: https://www.mulesoft.com/lp/dl/studio
  • Mule ESB 3.7.3 (Optional): https://developer.mulesoft.com/download-mule-esb-runtime
  • Mule Application Properties (mule-app.properties): Use the following properties or define them as per your system.
db.host=localhost
db.port=1521
db.instance=XE
db.user=scott
db.password=tiger


Backend Concepts

Let’s see a few concepts required from an Oracle PL/SQL perspective to fulfill this use case.

  • To create an array in PL/SQL, first you have to create a user defined type and then use the type to create a field or parameter. For our use case, we are supposed to create a type as TABLE OF NUMBER.
CREATE OR REPLACE TYPE NUMBER_T IS TABLE OF NUMBER;
  • Create the database table named STUDENTS, create the primary key constraint, create an index, and create the sequence for automatically creating the next sequence for STUDENT_ID.
CREATE TABLE STUDENTS (STUDENT_ID INTEGER NOT NULL, NAME VARCHAR2(100), ENG NUMBER, PHY NUMBER, CHEM NUMBER, MATH NUMBER);
CREATE UNIQUE INDEX STUDENTS_IDX1 ON STUDENTS(STUDENT_ID);
ALTER TABLE STUDENTS ADD CONSTRAINT STUDENTS_PK PRIMARY KEY (STUDENT_ID) USING INDEX;
CREATE SEQUENCE STUDENTS_SEQ START WITH 1 INCREMENT BY 1 CACHE 10000;
  • Create the Stored Procedure which should be defined with two parameters. The first parameter is for the name of the student and the second parameter is for holding the marks and therefore, should be of type NUMBER_T.
CREATE OR REPLACE PROCEDURE INSERT_STUDENTS (I_NAME VARCHAR2, I_MARKS NUMBER_T)
IS
BEGIN
  INSERT INTO STUDENTS (STUDENT_ID, NAME, ENG, PHY, CHEM,
  MATH) VALUES STUDENTS_SEQ.NEXTVAL, I_NAME, I_MARKS (1),  
  I_MARKS (2), I_MARKS (3), I_MARKS (4));
END INSERT_STUDENTS;
/
Note: The parameter I_MARKS defined above is considered as an array of numbers in Oracle PL/SQL and NUMBER_T is known as a user defined type. The parameters I_NAME and I_MARKS are considered as IN parameters, means input parameters.

Mule Application

The Mule application flow begins with a File inbound endpoint that reads a ‘student.csv’ file from ‘src/main/resources/input’ folder and moves the file to ‘src/main/resources/output’ folder after reading is completed. The read contents are then transformed into a collection of Java objects of type ‘student.Student’ through the Transform Message component. Once the transformation is successfully completed, the transformed payload is iterated using For Each scope wrapped with a Database Connector in order to invoke the Oracle stored procedure with two parameters, where the first parameter is the student’s name and the second parameter is the student’s marks as a Java array.

The following figure depicts the project structure for this application.

Image title

Resources

This application uses one CSV file name ‘student.csv’ as a resource containing the student’s records which are then processed and stored in the underlining Oracle database table SCOTT.STUDENTS with the help of a stored procedure invocation.  The below figure depicts the structure of the CSV file:

Name

Eng

Phy

Chem

Math

Praveen Kumar

96.0

77.0

85.0

89.0

Kamlesh Yadav

78.0

65.0

56.0

45.0

Manoj Kumar

89.0

77.0

86.0

98.0

Rajesh Mistry

87.0

56.0

45.0

89.0

Pankaj Sinha

77.0

89.0

76.0

56.0

Sumit Kumar

89.0

67.0

75.0

66.0

The CSV file is placed in src/main/resources/input folder.

Global Configurable Elements

This application requires the following global configurable elements:

  • Oracle Configuration
<db:oracle-config name="Oracle_Configuration"  host="${db.host}" port="${db.port}" instance="${db.instance}" user="${db.user}" password="${db.password}" doc:name="Oracle Configuration">
</db:oracle-config>
  • Global Function Configuration

The main purpose of this global function is to create an oracle.sql.ARRAY object with the given type name NUMBER_T and the elements of the Java array (Student Marks) so that the marks parameter of the invoking stored procedure can be mapped with the ARRAY type, as depicted in the below screenshot.

Image title

  • Oracle Configuration with Data Source
<db:oracle-config name="Oracle_Configuration" doc:name="Oracle Configuration" dataSource-ref="studentDataSource">
</db:oracle-config>

<spring:beans>
<spring:bean id="studentDataSource" name="studentDataSource" class="oracle.jdbc.pool.OracleDataSource">
            <spring:property name="password" value="tiger"/>
            <spring:property name="URL" value="jdbc:oracle:thin:@localhost:1521:XE"/>
            <spring:property name="user" value="scott"/>
            <spring:property name="dataSourceName" value="ds"/>
    </spring:bean>
</spring:beans>


  • Global Function Configuration   
<configuration doc:name="Configuration">
<expression-language autoResolveVariables="false">
<global-functions>

def castArray(marks,reg) {

javax.sql.DataSource ds = (javax.sql.DataSource)reg.get("studentDataSource");
oracle.jdbc.OracleConnection con = (oracle.jdbc.OracleConnection)ds.getConnection();
oracle.sql.ARRAY lMarks = con.createARRAY("SCOTT.NUMBER_T", marks);
con.close();
return lMarks;

}
</global-functions>
</expression-language>
</configuration>

Flow Elements Configuration

  • File Inbound Endpoint

The following properties pertaining to the General Tab should be set:



Property

Value

Display Name

Students CSV File

Path

\src\main\resources\input

Move to Pattern

Student.csv

Move to Directory

\src\main\resources\output

Image title

  • Transform Message Component

This component is used to transform the CSV file contents into a collection of Java objects of type student.Student class. Each record of the CSV file is transformed into a Java object of type student.Student class. The below figure depicts the transformation:

Image title

Set the metadata for Input and Output sections of the Transform Message component. The metadata for Input section is derived from the CSV file. The metadata for Output section is created with a Java Collection of student.Student class.

The structure of student.Student.java class is defined below:

package student;
public class Student implements java.io.Serializable {

    private static final long serialVersionUID = 7940976767680103084L;
      private String name;
      private float[] marks;

    public Student() {
            super();
      }

      public String getName() {
            return name;
      }

      public void setName(String name) {
            this.name = name;
      }

      public float[] getMarks() {
            return marks;
      }

      public void setMarks(float[] marks) {
            this.marks = marks;
      }
}
  • For Each Scope

The For Each scope is used for wrapping a Database Connector so that the stored procedure can be invoked for each student data and subsequently stored in the underlying Oracle database. The following figure depicts the properties to be defined for For Each scope:

Image title

The following table shows the properties to be set for ‘For Each’ scope:




Property

Value

Description

Collection

#[payload]   

Depicts the collection object transformed by the Transform Message component. The payload is List<Student>.

  • Database Connector

The properties depicted in the figure below should be set for the Database Connector:

Image title

The table below shows the properties to be set for the Database Connector:

Property

Value

Description

Connector configuration

Oracle_Configuration

Refer ‘Oracle Configuration’ bullet point under ‘Global Configurable Elements’ section.

Operation

Stored Procedure

Select Stored Procedure from the drop down list.

Type

Parameterized

Select Parameterized from the drop down list.

Parameterized query

{call scott.insert_students(:sname, :marks)}

Oracle stored procedure invocation statement with two parameters

Parameters

Parameter Name

IN/OUT

Value

Type

sname

IN

#[payload.getName()] 

VARCHAR

marks

IN

#[castArray(payload.getMarks(),app.registry)]

ARRAY

Setting the parameters for the Oracle stored procedure.

The point to be noted here is regarding the parameter section of the Database Connector, as depicted above where the value for marks parameter is defined as the global function call castArray which takes the Java array marks defined in student.Student class and app.registry as the parameters and creates oracle.sql.ARRAY object.

Conclusion

To conclude this tutorial, let us execute this application and check the result.

  • The below figure shows that the STUDENTS table is empty.

Image title

  • Run the application with the following records in the student.csv file using the Global Function ‘castArray’ written with Mule internals.

Name

Eng

Phy

Chem

Math

Praveen Kumar

96.0

77.0

85.0

89.0

Kamlesh Yadav

78.0

65.0

56.0

45.0

Manoj Kumar

89.0

77.0

86.0

98.0

Rajesh Mistry

87.0

56.0

45.0

89.0

Pankaj Sinha

77.0

89.0

76.0

56.0

Sumit Kumar

89.0

67.0

75.0

66.0


  • The below screen shot justifies that the above CSV records were inserted into the database table Students:

Image title

  • Run the application with the following records in the student.csv file using the Global Function ‘castArray’ written without Mule internals.

Name

Eng

Phy

Chem

Math

Navneet Singh

46.0

67.0

75.0

89.0

Pratap Sinha

58.0

75.0

96.0

55.0

Archana Yadav

69.0

87.0

56.0

88.0

Utpal Ghosh

67.0

86.0

95.0

49.0

Lalit Kumar

67.0

79.0

66.0

86.0

Chaman Pandit

59.0

77.0

65.0

86.0


  • The below screen shot justifies that the above CSV records were inserted into the database table Students.

Image title

  • Complete XML Code for this tutorial:

<?xml version="1.0" encoding="UTF-8"?>

<mule xmlns:mulexml="http://www.mulesoft.org/schema/mule/xml" xmlns:tracking="http://www.mulesoft.org/schema/mule/ee/tracking"
xmlns:scripting="http://www.mulesoft.org/schema/mule/scripting"
xmlns:db="http://www.mulesoft.org/schema/mule/db" xmlns:file="http://www.mulesoft.org/schema/mule/file"
xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns:dw="http://www.mulesoft.org/schema/mule/ee/dw"
xmlns:metadata="http://www.mulesoft.org/schema/mule/metadata"
xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core"
xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
xmlns:spring="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/ee/dw http://www.mulesoft.org/schema/mule/ee/dw/current/dw.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd
http://www.mulesoft.org/schema/mule/file http://www.mulesoft.org/schema/mule/file/current/mule-file.xsd
http://www.mulesoft.org/schema/mule/db http://www.mulesoft.org/schema/mule/db/current/mule-db.xsd
http://www.mulesoft.org/schema/mule/scripting http://www.mulesoft.org/schema/mule/scripting/current/mule-scripting.xsd
http://www.mulesoft.org/schema/mule/ee/tracking http://www.mulesoft.org/schema/mule/ee/tracking/current/mule-tracking-ee.xsd
http://www.mulesoft.org/schema/mule/xml http://www.mulesoft.org/schema/mule/xml/current/mule-xml.xsd">
<db:oracle-config name="Oracle_Configuration" 
    doc:name="Oracle Configuration" dataSource-ref="studentDataSource">
</db:oracle-config>
<configuration doc:name="Configuration">
<expression-language autoResolveVariables="false">
<global-functions>

def castArray(marks,reg) {

javax.sql.DataSource ds = (javax.sql.DataSource)reg.get("studentDataSource");
oracle.jdbc.OracleConnection con = (oracle.jdbc.OracleConnection)ds.getConnection();
oracle.sql.ARRAY lMarks = con.createARRAY("SCOTT.NUMBER_T", marks);
con.close();
return lMarks;

}
</global-functions>
</expression-language>
</configuration>
    <spring:beans>
        <spring:bean id="studentDataSource" name="studentDataSource" class="oracle.jdbc.pool.OracleDataSource">
            <spring:property name="password" value="tiger"/>
            <spring:property name="URL" value="jdbc:oracle:thin:@localhost:1521:XE"/>
            <spring:property name="user" value="scott"/>
            <spring:property name="dataSourceName" value="ds"/>
        </spring:bean>
    </spring:beans>

<flow name="PassingJavaArraysInOracleStoredProcedureFlow">
        <file:inbound-endpoint path="C:\Users\pbgho\AnypointStudio\workspace\Passing_Java_Arrays_in_Oracle_Stored_Procedure_from_Mule_ESB_Flow\src\main\resources\input" moveToPattern="student.csv" moveToDirectory="C:\Users\pbgho\AnypointStudio\workspace\Passing_Java_Arrays_in_Oracle_Stored_Procedure_from_Mule_ESB_Flow\src\main\resources\output" responseTimeout="10000" metadata:id="46b599ed-e0e6-46cc-93c9-0564bb8aff2f" doc:name="Students CSV File"/>
        <dw:transform-message metadata:id="eee60e7a-a87d-483b-bd84-1568e4ccda39" doc:name="Transform Message">
            <dw:set-payload><![CDATA[%dw 1.0
%output application/java
---
payload map ((payload01 , indexOfPayload01) -> {
name: payload01.Name,
marks: [payload01.Eng, payload01.Phy, payload01.Chem, payload01.Math]
}  as :object {class: "student.Student"})]]></dw:set-payload>
        </dw:transform-message>
        <foreach doc:name="For Each" collection="#[payload]">
            <db:stored-procedure config-ref="Oracle_Configuration" doc:name="Oracle Database 11.2g">
                <db:parameterized-query><![CDATA[{call scott.insert_students(:sname, :marks)}]]></db:parameterized-query>
                <db:in-param name="sname" type="VARCHAR" value="#[payload.getName()]"/>
                <db:in-param name="marks" type="ARRAY" value="#[castArray(payload.getMarks(),app.registry)]"/>
            </db:stored-procedure>
        </foreach>
        <logger message="Completed" level="INFO" doc:name="Logger"/>

</flow>


</mule>


I hope this tutorial will help those who were posting issues regarding stored procedure invocation issues in forums. You should also try for INOUT and OUT parameter types.

Author: Priyobroto Ghosh

Email: pbghosh@gmail.com

Strategies and techniques for building scalable and resilient microservices to refactor a monolithic application step-by-step, a free O'Reilly book. Brought to you in partnership with Lightbend.

Topics:
mule 3.7

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}