What is Ballerina?
Swan Lake

ballerinax/oracledb

 1.6.0

import ballerinax/oracledb;

Package overview

This package provides the functionality required to access and manipulate data stored in an Oracle database.

Prerequisite

Add the OracleDB thin driver ojdbc8.jar along with xdb.jar and xmlparserv2.jar as native library dependencies in your Ballerina project's Ballerina.toml file. It is recommended to use an oracle thin driver ojdbc8.jar version greater than 12.2.0.1.

Follow one of the methods below to add the JAR in the file:

  • Download the JAR and update the path.

    1[[platform.java11.dependency]]
    2path = "PATH"
  • Add JAR with the Maven dependency params.

    1[[platform.java11.dependency]]
    2groupId = "com.oracle.database.jdbc"
    3artifactId = "ojdbc8"
    4version = "12.2.0.1"
    5
    6[platform.java11.dependency]]
    7groupId = "com.oracle.database.xml"
    8artifactId = "xdb"
    9version = "21.1.0.0"
    10
    11[platform.java11.dependency]]
    12groupId = "com.oracle.database.xml"
    13artifactId = "xmlparserv2"
    14version = "12.2.0.1"

Client

To access a database, you must first create an oracledb:Client object. The samples for creating an OracleDB client can be found below.

Create a client

This sample shows the different ways of creating an oracledb:Client.

The client can be created with an empty constructor, and thereby, the client will be initialized with the default properties.

1oracledb:Client|sql:Error dbClient = new ();

The oracledb:Client receives the host, username, and password. Since the properties are passed in the same order as they are defined in the oracledb:Client, you can pass them without named parameters.

1oracledb:Client|sql:Error dbClient = new ("localhost", "adminUser", "adminPassword",
2 "ORCLCDB.localdomain", 1521);

In the example below, the oracledb:Client uses named parameters to pass the attributes since it is skipping some parameters in the constructor. Further, the oracledb:Options property is passed to configure the SSL, connection timeout, and a few other additional properties in the OracleDB client.

1string clientStorePath = check file:getAbsolutePath("./client-keystore.p12");
2string trustStorePath = check file:getAbsolutePath("./client-truststore.p12");
3
4oracledb:Options options = {
5 ssl: {
6 key: {
7 path: clientStorePath,
8 password: "password"
9 },
10 cert: {
11 path: trustStorePath,
12 password: "password"
13 }
14 },
15 loginTimeout: 1,
16 autoCommit: true,
17 connectTimeout: 30,
18 socketTimeout: 30
19};
20
21oracledb:Client|sql:Error dbClient = new (user = "adminUser", password = "adminPassword",
22 options = options);

Similarly, in the example below, the oracledb:Client uses the named parameters and it provides an unshared connection pool of the type of sql:ConnectionPool to be used within the client. For more details about connection pooling, see the sql Package.

1oracledb:Client|sql:Error dbClient = new (user = "adminUser", password = "adminPassword",
2 connectionPool = {maxOpenConnections: 5});

Handle connection pools

All database packages share the same connection pooling concept and there are three possible scenarios for connection pool handling. For its properties and possible values, see sql:ConnectionPool.

  1. Global, shareable, default connection pool

    If you do not provide the connectionPool field when creating the database client, a globally-shareable pool will be created for your database unless a connection pool matching with the properties you provided already exists.

    1oracledb:Client|sql:Error dbClient = new (user = "adminUser", password = "adminPassword");
  2. Client-owned, unsharable connection pool

    If you define the connectionPool field inline when creating the database client with the sql:ConnectionPool type, an unsharable connection pool will be created.

    1oracledb:Client|sql:Error dbClient = new (user = "adminUser", password = "adminPassword",
    2 connectionPool = { maxOpenConnections: 5 });
  3. Local, shareable connection pool

    If you create a record of the sql:ConnectionPool type and reuse that in the configuration of multiple clients, for each set of clients that connects to the same database instance with the same set of properties, a shared connection pool will be used.

    1sql:ConnectionPool connPool = {maxOpenConnections: 5};
    2
    3oracledb:Client|sql:Error dbClient1 =
    4 new (user = "adminUser", password = "adminPassword",
    5 connectionPool = connPool);
    6oracledb:Client|sql:Error dbClient2 =
    7 new (user = "adminUser", password = "adminPassword",
    8 connectionPool = connPool);
    9oracledb:Client|sql:Error dbClient3 =
    10 new (user = "adminUser", password = "adminPassword",
    11 connectionPool = connPool);

For more details about each property, see the oracledb:Client constructor.

The oracledb:Client references sql:Client and all the operations defined by the sql:Client will be supported by the oracledb:Client as well.

Close the client

Once all the database operations are performed, you can close the database client you have created by invoking the close() operation. This will close the corresponding connection pool if it is not shared by any other database clients.

1error? e = dbClient.close();

Or

1check dbClient.close();

Database operations

Once the client is created, database operations can be executed through that client. This package defines the interface and common properties that are shared among multiple database clients. It also supports querying, inserting, deleting, updating, and batch updating data.

Parameterized query

The sql:ParameterizedQuery is used to construct the SQL query to be executed by the client. You can create a query with constant or dynamic input data as follows.

Query with constant values

1sql:ParameterizedQuery query = `SELECT * FROM students
2 WHERE id < 10 AND age > 12`;

Query with dynamic values

1int[] ids = [10, 50];
2int age = 12;
3sql:ParameterizedQuery query = `SELECT * FROM students
4 WHERE id < ${ids[0]} AND age > ${age}`;

Moreover, the SQL package has sql:queryConcat() and sql:arrayFlattenQuery() util functions which make it easier to create a dynamic/constant complex query.

The sql:queryConcat() is used to create a single parameterized query by concatenating a set of parameterized queries. The sample below shows how to concatenate queries.

1int id = 10;
2int age = 12;
3sql:ParameterizedQuery query = `SELECT * FROM students`;
4sql:ParameterizedQuery query1 = ` WHERE id < ${id} AND age > ${age}`;
5sql:ParameterizedQuery sqlQuery = sql:queryConcat(query, query1);

The query with the IN operator can be created using the sql:ParameterizedQuery as shown below. Here you need to flatten the array and pass each element separated by a comma.

1int[] ids = [1, 2, 3];
2sql:ParameterizedQuery query = `SELECT count(*) as total FROM DataTable
3 WHERE row_id IN (${ids[0]}, ${ids[1]}, ${ids[2]})`;

The sql:arrayFlattenQuery() util function is used to make the array flatten easier. It makes the inclusion of varying array elements into the query easier by flattening the array to return a parameterized query. You can construct the complex dynamic query with the IN operator by using both functions as shown below.

1int[] ids = [1, 2];
2sql:ParameterizedQuery sqlQuery =
3 sql:queryConcat(`SELECT * FROM DataTable WHERE id IN (`,
4 sql:arrayFlattenQuery(ids), `)`);

Create tables

This sample creates a table with three columns. The first column is a primary key of type int while the second column is of type int and the other is of type varchar. The CREATE statement is executed via the execute remote function of the client.

1// Create the ‘Students’ table with the ‘id’, ‘name‘, and ‘age’ fields.
2sql:ExecutionResult result =
3 check dbClient->execute(`CREATE TABLE student (
4 id NUMBER GENERATED ALWAYS AS IDENTITY,,
5 age NUMBER,
6 name VARCHAR(255),
7 PRIMARY KEY (id)
8 )`);
9// A value of the `sql:ExecutionResult` type is returned for the `result`.

Insert data

These samples show the data insertion by executing an INSERT statement using the execute remote function of the client.

In this sample, the query parameter values are passed directly into the query statement of the execute remote function.

1sql:ExecutionResult result = check dbClient->execute(`INSERT INTO student(age, name)
2 VALUES (23, 'john')`);

In this sample, the parameter values, which are assigned to local variables are used to parameterize the SQL query in the execute remote function. This type of a parameterized SQL query can be used with any primitive Ballerina type such as string, int, float, or boolean and in that case, the corresponding SQL type of the parameter is derived from the type of the Ballerina variable that is passed in.

1string name = "Anne";
2int age = 8;
3
4sql:ParameterizedQuery query = `INSERT INTO student(age, name)
5 VALUES (${age}, ${name})`;
6sql:ExecutionResult result = check dbClient->execute(query);

In this sample, the parameter values are passed as an sql:TypedValue to the execute remote function. Use the corresponding subtype of the sql:TypedValue such as sql:VarcharValue, sql:CharValue, sql:IntegerValue, etc., when you need to provide more details such as the exact SQL type of the parameter.

1sql:VarcharValue name = new ("James");
2sql:IntegerValue age = new (10);
3
4sql:ParameterizedQuery query = `INSERT INTO student(age, name)
5 VALUES (${age}, ${name})`;
6sql:ExecutionResult result = check dbClient->execute(query);

Insert data with auto-generated keys

This sample demonstrates inserting data while returning the auto-generated keys. It achieves this by using the execute remote function to execute the INSERT statement.

1int age = 31;
2string name = "Kate";
3
4sql:ParameterizedQuery query = `INSERT INTO student(age, name)
5 VALUES (${age}, ${name})`;
6sql:ExecutionResult result = check dbClient->execute(query);
7
8// Number of rows affected by the execution of the query.
9int? count = result.affectedRowCount;
10
11// The integer or string generated by the database in response to a query execution.
12string|int? generatedKey = result.lastInsertId;

Query data

These samples show how to demonstrate the different usages of the query operation to query the database table and obtain the results.

This sample demonstrates querying data from a table in a database. First, a type is created to represent the returned result set. This record can be defined as an open or a closed record according to the requirement. If an open record is defined, the returned stream type will include both defined fields in the record and additional database columns fetched by the SQL query which are not defined in the record. Note the mapping of the database column to the returned record's property is case-insensitive if it is defined in the record(i.e., the ID column in the result can be mapped to the id property in the record). Additional column names added to the returned record as in the SQL query. If the record is defined as a closed record, only defined fields in the record are returned or gives an error when additional columns present in the SQL query. Next, the SELECT query is executed via the query remote function of the client. Once the query is executed, each data record can be retrieved by iterating through the result set. The stream returned by the SELECT operation holds a pointer to the actual data in the database and it loads data from the table only when it is accessed. This stream can be iterated only once.

1// Define an open record type to represent the results.
2type Student record {
3 int id;
4 int age;
5 string name;
6};
7
8// Select the data from the database table. The query parameters are passed
9// directly. Similar to the `execute` samples, parameters can be passed as
10// sub types of `sql:TypedValue` as well.
11int id = 10;
12int age = 12;
13sql:ParameterizedQuery query = `SELECT * FROM students
14 WHERE id < ${id} AND age > ${age}`;
15stream<Student, sql:Error?> resultStream = dbClient->query(query);
16
17// Iterating the returned table.
18check from Student student in resultStream
19 do {
20 // Can perform operations using the `student` record of type `Student`.
21 };

Defining the return type is optional and you can query the database without providing the result type. Hence, the above sample can be modified as follows with an open record type as the return type. The property name in the open record type will be the same as how the column is defined in the database.

1// Select the data from the database table. The query parameters are passed
2// directly. Similar to the `execute` samples, parameters can be passed as
3// sub types of `sql:TypedValue` as well.
4int id = 10;
5int age = 12;
6sql:ParameterizedQuery query = `SELECT * FROM students
7 WHERE id < ${id} AND age > ${age}`;
8stream<record{}, sql:Error?> resultStream = dbClient->query(query);
9
10// Iterating the returned table.
11check from record{} student in resultStream
12 do {
13 // Can perform operations using the `student` record.
14 io:println("Student name: ", student.value["name"]);
15 };

There are situations in which you may not want to iterate through the database and in that case, you may decide to use the queryRow() operation. If the provided return type is a record, this method returns only the first row retrieved by the query as a record.

1int id = 10;
2sql:ParameterizedQuery query = `SELECT * FROM students WHERE id = ${id}`;
3Student retrievedStudent = check dbClient->queryRow(query);

The queryRow() operation can also be used to retrieve a single value from the database (e.g., when querying using COUNT() and other SQL aggregation functions). If the provided return type is not a record (i.e., a primitive data type) , this operation will return the value of the first column of the first row retrieved by the query.

1int age = 12;
2sql:ParameterizedQuery query = `SELECT COUNT(*) FROM students WHERE age < ${age}`;
3int youngStudents = check dbClient->queryRow(query);

Update data

This sample demonstrates modifying data by executing an UPDATE statement via the execute remote function of the client.

1int age = 23;
2sql:ParameterizedQuery query = `UPDATE students SET name = 'John' WHERE age = ${age}`;
3sql:ExecutionResult result = check dbClient->execute(query);

Delete data

This sample demonstrates deleting data by executing a DELETE statement via the execute remote function of the client.

1string name = "John";
2sql:ParameterizedQuery query = `DELETE from students WHERE name = ${name}`;
3sql:ExecutionResult result = check dbClient->execute(query);

Batch update data

This sample demonstrates how to insert multiple records with a single INSERT statement that is executed via the batchExecute remote function of the client. This is done by creating a table with multiple records and parameterized SQL query as same as the above execute operations.

1// Create the table with the records that need to be inserted.
2var data = [
3 { name: "John", age: 25 },
4 { name: "Peter", age: 24 },
5 { name: "jane", age: 22 }
6];
7
8// Do the batch update by passing the batches.
9sql:ParameterizedQuery[] batch = from var row in data
10 select `INSERT INTO students ('name', 'age')
11 VALUES (${row.name}, ${row.age})`;
12sql:ExecutionResult[] result = check dbClient->batchExecute(batch);

Execute SQL stored procedures

This sample demonstrates how to execute a stored procedure with a single INSERT statement that is executed via the call remote function of the client.

1int uid = 10;
2sql:IntegerOutParameter insertId = new;
3
4sql:ProcedureCallResult result =
5 check dbClient->call(`call InsertPerson(${uid}, ${insertId})`);
6stream<record{}, sql:Error?>? resultStr = result.queryResult;
7if resultStr is stream<record{}, sql:Error?> {
8 check from record{} result in resultStr
9 do {
10 // Can perform operations using the `result` record.
11 };
12}
13check result.close();

Note that you have to invoke the close operation explicitly on the sql:ProcedureCallResult to release the connection resources and avoid a connection leak as shown above.

OracleDB specific custom data types

Interval types

OracleDB has two INTERVAL data types: INTERVAL YEAR TO MONTH and INTERVAL DAY TO SECOND to store the various INTERVAL periods.

The equivalent types in Ballerina are as follows.

1public type Sign +1|-1;
2
3public type IntervalYearToMonth record {|
4 Sign sign = +1;
5 int:Unsigned32 years?;
6 int:Unsigned32 months?;
7|};
8
9public type IntervalDayToSecond record {|
10 Sign sign = +1;
11 int:Unsigned32 days?;
12 int:Unsigned32 hours?;
13 int:Unsigned32 minutes?;
14 decimal seconds?;
15|};

Here, oracledb:Sign is used to mark the sign of the interval period and period values are always ZERO or positive integers.

1//INTERVAL '120-11' YEAR(3) TO MONTH
2oracledb:IntervalYearToMonth intervalYM = {years: 120, months: 11};
3//INTERVAL '120' YEAR(3)
4oracledb:IntervalYearToMonth intervalYM2 = {years: 120};
5//INTERVAL '-11' MONTH(3)
6oracledb:IntervalYearToMonth intervalYM3 = {months: 11, sign: -1};
7//INTERVAL '-120-11' YEAR(3) TO MONTH
8oracledb:IntervalYearToMonth intervalYM4 = {years: 120, months: 11, sign: -1};
9
10//INTERVAL '11 10:09:08.555' DAY TO SECOND(3)
11oracledb:IntervalDayToSecond intervalDS = {days: 11, hours: 10, minutes: 9, seconds: 8.555};
12//INTERVAL '-11 10:09:08.555' DAY TO SECOND(3)
13oracledb:IntervalDayToSecond intervalDS2 = {days: 11, hours: 10, minutes: 9, seconds: 8.555, sign: -1};
14//INTERVAL '-10:09:08.555' HOUR TO SECOND(3)
15oracledb:IntervalDayToSecond intervalDS3 = {hours: 10, minutes: 9, seconds: 8.555, sign: -1};
16//INTERVAL '11 00:09:08.55578' DAY TO SECOND(5)
17oracledb:IntervalDayToSecond intervalDS4 = {days: 11, minutes: 9, seconds: 8.55578};

VARRAY types

OracleDB has support for VARRAY data type and VARRAY consists a type name and elements attributes.

The VARRAY equivalent type in Ballerina is as follows.

1type ArrayValueType string?[]|int?[]|boolean?[]|float?[]|decimal?[]|byte[]?[];
2
3public type Varray record {|
4 string name;
5 ArrayValueType? elements;
6|};

Here, oracledb:Varray has two fields to set the type name and elements of the varray. In OracleDB, a VARRAY type can be created as follows.

1CREATE OR REPLACE TYPE CharArrayType AS VARRAY(6) OF VARCHAR(100);
2
3CREATE TABLE TestVarrayTable(
4 PK NUMBER GENERATED ALWAYS AS IDENTITY,
5 COL_CHARARR CharArrayType,
6 PRIMARY KEY(PK)
7);

In Ballerina, oracledb:Varray can be used to pass values for VARRAY data type as follows.

1string?[] charArray = [null, "Hello", "World"];
2Varray charVarray = { name:"CharArrayType", elements: charArray };

Note: The default thread pool size used in Ballerina is: the number of processors available * 2. You can configure the thread pool size by using the BALLERINA_MAX_POOL_SIZE environment variable.

Report issues

To report bugs, request new features, start new discussions, view project boards, etc., go to the Ballerina standard library parent repository

Useful links

Exported modules

ballerinax/oracledb


Digest

sha-256:

7e45fc558e22968be103e2452d15f60766db0ea3299bdf1610e47777417c5ab2


License: Apache-2.0

Created date: 29 November,2022


Ballerina compatibility

Platform: java11

Ballerina version: 2201.3.0


Keywords

database

client

network

SQL

RDBMS

OracleDB

Oracle


Contributors
B

Other versions