Using JDBC from C++

Introduction

If you've ever had to switch database drivers you know what a hassle that can be:

  • different performance issues.
  • different bugs.
  • different application behavior.

Many applications standardize on one or a few drivers for just that reason. If your environment is predominantly Java-based, your (JDBC) drivers won't help you much with C++ applications, unless you're willing to entrust your data health to the JDBC-ODBC bridge.

Exposing your Java data architecture to C++ can prevent driver-related problems or it can even allow you to add one of many free embedded Java database engines to your C++ application.

This is not the most compelling use case for most users, but we have a couple of customers who had such big problems with their database layers that they chose this integration route.

Architecture

Your C++ application uses generated in-process bindings to use the JDBC API. The following example code is from the bundled hsqldb example. hsqldb is a lightweight, embedded Java database, that you can now use within your C++ application.

#include <iostream>
using namespace std;

#include "xmog_java_client.h"
#include "java_lang_pkg.h"
#include "java_sql_pkg.h"
#include "java_util_pkg.h"

void dump( ResultSet rs ) 
{
	// the order of the rows in a cursor
	// are implementation dependent unless you use the SQL ORDER statement
	ResultSetMetaData meta   = rs.getMetaData();
	int               colmax = meta.getColumnCount();
	int               i;
    Object            o = null;

	// the result set is a cursor into the data.  You can only
	// point to one row at a time
	// assume we are pointing to BEFORE the first row
	// rs.next() points to next row and returns true
	// or false if there is no next row, which breaks the loop
	for (; rs.next(); ) {
		for (i = 0; i < colmax; ++i) {
			o = rs.getObject(i + 1);    // Is SQL the first column is indexed

			// with 1 not 0
			cout << (const char*)o.toString() << " ";
		}

		cout << " " << endl;
	}
}


void update( Connection conn, String expression)
{
	Statement st = null;

	st = conn.createStatement();    // statements

	int i = st.executeUpdate(expression);    // run the query

	if (i == -1) {
		cout << "db error : " << (const char*)expression << endl;
	}

	st.close();
}

void query( Connection conn, String expression )
{
	Statement st = null;
	ResultSet rs = null;

	st = conn.createStatement();         // statement objects can be reused with

	// repeated calls to execute but we
	// choose to make a new one each time
	rs = st.executeQuery(expression);    // run the query

	// do something with the result set.
	dump(rs);
	st.close();    
	// NOTE!! if you close a statement the associated ResultSet is closed 
	// too so you should copy the contents to some other object.
	// the result set is invalidated also  if you recycle an Statement
	// and try to execute some other query before the result set has been
	// completely examined.
}

int main(int argc, char * argv[])
{
	//the database filename
	const char *	db_file_name_prefix = argc > 1 ? argv[ 1 ] : "test_db";
	//the database connection
	Connection		conn = null;

	try
	{
		xmog_jvm_loader &	loader = xmog_jvm_loader::get_jvm_loader( true, true, TraceJvm, TraceWarnings );

		loader.appendToClassPath( "../lib/hsqldb.jar" );
		loader.appendToClassPath( "../../lib/hsqldb.jar" );

		if( argc > 1 && !strcmp( argv[ 1 ], "-info" ) )
			loader.printLdLibraryPathAndExit();

		// make the JDBC driver available by preloading it
		Class::forName("org.hsqldb.jdbcDriver");

        // connect to the database.   This will load the db files and start the
        // database if it is not alread running.
        // db_file_name_prefix is used to open or create files that hold the state
        // of the db.
        // It can contain directory names relative to the
        // current working directory
		conn = DriverManager::getConnection( String("jdbc:hsqldb:").concat( db_file_name_prefix ),
                                           "sa",                     // username
                                           "");                      // password

        // create a table and insert some entries
		// this will only worl the first time and throw an exception every following time
		// because the table already exists 
		try
		{
			update( conn, "CREATE TABLE sample_table ( id INTEGER IDENTITY, str_col VARCHAR(256), num_col INTEGER)");

			// add some rows - will create duplicates if run more then once
			// the id column is automatically generated
			update( conn, "INSERT INTO sample_table(str_col,num_col) VALUES('Ford', 100)");
			update( conn, "INSERT INTO sample_table(str_col,num_col) VALUES('Toyota', 200)");
			update( conn, "INSERT INTO sample_table(str_col,num_col) VALUES('Honda', 300)");
			update( conn, "INSERT INTO sample_table(str_col,num_col) VALUES('GM', 400)");
			update( conn, "INSERT INTO sample_table(str_col,num_col) VALUES('BMW', 80)");
			update( conn, "INSERT INTO sample_table(str_col,num_col) VALUES('Mercedes-Benz', 60)");
			update( conn, "INSERT INTO sample_table(str_col,num_col) VALUES('VW', 800)");
		}
		catch( Throwable & it )
		{
			cout << (const char*)it.toString() << endl;
		}
		catch( xmog_exception & ie )
		{
		}

		cout << "---------------" << endl;

		// do a query
        query( conn, "SELECT * FROM sample_table WHERE num_col < 250");

		cout << "---------------" << endl;

		// do another query
        query( conn, "SELECT str_col FROM sample_table WHERE num_col >= 100");

		cout << "---------------" << endl;

		conn.close();
	}
	catch( Throwable & t )
	{
		cout << "Caught throwable of type " << (const char*)t.getClass().getName() << endl;
		cout << (const char*)t.toString() << endl;
		return 1;
	}
	catch( xmog_exception & xe )
	{
		cout << "Caught xmog_exception" << endl;

		char *	msg = xe.get_message_chars();

		if( msg )
		{
			cout << msg << endl;
			xmog_java_string::free( msg );
		}
		return 1;
	}

	return 0;
}


Copyright 2006-2011 by Codemesh, Inc., ALL RIGHTS RESERVED

:
jdbc c++
home products support customers partners newsroom about us contact us