[关闭]
@liyuj 2016-10-06T20:54:31.000000Z 字数 11919 阅读 3906

Apache-Ignite-1.7.0-中文开发手册

22.ODBC驱动

22.1.入门

22.1.1.摘要

Ignite目前包括了一个ODBC驱动,可以通过标准SQL查询和原生ODBC API从缓存中获取分布式数据。
要了解ODBC的细节,可以参照ODBC开发者参考
Apache Ignite的ODBC驱动实现了ODBC API的3.0版。

22.1.2.先决条件

Ignite的ODBC驱动官方在如下环境中进行了测试:

OS Windows(XP及以上,32位和64位版本)
Windows Server(2008及以上,32位和64位版本)
Ubuntu(14.x和15.x,64位)
C++编译器 MS Visual C++ (10.0及以上), g++ (4.4.0及以上)
Visual Studio 2010及以上

22.1.3.构建ODBC驱动

Ignite的ODBC驱动作为Ignite发行版的一部分,以源代码的形式提供,因此在使用之前需要进行构建。关于如何获取和设置Ignite本身,可以参照1.基本概念章节。
因为ODBC驱动是用C++编写的,因此它是作为Ignite C++的一部分提供的,并且依赖于一些C++库,更具体的就是它依赖于utilsbinaryIgnite库,这就意味着,在构建ODBC驱动本身之前,需要先构建它们。
在Windows上构建
如果要在Windows上构建ODBC驱动,需要MS Visual Studio 2010及以后的版本,一旦打开了Ignite方案%IGNITE_HOME%\platforms\cpp\project\vs\ignite.sln(或者ignite_86.sln,32位平台),在方案浏览器中左击Ignite项目,然后选择“Build”,Visual Studio会自动地检测并且构建所有必要的依赖。

如果使用VS 2015及以后的版本(MSVC14.0及以后),如果要成功构建odbc项目,需要额外添加legacy_stdio_definitions.lib库,这是由于MS的破坏性变更导致的。

一旦构建过程完成,会在%IGNITE_HOME%\platforms\cpp\project\vs\x64\Release中找到odbc.dll
在Linux上构建
在一个基于Linux的操作系统中,如果要构建及使用Ignite ODBC驱动,需要手动安装选择的ODBC驱动管理器,Ignite ODBC驱动已经使用UnixODBC进行了测试。
要构建驱动及其依赖,还需要GCC,G++以及Make
如果所有必需的都安装好了,可以通过如下方式构建Ignite ODBC驱动:

  1. cd $IGNITE_HOME/platforms/cpp
  2. libtoolize && aclocal && autoheader && automake --add-missing && autoreconf
  3. ./configure --enable-odbc --disable-node --disable-core
  4. make
  5. #The following step will most probably require root privileges:
  6. make install

一旦构建过程完成,可以通过如下命令找到ODBC驱动位于何处:

  1. whereis libignite-odbc

路径很可能是:/usr/local/lib/libignite-odbc.so

22.1.4.安装ODBC驱动

要使用ODBC驱动,首先要在系统中进行注册,因此ODBC驱动管理器必须能找到它。
在Windows上安装
在32位的Windows上需要使用32位版本的驱动,而在64位的Windows上需要使用64位版本的驱动,也可以在64位的Windows上同时安装32位和64位版本的驱动,这样32位和64位的应用都可以使用驱动。
要在Windows上安装驱动,首先要为驱动在文件系统中选择一个目录,选择一个位置后就可以把驱动放在哪并且确保所有的驱动依赖可以被解析,也就是说,他们要么位于%PATH%,要么和驱动位于同一个目录。
之后,就需要使用%IGNITE_HOME%/platforms/cpp/odbc/install目录下的安装脚本之一,注意,要执行这些脚本,很可能需要管理员权限。
X86:

  1. install_x86 <absolute_path_to_32_bit_driver>

AMD64:

  1. install_amd64 <absolute_path_to_64_bit_driver> [<absolute_path_to_32_bit_driver>]

在Linux上安装
要在Linux上构建和安装ODBC驱动,首先需要安装ODBC驱动管理器,Ignite ODBC驱动已经使用UnixODBC进行了测试。
如果已经构建完成并且执行了make install命令,libignite-odbc.so很可能会位于/usr/local/lib,要在ODBC驱动管理器中安装ODBC驱动并且可以使用,需要按照如下的步骤进行操作:

到现在为止,Ignite的ODBC驱动已经安装好了并且可以用了,可以像其它ODBC驱动一样,连接、使用。

22.2.连接串

22.2.1.连接串格式

Ignite的ODBC驱动支持标准的连接串格式,下面是正常的语法:

  1. connection-string ::= empty-string[;] | attribute[;] | attribute; connection-string
  2. empty-string ::=
  3. attribute ::= attribute-keyword=attribute-value | DRIVER=[{]attribute-value[}]
  4. attribute-keyword ::= identifier
  5. attribute-value ::= character-string

简单来说,连接串就是分号分割的键值条目列表,在下面可以看到连接串的示例。

22.2.2.支持的参数

Ignite的ODBC驱动可以使用如下的连接串/DSN参数:

属性关键字 描述 默认值
SERVER 要连接的节点地址 localhost
PORT 节点的OdbcProcessor监听的端口 10800
CACHE 缓存名,如果未定义会使用默认的缓存,注意,缓存名是区分大小写的。

所有的参数名是不区分大小写的,因此,SERVERServerserver都是有效的参数名,并且指向同一个参数。

22.2.3.连接串示例

下面的串,可以用于SQLDriverConnectODBC调用,来建立与Ignite节点的连接。
指定缓存:

  1. DRIVER={Apache Ignite};SERVER=localhost;PORT=10800;CACHE=MyCache

默认缓存:

  1. DRIVER={Apache Ignite};SERVER=localhost;PORT=10800

22.3.查询数据

像数据库一样访问Ignite。
ODBC驱动内部使用字段查询来获取Ignite缓存中的数据,这意味着通过ODBC只可以访问这些SQL查询可以访问的字段。
下面显示的是可以通过ODBC驱动进行查询的两个类的示例:
Person:

  1. /** All fields of the class will be visible in SQL. */
  2. public class Person {
  3. @QuerySqlField
  4. private long id;
  5. @QuerySqlField
  6. public Long orgId;
  7. @QuerySqlField
  8. private String name;
  9. @QuerySqlField
  10. private double salary;
  11. }

Organization:

  1. /** All fields of the class will be visible in SQL. */
  2. public class Organization {
  3. @QuerySqlField
  4. private Long id;
  5. @QuerySqlField
  6. private String name;
  7. }

预定义字段
除了通过@QuerySqlField注解标注的所有字段外,每个表都有两个特别的预定义字段:_key_val,他们表示到整个键和值对象的链接。这非常有用,比如,当它们中的一个是基本类型并且希望通过它们的值进行过滤时,要实现这一点,可以执行像SELECT * FROM Person WHERE _key = 100这样的查询。

现在,可以试着使用ODBC来运行一个小例子来从缓存中查询一些数据,首先,需要稍微改一下上面的类:
Person:

  1. /** All fields of the class will be visible in SQL. */
  2. public class Person {
  3. private static final AtomicLong ID_GEN = new AtomicLong();
  4. @QuerySqlField
  5. private long id;
  6. @QuerySqlField
  7. public Long orgId;
  8. @QuerySqlField
  9. private String name;
  10. @QuerySqlField
  11. private double salary;
  12. public Person(Organization org, String name, double salary) {
  13. id = ID_GEN.incrementAndGet();
  14. orgId = org.id();
  15. this.name = name;
  16. this.salary = salary;
  17. }
  18. }

Organization:

  1. /** All fields of the class will be visible in SQL. */
  2. public class Organization {
  3. private static final AtomicLong ID_GEN = new AtomicLong();
  4. @QuerySqlField
  5. private Long id;
  6. @QuerySqlField
  7. private String name;
  8. public Organization(String name) {
  9. id = ID_GEN.incrementAndGet();
  10. this.name = name;
  11. }
  12. }

下一步,需要正确地创建和初始化要进行查询的缓存:

  1. // Using deafault config.
  2. try (Ignite ignite = Ignition.start("config/default-config.xml")) {
  3. CacheConfiguration<Long, Organization> orgCacheCfg = new CacheConfiguration<>("Organization");
  4. orgCacheCfg.setCacheMode(CacheMode.PARTITIONED); // Default.
  5. ogCacheCfg.setIndexedTypes(Long.class, Organization.class);
  6. CacheConfiguration<AffinityKey<Long>, Person> personCacheCfg = new CacheConfiguration<>("Person");
  7. personCacheCfg.setCacheMode(CacheMode.PARTITIONED); // Default.
  8. personCacheCfg.setIndexedTypes(AffinityKey.class, Person.class);
  9. // Populate cache.
  10. try (
  11. IgniteCache<Long, Organization> orgCache = ignite.getOrCreateCache(orgCacheCfg);
  12. IgniteCache<AffinityKey<Long>, Person> personCache = ignite.getOrCreateCache(personCacheCfg)
  13. ) {
  14. orgCache.clear();
  15. // Organizations.
  16. Organization org1 = new Organization("ApacheIgnite");
  17. Organization org2 = new Organization("Other");
  18. orgCache.put(org1.id(), org1);
  19. orgCache.put(org2.id(), org2);
  20. personCache.clear();
  21. // People.
  22. Person p1 = new Person(org1, "John Doe", 2000);
  23. Person p2 = new Person(org1, "Jane Doe", 1000);
  24. Person p3 = new Person(org2, "John Smith", 1000);
  25. Person p4 = new Person(org2, "Jane Smith", 2000);
  26. // Note that in this example we use custom affinity key for Person objects
  27. // to ensure that all persons are collocated with their organizations.
  28. personCache.put(p1.key(), p1);
  29. personCache.put(p2.key(), p2);
  30. personCache.put(p3.key(), p3);
  31. personCache.put(p4.key(), p4);
  32. }
  33. finally {
  34. // Distributed cache could be removed from cluster only by #destroyCache() call.
  35. ignite.destroyCache("Person");
  36. ignite.destroyCache("Organization");
  37. }
  38. }

连接和并置
就像通过IgniteCacheAPI进行的SQL查询一样,在分区缓存上进行的关联,只有在关联的对象以并置模式存储时才能正常运行,
跨缓存查询
驱动连接的缓存会被视为默认的模式,要跨越多个缓存进行查询,可以使用跨缓存查询功能。
复制和分区缓存
复制缓存上的查询只会在一个节点上执行,而在分区缓存上的查询会在所有缓存节点上分布式地执行。

最后,就可以使用ODBC的API像使用普通数据库那样在数据网格中执行SQL查询:
C++:

  1. #define BUFFER_SIZE 1024
  2. SQLHENV env;
  3. // Allocate an environment handle
  4. SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env);
  5. // We want ODBC 3 support
  6. SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, reinterpret_cast<void*>(SQL_OV_ODBC3), 0);
  7. SQLHDBC dbc;
  8. // Allocate a connection handle
  9. SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc);
  10. // Connection string
  11. SQLCHAR connectStr[] = "DRIVER={Apache Ignite};SERVER=localhost;PORT=11443;CACHE=Person;";
  12. SQLSMALLINT connectStrLen = static_cast<SQLSMALLINT>(sizeof(connectStr));
  13. SQLCHAR outStr[BUFFER_SIZE] = { 0 };
  14. SQLSMALLINT outStrLen = static_cast<SQLSMALLINT>(sizeof(outStr));;
  15. // Connecting to ODBC server.
  16. SQLRETURN ret = SQLDriverConnect(dbc, NULL, connectStr, connectStrLen, outStr, outStrLen, &outStrLen, SQL_DRIVER_COMPLETE);
  17. if (!SQL_SUCCEEDED(ret))
  18. {
  19. SQLCHAR sqlstate[7] = { 0 };
  20. SQLINTEGER nativeCode;
  21. SQLCHAR errMsg[BUFFER_SIZE] = { 0 };
  22. SQLSMALLINT errMsgLen = static_cast<SQLSMALLINT>(sizeof(errMsg));
  23. SQLGetDiagRec(SQL_HANDLE_DBC, dbc, 1, sqlstate, &nativeCode, errMsg, errMsgLen, &errMsgLen);
  24. std::cerr << "Failed to connect to Apache Ignite: "
  25. << reinterpret_cast<char*>(sqlstate) << ": "
  26. << reinterpret_cast<char*>(errMsg) << ", "
  27. << "Native error code: " << nativeCode
  28. << std::endl;
  29. // Releasing allocated handles.
  30. SQLFreeHandle(SQL_HANDLE_DBC, dbc);
  31. SQLFreeHandle(SQL_HANDLE_ENV, env);
  32. return;
  33. }
  34. SQLHSTMT stmt;
  35. // Allocate a statement handle
  36. SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
  37. SQLCHAR query[] = "SELECT name, salary, Organization.name FROM Person "
  38. "INNER JOIN \"Organization\".Organization ON Person.orgId = Organization.id";
  39. SQLSMALLINT queryLen = static_cast<SQLSMALLINT>(sizeof(queryLen));
  40. ret = SQLExecDirect(stmt, query, queryLen);
  41. if (!SQL_SUCCEEDED(ret))
  42. {
  43. SQLCHAR sqlstate[7] = { 0 };
  44. SQLINTEGER nativeCode;
  45. SQLCHAR errMsg[BUFFER_SIZE] = { 0 };
  46. SQLSMALLINT errMsgLen = static_cast<SQLSMALLINT>(sizeof(errMsg));
  47. SQLGetDiagRec(SQL_HANDLE_DBC, dbc, 1, sqlstate, &nativeCode, errMsg, errMsgLen, &errMsgLen);
  48. std::cerr << "Failed to perfrom SQL query upon Apache Ignite: "
  49. << reinterpret_cast<char*>(sqlstate) << ": "
  50. << reinterpret_cast<char*>(errMsg) << ", "
  51. << "Native error code: " << nativeCode
  52. << std::endl;
  53. }
  54. else
  55. {
  56. // Printing results.
  57. struct OdbcStringBuffer
  58. {
  59. SQLCHAR buffer[BUFFER_SIZE];
  60. SQLLEN resLen;
  61. };
  62. // Getting number of columns in result set.
  63. SQLSMALLINT columnsCnt = 0;
  64. SQLNumResultCols(stmt, &columnsCnt);
  65. // Allocating buffers for columns.
  66. std::vector<OdbcStringBuffer> columns(columnsCnt);
  67. // Binding colums. For simplicity we are going to use only
  68. // string buffers here.
  69. for (SQLSMALLINT i = 0; i < columnsCnt; ++i)
  70. SQLBindCol(stmt, i + 1, SQL_CHAR, columns[i].buffer, BUFFER_SIZE, &columns[i].resLen);
  71. // Fetching and printing data in a loop.
  72. ret = SQLFetch(stmt);
  73. while (SQL_SUCCEEDED(ret))
  74. {
  75. for (size_t i = 0; i < columns.size(); ++i)
  76. std::cout << std::setw(16) << std::left << columns[i].buffer << " ";
  77. std::cout << std::endl;
  78. ret = SQLFetch(stmt);
  79. }
  80. }
  81. // Releasing statement handle.
  82. SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  83. // Disconneting from the server.
  84. SQLDisconnect(dbc);
  85. // Releasing allocated handles.
  86. SQLFreeHandle(SQL_HANDLE_DBC, dbc);
  87. SQLFreeHandle(SQL_HANDLE_ENV, env);

22.4.一致性

ODBC接口一致性

22.4.1.摘要

ODBC定义了若干接口一致性级别,在本章中可以知道Ignite的ODBC驱动支持哪些特性。

22.4.2.核心接口一致性

特性 Ignite是否支持 备注
通过调用SQLAllocHandle和SQLFreeHandle来分配和释放所有处理器类型
使用SQLFreeStmt函数的所有形式
通过调用SQLBindCol,绑定列结果集
通过调用SQLBindParameter和SQLNumParams,处理动态参数,包括参数数组,只针对输入方向,
指定绑定偏移量
使用数据执行对话框,涉及SQLParamData和SQLPutData的调用 主要用于INSERT和UPDATE,Ignite不支持
管理游标和游标名 部分 实现了SQLCloseCursor,Ignite不支持命名游标
通过调用SQLColAttribute,SQLDescribeCol,SQLNumResultCols和SQLRowCount,访问结果集的描述(元数据)
通过调用目录函数SQLColumns,SQLGetTypeInfo,SQLStatistics和SQLStatistics查询数据字典 部分 不支持SQLStatistics
通过调用SQLConnect,SQLDataSources,SQLDisconnect和SQLDriverConnect管理数据源和连接,通过SQLDrivers获取驱动的信息,不管支持ODBC那个级别。
通过调用SQLExecDirect,SQLExecute和SQLPrepare预编译和执行SQL语句。
通过调用SQLFetch,或者将FetchOrientation参数设置为SQL_FETCH_NEXT之后调用SQLFetchScroll,获取一个结果集或者多行数据中的一行,只能向前
通过调用SQLGetData,获得一个未绑定的列
通过调用SQLGetConnectAttr、SQLGetEnvAttr、SQLGetStmtAttr,获取所有属性的当前值,或者通过调用SQLSetConnectAttr、SQLSetEnvAttr、SQLSetStmtAttr,将所有属性赋为默认值,以及为特定属性赋为非默认值。 部分 并不支持所有属性
通过调用SQLCopyDesc、SQLGetDescField、SQLGetDescRec、SQLSetDescField、SQLSetDescRec,操作描述符的特定字段。
通过调用SQLGetDiagField、SQLGetDiagRec,获得诊断信息。
通过调用SQLGetFunctions和SQLGetInfo,检测驱动兼容性,以及,通过调用SQLNativeSql,在发送到数据源之前检测SQL语句中的任何文本代换的结果 部分 未实现SQLGetFunctions,SQLGetInfo实现了一部分,实现了SQLNativeSql
使用SQLEndTran的语法提交一个事务,驱动的核心级别不需要支持真事务,因此,应用无法指定SQL_ROLLBACK或者为SQL_ATTR_AUTOCOMMIT连接属性指定SQL_AUTOCOMMIT_OFF
调用SQLCancel取消数据执行对话框,以及多线程环境中,在另一个线程中取消ODBC函数的执行,核心级别的接口一致性不需要支持函数的异步执行,也不需要使用SQLCancel取消一个ODBC函数的异步执行。平台和ODBC驱动都不需要多线程地同时自主活动,然而在多线程环境中,ODBC驱动必须是线程安全的,从应用来的请求的序列化是实现这个规范的一致的方式,即使他导致了一系列的性能问题。 当前的ODBC驱动实现不支持异步执行也不支持数据处理特性
通过调用SQLSpecialColumns获得表的行标识符SQL_BEST_ROWID。 当前的实现总是返回空

22.4.3.函数支持

函数名 Ignite是否支持 一致性级别
SQLAllocHandle Core
SQLBindCol Core
SQLBindParameter Core
SQLBrowseConnect Level1
SQLBulkOperations Level1
SQLCancel Core
SQLCloseCursor Core
SQLColAttribute Core
SQLColumnPrivileges Level2
SQLColumns Core
SQLConnect Core
SQLCopyDesc Core
SQLDataSources N/A Core
SQLDescribeCol Core
SQLDescribeParam Level2
SQLDisconnect Core
SQLDriverConnect Core
SQLDrivers N/A Core
SQLEndTran 部分 Core
SQLExecDirect Core
SQLExecute Core
SQLFetch Core
SQLFetchScroll Core
SQLForeignKeys 部分 Level2
SQLFreeHandle Core
SQLFreeStmt Core
SQLGetConnectAttr Core
SQLGetCursorName Core
SQLGetData Core
SQLGetDescField Core
SQLGetDescRec Core
SQLGetDiagField Core
SQLGetDiagRec Core
SQLGetEnvAttr Core
SQLGetFunctions Core
SQLGetInfo Core
SQLGetStmtAttr 部分 Core
SQLGetTypeInfo Core
SQLMoreResults 部分 Level1
SQLNativeSql Core
SQLNumParams Core
SQLNumResultCols Core
SQLParamData Core
SQLPrepare Core
SQLPrimaryKeys Level1
SQLProcedureColumns Level1
SQLProcedures Level1
SQLPutData Core
SQLRowCount Core
SQLSetConnectAttr Core
SQLSetCursorName Core
SQLSetDescField Core
SQLSetDescRec Core
SQLSetEnvAttr Core
SQLSetPos Level1
SQLSetStmtAttr 部分 Core
SQLSpecialColumns Core
SQLStatistics Core
SQLTablePrivileges Level2
SQLTables Core
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注