私が実現したメモリデータベースJDBCドライブ
15404 ワード
私は去年メモリデータベースを作りました.機能が強いと思っています.メモリデータベースは独立して実行するプログラムで、クライアントはsocketを通じてアクセスして、SQL文を転送してそして結果を得て、そのため私は1つのCインタフェースのクライアントAPIを提供しました.
しかし、より汎用性を向上させるには、Javaプログラマーとc#プログラマーの2つのタイプのIT世界で最も多くの人に配慮する必要があります.c#私の同僚はado.netドライバをパッケージしました.ここ数日私はjdbcの駆動を実現して、本文は実現過程のいくつかの心得を記録します.
私の得意なのはC/C++で、javaのくどい文法に対して本当にあまり風邪を引かないで、この間javaで1つの規模の大きくないandroidプログラムを書いたことがあって、やっと1つのjdbc駆動の任務を書くことに挑戦します.
私の開発環境は比較的小さいmac os xで、ちょうどjavaのいわゆるプラットフォームを試してみました.
まず、CインタフェースのクライアントAPIをjavaクラスにカプセル化します:LxjDbApi.java
次にjavahでc言語のヘッダファイルを生成してjniをコンパイルし、cファイルをjniディレクトリの下に保存し、端末の下でコマンドラインを使用します.
javah -classpath bin -d jni com.lxjdb.jdbc.LxjDbApi
次はcppコードです.私たちのc言語apiを呼び出します.
makefileをもう1つ構築するには:
makeの後、os xの共有ライブラリが生成されます.
ステップ2では、各インタフェースクラスを実装します.
1、Driver類
注意jdbcCompliant()メソッドを書き換え、falseを返す必要があります.必要な部分を実装する限り、すべてのjdbcインタフェースを完全に互換性を持つ必要はありません.
主にconnect()メソッドを実装し、コードは以下の通りです.
2、Connectionクラス
主にcreateStatement()メソッドを実装します.
3、
Statementクラス
主に2つの方法を実装します.1つは、結果セットが返されないことを実行することです.
executeUpdate()メソッドは、update、delete、insertなどのsql文を実行します.
二つ目はselectクエリ文を実行するResultSet executeQuery(String sql)メソッドであり、このメソッドは結果セットを返すことは明らかである.
4、
ResultSetクラス
結果セットを処理します.我々のメモリデータベースは比較的簡略化されており,返されるデータは文字列タイプであるため,getString()や他のgetInt(),getLong(),getDouble()などのデータ型を実現すれば実現しない.
もちろん、結果セットのカーソル行ポインタを移動する方法もいくつかあります.
手順3では、コンパイル後にjarファイルにエクスポートします.
Eclipseでアイテムを右クリックし、「Export...」を選択してLxjDbJdbc.jarをエクスポートします.
これによりjdbc駆動を実現することに成功した.
あとでテストできます.
ステップ4、テスト:
テストコードは簡単で、あまり説明しなくてもいいです.
注意、プロジェクトでLxjDbJdbc.jarの位置を正しく設定するには、プロジェクトで右クリックメニュー「Build Path」を選択し、「Configure Build Path」のLibraresラベルを選択し、「Add Externd JARs...」ボタンをクリックします.
LxjDbJdbc.jarをプロジェクトに追加すると、実行できます.結果は完全に正しいです.
(ブルースクリーンメモリデータベースは無料で、試用を求めることを歓迎します.ブルースクリーンデータベースは速度が非常に速く、挿入速度がもっと速い;非常に安定しています;スナップショット機能をサポートして、電源を切って再起動してデータを失うことはありません.各種のオペレーティングシステムwindows、Mac OS X、Linuxなどをサポートして、インストールする必要がなくて、直接運行します.)
しかし、より汎用性を向上させるには、Javaプログラマーとc#プログラマーの2つのタイプのIT世界で最も多くの人に配慮する必要があります.c#私の同僚はado.netドライバをパッケージしました.ここ数日私はjdbcの駆動を実現して、本文は実現過程のいくつかの心得を記録します.
私の得意なのはC/C++で、javaのくどい文法に対して本当にあまり風邪を引かないで、この間javaで1つの規模の大きくないandroidプログラムを書いたことがあって、やっと1つの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 JNICALL Java_com_lxjdb_jdbc_LxjDbApi_Rows
(JNIEnv * env, jobject obj, jlong conn)
{
void* pConn = (void*)conn;
return( LxjDbRows(pConn) ); //
}
/*
* Class: com_lxjdb_jdbc_LxjDbApi
* Method: Cols
* Signature: (J)I
*/
JNIEXPORT jint JNICALL Java_com_lxjdb_jdbc_LxjDbApi_Cols
(JNIEnv * env, jobject obj, jlong conn)
{
void* pConn = (void*)conn;
return(LxjDbCols(pConn)); //
}
/*
* Class: com_lxjdb_jdbc_LxjDbApi
* Method: GetColInfoByIndex
* Signature: (JI[Ljava/lang/String;[I)I
*/
JNIEXPORT jint JNICALL Java_com_lxjdb_jdbc_LxjDbApi_GetColInfoByIndex
(JNIEnv * env, jobject obj, jlong conn, jint col, jobjectArray retName, jintArray lenTypePos)
{
void* pConn = (void*)conn;
char name[200];
name[0] = '\0';
int len=0;
int type=0;
int pos=0;
int ret = LxjDbGetColInfoByIndex(pConn, col, name, len, type, pos); // ( 0 )
env->SetObjectArrayElement(retName, 0, env->NewStringUTF(name));
jint *pArr = env->GetIntArrayElements(lenTypePos, NULL);
pArr[0] = len;
pArr[1] = type;
pArr[2] = pos;
env->ReleaseIntArrayElements(lenTypePos, pArr, NULL);
return(ret);
}
/*
* Class: com_lxjdb_jdbc_LxjDbApi
* Method: GetColInfoByName
* Signature: (JLjava/lang/String;[I)I
*/
JNIEXPORT jint JNICALL Java_com_lxjdb_jdbc_LxjDbApi_GetColInfoByName
(JNIEnv * env, jobject obj, jlong conn, jstring name, jintArray lenTypePos)
{
void* pConn = (void*)conn;
const char* pName = env->GetStringUTFChars(name, 0);
int len = 0;
int type = 0;
int pos = 0;
int ret = LxjDbGetColInfoByName(pConn, pName, len, type, pos);
env->ReleaseStringUTFChars(name, pName);
jint *pArr = env->GetIntArrayElements(lenTypePos, NULL);
pArr[0] = len;
pArr[1] = type;
pArr[2] = pos;
env->ReleaseIntArrayElements(lenTypePos, pArr, NULL);
return(ret);
}
/*
* Class: com_lxjdb_jdbc_LxjDbApi
* Method: Next
* Signature: (J)I
*/
JNIEXPORT jint JNICALL Java_com_lxjdb_jdbc_LxjDbApi_Next
(JNIEnv * env, jobject obj, jlong conn)
{
void* pConn = (void*)conn;
return(LxjDbNext(pConn)); //
}
/*
* Class: com_lxjdb_jdbc_LxjDbApi
* Method: GotoRec
* Signature: (JI)I
*/
JNIEXPORT jint JNICALL Java_com_lxjdb_jdbc_LxjDbApi_GotoRec
(JNIEnv * env, jobject obj, jlong conn, jint recNo)
{
void* pConn = (void*)conn;
return(LxjDbGotoRec(pConn, recNo)); //
}
/*
* Class: com_lxjdb_jdbc_LxjDbApi
* Method: LxjDbGetValByName
* Signature: (JLjava/lang/String;[Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_lxjdb_jdbc_LxjDbApi_LxjDbGetValByName
(JNIEnv * env, jobject obj, jlong conn, jstring name, jobjectArray retVal)
{
void* pConn = (void*)conn;
const char* pName = env->GetStringUTFChars(name, 0);
char val[2048];
val[0] = '\0';
int ret = LxjDbGetValByName(pConn, pName, val);
env->ReleaseStringUTFChars(name, pName);
env->SetObjectArrayElement(retVal, 0, env->NewStringUTF(val));
return(ret);
}
/*
* Class: com_lxjdb_jdbc_LxjDbApi
* Method: GetValByIndex
* Signature: (JI[Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_lxjdb_jdbc_LxjDbApi_GetValByIndex
(JNIEnv * env, jobject obj, jlong conn, jint col, jobjectArray retVal)
{
void* pConn = (void*)conn;
char val[2048];
val[0] = '\0';
int ret = LxjDbGetValByIndex(pConn, col, val);
env->SetObjectArrayElement(retVal, 0, env->NewStringUTF(val));
return(ret);
}
makefileをもう1つ構築するには:
LIBFLAG= -lstdc++ -lpthread -ldl
CPPFLAGS = -c -fPIC -I /Library/Java/JavaVirtualMachines/jdk1.8.0_05.jdk/Contents/Home/include -I /System/Library/Frameworks/JavaVM.framework/Versions/A/Headers
COMMON_OBJs = com_lxjdb_jdbc_LxjDbApi.o
SHARELIB = -lLxjDbApi
libLxjDbJdbcApi: $(COMMON_OBJs)
gcc $(COMMON_OBJs) $(LIBFLAG) $(SHARELIB) -rdynamic -dynamiclib -fPIC -install_name /usr/lib/libLxjDbJdbcApi.dylib -o libLxjDbJdbcApi.dylib
makeの後、os xの共有ライブラリが生成されます.
libLxjDbJdbcApi.dylib
javaのjniはmacで共有ライブラリを使用するのが特別であるため、libLxjDbJdbcApi.jnilibと名前を変更し、/usr/lib/javaディレクトリにコピーする必要があることに注意してください.ステップ2では、各インタフェースクラスを実装します.
1、Driver類
注意jdbcCompliant()メソッドを書き換え、falseを返す必要があります.必要な部分を実装する限り、すべてのjdbcインタフェースを完全に互換性を持つ必要はありません.
主にconnect()メソッドを実装し、コードは以下の通りです.
package com.lxjdb.jdbc;
import java.sql.*;
import java.util.*;
import java.util.logging.Logger;
public class Driver implements java.sql.Driver{
public LxjDbApi lxjdb;
static{
try{
java.sql.DriverManager.registerDriver(new Driver());
}
catch(SQLException e){
// System.err.println(e);
throw new RuntimeException("Can't register driver!");
}
}
public Driver() throws SQLException {
// Required for Class.forName().newInstance()
lxjdb = new LxjDbApi();
}
public boolean acceptsURL(String url) throws SQLException{
return url.startsWith("jdbc:lxjdb://");
}
public Connection connect(String url, Properties info) throws SQLException{
if( !acceptsURL( url)){
return null;
}
// url , info
try {
String[] arr=url.split("//");
String url2 = arr[1];
String[] arr2=url2.split(":");
String host = arr2[0];
int port = Integer.parseInt(arr2[1]);
String user = info.getProperty("user");
String pwd = info.getProperty("password");
return new LxjDbConnection(lxjdb, host, port, user, pwd);
}
catch(Exception e){
throw new SQLException(e.getMessage());
}
}
public boolean jdbcCompliant(){
return false;
}
public DriverPropertyInfo[] getPropertyInfo(String url, Properties info)
throws SQLException {
// TODO Auto-generated method stub
return new DriverPropertyInfo[0];
}
public int getMajorVersion() {
// TODO Auto-generated method stub
return 1;
}
public int getMinorVersion() {
// TODO Auto-generated method stub
return 0;
}
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
// TODO Auto-generated method stub
return null;
}
}
2、Connectionクラス
主にcreateStatement()メソッドを実装します.
package com.lxjdb.jdbc;
import java.sql.*;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
public class LxjDbConnection implements java.sql.Connection{
public long conn=0;
public LxjDbApi lxjdb;
public LxjDbConnection(LxjDbApi plxjdb, String host, int port, String user, String pwd)
throws SQLException {
lxjdb = plxjdb;
conn = lxjdb.Open(host, port, user, pwd);
if(conn==0){
String info = "Open Err: "+host+":"+port+",user="+user;
throw new SQLException(info);
}
}
public Statement createStatement() throws SQLException {
return new LxjDbStatement(this);
}
}
3、
Statementクラス
主に2つの方法を実装します.1つは、結果セットが返されないことを実行することです.
executeUpdate()メソッドは、update、delete、insertなどのsql文を実行します.
二つ目はselectクエリ文を実行するResultSet executeQuery(String sql)メソッドであり、このメソッドは結果セットを返すことは明らかである.
package com.lxjdb.jdbc;
import java.sql.*;
public class LxjDbStatement implements java.sql.Statement {
public LxjDbConnection conntion;
public LxjDbApi lxjdb;
public long conn;
public LxjDbStatement(LxjDbConnection pconn){
conntion = pconn;
lxjdb = pconn.lxjdb;
conn = pconn.conn;
}
public ResultSet executeQuery(String sql) throws SQLException {
String[] dbInfo = new String[1];
int ret = lxjdb.Exec(conn, sql, dbInfo);
if(ret<0){
throw new SQLException(dbInfo[0]);
}
return new LxjDbResultSet(conntion);
}
public int executeUpdate(String sql) throws SQLException {
String[] dbInfo = new String[1];
int ret = lxjdb.Exec(conn, sql, dbInfo);
if(ret<0){
throw new SQLException(dbInfo[0]);
}
return ret;
}
}
4、
ResultSetクラス
結果セットを処理します.我々のメモリデータベースは比較的簡略化されており,返されるデータは文字列タイプであるため,getString()や他のgetInt(),getLong(),getDouble()などのデータ型を実現すれば実現しない.
もちろん、結果セットのカーソル行ポインタを移動する方法もいくつかあります.
package com.lxjdb.jdbc;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.*;
import java.util.Calendar;
import java.util.Map;
public class LxjDbResultSet implements java.sql.ResultSet {
public LxjDbApi lxjdb;
public long conn;
public LxjDbResultSet(LxjDbConnection pconn){
lxjdb = pconn.lxjdb;
conn = pconn.conn;
}
public boolean next() throws SQLException {
int ret = lxjdb.Next(conn);
return ret>0 ? true : false;
}
public boolean first() throws SQLException {
if(lxjdb.Rows(conn)<1)
return false;
int ret = lxjdb.GotoRec(conn, 1);
return ret>0 ? true : false;
}
public boolean last() throws SQLException {
int r = lxjdb.Rows(conn);
if(r<1)
return false;
int ret = lxjdb.GotoRec(conn, r);
return ret>0 ? true : false;
}
public String getString(int columnIndex) throws SQLException {
String[] retVal = new String[1];
lxjdb.GetValByIndex(conn, columnIndex-1, retVal); // columnIndex 1
return retVal[0];
}
public String getString(String columnLabel) throws SQLException {
String[] retVal = new String[1];
lxjdb.LxjDbGetValByName(conn, columnLabel, retVal);
return retVal[0];
}
public int getFetchSize() throws SQLException {
return lxjdb.Rows(conn);
}
}
手順3では、コンパイル後にjarファイルにエクスポートします.
Eclipseでアイテムを右クリックし、「Export...」を選択してLxjDbJdbc.jarをエクスポートします.
これによりjdbc駆動を実現することに成功した.
あとでテストできます.
ステップ4、テスト:
テストコードは簡単で、あまり説明しなくてもいいです.
import java.sql.*;
public class JdbcTest {
public static void main(String[] args) {
String driver = "com.lxjdb.jdbc.Driver";
String userName = "sa";
String passwrod = "********";
String url = "jdbc:lxjdb://192.168.0.106:2013";
String sql = "select * from OnlineUser";
try {
System.out.println("path:["+System.getProperties().get("java.library.path")+"]");
Class.forName(driver);
Connection conn = DriverManager.getConnection(url, userName,
passwrod);
Statement stmt = conn.createStatement() ;
int ret = stmt.executeUpdate("insert into OnlineUser(UserId, DevId, Addr, LastTime, Expires) values('9999','mac','192.168.0.106:888',getdate(),2000)");
System.out.println("executeUpdate:"+ret);
ResultSet rs = stmt.executeQuery(sql);
while (rs.next()) {
System.out.println("userId : " + rs.getString(1) + " dev : "
+ rs.getString(2) + " addr : " + rs.getString("Addr")+ " time : " + rs.getString(4));
}
//
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
//
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
注意、プロジェクトでLxjDbJdbc.jarの位置を正しく設定するには、プロジェクトで右クリックメニュー「Build Path」を選択し、「Configure Build Path」のLibraresラベルを選択し、「Add Externd JARs...」ボタンをクリックします.
LxjDbJdbc.jarをプロジェクトに追加すると、実行できます.結果は完全に正しいです.
(ブルースクリーンメモリデータベースは無料で、試用を求めることを歓迎します.ブルースクリーンデータベースは速度が非常に速く、挿入速度がもっと速い;非常に安定しています;スナップショット機能をサポートして、電源を切って再起動してデータを失うことはありません.各種のオペレーティングシステムwindows、Mac OS X、Linuxなどをサポートして、インストールする必要がなくて、直接運行します.)