我去年做了个内存数据库,自以为功能很强大。内存数据库是独立运行的程序,客户端通过socket访问,传送SQL语句并得到结果,为此我提供了一个C接口的客户端API。
但如果要做到更好的通用性,必须照顾IT世界数量最多的两类人:java程序员和c#程序员。c#我的同事封装了ado.net驱动。这几天我实现了jdbc的驱动,本文记录实现过程的一些心得。
本人较为擅长的是C/C++,对java的?嗦语法实在不太感冒,前阵子用java写过一个规模不大的android程序,才敢挑战写一个jdbc驱动的任务。
我的开发环境是比较小众的mac os x,正好考验一下java所谓的跨平台。
第一步,先将C接口的客户端API封装成一个java类:LxjDbApi.java
package com.lxjdb.jdbc;
public class LxjDbApi {
public LxjDbApi(){
System.loadLibrary("LxjDbJdbcApi");
}
public native long Open(String host, int port, String user, String pwd);
public native void Close(long conn);
public native int Exec(long conn, String sql, String[] dbInfo); // 执行一个sql语句
public native int Rows(long conn); // 总行数
public native int Cols(long conn); // 总列数
// 得到列信息
public native int GetColInfoByIndex(long conn, int col, String[] retName, int[] lenTypePos);
public native int GetColInfoByName(long conn, String name, int[] lenTypePos);
public native int Next(long conn); // 到下一条记录,成功返回1,到记录集结束,则为0,错误返回负数
public native int GotoRec(long conn, int recNo); // 记录号从1开始
// 取字段值:1.总是返回字符串结果,2.字符串变量要预先分配至少2048大小的长度
public native int LxjDbGetValByName(long conn, String fieldName, String[] retVal);
// 列编号从0开始
public native int GetValByIndex(long conn, int col, String[] retVal);
}
然后用javah生成
c语言的头文件来编译jni,将c文件存放在jni目录下,在终端下使用命令行。
javah -classpath bin -d jni com.lxjdb.jdbc.LxjDbApi
下面是cpp代码,是调用我们的c语言api:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "com_lxjdb_jdbc_LxjDbApi.h"
/* Header for class com_lxjdb_jdbc_LxjDbApi */
#include "LxjDbApi.h"
/*
* Class: com_lxjdb_jdbc_LxjDbApi
* Method: Open
* Signature: (Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)Ljava/lang/Long;
*/
JNIEXPORT jlong JNICALL Java_com_lxjdb_jdbc_LxjDbApi_Open
(JNIEnv * env, jobject obj, jstring host, jint port, jstring user, jstring pwd)
{
LxjDbInit();
const char* pHost = env->GetStringUTFChars(host, 0);
const char* pUser = env->GetStringUTFChars(user, 0);
const char* pPwd = env->GetStringUTFChars(pwd, 0);
void* conn = LxjDbOpen(pHost, port, "", pUser, pPwd);
env->ReleaseStringUTFChars(host, pHost);
env->ReleaseStringUTFChars(pwd, pPwd);
env->ReleaseStringUTFChars(user, pUser);
return((jlong)conn);
}
/*
* Class: com_lxjdb_jdbc_LxjDbApi
* Method: Close
* Signature: (Ljava/lang/Long;)Ljava/lang/Long;
*/
JNIEXPORT jint JNICALL Java_com_lxjdb_jdbc_LxjDbApi_Close
(JNIEnv * env, jobject obj, jlong conn)
{
void* pConn = (void*)conn;
int ret = LxjDbClose(pConn);
return(ret);
}
/*
* Class: com_lxjdb_jdbc_LxjDbApi
* Method: Exec
* Signature: (JLjava/lang/String;[Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_lxjdb_jdbc_LxjDbApi_Exec
(JNIEnv * env, jobject obj, jlong conn, jstring sql, jobjectArray dbInfo)
{
void* pConn = (void*)conn;
const char* pSql = env->GetStringUTFChars(sql, 0);
int retCode = 0;
char pDbInfo[512];
pDbInfo[0] = '\0';
int ret = LxjDbExec(pConn, pSql, retCode, pDbInfo);
env->ReleaseStringUTFChars(sql, pSql);
env->SetObjectArrayElement(dbInfo, 0, env->NewStringUTF(pDbInfo));
return(ret>0 ? retCode : ret);
}
/*
* Class: com_lxjdb_jdbc_LxjDbApi
* Method: Rows
* Signature: (J)I
*/
JNIEXPORT jint