Skip to content

bluecolor/nodejdbc

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

86 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

nodejdbc

JDBC API wrapper for nodejs

A promise based JDBC wrapper for nodejs. Uses bluebird for promise integration and node-java for calling java methods.

One of the good things about java is its standard JDBC API. JDBC API provides a standard way for the client applications to access a data source. It has interface methods that enable clients to perform DDL(Data Definition Language) or DML(Data Manipulation Language) operations on the target data source. JDBC is oriented towards relational database systems.

nodejdbc tries to wrap JDBC API and provide a consistent way of accessing relational database systems. nodejdbc API also tries to extend JDBC API by introducing new methods

Installation

npm install nodejdbc

Building

  • make sure git is installed
  • cd path/to/install/directory
  • git clone https://github.com/blue-color/nodejdbc.git
  • cd nodejdbc && make build
  • check out the lib folder for library files

Examples

To give a real insight about the usage of the API lets create sample datastores on SQLite database. Other JDBC-Compliant databases can also be chosen but do not forget to convert DDL,DML statements to apropriate syntax.

You can follow the tests by preparing your environment like;

  • under nodejdbc mkdir demo && cd demo
  • mkdir driver # i preferer lib as name but it would be confusing
  • make sure you have the JDBC driver for SQLite under the driver folder you have created on previous step
  • touch test.coffee # Assuming you have the coffeescript installed
  • install following packages lodash , bluebird
  • use your text editor to copy and try the following examples

Example Outlaw DB

Our example datasource contains data about the famous outlaws of the Wild West

_ = require 'lodash'
NodeJDBC = require './nodejdbc'
Promise = require 'bluebird'

OUTLAW_DDL = '
    CREATE TABLE OUTLAW
    (
        ID              INT,
        NAME            TEXT,
        AKA             TEXT,
        GANG            INT,
        BIRTH_DATE      DATE,
        DEATH_DATE      DATE,
        HANDEDNESS      INT
    )'

OUTLAW_DML = [
    'INSERT INTO OUTLAW 
        (ID, NAME, AKA, GANG, BIRTH_DATE, DEATH_DATE, HANDEDNESS)
     VALUES 
        (10, "Henry McCarty", "Billy the Kid", 10, "1859.September.17","1881.July.14",10)',

    'INSERT INTO OUTLAW 
        (ID, NAME, AKA, GANG, BIRTH_DATE, DEATH_DATE, HANDEDNESS)
     VALUES 
        (20, "Josiah Gordon Scurlock", "Doc", 10, "1849.January.11","1929.July.25",20)',
    
    'INSERT INTO OUTLAW 
        (ID, NAME, AKA, GANG, BIRTH_DATE, DEATH_DATE, HANDEDNESS)
     VALUES 
        (30, "Jesse Woodson James", "Jesse James", 20, "1847.September.05","1882.April.03",20)',
     
    'INSERT INTO OUTLAW
        (ID, NAME, AKA, GANG, BIRTH_DATE, DEATH_DATE, HANDEDNESS)
     VALUES  
        (40, "Alexander Franklin James", "Frank", 20, "1843.January.10","1915.February.18",20)',

    'INSERT INTO OUTLAW 
        (ID, NAME, AKA, GANG, BIRTH_DATE, DEATH_DATE, HANDEDNESS)
     VALUES
        (50,"Harry Alonzo Longabaugh","Sundance Kid",30, "1867.July.17","1908.November.07",30)',

    'INSERT INTO OUTLAW 
        (ID, NAME, AKA, GANG, BIRTH_DATE, DEATH_DATE, HANDEDNESS)
     VALUES
        (60,"Robert Leroy Parker","Butch Cassidy",30, "1867.July.17","1908.November.07",30)',

    'INSERT INTO OUTLAW 
        (ID, NAME, AKA, GANG, BIRTH_DATE, DEATH_DATE, HANDEDNESS)
     VALUES
        (70,"John Wesley Hardin","Little Arkansaw",40, "1853.May.26","1895.Agust.19",40)'    
]

GANG_DDL = '
    CREATE TABLE GANG
    (
        ID      INT,
        NAME    TEXT,
        LEADER  INT
    )'
    
GANG_DML = [
    'INSERT INTO GANG (ID,NAME, LEADER) VALUES (10,"Regulators",10)',
    'INSERT INTO GANG (ID,NAME, LEADER) VALUES (20,"James-Younger",30)'
    'INSERT INTO GANG (ID,NAME, LEADER) VALUES (30,"Wild Bunch",60)',
    'INSERT INTO GANG (ID,NAME, LEADER) VALUES (40,"Hardin Gang",70)'
]

HANDEDNESS_DDL = '
    CREATE TABLE HANDEDNESS
    (
        ID      INT,
        NAME    TEXT,
        DESC    TEXT
    )'

HANDEDNESS_DML = [
    'INSERT INTO HANDEDNESS 
        (ID, NAME, DESC) 
    VALUES 
        (10, "Left Handed",  "Uses left hand primarily")',
    
    'INSERT INTO HANDEDNESS 
        (ID, NAME, DESC) 
    VALUES 
        (20, "Right Handed", "Uses right hand primarily")',
    
    'INSERT INTO HANDEDNESS 
        (ID, NAME, DESC) 
    VALUES
        (30, "Mixed Handed", "Uses one or the other hand for different tasks")',
    
    'INSERT INTO HANDEDNESS 
        (ID, NAME, DESC) 
    VALUES 
        (40, "Ambidextrous", "Uses both hands equally.")'
]

Above code block contains the require statements for needed libraries and SQL statements for creating and loading our data stores. You have to modify these SQL statements if you connect to a database other than SQLite.

Store SQL ddl and dml statements in corresponding arrays for easy way to acceess them.

ddls    = [OUTLAW_DDL,GANG_DDL,HANDEDNESS_DDL]
dmls    = [OUTLAW_DML,GANG_DML,HANDEDNESS_DML]
tables  = ['OUTLAW','GANG','HANDEDNESS']  

Create a configuration object like the following to access to your database.

config =
    libs : ['driver/sqlite-jdbc-3.8.11.2.jar']
    className: 'org.sqlite.JDBC'
    url: 'jdbc:sqlite:test.db'

This config object contains attribute;

  • libs: SQLite JDBC driver path (relative to your working directory)
  • className: Name of the class that will be loaded. (check out this discussion for more info.)
  • url: jdbc url for acceessing our SQLite database

Again, these settings has to be modified for different databases.

Now lets define some methods that demostrates CRUD operations.

DROP

###
    @Method dropClean

    Drops all _DDL tables. This method throws
    TableNotFound exception if the tables do not exist
    in database.

    @return returns a promise when all the tables are dropped 
    successfully  
###
dropClean = ->
    nodejdbc = new NodeJDBC(config)
    promises = tables.map (table) ->
        nodejdbc.createStatement().then (statement) ->
            sql = 'DROP TABLE ' + table
            console.log "Executing #{sql}"

            exception = (e) ->
                console.log "Failed to drop table #{table}"
                console.log e
            statement.executeUpdate(sql).catch(exception) 

    close = ->
        nodejdbc.getConnection().then (connection) -> 
            connection.close()

    Promise.all(promises).finally(close)        

dropClean method executes a DROP TABLE statement for each *_DDL table and returns a promise when all the statements are fulfilled.

DELETE

###
    @Method deleteClean

    Deletes all the records from the tables in our Outlaw DB.
    We can also get and map the number of records deleted for each statement.
    
    @return returns a promise when all the DELETE statements are fulfilled.

###
deleteClean = ->
    nodejdbc = new NodeJDBC(config)
    promises = tables.map (table) ->
        nodejdbc.createStatement().then (statement) ->
            sql = 'DELETE FROM ' + table
            console.log "Executing #{sql}"

            exception = (e) ->
                console.log "Failed to delete table #{table}"
                console.log e
        
            statement.executeUpdate(sql).catch(exception)

    # if db is not in AutoCommit mode          
    commit = ->
        nodejdbc.getConnection().then (connection) ->
            connection.commit()
    close = ->
        nodejdbc.getConnection().then (connection) -> 
            connection.close()

    Promise.all(promises).finally(close)   

deleteClean method executes a DELETE FROM statement for all tables in our DB. Returns a promise when all the statements are fulfilled. In here we can also return the number of records modified for each statement.

CREATE

###
    @Method create

    executes CREATE ddl statements.
    
    @return returns a promise when all the CREATE statements are fulfilled.
###
create = ->
    nodejdbc = new NodeJDBC(config)
    promises = ddls.map (ddl) ->
            nodejdbc.createStatement().then (statement) ->
                console.log "Executing \n #{ddl}"

                exception = (e) ->
                    console.log e
                
                statement.executeUpdate(ddl).catch(exception) 

    close = ->
        nodejdbc.getConnection().then (connection) -> 
            connection.close()
            

    Promise.all(promises).then(close) 

create method executes a CREATE TABLE {TABLE_NAME} ( ... ) for the tables in our Outlaw DB. Returns a promise when all the statements are fulfilled.

INSERT

###
    @Method load
    Inserts records to tables.
    @return returns a promise when all the CREATE statements are fulfilled.
###
load = ->
    nodejdbc = new NodeJDBC(config)
    promises = _.flatten(dmls).map (dml) ->
            nodejdbc.createStatement().then (statement) ->
                console.log "Executing \n #{dml}"

                exception = (e) ->
                    console.log e
                statement.executeUpdate(dml).catch(exception)

    # if db is not in AutoCommit mode            
    commit = ->
        nodejdbc.getConnection().then (connection) ->
            connection.commit()
    close = ->
        nodejdbc.getConnection().then (connection) -> 
            connection.close() 
                            
    Promise.all(promises).then(close)

load method executes a INSERT statements on our datastores and loads demo data. Returns promise when all the statements are fulfilled.

SELECT

###
    @Method read
    executes a sample select statement and returns all the records as a json object

    @return [Object] result all records returned by the sample SQL query
###
read = ->
    sql = 'SELECT NAME,AKA FROM OUTLAW'
    nodejdbc = new NodeJDBC(config)
    promise = nodejdbc.createStatement().then (statement) ->
        statement.executeQuery(sql).then (rs)->
            result = []
            while rs.next()
                name = rs.getString('NAME')
                aka = rs.getString('AKA')
                result.push {} =
                    name: name
                    aka: aka
            result

            
    promise.then (result) ->
        nodejdbc.getConnection().then (connection) -> 
            connection.close()
            result   

read method executes a sample SELECT statement and returns a promise that resolves to an array containing all the records returned by the given sample query.

Now lets call these methods to execute some test stuff...

Call the following to execute drop , create and load operations in order.

# Drop -> Create -> Insert
dropClean().then(create).then(load)

If it is the first time you execute this script or in other words; if the tables do not exist in db yet you will see some exception messages on the screen, these messages can be ignored.

drop, clean, load, delete which means eventually we will get set of empty tables

# Drop -> Create -> Insert -> Delete
dropClean().then(create).then(load).then(deleteClean)

Execute a sample SELECT statement. we can also modify our read method so that it accepts sql statements and returns result like read(sql)

# Read
read().then (result) ->
    console.log result

Stored Procedure Call

It is time to call stored procedures. For that section you can use any database that allows you to write stored procedures. Make sure you have the appropriate JDBC driver and able to connect to your database and have the the necessary permissions granted to your user.

The following example shows how to call a procedure in oracle db. To start with; we can create a package that will contain the procedures ,we will be calling using NodeJDBC

Without too much detail, example oracle db configuration is like;

  • jdbc url: jdbc:oracle:thin:@//win:1521/orcl
  • user name : demo
  • password : demo

The name of our package will be PKG_OUTLAW

and the package spec. for DEMO.PKG_OUTLAW is;

create or replace package DEMO.PKG_OUTLAW
as
    procedure PRC_FLIP_COIN(guess in varchar2, result out varchar2, message out varchar2);
end;

package body of DEMO.PKG_OUTLAW is;

create or replace package body DEMO.PKG_OUTLAW
IS
    
    -- procedure PRC_FLIP_COIN
    -- Flips a coin and returns result and the message 
    -- 
    -- @param [in] [varchar2] guess your guess it can be either 'Heads' or 'Tails' (case insensitve)
    -- @param [out][varchar2] result result of the coin toss. can be 'Heads' or 'Tails'
    -- @param [out][varchar2] message text about game result
    procedure PRC_FLIP_COIN(guess in varchar2, result out varchar2, message out varchar2)
    is
        one_two int;  
        type result_set IS TABLE OF VARCHAR2(5);
        heads_tails result_set := result_set('HEADS','TAILS');
    begin
        
        if not ( upper(guess) member of heads_tails )
        then  
            result  := 'Upright';
            message := 'Please pass only "HEADS" or "TAILS" as input';
            return;
        end if;
        
        one_two := round(dbms_random.value) + 1;
        result  := initcap(heads_tails(one_two));
        message := case upper(result) when upper(guess) then 'Nice Shot! You Win' else 'Sorry! You Lost' end;
        
    end;

end;

This is a simple coin toss example.The procedure PRC_FLIP_COIN takes three arguments

  • guess in varchar2 input parameter guess, which can be either 'Heads' or 'Tails' (case insensitve)
  • result out varchar2 output parameter result, can be 'Heads' or 'Tails'
  • message out varchar2 output parameter message, text about game result

Afer you have compiled the package successfuly. You can test the PRC_FLIP_COIN in plsq with

declare 
    result  varchar2(100);
    message varchar2(100);
begin
  DEMO.PKG_OUTLAW.PRC_FLIP_COIN('Heads',result,message);
  
  dbms_output.put_line('Result : ' || result);
  dbms_output.put_line('Message: ' || message);
  
end;

Now, we can write the above script with NodeJDBC like;

config =
    libs : ['test/lib/ojdbc7.jar']
    className: 'oracle.jdbc.driver.OracleDriver'
    url: 'jdbc:oracle:thin:@//win:1521/orcl',
    username: 'demo',
    password: 'demo'

nodejdbc = new NodeJDBC(config)

nodejdbc.getConnection().then (connection) ->    
    call = '{call demo.pkg_outlaw.prc_flip_coin(?,?,?)}'
    connection.prepareCall(call).then (statement) ->
        statement.setString 1, 'Heads'
        statement.registerOutParameter 2,'VARCHAR'
        statement.registerOutParameter 3,'VARCHAR'
        statement.executeUpdate().then ()->
            console.log "Result : #{statement.getString 2 }"
            console.log "Message: #{statement.getString 3 }"
            connection.close()

If you are lucky, this should print

Result : Heads
Message: Nice Shot! You Win 

These examples are also available in the repository on a single file test.coffee

API Docs

API docs are located under doc folder. Thanks to codo to generate API docs just run;

make docs

Tests

Unit tests are located under test folder. You'll need mocha to execute unit tests. Run the tests with

make build
make test

Make sure you have the followind test/lib/sqlite-jdbc-3.8.11.2.jar .

Getting Help

Use issues with appropriate labels for;

  • Bugs
  • Feature requests
  • Questions

License

Copyright (c) 2014-2015, BlueColor

Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

About

JDBC API wrapper for nodejs

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •