(广州大学 计算机科学与网络工程学院, 广东 广州 510006)
数据库应用广泛,每天有无数的开发人员在书写与数据库交互的代码,这些交互代码可以由程序员纯手动编写,也可以使用现有的专业ORM框架工具生成.目前,可用的专业ORM框架工具很多,如Hibernate[1]、ADO.NET Entity Framework[2]等.Hibernate是一个开放源代码、全自动的ORM框架,可以自动生成SQL语句、自动执行,使得Java程序员可以使用对象编程思维来操纵数据库.ADO.NET Entity Framework是微软以 ADO.NET为基础所发展出来的ORM解决方案[3].Entity Framework 利用了抽象化数据结构的方式,将每个数据库对象都转换成应用程序对象,让数据库的E-R模型完全转成对象模型.此外,还有其他的一些专业的ORM框架,如Mybatis[4]、NHibernate[5]、Speed[6]、SqlBuilder.NET[7]等,文献[8]提出了云计算环境下的一种基于Hbase的ORM设计方案.这些专业的ORM框架工具生成的代码的优点是功能丰富,使用比较方便、灵活,但也存在一些问题:
(1)有些框架代码的执行需要动态生成SQL,并通过相应的配置信息完成对象与关系之间的映射,因此,执行效率不高[9];
(2)当ORM框架的版本更新时,程序员使用ORM框架的代码也需要更新,因此,代码随ORM框架版本的改变而改变;
(3)ORM框架只支持部分关系数据库或部分程序设计语言;
(4)ORM框架比较复杂,程序员需要进行培训学习才能熟练掌握ORM框架的使用.
基于以上原因,很多对执行效率有要求的项目以及不提供ORM框架的关系数据库,都必须采用纯手工编写数据库访问层的源代码,但是,纯手工编写数据库访问层代码容易出错,而且需要耗费大量时间.为了减轻开发人员的工作量,本文提出一种数据库访问代码自动生成方法,利用该方法,可以生成“纯手工编写”的源代码,把开发人员从枯燥、重复的数据库访问代码编写工作中解放出来,提高工作效率.
当前,大多数手工编写的代码使用数据库开发商提供的接口(如JDBC[10]、ADO.net)访问数据库,使用这些接口访问数据库需要经过“与数据库建立连接、发送SQL语句并处理结果”的步骤,这些步骤的源代码自动生成需要解决三个关键问题:①数据库的数据类型与程序设计语言中的数据类型及获取方法之间的映射关系;②从数据库的数据字典中获取数据库的元数据;③自动生成代码的算法设计.接下来,本文以C#语言访问SQL Server数据库的代码为例,描述访问代码自动生成的过程.
(1)SQL Server数据类型与c#数据类型及获取方法之间的映射关系.
SQL Server 和 .NET Framework 基于不同的类型系统.针对不同的SQL Server数据库引擎类型,类型SqlDataReader公开了用于返回 .NET Framework 类型的访问器方法.本文在表1中列出了部分SQL Server数据类型与c#数据类型及获取方法之间的映射关系,详细的映射关系参见MSDN文档[11].
表1 SQL Server数据类型映射
(2)从数据库的数据字典中获取数据库的元数据.
当使用SQL命令在数据库中创建表、视图、存储过程时,数据库将表、视图、存储过程的元数据存储到系统表中,通过查询数据库系统表,可以获取到创建表、视图、存储过程的所有元数据,例如,可以使用以下SQL语句在SQL SERVER中获取STUDENT表的字段元数据.
select col.name columnName, tp.name dataType,col.max_length dataLength,col.precision dataPrecision,col.scale dataScale, case col.is_nullable when 1 then 'Y' when 0 then 'N' end isNullable, case col.is_identity when 1 then 'Y' when 0 then 'N' end isIdentity, case col.is_computed when 1 then 'Y' when 0 then 'N' end isComputed,sc.text defaultValue from sys.objects obj inner join sys.COLUMNS col on obj.object_id= col.object_id inner join sys.types tp on col.user_type_id=tp.user_type_id left join syscomments sc on col.default_object_id=sc.id where obj.type in ('U','V') and obj.name ='STUDENT'
每个表或视图需要获取的相关的元数据信息包括列名、列数据类型、数据长度、可否空、是否自增字段、是否自动计算字段、默认值等,如果是数字,还必须考虑数字的有效位数和小数位数.如果是表,还必须考虑表的约束元数据,以确定主码包括哪些字段、外码包括哪些字段.
对于存储过程,需要通过系统表获取存储过程的名称、参数类型、参数个数和顺序等元数据,以生成调用存储过程的源代码.
(3)自动生成代码的算法设计.
本文设计了两种代码生成算法即常规代码生成算法和按SQL生成代码.常规代码生成算法的过程(本文以表为例):①与数据库进行连接,获取表的元数据信息;②根据获取的元数据信息和表1中的类型映射信息,生成实体类.如图1所示,表名称映射为类名称,表的每个字段映射为类的属性成员.③根据获取的元数据信息和生成实体类,生成select、insert、update、delete等SQL语句对应的静态成员函数.
create table student(sno varchar(10) primary key,sname varchar(20) not null,sage int);public partial class student{public String Sno{get;set;}public String Sname{get;set;}public Nullable
图1 Student表与实体类student
Fig.1 Student table and student entity class
作为示例,以下生成的代码是根据学生的学号获取对应的学生信息,其中,类SqlHelper封装了访问数据库的静态方法.
public static Student GetObjectByPK(String SNO)
{
Student tmp=null;
string sql="SELECT SNO,SNAME,SAGE FROM STUDENT WHERE SNO=@SNO ";
SqlParameter[] para=new SqlParameter[]{new SqlParameter("@SNO",SqlDbType.VarChar,10)};
para[0].Value=SNO;
using(SqlDataReader sdr=SqlHelper.ExecuteReader(SqlHelper.strCon, CommandType.Text, sql, para))
{
if (sdr.Read())
{
tmp=new Student();
tmp.Sno=sdr.GetSqlString(0).Value;
tmp.Sname=sdr.GetSqlString(1).Value;
if (!sdr.IsDBNull(2))
tmp.Sage=sdr.GetSqlInt32(2).Value;
}
}
return tmp;
}
按SQL生成代码的方法首先对SQL语句分析,解析出SQL语句中包含的表以及字段信息,然后按照“常规代码生成算法”的过程,生成对应的静态操作方法;对于多表查询的SQL语句,可以在数据库中创建为视图,然后按照“常规代码生成算法”的过程,生成对应的静态操作方法.
本文提出一种自动生成“纯手工编写”的源代码的方法,生成的代码规范、易于维护,减轻了程序员的编码工作,提高了开发效率.