> 数据库 > MySQL >

Ensemble(Cache)通过ODBC连接Mysql数据库并调用其存储过程

   有同事问到如何用caché数据库连到MySQL数据,并且调用Mysql数据库中的存储过程,开始认为mysql不可能为m语言单独写驱动所以不能调用存储过程。

     在mysql官网可以看到mysql提供了以下几种方式连接它,见下图. (ODBC,NET,JDBC,Python,C++,C,PHP) 确实没有M语言的连接驱动。

      mysql连接方式

后面领导说可以看看sql gateway,去研究了下,发现caché有界面来配置SQL Gateway Settings界面,进入方式System Management Portal--Home>Configuration>Object/SQL Gateway Settings-SQL Gateway Connections,界面可以Create New Connection.

创建connection得先有数据库,所以第一步安装mysql数据库

1. 下载mysql数据库安装程序,可以点这里下载Mysql

     安装好后,在开始菜单框内输入cmd,在cmd.exe上右键以管理员身份运行。

    //开启mysql服务

    C:\Windows\system32>net start mysql
     MySQL 服务正在启动 ..............
     MySQL 服务已经启动成功。    

     //进入mysql数据库, 默认用户名root , 密码为空

    C:\Windows\system32>mysql -h localhost -u root -p
     Enter password:

    //切换到test数据库下

    mysql>show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
4 rows in set (0.06 sec)


mysql> use test;
Database changed

//建立测试表 dhc_user,并插入数据,在这就不一贴过程,最终查询出来的样子是

mysql> select * from dhc_user;
+--------+------+-------+
| name   | age  | phone |
+--------+------+-------+
| w      |   12 | NULL  |
| h      |   13 | NULL  |
| wanghc |   11 |       |
| wanghc |   12 |       |
| wanghc |   13 |       |
+--------+------+-------+
5 rows in set (0.09 sec)

//写一get_age存储过程, 通过name查询age

mysql> DELIMITER //
mysql> CREATE PROCEDURE get_age(n VARCHAR(50))
    -> BEGIN
    -> SELECT age from dhc_user where name=n;
    -> END //
Query OK, 0 rows affected (0.07 sec)


mysql> DELIMITER ;

// 调用下get_age,能通过

mysql> call get_age("wanghc")
    -> ;
+------+
| age  |
+------+
|   11 |
|   12 |
|   13 |
+------+
3 rows in set (0.07 sec)

Query OK, 0 rows affected (0.07 sec)

2. 下载Mysql数据的ODBC驱动,点这里下载mysql的ODBC驱动

      Caché数据库通过ODBC连接时就用到了驱动,

     安装好后,在开始菜单框内输入odbc,打开数据源管理界面,在系统DSN界面中添加Mysql的DSN

dsn
mysqldns

3. 进入Home>Configuration>Object/SQL Gateway Settings-SQL Gateway Connections,界面可以Create New Connection界面,

  

 点击Test Connection,测试成功

4. 进入Home>SQL界面,选中DHC-APP名字空间,点击Link Procedure Wizard

    

下一步,修改下包名与类型,生成一个web.test.mysql.DHCUser.cls类生成"get_age"方法

进入cahcé的terminal运行

 DHC-APP>d ##class(web.test.mysql.DHCUser)."get_age"("wanghc")

运行会报错,说mysql语法错误,看了下生成的"get_age"方法,生现call后面语句有问题,{call """".get_age(?)}改成{call test.get_age(?)},再运行

 DHC-APP>d ##class(web.test.mysql.DHCUser)."get_age"("wanghc")

没有报错,但也看不到返回值,进入Home>SQL 用call nullschema.get_age("wanghc")只说count是三条,看不到记录。用sqldbx工具运行call nullschema.get_age("wanghc")倒是可以看到三条记录与mysql内看到的一样。应该是caché自身的SQL没有从%sqlcontext内拿ResultSet显示出来,只是显示了SQLCODE与Message,ROWCOUNT。

5. 不用系统生成类方法,自己编写. 

    看了下生成的代码有个关键类%Library.SQLGatewayConnection,看了下他的api与例子,
    在web.test.mysql.DHCUser.cls内增加了个方法,没有用portal>sql gateway settings,用了dsn连mysql,再去运行 test.get_age()。

 
  1. ClassMethod Call(name) As %Status  
  2. {  
  3.         set gc=##class(%SQLGatewayConnection).%New()  
  4.         If gc=
    $NULLOREFquit
    $ERROR($$$GeneralError,"Cannot create %SQLGatewayConnection.")  
  5.                 
  6.         //Make connection to target DSN  
  7.         s pDSN="mysqltest"  
  8.         s usr="root"  
  9.         s pwd=""  
  10.         set sc=gc.Connect(pDSN,usr,pwd,0)   
  11.         If $$$ISERR(sc) quit sc  
  12.         if gc.ConnectionHandle="" quit 
    $ERROR(
    $GeneralError,"Connection failed")  
  13.                 
  14.         set sc=gc.AllocateStatement(.hstmt)   
  15.         if $$$ISERR(sc) quit sc  
  16.         set pQuery= "{call test.get_age(?)}"  
  17.         set sc=gc.Prepare(hstmt,pQuery)   
  18.         if $$$ISERR(sc) quit sc  
  19.         set sc = gc.BindParameter(hstmt,1,1,1,12,25,0,25)  
  20.         set sc = gc.SetParameter(hstmt,$lb(name),1)  
  21.         //Execute statement  
  22.         set sc=gc.Execute(hstmt)  
  23.         if $$$ISERR(sc) quit sc  
  24.         //Get list of columns returned by query  
  25.         set sc=gc.DescribeColumns(hstmt, .columnlist)   
  26.         if $$$ISERR(sc) quit sc  
  27.            
  28.         //display column headers delimited by ":"  
  29.         set numcols=$listlength(columnlist)-1  //get number of columns  
  30.         for colnum=2:1:numcols+1 {  
  31.                 Write $listget($listget(columnlist,colnum),1),":"  
  32.                 }  
  33.         write !  
  34.            
  35.         //Return first 200 rows     
  36.         set sc=gc.Fetch(hstmt)  
  37.         if $$$ISERR(sc) quit sc  
  38.         s rownum=1  
  39.         while((gc.sqlcode'=100) && (rownum<=200)) {  
  40.                 for ii=1:1:numcols {  
  41.                     s sc=gc.GetData(hstmt, ii, 1, .val)  
  42.                     w " "_val  
  43.                     if $$$ISERR(sc) break  
  44.                 }  
  45.                 s rownum=rownum+1  
  46.                 write !  
  47.                 set sc=gc.Fetch(hstmt)  
  48.                 if $$$ISERR(sc) break  
  49.                 }  
  50.               
  51.           //Close cursor and then disconnect  
  52.         set sc=gc.CloseCursor(hstmt)  
  53.         if $$$ISERR(sc) quit sc  
  54.             
  55.         set sc=gc.Disconnect()  
  56.             
  57.         Quit sc  
  58. }  
进入terminal运行下
DHC-APP>d ##class(web.test.mysql.DHCUser).Call("wanghc")
age:
 11
 12
 13
Call方法是用OOP写的,再参考了下生成的"get_age"方法写了另一MCall方法

[plain] view plain copy
 
  1. ClassMethod MCall(name)  
  2. {  
  3. Set DLLName=$g(^%SYS("bindir"))_$s(
    $isWINDOWS:"cgate.dll",
    $isUNIX:"cgate.so",$$$isVMS:"cgate.exe",1:"cgate.dll")  
  4. Set DLLHandle = $zf(-4,1,DLLName)  
  5. Set ConnectionHandle = $zf(-5,DLLHandle,45,"mysqltest","root","",15)   
  6. Set hstmt=$zf(-5,DLLHandle,5,ConnectionHandle)  
  7. Set sqlcode=$zf(-5,DLLHandle,3,hstmt,"{call test.get_age(?)}") ; Prepare=3  
  8. Set sqlcode=$zf(-5,DLLHandle,62,hstmt,1,1,1,12,25,0,25)  
  9.     Set sqlcode=$zf(-5,DLLHandle,9,hstmt,$lb(name),1)  
  10.     Set sqlcode=$zf(-5,DLLHandle,4,hstmt) ; Execute=4  
  11.     ;Set %ROWCOUNT=$zf(-5,DLLHandle,31,hstmt)  
  12.     ;Write "%ROWCOUNT=",%ROWCOUNT,!  
  13.     Set sqlcode=$zf(-5,DLLHandle,7,hstmt) ;Fetch=7  
  14. while (sqlcode'=100){  
  15. Set val = $zf(-5,DLLHandle,25,hstmt, 1, 1) ;GetData=25  
  16.     Write val,!  
  17.     Set sqlcode=$zf(-5,DLLHandle,7,hstmt) ;Fetch=7  
  18. }  
  19. }  
在teminal内运行下
DHC-APP>d ##class(web.test.mysql.DHCUser).MCall("wanghc")
11
12
13
也能出来结果。
MCall方法大量用到了$zf。$zf是调用的active dll有兴趣的可以看下。
调用的是caché数据库安装目录bin下的cgate.dll,通过这个dll再去调用dns。
 

类代码xml见附件,本地库是2010.2.8



(责任编辑:IT)