Module sqlite
kanushka/sqlite
Overview
The kanushka/sqlite package provides a native Ballerina connector for SQLite databases. This connector enables seamless integration with SQLite, offering a simple and type-safe API for database operations.
SQLite is a lightweight, serverless, self-contained SQL database engine that is widely used for embedded database applications, mobile apps, and local data storage.
Features
- Easy initialization - Simple client setup with file-based or in-memory databases
- Type-safe operations - Parameterized queries with compile-time type checking
- Connection pooling - Built-in connection pool management for optimal performance
- CRUD operations - Complete support for Create, Read, Update, and Delete operations
- Batch operations - Execute multiple SQL statements efficiently
- Stream processing - Handle large result sets with stream-based query results
- Auto-create databases - Automatically creates database files if they don't exist
Quickstart
Step 1: Install the package
Execute the command below to add the kanushka/sqlite package as a dependency to your Ballerina project.
bal add kanushka/sqlite
Step 2: Import the package
Import the kanushka/sqlite package into your Ballerina program.
import kanushka/sqlite; import ballerina/io;
Step 3: Initialize the SQLite client
Create a SQLite client by providing the database file path.
sqlite:Client sqliteClient = check new ({path: "./myapp.db"});
Step 4: Execute database operations
Create a table
_ = check sqliteClient->execute(` CREATE TABLE IF NOT EXISTS products ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, price REAL NOT NULL, quantity INTEGER DEFAULT 0 ) `);
Insert data
string productName = "Laptop"; float productPrice = 999.99; int productQuantity = 10; sql:ExecutionResult result = check sqliteClient->execute(` INSERT INTO products (name, price, quantity) VALUES (${productName}, ${productPrice}, ${productQuantity}) `); io:println("Inserted product with ID: ", result.lastInsertId);
Query data
stream<record {}, sql:Error?> productStream = sqliteClient->query(`SELECT * FROM products WHERE price > 500`); check from record {} product in productStream do { io:println("Product: ", product); };
Query single row
int productId = 1; record {}|sql:Error product = sqliteClient->queryRow(`SELECT * FROM products WHERE id = ${productId}`); if product is record {} { io:println("Found product: ", product); }
Update data
int newQuantity = 15; int productId = 1; sql:ExecutionResult updateResult = check sqliteClient->execute(` UPDATE products SET quantity = ${newQuantity} WHERE id = ${productId} `); io:println("Rows affected: ", updateResult.affectedRowCount);
Delete data
int productId = 1; sql:ExecutionResult deleteResult = check sqliteClient->execute(` DELETE FROM products WHERE id = ${productId} `);
Step 5: Close the client
Always close the client when done to release resources.
check sqliteClient.close();
Examples
Example 1: File-based database
import kanushka/sqlite; import ballerina/io; import ballerina/sql; public function main() returns error? { // Initialize client with file-based database sqlite:Client db = check new ({path: "./users.db"}); // Create table _ = check db->execute(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, email TEXT NOT NULL, created_at INTEGER DEFAULT (strftime('%s', 'now')) ) `); // Insert user string username = "john_doe"; string email = "john@example.com"; sql:ExecutionResult insertResult = check db->execute(` INSERT INTO users (username, email) VALUES (${username}, ${email}) `); io:println("User created with ID: ", insertResult.lastInsertId); // Query all users stream<record {}, sql:Error?> users = db->query(`SELECT * FROM users`); check from record {} user in users do { io:println("User: ", user); }; check db.close(); }
Example 2: In-memory database
import kanushka/sqlite; import ballerina/io; import ballerina/sql; public function main() returns error? { // Initialize in-memory database (data lost when connection closes) sqlite:Client db = check new ({path: ":memory:"}); // Create temporary table _ = check db->execute(` CREATE TABLE temp_data ( id INTEGER PRIMARY KEY, value TEXT ) `); // Insert temporary data _ = check db->execute(`INSERT INTO temp_data VALUES (1, 'temporary')`); // Query data record {}|sql:Error result = db->queryRow(`SELECT * FROM temp_data WHERE id = 1`); if result is record {} { io:println("Temp data: ", result); } check db.close(); }
Example 3: Batch operations
import kanushka/sqlite; import ballerina/io; import ballerina/sql; public function main() returns error? { sqlite:Client db = check new ({path: "./inventory.db"}); _ = check db->execute(` CREATE TABLE IF NOT EXISTS items ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, stock INTEGER NOT NULL ) `); // Batch insert multiple items sql:ParameterizedQuery[] insertQueries = [ `INSERT INTO items (name, stock) VALUES ('Item A', 100)`, `INSERT INTO items (name, stock) VALUES ('Item B', 200)`, `INSERT INTO items (name, stock) VALUES ('Item C', 150)` ]; sql:ExecutionResult[] results = check db->batchExecute(insertQueries); io:println("Inserted ", results.length(), " items"); check db.close(); }
Example 4: Connection pooling
import kanushka/sqlite; import ballerina/sql; public function main() returns error? { // Configure connection pool sql:ConnectionPool connectionPool = { maxOpenConnections: 10, maxConnectionLifeTime: 1800, minIdleConnections: 5 }; sqlite:Client db = check new ({ path: "./app.db", connectionPool: connectionPool }); // Use the client for database operations // Connection pool manages connections automatically check db.close(); }
Example 5: Using with custom properties
import kanushka/sqlite; import ballerina/sql; public function main() returns error? { // Configure SQLite-specific properties sqlite:Options options = { properties: { "journal_mode": "WAL", "synchronous": "NORMAL" } }; sqlite:Client db = check new ({ path: "./app.db", options: options }); // Use the client check db.close(); }
Configuration
Client configuration
The ClientConfiguration record supports the following fields:
| Field | Type | Description | Required |
|---|---|---|---|
path | string | Path to the SQLite database file (e.g., "./mydb.db") or ":memory:" for in-memory database | Yes |
options | Options? | SQLite-specific options including custom properties | No |
connectionPool | sql:ConnectionPool? | Connection pool configuration | No |
Connection pool configuration
The sql:ConnectionPool record supports the following fields:
| Field | Type | Description | Default |
|---|---|---|---|
maxOpenConnections | int | Maximum number of open connections | 15 |
maxConnectionLifeTime | decimal | Maximum lifetime of a connection in seconds | 1800 |
minIdleConnections | int | Minimum number of idle connections | Same as maxOpenConnections |
connectionTimeout | decimal | Maximum time to wait for a connection in seconds | 30 |
Best practices
- Always close the client - Call
close()when done to release resources - Use parameterized queries - Prevent SQL injection by using parameterized queries with
${}syntax - Handle errors properly - Use
checkor explicit error handling for all database operations - Configure connection pools - Adjust pool settings based on your application's needs
- Use transactions - For multiple related operations, consider using transactions (if supported)
- Stream large results - Use query streams for large result sets to avoid memory issues
Issues and projects
If you encounter any issues or have suggestions, please report them in the GitHub repository.