JScript 快速开发框架 Edk:DAO 数据库连接

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 访问 http://code.google.com/p/naturaljs/on Google Code 可获取 JScript快速开发框架 Edk 之源码。 相信许多人学 Web 的第一步便是从连接数据开始,封装过程亦如是。

访问 http://code.google.com/p/naturaljs/on Google Code 可获取 JScript快速开发框架 Edk 之源码。

相信许多人学 Web 的第一步便是从连接数据开始,封装过程亦如是。连接数据库这道板斧似乎不用多说,无非那个几个步骤,但还是有必要讲讲的。于是,在介绍代码之前,先重温调用数据的一般执行步骤。一、连接数据库;二、执行 SQL 操作。三、返回数据集合。这里有个优化的地方,也是常见的技巧,就是其中连接了的数据库应该对象不应立刻销毁,应予保留,以便反复使用。因为一次请求就会有不止一次的查询,所以保留下来就可以重复使用链接对象。但是为什么不使用链接池(Pool)?由于 JScript 不是守护进程,一次请求关闭一个线程,立刻回收率代码“所占”的资源,所以连接池的算法可能就无用武之地了。

学习了 Java,想起来 Java 的那些概念,所以,连类名称都叫做 DAO。DAO = Data Access Object 数据访问对象,既可读又可写的。当前 DAO 不包含读写的操作,而是分别由 $$.sql.writer.Writer 和 $$.sql.reader.Reader 提供数据库数据的写、读。

DAO 的核心方法为 execute():

/** * 执行SQL语句,返回结果集合。 * @param {String} sql 要执行的 SQL 语句(可选的)。 * @param {Objcec} cfg 配置对象,可以包含连接数据库的字符串(可选的) * @return {ADODB.Recordset} 数据库记录集合。 */ execute : (function(){ var connectObj; // 数据库连接对象,只设置一次,无须重复创建。多次创建则浪费。 function connectDB(cfg){ var dbType // 链接数据库的类型,是Access呢?MySQL呢?还是SQLServer? ,dbPath // 数据库文件的路径,如果通过磁盘访问的话。 ,connectStr // 链接数据库的字符串。 // 数据库对象,同前面的 var connectObj; 的对象 ,connectObj = new ActiveXObject("ADODB.CONNECTION"); if(cfg){ dbType = cfg.dbType; }else if(!cfg && $$.cfg){ dbType = $$.cfg.edk_dbType.toLowerCase(); }else{ dbType = 'access'; // 默认为MS Access数据库 } switch(dbType){ case 'access' : if(cfg && cfg.dbPath){ dbPath = cfg.dbPath; }else if(!cfg && $$.cfg){ dbPath = $$.cfg.edk_isDebugging ? $$.cfg.edk_dbFilePath_Test : Server.mappath($$.cfg.edk_dbFilePath); } connectStr = "DBQ={0};DefaultDir=;DRIVER={Microsoft Access Driver (*.mdb)};".format(dbPath); break; case 'sqlservver' : throw '尚未实现'; case 'mysql' : // 使用driver模式不用有空格在连接字符串中 connectStr = "DRIVER={mysql odbc 5.1 driver};" + "SERVER={0};" + "PORT={1};" + "UID={2};" + "PASSWORD={3};" + "DATABASE={4};" + "OPTION=3"; connectStr = connectStr.format('localhost', 3306, 'root' ,'123', 'test'); break; case 'sqlite' : dbPath = 'd://test.db'; connectStr = "DRIVER={SQLite3 ODBC Driver};Database=" + dbPath; break; default: throw '非法数据库连接类型!'; }; connectObj.open(connectStr); return connectObj; } return function(sql, cfg){ if(!connectObj){ connectObj = this.execute.connectObj = connectDB(cfg); // call only once. } return sql ? connectObj.execute(sql) : connectObj; } })()

有没有发现,connect Object 只创建一次?以后反复调用这个单例。但值得一提的是,我们把数据库连接对象 connect Object 的引用定义在 execute() 方法上!——仔细想想,也没有什么好奇怪的。利用 JavaScript 的超强动态绑定,甚至可以在方法上定义新的元素,建立新的引用,也是毫无问题。现在我们就是利用这个方法设计 DAO 的数据结构。(当然,一些情况就不是“毫无问题”的,例如客户端编程中,DOM 对象千万就不要绑定其他内容,否则极容易 Memory Leak,保持其 DOM API怎么样就怎么样便对了)。这样,我们的数据库连接对象不直接依附在 this 对象上,而是通过 this.execute.connectObj 才可访问。目的就是避免 this 的 API 太乱。

关于静态属性

上述定义一个 connect Object 可以说进行了一次静态属性的分配。为什么是静态的而不是动态的呢?静态的原因在于值只要分配一次;实例属性则依据每次实例化不同而有不同的值。不过 JavaScript 的 OO 特性并不明显,很难从代码中说明我那个地方就是要“静态属性”的,呵呵。怎么搞才好?我开始的做法是写一个 getStaticProerty() 函数,经过这个函数来返回属性,而不是直接获取的。但是这个方法虽然封装是封装了,不过我后来却否决了这个想法,原因就在于一个问题,原本实现静态的逻辑很简单,就是设置一个变量,然后判断是否 undefined,如 undefined 则写入静态值,然后以后不断地访问这个函数就返回静态值。很简单没有什么复杂的地方。特别另外地写一个 getStaticProerty() 函数,反而带来额外的维护成本,也不见得复用性有多么的明显,于是,决定以后在具体实现中实现“静态属性”的思想就可以了,不搞什么 getStaticProerty()。

 不过为了更加说明问题所在,我列一列原来的方案,方便理解我说的话:

// 定义函数对象的新方法,即获取属性的函数。 Function.prototype.getStaticProperty = function(setHandler){ function getFunctionName(fn){ return (/function/s+(/w+)/s*/(/)/.exec(fn + ''))[1]; } var propertyName = getFunctionName(setHandler); if(typeof (this[propertyName]) == 'undefined'){ this[propertyName] = setHandler.call(this); return this[propertyName]; }else{ return this[propertyName]; } } // 比较一下这个execute() this.execute = function (sql, cfg){ // call only once. var connectObj = this.execute.getStaticProperty(function connectObj(){ var dbPath ,dbType = ($$.cfg ? $$.cfg.edk_dbType.toLowerCase() : null) || (cfg ? cfg.dbType : null) || 'access' // 如不指定默认为Access数据库。 ,connectString ,connectObj; switch(dbType){ case 'mysql' : // 使用driver模式不用有空格在连接字符串中 connectString = "DRIVER={mysql odbc 5.1 driver};" + "SERVER={0};" + "PORT={1};" + "UID={2};" + "PASSWORD={3};" + "DATABASE={4};" + "OPTION=3"; connectString = connectString.format('localhost', 3306, 'root' ,'123', 'test'); break; case 'access' : if(cfg && cfg.dbPath){ dbPath = cfg.dbPath; }else{ if($$.cfg){ dbPath = $$.cfg.edk_isDebugging ? $$.cfg.edk_dbFilePath_Test : Server.mappath($$.cfg.edk_dbFilePath); } } connectString = "DBQ={0};DefaultDir=;DRIVER={Microsoft Access Driver (*.mdb)};".format(dbPath); break; case 'sqlite' : connectString = "DRIVER={SQLite3 ODBC Driver};Database=d://test.db"; break; default: throw '非法数据库连接类型!'; }; connectObj = new ActiveXObject("ADODB.CONNECTION"); connectObj.open(connectString); return connectObj; }); return sql ? connectObj.execute(sql) : connectObj; }

 当前 DAO 支持 Access、MySQL(要装ODBC驱动)和 SQLite(同样,也要装驱动)的字符连接串。

 针对已查询出来的记录集,是不是要再经过一定的处理呢?是的。我们是 JS 的语言,就转换为转换为天然的 JSON 结构吧。过程很简单,就是双循环 RecordSet。其中要注意的就是 ADO RecordSet 数量类型变为JS类型,即 sql2json(value, dataType) 函数。当前我选择一个简单的转换,如果要复杂、全面的 ADO 类型检测,可以通过请查阅文件: adovbs.inc 了解更多常量信息,该文件在系统中默认的路径为: C:/Program Files/Common Files/System/ado/adovbs.inc,或者直接看看我后面贴的函数。这个函数很全面的了,但我用不上很多的地方,所以就进行简化,于是得到当前的 sql2json。

/** * 针对已查询出来的记录集,转换为 JavaScript 的 JSON 结构。 * @param {RecroedSet} rs ADO 对象集合。 * @return {Array} 输出的 JSON 数组。 */ ,row2json : (function(){ /** * 转换值的 ADO 类型为 JavaScript 类型。 * @private * @param {Number} adoField * @param {Any} value * @return {Any} */ function sql2json(value, dataType){ switch(dataType){ case "string": case 129: // 'char': case 130: // 'nchar': case 200: // 'varchar': case 201: // 'text': case 202: // 'nvarchar': case 203: // 'ntext': return String(value); case "number": case 20: // 'bigint': case 2: // 'smallint': case 16: // 'tinyint': case 14: // 'decimal': case 5: // 'double': return Number(value); case 11: // "boolean": case 'bol': return Boolean(value); case "date": case "object": case 135: // 'datetime': case 64: // 'fileTime': case 134: case 137: return new Date(value).format("yyyy-mm-dd"); } } return function(rs){ var output = [] // Array 输出的 JSON 数组 ,rawArr = rs.getRows().toArray() // Array getRows() 返回的数组 ,fields = rs.Fields // comObj rs.Fields 字段对象,每一列都不同。 ,fLen = fields.Count // int 字段总数 ,len = rawArr.length / fLen // int 记录数 ,sp // int 位数 ,row; // object 构成 JSON 的行对象 // 自动关闭 RecordSet 集合,可以尽快释放 RecordSet 对象占用的资源。 rs.Close(); for (var i = 0; i < len; i++){ sp = i * fLen ,row = {}; for (var j = 0; j < fLen; j++){ row[fields(j).name] = sql2json(rawArr[sp + j], fields(j).Type); } output.push(row); } return output; } })()

文章最后谨记:在程序写完阶段关闭数据库连接,即 $$.sql.close();

附:

/** * 转换值的 ADO 类型为 JavaScript 类型。 * 更多常量信息请查阅文件: adovbs.inc ,该文件在系统中默认的路径为: * C:/Program Files/Common Files/System/ado/adovbs.inc * 其他系统请自行搜索。 * @private * @param {Any} value * @param {RecordSet.Field} adoField * @return {Any} */ sql2json = function(value, adoField){ // Base Types var BASE_TYPE_UNKNOWN = 0; var BASE_TYPE_NUMBER = 1; var BASE_TYPE_DATE = 2; var BASE_TYPE_BOOLEAN = 3; var BASE_TYPE_TEXT = 4; // ADO Types var ADO_TYPE_INT = 3; var ADO_TYPE_FLOAT = 5; var ADO_TYPE_MONEY = 6; var ADO_TYPE_DATETIME = 7; var ADO_TYPE_BIT = 11; var ADO_TYPE_VARCHAR = 200; var ADO_TYPE_TEXT = 201; var ADO_TYPE_NVARCHAR = 202; var ADO_TYPE_NTEXT = 203; var __adoFieldTypes = { 20: { name: "adBigInt", type: BASE_TYPE_NUMBER }, 128: { name: "adBinary", type: BASE_TYPE_UNKNOWN }, 11: { name: "adBoolean", type: BASE_TYPE_BOOLEAN }, 8: { name: "adBSTR", type: BASE_TYPE_UNKNOWN }, 136: { name: "adChapter", type: BASE_TYPE_UNKNOWN }, 129: { name: "adChar", type: BASE_TYPE_TEXT }, 6: { name: "adCurrency", type: BASE_TYPE_NUMBER }, 7: { name: "adDate", type: BASE_TYPE_DATE }, 133: { name: "adDBDate", type: BASE_TYPE_DATE }, 137: { name: "adDBFileTime", type: BASE_TYPE_DATE }, 134: { name: "adDBTime", type: BASE_TYPE_DATE }, 135: { name: "adDBTimeStamp", type: BASE_TYPE_DATE }, 14: { name: "adDecimal", type: BASE_TYPE_NUMBER }, 5: { name: "adDouble", type: BASE_TYPE_NUMBER }, 0: { name: "adEmpty", type: BASE_TYPE_UNKNOWN }, 10: { name: "adError", type: BASE_TYPE_UNKNOWN }, 64: { name: "adFileTime", type: BASE_TYPE_DATE }, 72: { name: "adGUID", type: BASE_TYPE_UNKNOWN }, 9: { name: "adIDispatch", type: BASE_TYPE_UNKNOWN }, 3: { name: "adInteger", type: BASE_TYPE_NUMBER }, 13: { name: "adIUnknown", type: BASE_TYPE_UNKNOWN }, 205: { name: "adLongVarBinary", type: BASE_TYPE_UNKNOWN }, 201: { name: "adLongVarChar", type: BASE_TYPE_TEXT }, 203: { name: "adLongVarWChar", type: BASE_TYPE_TEXT }, 131: { name: "adNumeric", type: BASE_TYPE_NUMBER }, 138: { name: "adPropVariant", type: BASE_TYPE_UNKNOWN }, 4: { name: "adSingle", type: BASE_TYPE_NUMBER }, 2: { name: "adSmallInt", type: BASE_TYPE_NUMBER }, 16: { name: "adTinyInt", type: BASE_TYPE_NUMBER }, 21: { name: "adUnsignedBigInt", type: BASE_TYPE_NUMBER }, 19: { name: "adUnsignedInt", type: BASE_TYPE_NUMBER }, 18: { name: "adUnsignedSmallInt", type: BASE_TYPE_NUMBER }, 17: { name: "adUnsignedTinyInt", type: BASE_TYPE_NUMBER }, 132: { name: "adUserDefined", type: BASE_TYPE_UNKNOWN }, 204: { name: "adVarBinary", type: BASE_TYPE_UNKNOWN }, 200: { name: "adVarChar", type: BASE_TYPE_TEXT }, 12: { name: "adVariant", type: BASE_TYPE_UNKNOWN }, 139: { name: "adVarNumeric", type: BASE_TYPE_NUMBER }, 202: { name: "adVarWChar", type: BASE_TYPE_TEXT }, 130: { name: "adWChar", type: BASE_TYPE_TEXT }, 8192: { name: "adArray", type: BASE_TYPE_UNKNOWN } }; // __getValueByType() function __getValueByType(type, value) { if (typeof value === "undefined") return null; switch (type) { case "string" : case BASE_TYPE_TEXT : return String(value); case "number" : case BASE_TYPE_NUMBER : return Number(value); case "boolean" : case BASE_TYPE_BOOLEAN : return Boolean(value); case "date" : case "object" : case BASE_TYPE_DATE : return new Date(value).format("yyyy-mm-dd"); default : return value; } } return __getValueByType(__adoFieldTypes[adoField.Type].type, value); }

 附:

在 ASP 程序中使用参数化查询

ASP 环境下的参数化查询主要由 Connection 对象和 Command 对象完成。

Access 数据库只支持匿名参数,在传入参数的位置用问号代替即可。SQL Server 数据库虽然支持匿名和非匿名的参数,但是在 ASP 中也仅能使用匿名参数。

var conn = Server.CreateObject("ADODB.Connection");
conn.ConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + Server.MapPath("Test.mdb");
conn.Open();

var cmd = Server.CreateObject("ADODB.Command");
cmd.ActiveConnection = conn;
cmd.CommandType = 1;
cmd.CommandText = "SELECT TOP 1 * FROM [User] WHERE UserName = ? AND Password = ?";
cmd.Parameters.Append(cmd.CreateParameter("@UserName", 200, 1, 20, "user01"));
cmd.Parameters.Append(cmd.CreateParameter("@Password", 200, 1, 16, "123456"));

var rs = cmd.Execute();
Response.Write(rs("UserId").value);

rs.Close();
conn.Close();
http://heeroluo.net/Article/Detail/61

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
4月前
|
存储 关系型数据库 数据库
附部署代码|云数据库RDS 全托管 Supabase服务:小白轻松搞定开发AI应用
本文通过一个 Agentic RAG 应用的完整构建流程,展示了如何借助 RDS Supabase 快速搭建具备知识处理与智能决策能力的 AI 应用,展示从数据准备到应用部署的全流程,相较于传统开发模式效率大幅提升。
附部署代码|云数据库RDS 全托管 Supabase服务:小白轻松搞定开发AI应用
|
2月前
|
存储 JSON 数据建模
鸿蒙 HarmonyOS NEXT端云一体化开发-云数据库篇
云数据库采用存储区、对象类型、对象三级结构,支持灵活的数据建模与权限管理,可通过AGC平台或本地项目初始化,实现数据的增删改查及端侧高效调用。
160 1
|
6月前
|
人工智能 数据挖掘 API
基于neo4j数据库和dify大模型框架的rag模型搭建——后续补充
基于neo4j数据库和dify大模型框架的rag模型搭建——后续补充
753 21
基于neo4j数据库和dify大模型框架的rag模型搭建——后续补充
|
4月前
|
存储 SQL 前端开发
跟老卫学HarmonyOS开发:ArkTS关系型数据库开发
本节以“账本”为例,使用关系型数据库接口实现账单的增、删、改、查操作。通过创建ArkTSRdb应用,演示如何操作RdbStore进行数据管理,并结合界面按钮实现交互功能。
198 0
跟老卫学HarmonyOS开发:ArkTS关系型数据库开发
|
6月前
|
Java 数据库 Docker
基于neo4j数据库和dify大模型框架的rag模型搭建
基于neo4j数据库和dify大模型框架的rag模型搭建
1700 35
|
6月前
|
存储 缓存 自然语言处理
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
183 8
评论功能开发全解析:从数据库设计到多语言实现-优雅草卓伊凡
|
4月前
|
SQL XML Java
配置Spring框架以连接SQL Server数据库
最后,需要集成Spring配置到应用中,这通常在 `main`方法或者Spring Boot的应用配置类中通过加载XML配置或使用注解来实现。
429 0
|
7月前
|
SQL 数据库连接 数据库
在C++的QT框架中实现SQLite数据库的连接与操作
以上就是在C++的QT框架中实现SQLite数据库的连接与操作的基本步骤。这些步骤包括创建数据库连接、执行SQL命令、处理查询结果和关闭数据库连接。在实际使用中,你可能需要根据具体的需求来修改这些代码。
460 14
|
7月前
|
SQL 调度 数据库
开发YashanDB数据库?用 DBeaver for YashanDB 更顺手
数据库开发复杂易错,尤其在企业级场景中。为提升效率,YashanDB 团队基于 DBeaver 开源工具打造专属解决方案——DBeaver for YashanDB。它支持多类型数据库对象管理(表、视图、函数等),适配 YashanDB 特有表结构(HEAP、LSC),提供智能补全、语法高亮、SQL 调试等功能,让开发更高效流畅。推荐用于数据库应用开发团队、高频调试用户及中大型企业统一工具栈场景。

热门文章

最新文章

下一篇
oss云网关配置