This module provides access to JDBC from Node.js.
JDBC is "Java Database Connectivity" and it is the standard API and library set for connecting to SQL databases from Java.
This module works with Trireme, which is a Node.js runtime built in Java. It will not work in standard Node.js. There are other modules out there that use other techniques to attach to databases from Node.
Trireme itself is used at Apigee and elsewhere to embed Node.js scripts (possibly in large numbers) inside a Java Virtual Machine. However, it cannot execute the same native code libraries that "normal" node.js code can. On the other hand, since it runs in Java, we can take advantage of JDBC, and the huge number of quality drivers that provide database access without compiling any native code.
var jdbc = require('trireme-jdbc');
var db = new jdbc.Database({
url: 'jdbc:oracle:thin:@//oraclehostname:1521/ORCL',
properties: {
user: 'scott',
password: 'tiger',
},
minConnections: 1,
maxConnections: 20,
idleTimeout: 60
});
Note: If you are running "regular" Node.js and not Trireme, then you will get an error at this point.
The "Database" object represents a pool of connections to the database. It may be used to run queries or manage transactions. It takes as its argument a single object, with the following options:
Since the "Database" does not try to connect to the actual database until it's used, it does not block and returns the result immediately.
db.execute('select * from employees',
function(err, result, rows) {
rows.forEach(function (row) {
console.log('Row: %j', row);
});
});
A basic query is an SQL statement. The callback is called after the query is complete with three arguments:
Queries are non-blocking. The result callback will only be delivered when the query is complete and will never block the thread.
Each successful query returns a "result" object. It may contain one field:
Each row is an object. Each column in the row is represented as a single property, with the name of the propery being the column name, and the value being the column value. The type of value depends on what the database returns -- see below for more.
Note that some JDBC drivers return upper-case column names even if the query used lowercase or mixed-case.
db.execute('select * from employees where salary > ?',
[ 100000 ],
function(err, result, rows) {
// ...
}
});
db.execute('insert into employees (name, salary) values (?, ?)',
[ 'Josiah Carberry', 0 ]
function(err) {
// ...
}
});
db.execute('update employees set salary = 10000 where name = ?',
[ 'Josiah Carberry' ],
function(err, result) {
console.log('Affected %d rows', result.updateCount);
// ...
}
});
Parameters may be inserted into any query using "?" just like in regular JDBC. When parameters are used, the second argument to "execute" must be an array, with the parameters in the order in which the "?"s appear.
Note that parameterized queries like this are always the correct way to write queries in JDBC for two important reasons:
db.executeStreaming('select * from employees',
function(err, result, rowStream) {
rowStream.on('data', function(row) {
console.log('Got row %j', row);
});
rowStream.on('end', function() {
console.log('End of query');
});
});
Large queries will use lots of memory because the entire result set is streamed into memory, then turned into a JavaScript array, before invoking the callback.
"executeStreaming" works just like "execute," but the third parameter to the callback is a stream rather than an array of rows. The caller must then use normal stream techniques such as calling "read" or listening for "data" callbacks to receive the results.
Technically, the object that is returned is a "ReadableStream" that runs in object mode. That means that each time the "data" callback is called, or "read" is called, the result is a single JavaScript object that represents a single row.
Since that is the case, the stream may of course be paused and resumed just like a real stream, which reduces memory pressure on the node app, since the JDBC driver can buffer batches of rows on the database side as well.
Note that the database connection is kept open and allocated to the stream until the entire stream has been read. If the goal is not to read the entire stream, then you must call "destroy" on the stream to return the connection to the pool.
db.beginTransaction(function(err, tran) {
tran.execute('select * from employees where name = ?',
[ 'Homer Simpson' ],
function(err, result, rows) {
// ...
tran.commit(function(err) {
// .. Should check the error here
});
});
});
"beginTransaction" starts a database transaction. The transaction object that is passed to the callback (as the second element) may be used to call "execute" and "executeStreaming" just like a regular connection.
When finished, you must call either "rollback" or "commit." Otherwise, the connection will never be returned to the pool.
db.close();
It's a good idea to call "close" on the Database to return connections if you know that you are done with the database and don't need it any more.
When results are returned from the database, the value of each column is mapped to a JavaScript type depending on the SQL data type that is returned from the database. The mapping is as follows:
When passing parameters to the "execute" methods to execute a parameterized query, the following rules are followed to map each JavaScript object to an SQL type:
The "Database" object maintains a connection pool automatically. Every time "execute" is called, a connection is removed from the pool.
Connections are returned in the following cases:
This module is a part of the "trireme-utils" module. When embedding Trireme into an existing Java application, include "trireme-utils" and this functionality will be automatically available.
You can also run "trireme" from the command line by pulling it from NPM:
sudo npm install -g trireme
trireme <script name>
You will need to have your JDBC driver in the classpath for this to work. The "trireme" script installed by the NPM module supports the environment variable "TRIREME_CLASSPATH", which will be appended to the normal classpath used to run Trireme.
"npm test" will test this module using the Trireme binary that it depends on, and also using the HSQLDB driver included with the distribution.
In order to test with another database, do the following:
For example:
export TRIREME_CLASSPATH=$PWD/drivers/hsqldb-2.3.2.jar
trireme /usr/local/bin/mocha -R spec
Note that not every data type is supported by every JDBC driver, so if this fails with your database, then let's take a look at the data types.