posts - 25, comments - 2, trackbacks - 1, articles - 1
  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理

2008年11月14日

这种方式的原理是,在某页面(任何页面比如a.htm),把要导出的内容或结果集,通过表单提交到一个b.aspx页面。
aspx页面中,生成一个具有唯一文件名的excel文件,然后自动输出到前台。由前台来保存。
1.
a.htm页面,只需要把表单提交到b.aspx中即可。至于要提交什么内容,放入表单中的hidden控件即可。
你甚至可以动态改变hidden控件的value的值(这个很有用)。
关键字:
<form method="post" action="b.aspx" target="_blank">
<input type="hidden" name="TextSql" value="要提交的内容" ID="TextSql"></input>
</form>


2.
在aspx页面后台关键代码:


using System.IO;
using System.Text;

 // 接收表单的值
  private void Page_Load(object sender, System.EventArgs e)
  {
  
   string destString = Request.Form["TextSql"].ToString();
   ExportToFile(destString);

  }
 

//存为excel文件
 private void ExportToFile(string esql)
  {
   //new 一个 guid
   string FileName = Guid.NewGuid().ToString() + ".xls"; //GUID生成唯一文件名
   string FileType="application/ms-excel";
   try
   {
    System.Web.HttpResponse httpResponse = Page.Response;
    httpResponse.AppendHeader("Content-Disposition","attachment;filename="+HttpUtility.UrlEncode(FileName,System.Text.Encoding.UTF8));
    httpResponse.ContentEncoding=System.Text.Encoding.GetEncoding("GB2312");
    httpResponse.ContentType = FileType;
    StringBuilder ckpw = new StringBuilder(esql);
    System.IO.StringWriter  tw = new System.IO.StringWriter(ckpw) ;
    string filePath = Server.MapPath("..")+"\\"+FileName;
    System.IO.StreamWriter sw = System.IO.File.CreateText(filePath);
    sw.Write(tw.ToString());
    sw.Close();

    DownFile(httpResponse,FileName,filePath);
    httpResponse.End();
   }
   catch (Exception ee)
   {
    throw ee;
   }    
  }


//输出到前台  
private  bool DownFile(System.Web.HttpResponse Response,string fileName,string fullPath)
  {
   try
   {
    Response.ContentType = "application/octet-stream";

    Response.AppendHeader("Content-Disposition","attachment;filename=" +
     HttpUtility.UrlEncode(fileName,System.Text.Encoding.UTF8) + ";charset=GB2312");
    System.IO.FileStream fs= System.IO.File.OpenRead(fullPath);
    long fLen=fs.Length;
    int size=102400;//每100K同时下载数据
    byte[] readData = new byte[size];//指定缓冲区的大小
    if(size>fLen)size=Convert.ToInt32(fLen);
    long fPos=0;
    bool isEnd=false;
    while (!isEnd)
    {
     if((fPos+size)>fLen)
     {
      size=Convert.ToInt32(fLen-fPos);
      readData = new byte[size];
      isEnd=true;
     }
     fs.Read(readData, 0, size);//读入一个压缩块
     Response.BinaryWrite(readData);
     fPos+=size;
    }
    fs.Close();
    System.IO.File.Delete(fullPath);
    return true;
   }
   catch
   {
    return false;
   }
  }


 

Feedback

#1楼    回复  引用    

2007-12-14 09:50 by kitwen [未注册用户]
<form method="post" action="b.aspx" target="_blank">
<input type="hidden" name="TextSql" value="要提交的内容" ID="TextSql"></input>
</form>

1.要提交的内容要用什么形式?html?二进制流?
2.为什么不可以直接在b.aspx写要输出的内容呢?

#2楼 [楼主]   回复  引用  查看    

2008-04-02 11:40 by 暴走的猪      
@kitwen
1.要提交的内容你可以动态写入 hidden控件内。可以是形式合法的html table或者xml
2.当然可以直接用b.aspx 。。我这个是在htm页面要导出到excecl,所以得把它提交倒b.aspx

posted @ 2008-11-14 10:21 庞滨 阅读(25) 评论(0) 编辑

2008年10月24日

查询速度慢的原因很多,常见如下几种:

1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)

2、I/O吞吐量小,形成了瓶颈效应。

3、没有创建计算列导致查询不优化。

4、内存不足

5、网络速度慢

6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)

7、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷)

8、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。

9、返回了不必要的行和列

10、查询语句不好,没有优化

可以通过如下方法来优化查询 :

1、把数据、日志、索引放到不同的I/O设备上,增加读取速度,以前可以将Tempdb应放在RAID0上,SQL2000不在支持。数据量(尺寸)越大,提高I/O越重要.

2、纵向、横向分割表,减少表的尺寸(sp_spaceuse)

3、升级硬件

4、根据查询条件,建立索引,优化索引、优化访问方式,限制结果集的数据量。注意填充因子要适当(最好是使用默认值0)。索引应该尽量小,使用字节数小的列建索引好(参照索引的创建),不要对有限的几个值的字段建单一索引如性别字段

5、提高网速;

6、扩大服务器的内存,Windows 2000和SQL server 2000能支持4-8G的内存。配置虚拟内存:虚拟内存大小应基于计算机上并发运行的服务进行配置。运行 Microsoft SQL Server? 2000 时,可考虑将虚拟内存大小设置为计算机中安装的物理内存的 1.5 倍。如果另外安装了全文检索功能,并打算运行 Microsoft 搜索服务以便执行全文索引和查询,可考虑:将虚拟内存大小配置为至少是计算机中安装的物理内存的 3 倍。将 SQL Server max server memory 服务器配置选项配置为物理内存的 1.5 倍(虚拟内存大小设置的一半)。

7、增加服务器CPU个数;但是必须明白并行处理串行处理更需要资源例如内存。使用并行还是串行程是MsSQL自动评估选择的。单个任务分解成多个任务,就可以在处理器上运行。例如耽搁查询的排序、连接、扫描和GROUP BY字句同时执行,SQL SERVER根据系统的负载情况决定最优的并行等级,复杂的需要消耗大量的CPU的查询最适合并行处理。但是更新操作UPDATE,INSERT,DELETE还不能并行处理。

8、如果是使用like进行查询的话,简单的使用index是不行的,但是全文索引,耗空间。 like 'a%' 使用索引 like '%a' 不使用索引用 like '%a%' 查询时,查询耗时和字段值总长度成正比,所以不能用CHAR类型,而是VARCHAR。对于字段的值很长的建全文索引。

9、DB Server 和APPLication Server 分离;OLTP和OLAP分离

10、分布式分区视图可用于实现数据库服务器联合体。联合体是一组分开管理的服务器,但它们相互协作分担系统的处理负荷。这种通过分区数据形成数据库服务器联合体的机制能够扩大一组服务器,以支持大型的多层 Web 站点的处理需要。有关更多信息,参见设计联合数据库服务器。(参照SQL帮助文件'分区视图')

    a、在实现分区视图之前,必须先水平分区表

    b、在创建成员表后,在每个成员服务器上定义一个分布式分区视图,并且每个视图具有相同的名称。这样,引用分布式分区视图名的查询可以在任何一个成员服务器上运行。系统操作如同每个成员服务器上都有一个原始表的复本一样,但其实每个服务器上只有一个成员表和一个分布式分区视图。数据的位置对应用程序是透明的。

11、重建索引 DBCC REINDEX ,DBCC INDEXDEFRAG,收缩数据和日志 DBCC SHRINKDB,DBCC SHRINKFILE. 设置自动收缩日志.对于大的数据库不要设置数据库自动增长,它会降低服务器的性能。 在T-sql的写法上有很大的讲究,下面列出常见的要点:首先,DBMS处理查询计划的过程是这样的:

    1、 查询语句的词法、语法检查

    2、 将语句提交给DBMS的查询优化器

    3、 优化器做代数优化和存取路径的优化

    4、 由预编译模块生成查询规划

    5、 然后在合适的时间提交给系统处理执行

    6、 最后将执行结果返回给用户其次,看一下SQL SERVER的数据存放的结构:一个页面的大小为8K(8060)字节,8个页面为一个盘区,按照B树存放。

12、Commit和rollback的区别 Rollback:回滚所有的事物。 Commit:提交当前的事物. 没有必要在动态SQL里写事物,如果要写请写在外面如: begin tran exec(@s) commit trans 或者将动态SQL 写成函数或者存储过程。

13、在查询Select语句中用Where字句限制返回的行数,避免表扫描,如果返回不必要的数据,浪费了服务器的I/O资源,加重了网络的负担降低性能。如果表很大,在表扫描的期间将表锁住,禁止其他的联接访问表,后果严重。

14、SQL的注释申明对执行没有任何影响



15、尽可能不使用游标,它占用大量的资源。如果需要row-by-row地执行,尽量采用非游标技术,如:在客户端循环,用临时表,Table变量,用子查询,用Case语句等等。游标可以按照它所支持的提取选项进行分类: 只进 必须按照从第一行到最后一行的顺序提取行。FETCH NEXT 是唯一允许的提取操作,也是默认方式。可滚动性 可以在游标中任何地方随机提取任意行。游标的技术在SQL2000下变得功能很强大,他的目的是支持循环。有四个并发选项 READ_ONLY:不允许通过游标定位更新(Update),且在组成结果集的行中没有锁。 OPTIMISTIC WITH valueS:乐观并发控制是事务控制理论的一个标准部分。乐观并发控制用于这样的情形,即在打开游标及更新行的间隔中,只有很小的机会让第二个用户更新某一行。当某个游标以此选项打开时,没有锁控制其中的行,这将有助于最大化其处理能力。如果用户试图修改某一行,则此行的当前值会与最后一次提取此行时获取的值进行比较。如果任何值发生改变,则服务器就会知道其他人已更新了此行,并会返回一个错误。如果值是一样的,服务器就执行修改。 选择这个并发选项OPTIMISTIC WITH ROW VERSIONING:此乐观并发控制选项基于行版本控制。使用行版本控制,其中的表必须具有某种版本标识符,服务器可用它来确定该行在读入游标后是否有所更改。在 SQL Server 中,这个性能由 timestamp 数据类型提供,它是一个二进制数字,表示数据库中更改的相对顺序。每个数据库都有一个全局当前时间戳值:@@DBTS。每次以任何方式更改带有 timestamp 列的行时,SQL Server 先在时间戳列中存储当前的 @@DBTS 值,然后增加 @@DBTS 的值。如果某 个表具有 timestamp 列,则时间戳会被记到行级。服务器就可以比较某行的当前时间戳值和上次提取时所存储的时间戳值,从而确定该行是否已更新。服务器不必比较所有列的值,只需比较 timestamp 列即可。如果应用程序对没有 timestamp 列的表要求基于行版本控制的乐观并发,则游标默认为基于数值的乐观并发控制。 SCROLL LOCKS 这个选项实现悲观并发控制。在悲观并发控制中,在把数据库的行读入游标结果集时,应用程序将试图锁定数据库行。在使用服务器游标时,将行读入游标时会在其上放置一个更新锁。如果在事务内打开游标,则该事务更新锁将一直保持到事务被提交或回滚;当提取下一行时,将除去游标锁。如果在事务外打开游标,则提取下一行时,锁就被丢弃。因此,每当用户需要完全的悲观并发控制时,游标都应在事务内打开。更新锁将阻止任何其它任务获取更新锁或排它锁,从而阻止其它任务更新该行。然而,更新锁并不阻止共享锁,所以它不会阻止其它任务读取行,除非第二个任务也在要求带更新锁的读取。滚动锁根据在游标定义的 SELECT 语句中指定的锁提示,这些游标并发选项可以生成滚动锁。滚动锁在提取时在每行上获取,并保持到下次提取或者游标关闭,以先发生者为准。下次提取时,服务器为新提取中的行获取滚动锁,并释放上次提取中行的滚动锁。滚动锁独立于事务锁,并可以保持到一个提交或回滚操作之后。如果提交时关闭游标的选项为关,则 COMMIT 语句并不关闭任何打开的游标,而且滚动锁被保留到提交之后,以维护对所提取数据的隔离。所获取滚动锁的类型取决于游标并发选项和游标 SELECT 语句中的锁提示。锁提示 只读 乐观数值 乐观行版本控制 锁定无提示 未锁定 未锁定 未锁定 更新 NOLOCK 未锁定 未锁定 未锁定 未锁定 HOLDLOCK 共享 共享 共享 更新 UPDLOCK 错误 更新 更新 更新 TABLOCKX 错误 未锁定 未锁定 更新其它 未锁定 未锁定 未锁定 更新 *指定 NOLOCK 提示将使指定了该提示的表在游标内是只读的。

16、用Profiler来跟踪查询,得到查询所需的时间,找出SQL的问题所在;用索引优化器优化索引

17、注意UNion和UNion all 的区别。UNION all好

18、注意使用DISTINCT,在没有必要时不要用,它同UNION一样会使查询变慢。重复的记录在查询里是没有问题的

19、查询时不要返回不需要的行、列

20、用sp_configure 'query governor cost limit'或者SET QUERY_GOVERNOR_COST_LIMIT来限制查询消耗的资源。当评估查询消耗的资源超出限制时,服务器自动取消查询,在查询之前就扼杀掉。SET LOCKTIME设置锁的时间

21、用select top 100 / 10 Percent 来限制用户返回的行数或者SET ROWCOUNT来限制操作的行

22、在SQL2000以前,一般不要用如下的字句: "IS NULL", "<>", "!=", "!>", "!<", "NOT", "NOT EXISTS", "NOT IN", "NOT LIKE", and "LIKE '%500'",因为他们不走索引全是表扫描。也不要在WHere字句中的列名加函数,如Convert,substring等,如果必须用函数的时候,创建计算列再创建索引来替代.还可以变通写法:WHERE SUBSTRING(firstname,1,1) = 'm'改为WHERE firstname like 'm%'(索引扫描),一定要将函数和列名分开。并且索引不能建得太多和太大。NOT IN会多次扫描表,使用EXISTS、NOT EXISTS ,IN , LEFT OUTER JOIN 来替代,特别是左连接,而Exists比IN更快,最慢的是NOT操作.如果列的值含有空,以前它的索引不起作用,现在2000的优化器能够处理了。相同的是IS NULL,"NOT", "NOT EXISTS", "NOT IN"能优化她,而"<>"等还是不能优化,用不到索引。

23、使用Query Analyzer,查看SQL语句的查询计划和评估分析是否是优化的SQL。一般的20%的代码占据了80%的资源,我们优化的重点是这些慢的地方。

24、如果使用了IN或者OR等时发现查询没有走索引,使用显示申明指定索引: SELECT * FROM PersonMember (INDEX = IX_Title) WHERE processid IN ('男','女')

25、将需要查询的结果预先计算好放在表中,查询的时候再SELECT。这在SQL7.0以前是最重要的手段。例如医院的住院费计算。

26、MIN() 和 MAX()能使用到合适的索引。

27、数据库有一个原则是代码离数据越近越好,所以优先选择Default,依次为Rules,Triggers, Constraint(约束如外健主健CheckUNIQUE……,数据类型的最大长度等等都是约束),Procedure.这样不仅维护工作小,编写程序质量高,并且执行的速度快。

28、如果要插入大的二进制值到Image列,使用存储过程,千万不要用内嵌INsert来插入(不知JAVA是否)。因为这样应用程序首先将二进制值转换成字符串(尺寸是它的两倍),服务器受到字符后又将他转换成二进制值.存储过程就没有这些动作: 方法:Create procedure p_insert as insert into table(Fimage) values (@image), 在前台调用这个存储过程传入二进制参数,这样处理速度明显改善。

29、Between在某些时候比IN速度更快,Between能够更快地根据索引找到范围。用查询优化器可见到差别。 select * from chineseresume where title in ('男','女') Select * from chineseresume where between '男' and '女' 是一样的。由于in会在比较多次,所以有时会慢些。

30、在必要是对全局或者局部临时表创建索引,有时能够提高速度,但不是一定会这样,因为索引也耗费大量的资源。他的创建同是实际表一样。

31、不要建没有作用的事物例如产生报表时,浪费资源。只有在必要使用事物时使用它。

32、用OR的字句可以分解成多个查询,并且通过UNION 连接多个查询。他们的速度只同是否使用索引有关,如果查询需要用到联合索引,用UNION all执行的效率更高.多个OR的字句没有用到索引,改写成UNION的形式再试图与索引匹配。一个关键的问题是否用到索引。



33、尽量少用视图,它的效率低。对视图操作比直接对表操作慢,可以用stored procedure来代替她。特别的是不要用视图嵌套,嵌套视图增加了寻找原始资料的难度。我们看视图的本质:它是存放在服务器上的被优化好了的已经产生了查询规划的SQL。对单个表检索数据时,不要使用指向多个表的视图,直接从表检索或者仅仅包含这个表的视图上读,否则增加了不必要的开销,查询受到干扰.为了加快视图的查询,MsSQL增加了视图索引的功能。

34、没有必要时不要用DISTINCT和ORDER BY,这些动作可以改在客户端执行。它们增加了额外的开销。这同UNION 和UNION ALL一样的道理。

select top 20 ad.companyname,comid,position,ad.referenceid,worklocation, convert(varchar(10),ad.postDate,120) as postDate1,workyear,degreedescription FROM jobcn_query.dbo.COMPANYAD_query ad where referenceID in('JCNAD00329667','JCNAD132168','JCNAD00337748','JCNAD00338345',

'JCNAD00333138','JCNAD00303570','JCNAD00303569',

'JCNAD00303568','JCNAD00306698','JCNAD00231935','JCNAD00231933',

'JCNAD00254567','JCNAD00254585','JCNAD00254608',

'JCNAD00254607','JCNAD00258524','JCNAD00332133','JCNAD00268618',

'JCNAD00279196','JCNAD00268613') order by postdate desc

35、在IN后面值的列表中,将出现最频繁的值放在最前面,出现得最少的放在最后面,减少判断的次数。

36、当用SELECT INTO时,它会锁住系统表(sysobjects,sysindexes等等),阻塞其他的连接的存取。创建临时表时用显示申明语句,而不是select INTO. drop table t_lxh begin tran select * into t_lxh from chineseresume where name = 'XYZ' --commit 在另一个连接中SELECT * from sysobjects可以看到 SELECT INTO 会锁住系统表,Create table 也会锁系统表(不管是临时表还是系统表)。所以千万不要在事物内使用它!!!这样的话如果是经常要用的临时表请使用实表,或者临时表变量。

37、一般在GROUP BY 个HAVING字句之前就能剔除多余的行,所以尽量不要用它们来做剔除行的工作。他们的执行顺序应该如下最优:select 的Where字句选择所有合适的行,Group By用来分组个统计行,Having字句用来剔除多余的分组。这样Group By 个Having的开销小,查询快.对于大的数据行进行分组和Having十分消耗资源。如果Group BY的目的不包括计算,只是分组,那么用Distinct更快

38、一次更新多条记录比分多次更新每次一条快,就是说批处理好

39、少用临时表,尽量用结果集和Table类性的变量来代替它,Table 类型的变量比临时表好

40、在SQL2000下,计算字段是可以索引的,需要满足的条件如下:

    a、计算字段的表达是确定的

    b、不能用在TEXT,Ntext,Image数据类型

    c、必须配制如下选项 ANSI_NULLS = ON, ANSI_PADDINGS = ON, …….

41、尽量将数据的处理工作放在服务器上,减少网络的开销,如使用存储过程。存储过程是编译好、优化过、并且被组织到一个执行规划里、且存储在数据库中的SQL语句,是控制流语言的集合,速度当然快。反复执行的动态SQL,可以使用临时存储过程,该过程(临时表)被放在Tempdb中。以前由于SQL SERVER对复杂的数学计算不支持,所以不得不将这个工作放在其他的层上而增加网络的开销。SQL2000支持UDFs,现在支持复杂的数学计算,函数的返回值不要太大,这样的开销很大。用户自定义函数象光标一样执行的消耗大量的资源,如果返回大的结果采用存储过程

42、不要在一句话里再三的使用相同的函数,浪费资源,将结果放在变量里再调用更快

43、SELECT COUNT(*)的效率教低,尽量变通他的写法,而EXISTS快.同时请注意区别: select count(Field of null) from Table 和 select count(Field of NOT null) from Table 的返回值是不同的!!!

44、当服务器的内存够多时,配制线程数量 = 最大连接数+5,这样能发挥最大的效率;否则使用 配制线程数量<最大连接数启用SQL SERVER的线程池来解决,如果还是数量 = 最大连接数+5,严重的损害服务器的性能。

45、按照一定的次序来访问你的表。如果你先锁住表A,再锁住表B,那么在所有的存储过程中都要按照这个顺序来锁定它们。如果你(不经意的)某个存储过程中先锁定表B,再锁定表A,这可能就会导致一个死锁。如果锁定顺序没有被预先详细的设计好,死锁很难被发现


46、通过SQL Server Performance Monitor监视相应硬件的负载 Memory: Page Faults / sec计数器如果该值偶尔走高,表明当时有线程竞争内存。如果持续很高,则内存可能是瓶颈。

Process:

    1、% DPC Time 指在范例间隔期间处理器用在缓延程序调用(DPC)接收和提供服务的百分比。(DPC 正在运行的为比标准间隔优先权低的间隔)。 由于 DPC 是以特权模式执行的,DPC 时间的百分比为特权时间 百分比的一部分。这些时间单独计算并且不属于间隔计算总数的一部 分。这个总数显示了作为实例时间百分比的平均忙时。

    2、%Processor Time计数器 如果该参数值持续超过95%,表明瓶颈是CPU。可以考虑增加一个处理器或换一个更快的处理器。

    3、% Privileged Time 指非闲置处理器时间用于特权模式的百分比。(特权模式是为操作系统组件和操纵硬件驱动程序而设计的一种处理模式。它允许直接访问硬件和所有内存。另一种模式为用户模式,它是一种为应用程序、环境分系统和整数分系统设计的一种有限处理模式。操作系统将应用程序线程转换成特权模式以访问操作系统服务)。 特权时间的 % 包括为间断和 DPC 提供服务的时间。特权时间比率高可能是由于失败设备产生的大数量的间隔而引起的。这个计数器将平均忙时作为样本时间的一部分显示。

    4、% User Time表示耗费CPU的数据库操作,如排序,执行aggregate functions等。如果该值很高,可考虑增加索引,尽量使用简单的表联接,水平分割大表格等方法来降低该值。 Physical Disk: Curretn Disk Queue Length计数器该值应不超过磁盘数的1.5~2倍。要提高性能,可增加磁盘。 SQLServer:Cache Hit Ratio计数器该值越高越好。如果持续低于80%,应考虑增加内存。 注意该参数值是从SQL Server启动后,就一直累加记数,所以运行经过一段时间后,该值将不能反映系统当前值。

47、分析select emp_name form employee where salary > 3000 在此语句中若salary是Float类型的,则优化器对其进行优化为Convert(float,3000),因为3000是个整数,我们应在编程时使用3000.0而不要等运行时让DBMS进行转化。同样字符和整型数据的转换。

48、查询的关联同写的顺序

select a.personMemberID, * from chineseresume a,personmember b where personMemberID = b.referenceid and a.personMemberID = 'JCNPRH39681' (A = B ,B = '号码')

select a.personMemberID, * from chineseresume a,personmember b where a.personMemberID = b.referenceid and a.personMemberID = 'JCNPRH39681' and b.referenceid = 'JCNPRH39681' (A = B ,B = '号码', A = '号码')

select a.personMemberID, * from chineseresume a,personmember b where b.referenceid = 'JCNPRH39681' and a.personMemberID = 'JCNPRH39681' (B = '号码', A = '号码')

49、

(1)IF 没有输入负责人代码 THEN code1=0 code2=9999 ELSE code1=code2=负责人代码 END IF 执行SQL语句为: SELECT 负责人名 FROM P2000 WHERE 负责人代码>=:code1 AND负责人代码 <=:code2

(2)IF 没有输入负责人代码 THEN  SELECT 负责人名 FROM P2000 ELSE code= 负责人代码 SELECT 负责人代码 FROM P2000 WHERE 负责人代码=:code END IF 第一种方法只用了一条SQL语句,第二种方法用了两条SQL语句。在没有输入负责人代码时,第二种方法显然比第一种方法执行效率高,因为它没有限制条件;在输入了负责人代码时,第二种方法仍然比第一种方法效率高,不仅是少了一个限制条件,还因相等运算是最快的查询运算。我们写程序不要怕麻烦

50、关于JOBCN现在查询分页的新方法(如下),用性能优化器分析性能的瓶颈,如果在I/O或者网络的速度上,如下的方法优化切实有效,如果在CPU或者内存上,用现在的方法更好。请区分如下的方法,说明索引越小越好。

begin

DECLARE @local_variable table (FID int identity(1,1),ReferenceID varchar(20))

insert into @local_variable (ReferenceID)

select top 100000 ReferenceID from chineseresume order by ReferenceID

select * from @local_variable where Fid > 40 and fid <= 60

end 和

begin

DECLARE @local_variable table (FID int identity(1,1),ReferenceID varchar(20))

insert into @local_variable (ReferenceID)

select top 100000 ReferenceID from chineseresume order by updatedate

select * from @local_variable where Fid > 40 and fid <= 60

end 的不同

begin

create table #temp (FID int identity(1,1),ReferenceID varchar(20))

insert into #temp (ReferenceID)

select top 100000 ReferenceID from chineseresume order by updatedate

select * from #temp where Fid > 40 and fid <= 60 drop table #temp

end

posted @ 2008-10-24 23:40 庞滨 阅读(27) 评论(0) 编辑

MS SQL Server数据库事务锁机制分析  
  2003-1-11 13:59:38  
 
 
  北京师范大学 刘永明  
    
  锁是网络数据库中的一个非常重要的概念,它主要用于多用户环境下保证数据库完整性和一致性。各种大型数据库所采用的锁的基本理论是一致的,但在具体实现上各有差别。目前,大多数数据库管理系统都或多或少具有自我调节、自我管理的功能,因此很多用户实际上不清楚锁的理论和所用数据库中锁的具体实现。

Microsoft SQL Server(以下简称SQL Server)作为一种中小型数据库管理系统,已经得到了广泛的应用,该系统更强调由系统来管理锁。在用户有SQL请求时,系统分析请求,自动在满足锁定条件和系统性能之间为数据库加上适当的锁,同时系统在运行期间常常自动进行优化处理,实行动态加锁。对于一般的用户而言,通过系统的自动锁定管理机制基本可以满足使用要求,但如果对数据安全、数据库完整性和一致性有特殊要求,就必须自己控制数据库的锁定和解锁,这就需要了解SQL Server的锁机制,掌握数据库锁定方法。

锁的多粒度性以及锁升级

数据库中的锁是指一种软件机制,用来指示某个用户(也即进程会话,下同)已经占用了某种资源,从而防止其他用户做出影响本用户的数据修改或导致数据库数据的非完整性和非一致性。这儿所谓资源,主要指用户可以操作的数据行、索引以及数据表等。根据资源的不同,锁有多粒度(multigranular)的概念,也就是指可以锁定的资源的层次。SQL Server中能够锁定的资源粒度包括:数据库、表、区域、页面、键值(指带有索引的行数据)、行标识符(RID,即表中的单行数据)。

采用多粒度锁的重要用途是用来支持并发操作和保证数据的完整性。SQL Server根据用户的请求,做出分析后自动给数据库加上合适的锁。假设某用户只操作一个表中的部分行数据,系统可能会只添加几个行锁(RID)或页面锁,这样可以尽可能多地支持多用户的并发操作。但是,如果用户事务中频繁对某个表中的多条记录操作,将导致对该表的许多记录行都加上了行级锁,数据库系统中锁的数目会急剧增加,这样就加重了系统负荷,影响系统性能。因此,在数据库系统中,一般都支持锁升级(lock escalation)。所谓锁升级是指调整锁的粒度,将多个低粒度的锁替换成少数的更高粒度的锁,以此来降低系统负荷。在SQL Server中当一个事务中的锁较多,达到锁升级门限时,系统自动将行级锁和页面锁升级为表级锁。特别值得注意的是,在SQL Server中,锁的升级门限以及锁升级是由系统自动来确定的,不需要用户设置。

锁的模式和兼容性

在数据库中加锁时,除了可以对不同的资源加锁,还可以使用不同程度的加锁方式,即锁有多种模式,SQL Server中锁模式包括:

1.共享锁

SQL Server中,共享锁用于所有的只读数据操作。共享锁是非独占的,允许多个并发事务读取其锁定的资源。默认情况下,数据被读取后,SQL Server立即释放共享锁。例如,执行查询“SELECT * FROM my_table”时,首先锁定第一页,读取之后,释放对第一页的锁定,然后锁定第二页。这样,就允许在读操作过程中,修改未被锁定的第一页。但是,事务隔离级别连接选项设置和SELECT语句中的锁定设置都可以改变SQL Server的这种默认设置。例如,“ SELECT * FROM my_table HOLDLOCK”就要求在整个查询过程中,保持对表的锁定,直到查询完成才释放锁定。

2.修改锁

修改锁在修改操作的初始化阶段用来锁定可能要被修改的资源,这样可以避免使用共享锁造成的死锁现象。因为使用共享锁时,修改数据的操作分为两步,首先获得一个共享锁,读取数据,然后将共享锁升级为独占锁,然后再执行修改操作。这样如果同时有两个或多个事务同时对一个事务申请了共享锁,在修改数据的时候,这些事务都要将共享锁升级为独占锁。这时,这些事务都不会释放共享锁而是一直等待对方释放,这样就造成了死锁。如果一个数据在修改前直接申请修改锁,在数据修改的时候再升级为独占锁,就可以避免死锁。修改锁与共享锁是兼容的,也就是说一个资源用共享锁锁定后,允许再用修改锁锁定。

3.独占锁

独占锁是为修改数据而保留的。它所锁定的资源,其他事务不能读取也不能修改。独占锁不能和其他锁兼容。

4.结构锁

结构锁分为结构修改锁(Sch-M)和结构稳定锁(Sch-S)。执行表定义语言操作时,SQL Server采用Sch-M锁,编译查询时,SQL Server采用Sch-S锁。

5.意向锁

意向锁说明SQL Server有在资源的低层获得共享锁或独占锁的意向。例如,表级的共享意向锁说明事务意图将独占锁释放到表中的页或者行。意向锁又可以分为共享意向锁、独占意向锁和共享式独占意向锁。共享意向锁说明事务意图在共享意向锁所锁定的低层资源上放置共享锁来读取数据。独占意向锁说明事务意图在共享意向锁所锁定的低层资源上放置独占锁来修改数据。共享式独占锁说明事务允许其他事务使用共享锁来读取顶层资源,并意图在该资源低层上放置独占锁。

6.批量修改锁

批量复制数据时使用批量修改锁。可以通过表的TabLock提示或者使用系统存储过程sp_tableoption的“table lock on bulk load”选项设定批量修改锁。

另外,SQL Server命令语句操作会影响锁定的方式,语句的组合也同样能产生不同的锁定,详情如下表: 

锁冲突及其防止办法

在数据库系统中,死锁是指多个用户(进程)分别锁定了一个资源,并又试图请求锁定对方已经锁定的资源,这就产生了一个锁定请求环,导致多个用户(进程)都处于等待对方释放所锁定资源的状态。

在SQL Server中,系统能够自动定期搜索和处理死锁问题。系统在每次搜索中标识所有等待锁定请求的进程会话,如果在下一次搜索中该被标识的进程仍处于等待状态,SQL Server就开始递归死锁搜索。

(上接第D21版) 当搜索检测到锁定请求环时,系统将根据各进程会话的死锁优先级别来结束一个优先级最低的事务,此后,系统回滚该事务,并向该进程发出1205号错误信息。这样,其他事务就有可能继续运行了。死锁优先级的设置语句为:

SET DEADLOCK_PRIORITY { LOW | NORMAL}

其中LOW说明该进程会话的优先级较低,在出现死锁时,可以首先中断该进程的事务。另外,各进程中通过设置LOCK_TIMEOUT选项能够设置进程处于锁定请求状态的最长等待时间。该设置的语句:

SET LOCK_TIMEOUT { timeout_period }

其中,timeout_period以毫秒为单位。

理解了死锁的概念,在应用程序中就可以采用下面的一些方法来尽量避免死锁了:

(1)合理安排表访问顺序。

(2)在事务中尽量避免用户干预,尽量使一个事务处理的任务少些。

(3)采用脏读技术。脏读由于不对被访问的表加锁,而避免了锁冲突。在客户机/服务器应用环境中,有些事务往往不允许读脏数据,但在特定的条件下,我们可以用脏读。

(4)数据访问时域离散法。数据访问时域离散法是指在客户机/服务器结构中,采取各种控制手段控制对数据库或数据库中的对象访问时间段。主要通过以下方式实现: 合理安排后台事务的执行时间,采用工作流对后台事务进行统一管理。工作流在管理任务时,一方面限制同一类任务的线程数(往往限制为1个),防止资源过多占用; 另一方面合理安排不同任务执行时序、时间,尽量避免多个后台任务同时执行,另外, 避免在前台交易高峰时间运行后台任务。

(5)数据存储空间离散法。数据存储空间离散法是指采取各种手段,将逻辑上在一个表中的数据分散到若干离散的空间上去,以便改善对表的访问性能。主要通过以下方法实现: 第一,将大表按行或列分解为若干小表; 第二,按不同的用户群分解。

(6)使用尽可能低的隔离性级别。隔离性级别是指为保证数据库数据的完整性和一致性而使多用户事务隔离的程度,SQL92定义了4种隔离性级别:未提交读、提交读、可重复读和可串行。如果选择过高的隔离性级别,如可串行,虽然系统可以因实现更好隔离性而更大程度上保证数据的完整性和一致性,但各事务间冲突而死锁的机会大大增加,大大影响了系统性能。

(7)使用Bound Connections。Bound connections 允许两个或多个事务连接共享事务和锁,而且任何一个事务连接要申请锁如同另外一个事务要申请锁一样,因此可以允许这些事务共享数据而不会有加锁的冲突。

(8)考虑使用乐观锁定或使事务首先获得一个独占锁定。一个最常见的死锁情况发生在系列号生成器中,它们通常是这样编写的:

begin tran

select new_id from keytab holdlock

update keytab set new_id=new_id+l

commit tran

如果有两个用户在同时运行这一事务,他们都会得到共享锁定并保持它。当两个用户都试图得到keytab表的独占锁定时,就会进入死锁。为了避免这种情况的发生,应将上述事务重写成如下形式:

begin tran

update keytab set new_id=new_id+l

select new_id from keytab

commit tran

以这种方式改写后,只有一个事务能得到keytab的独占锁定,其他进程必须等到第一个事务的完成,这样虽增加了执行时间,但避免了死锁。

如果要求在一个事务中具有读取的可重复能力,就要考虑以这种方式来编写事务,以获得资源的独占锁定,然后再去读数据。例如,如果一个事务需要检索出titles表中所有书的平均价格,并保证在update被应用前,结果不会改变,优化器就会分配一个独占的表锁定。考虑如下的SQL代码:

begin tran

update titles set title_idid=title_id .

where 1=2

if (selectavg(price)fromtitles)>$15

begin

/* perform some additional processing */

end

update titles set price=price*1.10

where price<(select avg(price)from titles)

commit tran

在这个事务中,重要的是没有其他进程修改表中任何行的price,或者说在事务结束时检索的值与事务开始时检索的值不同。这里的where子句看起来很奇怪,但是不管你相信与否,这是迄今为止优化器所遇到的最完美有效的where子句,尽管计算出的结果总是false。当优化器处理此查询时,因为它找不到任何有效的SARG,它的查询规划就会强制使用一个独占锁定来进行表扫描。此事务执行时,where子句立即得到一个false值,于是不会执行实际上的扫描,但此进程仍得到了一个独占的表锁定。

因为此进程现在已有一个独占的表锁,所以可以保证没有其他事务会修改任何数据行,能进行重复读,且避免了由于holdlock所引起的潜在性死锁。但是,要避免死锁,不可能不付出代价。在使用表锁定来尽可能地减少死锁的同时,也增加了对表锁定的争用。因此,在实现这种方法之前,你需要权衡一下:避免死锁是否比允许并发地对表进行访问更重要。

手工加锁

SQL Server系统中建议让系统自动管理锁,该系统会分析用户的SQL语句要求,自动为该请求加上合适的锁,而且在锁的数目太多时,系统会自动进行锁升级。如前所述,升级的门限由系统自动配置,并不需要用户配置。

在实际应用中,有时为了应用程序正确运行和保持数据的一致性,必须人为地给数据库的某个表加锁。比如,在某应用程序的一个事务操作中,需要根据一编号对几个数据表做统计操作,为保证统计数据时间的一致性和正确性,从统计第一个表开始到全部表结束,其他应用程序或事务不能再对这几个表写入数据,这个时候,该应用程序希望在从统计第一个数据表开始或在整个事务开始时能够由程序人为地(显式地)锁定这几个表,这就需要用到手工加锁(也称显式加锁)技术。

在SQL Server 的SQL语句(SELECT、INSERT、DELETE、UPDATE)支持显式加锁。这4个语句在显式加锁的语法上类似,下面仅以SELECT语句为例给出语法:

SELECT FROM [ WITH ]

其中,指需要在该语句执行时添加在该表上的锁类型。所指定的锁类型有如下几种:

1.HOLDLOCK: 在该表上保持共享锁,直到整个事务结束,而不是在语句执行完立即释放所添加的锁。

2.NOLOCK:不添加共享锁和排它锁,当这个选项生效后,可能读到未提交读的数据或“脏数据”,这个选项仅仅应用于SELECT语句。

3. PAGLOCK:指定添加页面锁(否则通常可能添加表锁)。

4.READCOMMITTED:设置事务为读提交隔离性级别。

5.READPAST: 跳过已经加锁的数据行,这个选项将使事务读取数据时跳过那些已经被其他事务锁定的数据行,而不是阻塞直到其他事务释放锁,READPAST仅仅应用于READ COMMITTED隔离性级别下事务操作中的SELECT语句操作。

6.READUNCOMMITTED:等同于NOLOCK。

7.REPEATABLEREAD:设置事务为可重复读隔离性级别。

8.ROWLOCK:指定使用行级锁。

9.SERIALIZABLE:设置事务为可串行的隔离性级别。

10.TABLOCK:指定使用表级锁,而不是使用行级或页面级的锁,SQL Server在该语句执行完后释放这个锁,而如果同时指定了HOLDLOCK,该锁一直保持到这个事务结束。

11.TABLOCKX:指定在表上使用排它锁,这个锁可以阻止其他事务读或更新这个表的数据,直到这个语句或整个事务结束。

12. UPDLOCK :指定在读表中数据时设置修改锁(update lock)而不是设置共享锁,该锁一直保持到这个语句或整个事务结束,使用UPDLOCK的作用是允许用户先读取数据(而且不阻塞其他用户读数据),并且保证在后来再更新数据时,这一段时间内这些数据没有被其他用户修改。

由上可见,在SQL Server中可以灵活多样地为SQL语句显式加锁,若适当使用,我们完全可以完成一些程序的特殊要求,保证数据的一致性和完整性。对于一般使用者而言,了解锁机制并不意味着必须使用它。事实上,SQL Server建议让系统自动管理数据库中的锁,而且一些关于锁的设置选项也没有提供给用户和数据库管理人员,对于特殊用户,通过给数据库中的资源显式加锁,可以满足很高的数据一致性和可靠性要求,只是需要特别注意避免死锁现象的出现。

posted @ 2008-10-24 23:32 庞滨 阅读(27) 评论(0) 编辑

2008年7月19日

IBATIS:最大的优点是可以有效的控制sql发送的数目,提高数据层的执行效率!好象阿里巴巴现在就用的是IBATIS;它需要程序员自己去写sql语句,不想hibernate那样是完全面向对象的,自动化的,ibatis是半自动化的,通过表和对象的映射以及手工书写的sql语句,能够实现比hibernate等更高的查询效率。

给个文章你参考下:


1.优点

简单:
易于学习,易于使用,通过文档和源代码,可以比较完全的掌握它的设计思路和实现。

实用:
提供了数据映射功能,提供了对底层数据访问的封装(例如ado.net),提供了DAO框架,可以使我们更容易的开发和配置我们的DAL层。灵活:
通过sql基本上可以实现我们不使用数据访问框架可以实现的所有功能,或许更多。功能完整:
提供了连接管理,缓存支持,线程支持,(分布式)事物管理,通过配置作关系对象映射等数据访问层需要解决的问题。提供了DAO支持,并在DAO框架中封装了ADO.NET,NHibernate和DataMapper。增强系统的可维护性:
通过提供DAL层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。sql和代码的分离,提高了可维护性。
2.缺点

滞后性:
还没有明确对.NET2.0的支持。最新版本在2.0下编译可以,但有些单元测试不能通过。
不成熟,工程实践较少:
IbatisNet在实际项目中的使用较少。 只是理论上可行.
半ORM,工具支持较少:
需要我们自己写sql,并且.NET下还未发现可以自动生成业务层类和配置文件的工具,这点和NHibernate不一样,NHibernate会为我们的数据库直接产生sql,并有一些辅助工具。因此使用Ibatis比NHibernate要多做一些工作。
3.可行性

没有最好的框架,只有最适合的框架。 存在的便是合理的,它存在就说明有它存在的道理。但它未必为我们存在。所以选择一个框架最主要的是看它对你有没有意义,意义有多大,是不是比其他框架带给你的好处要多。没有绝对的优点也没有绝对的缺点,重要的是看在什么情况下讨论。 上面说了部分的Ibatis的优点和部分缺点。这些优点从理论上证明Ibatis对任何数据持久层都合适,但未必是最好的选择。下面对上面的优缺点分别从两方面讨论。简单: 我们都喜欢简单,简单意味着学习成本低,使用中出错的可能性低。同时,简单的东西一般来说功能不够强大。反过来,复杂的东西学习成本高,用起来不方便,并且团队没有很强的技术实力,一般不要使用。实用:
解决了项目中需要解决的问题,这是任何实际工程中采用的框架和工具都应具有的性质,否则就不要拿到实际项目中来。灵活: 灵活有两层意思,一种是简单易扩展,另一种是功能强大提供了很多选项。Ibatis属于前者,Hibernate属于后者。两者各有优缺点。功能完整: Ibatis的功能完整也是相对的,比我们自己开发的框架应该完整,但对比其他框架肯定也有一些解决不了的问题。增强系统的可维护性: 利用Ibatis可以做到sql和代码分离,可以设计出一个清晰的数据访问层(DAL)。但项目架构是否科学合理,是否以维护,关键不在Ibatis,因为它只是一个数据层框架。但是我们也不得不清楚,要想发挥Ibatis的优势,我们需要做一些额外工作,比如最好设计DAO接口,需要将业务层实体和对实体的访问放在不同的工程中,同时需要维护xml配置文件。滞后性: Ibatis组现在还没有提到要支持.NET2.0,很多人在.NET2.0下使用Ibatis都出现了问题。所以如果要使用.NET2.0开发,IBatis不是一个好选择,还需要等待。不成熟: 开源的东西很难说成熟,但一般比我们自己写的框架要成熟。由于我们可以拿到他的源代码,所以关键在于我们能否驾驭它。半ORM,工具支持少: 这注定了Ibatis不能从本质上提升开发效率,我们需要自己写sql,写实体类,写配置文件。但这也是它优越的地方,它没有为我们做的他多,所以我们就有更多的施展空间。而且它非常适合那些并不能完全控制数据库的系统和需要利用数据库本身提供的高级特性的统计查询系统的开发。
使用Ibatis需要自己写sql,由于我们的sql不可能完全符合sql标准,比起NHibernate产生的sql来,可移植性差。不过由于我们更改数据库的可能性较小,对我们来说sql符合标准以便可以在迁移到不同服务器时代价最小并不是十分必要的。另一方面,NHibernate虽然可以屏蔽很多数据库间的不同,但是却很难利用某些数据库的高级特性,比如Oracle的分析统计函数。
NHibernate不适合数据库模式不规范,约束不完整,需要大量复杂查询的系统,同时NHibernate的学习成本较高,完全掌握NHibernate也较困难,风险较大。 自己写框架未必比Ibatis的好,稳定,强大和可扩展。而且自己开发框架也需要较大的工作量。 如果使用DotNet并且要选一个数据层框架,而系统中有相当一部分较复杂的sql,或数据库设计不合理,脏数据多,对性能和资源要求严格,Ibatis是一个比较不错的选择。他的那些缺点并不是致命的,而且也是有一些解决方案的。尤其是,当选用了Ibatis的DataAccess作为DAO框架时,我们可以同时使用NHibernate,ADO.NET和DataMapper(IbatisNet的核心组件),那样将会使风险降到最低,并且整个系统的框架比较合理。
另外,利用Ibatis可以统一编码风格,节约开发成本,大家不会再把精力浪费到分页 连接池 主键生成等地方了,可以集中精力进行业务组件的编写。

综上:
很多时候我们要在是自己开发框架和选用第三方框架和选用什么样的框架问题上进行综合考虑。考虑的标准当然是项目的当前情况和我们希望达到目的的一个平衡。

Ibatis只是封装了数据访问层,替我们做了部分的对象关系映射。但我们的代价是必须要写xml配置文件,相对于Hibernate我们还要写很多sql。Hibernate通过工具直接从数据库模式生成实体类和基本的配置文件,而且大部分情况下不需要我们写sql,会较大的提升开发效率。但这些也有很多的局限性,尤其是对环境的要求较高(数据库设计,对象设计,团队的协作等)。 个人感觉Ibatis对项目比较有意义的地方在于它小巧灵活,可扩展,封装了数据访问层(事务,缓存,异常,日志),并提供了DAO框架支持。
利用Ibatis我们可以做到代码和sql的分离,只要sql能够解决的问题,Ibatis就能帮我们较容易的解决,同时也使我们的项目对某一框架的依赖性变小(因为Ibatis是非侵入性的)。这将极大的降低项目风险,减少解决复杂问题的时间,使项目的维护变得简单。
Ibatis对于应用的修改,调试,扩充和维护将会变得容易自然。修改时,我们主要修改的是代表模型的实体对象,xml配置文件中的sql,和/或配置文件的ResultMap(很多时候是不需要的)。同时,sql和代码分离,我们不用在代码的StringBuffer的append方法之间寻找需要修改的sql。配置文件中的sql便利了我们的调试和对sql的评审及以后的sql重用。
利用一些框架在前期一般会拖慢开发效率。因为我们需要付出学习成本,很多时候,使用框架需要写很多配置文件,在使用不熟时开发速度较慢;同时利用框架往往使系统代码量增大,比如Model1和Model2模型,开发效率应该还是Model1快,四层的架构肯定比两层的代码量大。 但对于中后期开发和维护将会极大的提高效率。
利用一些较完全的开发框架和代码生成工具,在前期会较大的提高开发效率,但在后期常常会拖慢进度,并有可能成为以后维护的梦魇。比如torque生成实体类和其对应的sql,虽大幅提高了效率,但修改负担较大。
比较理想的开发方式是使用简单框架结合简单的代码生成工具。框架提供系统的基础服务,并规范开发。框架一方面提供了开发中某一方面的开发基础支持,比如数据访问层,事务,日志,公用类,异常等。另一方面,也为开发定义了模式,定义了系统的基本轮廓。同时,通过简单的代码生成工具生成部分低级的代码。比如通过工具从数据库模式生成实体类。这些类生成后我们可以自由修改。

Hibernate是十分强大,比较完善的ORM框架,不过这是它的优点也是它的缺点。 j2ee系统是否采用Hibernate3,是一个需要认真评估的问题。
要想Hibernate工作的好,数据库的设计必须好。同时对于复杂的数据操作同时需要使用sql,Hibernate3对于直接使用sql的支持比Hibernate2要自然,这一点是可以接受的。
Hibernate比较复杂,功能强大而灵活,要用好Hibernate确实不是很简单,当然Spring框架提供了对Hibernate的封装,使Hibernate的使用变得简单了点。 可以说Ibatis在任何系统里都适用,但未必是最好选择。不过Ibatis提供的思路是我们应该仔细考虑的。

posted @ 2008-07-19 15:55 庞滨 阅读(330) 评论(0) 编辑

iBATIS就是我们通常所说的数据映射器data mapper)。Martin Fowler在他的著作Patterns of Enterprise Application ArchitectureAddison-Wesley Professional, 2002)中,对数据映射器模式是这样描述的:

所谓映射器,是用于在对象和数据库之间搬运数据,同时保证对象、数据库以及映射器本身都相互独立。

Martin在区分数据映射以及元数据映射上,确实做了一件非常出色的工作,元数据映射正是适合使用对象/关系映射(O/RM)工具的地方。O/RM工具将数据库表及其列映射为应用中的类及字段。或者说,O/RM工具在数据库的元数据与类的元数据之间建立起了一种映射关系。图2-1展示了所谓的O/RM,它在一个类与数据库表之间建立了映射关系。在这种情况下,类的每一个字段都被映射为数据库中相应表的唯一列。

2-1 对象/关系映射

iBATIS与之不同,它不是直接把类映射为数据库表或者说把类的字段映射为数据库列,而是把SQL语句的参数与结果(也即输入和输出)映射为类。正如你在本书的剩余部分中将会学到的,iBATIS在类和数据库表之间建立了一个额外的间接层,这就为如何在类和数据库表之间建立映射关系带来了更大的灵活性,使得在不用改变数据模型或者对象模型的情况下改变它们的映射关系成为可能。其实我们这里讨论的这个间接层就是SQLSQL这个额外的间接层使得iBATIS能够更好地隔离数据库设计和应用中使用的对象模型。这就使得它们两者之间的相关性能够降至最少。图2-2展示了iBATIS如何使用SQL映射数据。

2-2 iBATISSQL映射

正如你在图2-2中所看到的,iBATIS的映射层其实就是SQLiBATIS让你自己写SQL语句,iBATIS负责在类的字段和数据库表的列之间映射参数和结果。基于这个原因,也考虑到与其他各种各样的映射方式的区分,为避免混淆,iBATIS小组通常将所谓的“数据映射器”称为SQL映射器(SQL mapper)。

1           映射SQL语句

任何一条SQL语句都可以看作是一个函数,有输入输出集合。输入即参数(parameter),通常可以在SQL语句的WHERE子句中找到。输出则是SELECT子句中指定的那些列。图2-3描述了这个思想。

2-3 SQL语句可以被看作一个包含输入输出的函数

这种方式的最大优点就在于SQL语句使得开发者能够自己把握巨大的灵活性。他可以在不改变数据模型的前提下轻易地操纵数据以适应对象模型。甚至,开发者还可以通过数据库内建的函数和存储过程引入多表和结果集。总之,他们可以随心所欲地利用SQL的强大能力。

iBATIS使用一个简单的XML描述文件来映射SQL语句的输入和输出。代码清单2-1给出了一个这样的例子。

代码清单2-1 一个SQL映射的例子

从以上代码中我们看到了一条SELECT语句,它返回address表中的数据。从<select>元素的parameterClass属性中我们可以看出该语句使用一个Integer对象作为参数,该参数是通过WHERE子句中的#id#符号标记的;从<select>元素的resultClass属性我们还可以看出该语句的结果是一个Address类的实例,假设Address类的所有字段名与SELECT语句中指定的各个列的别名(通过as关键字)相同。例如,ADR_ID列别名为id,故会映射到Address类中名为id的字段上。不论你是否相信,要映射一个SQL语句使其接受一个Integer作为参数并返回一个Address对象作为结果,你要做的就是这些了。用于执行以上语句的Java代码如下:

Address address = (Address) sqlMap.queryForObject("getAddress",

   new Integer(5));

SQL映射这个概念具有很好的可移植性,可应用于任何一个功能完备的编程语言。例如,基于iBATIS.NETC#代码就与之前的Java代码几乎是一模一样的:

Address address = (Address) sqlMap.QueryForObject("getAddress", 5);

当然,iBATIS针对SQL映射还有许多高级选项,特别是针对映射的返回结果。关于这些高级选项,我们会在本书的第二部分“iBATIS基础”中详细讨论。现在,我们最重要的任务就是了解iBATIS的特性和好处,以及它如何工作。

2 iBATIS如何工作

尤其要注意的是,有了iBATIS,你就再不需要直接编写JDBC代码或ADO.NET代码了。JDBCADO.NET的确非常强大,但使用起来总不免觉得太过繁琐。代码清单2-2给出了一个使用JDBC的例子。

代码清单2-2 使用JDBC的代码示例

 

从这个例子中很容易看出,使用JDBC API编写代码需要许多额外的工作。尽管如此,每一行代码又都是必不可少的,所以要减少代码量还真不是一件容易的事情。最多也只不过能将其中的一些代码挪到某个实用方法(utility method)中,最明显的就是那些关闭资源(如PreparedStatementResultSet)的代码。

其实,如果使用iBATISiBATIS在后台也是运行这些相同的JDBC代码。iBATIS会获取数据库连接,设置SQL语句的参数,执行SQL语句,获取执行结果,并在最后关闭所有的资源。然而,你需要自己亲自编写的代码量却大大地减少了。代码清单2-3给出了使用iBATIS运行相同的SQL语句时你需要编写的代码。

代码清单2-3 iBATIS显然比JDBC要精简得多

根本无须比较,iBATIS代码明显更加简洁,更容易阅读,因此也更容易维护。本章稍后会讨论更多关于使用iBATIS的好处。但是现在,你最关心的可能是如何用Java代码运行以上的“SQL语句”。其实就像你在之前的示例中已经看到的,运行它仅仅需要一行简单代码:

Employee emp = (Employee) sqlMap.queryForObject("getEmployee",

                                            new Integer(5));

没什么好多说的。这行代码会执行相应的SQL语句,设置其参数并返回一个真实的Java对象作为结果。SQL语句被“干干净净”地封装在Java代码之外的一个XML文件中。iBATIS负责管理幕后的所有资源,其运行的实际效果与我们之前所见的代码清单2-2所示的JDBC的例子是完全一样的。

这就引起一个问题,iBATIS对所有的系统来说都以一样的方式工作吗?或者它仅仅是适用于某一类特定的应用?以下几节我们将回答这个问题,首先从iBATIS是多么的适合于小型应用说起。

2.1.     iBATIS之于小型、简单系统

小型应用通常只涉及一个数据库,只有一些相当简单的用户接口和领域模型。它的业务逻辑层非常简单,有时对一些简单的只涉及增查改删(CRUDCreate, Read, Update, Delete)操作的应用来说甚至根本就不存在业务逻辑。iBATIS之所以非常适合于小型应用,有3个原因。

第一,iBATIS自己就很小并且简单。它不需要服务器或者其他任何类型的中间件。不需要任何额外的基础设施(infrastructure)。iBATIS也没有任何第三方依赖。iBATIS的最简安装只需要2JAR文件,总计不过375KB。除了需要配置一下你的SQL映射文件外,iBATIS不需要进行任何安装,因此只需要几分钟时间你就可以拥有一个可工作的持久层了。

第二,iBATIS不会对现存应用的设计或者数据库结构强加任何影响。因此,如果你有一个小型系统,且已经部分实现或者甚至已经发布了,则你仍然可以重构你的持久层以使用iBATIS,这非常简单。因为iBATIS很简单,所以它根本不会使得应用的架构过分复杂。而如果使用对象/关系映射工具或者代码生成工具,因为它们都事先就对应用以及数据库的设计做了某些假设,因此它们不可能对应用的架构毫无影响。

最后,只要你有过软件开发的经验,相信你就不会怀疑,任何一个小软件都几乎不可避免地有一天会成长为一个大软件。所有成功的软件都有进一步成长的趋势。这是一件好事,而我们接下来想说的就是,iBATIS同样非常适合于大型系统,它甚至可以扩展以满足企业级应用的需要。

2.2     iBATIS之于大型、企业级系统

iBATIS就是为企业级应用而设计的。最重要的是,iBATIS在这个领域比之其他解决方案有着大量的优点。iBATIS最初的创建者只有从大型应用到企业级应用系统的开发经验。这一类系统通常都涉及不止一个数据库,且所有这些数据库都是不可控的。在第一章中我们讨论了各种类型的数据库,包括企业级数据库、私有数据库和遗留数据库。作者创建iBATIS框架一个很重要的原因就是针对这样的数据库。因此,iBATIS拥有许多使其非常适合于企业应用环境的特点。

其实iBATIS适用于大型系统中的第一个原因我们已经说过了,不过这个原因的确很重要,所以我们还是想再强调一下:iBATIS没有对你的数据库模型和对象模型的设计做任何假设。不论你的应用中这两个模型之间是多么不匹配,iBATIS都能适用。更进一步,iBATIS没有对你的企业级应用的架构做出任何假设。不论你对数据库是根据业务功能纵向划分,还是按照技术横向划分,iBATIS都允许你高效地处理数据并将它们整合到你的面向对象的应用中去。

第二点,iBATIS的某些特性使得它能够非常高效地处理大型数据集。iBATIS支持的行处理器(row handler)使得它能够批处理超大型记录集,一次一条记录。iBATIS也支持只获取某个范围内的结果,这就使得你可以只获取那些你当前亟需的数据。例如,假设你获取了10,000条记录,而你其实只需要其中的第500600条,那你就可以简单的仅获取这些记录。iBATIS支持驱动提示使得执行这样的操作非常高效。

最后一点,iBATIS允许你用多种方式建立从对象到数据库的映射关系。一个企业级系统只以一种模式工作的情况是非常少见的。许多企业级系统需要在白天执行事务性的工作,而在晚上执行批处理工作。iBATIS允许你将同一个类以多种方式映射,以保证每一种工作都能以最高效的方式执行。iBATIS同样支持多种数据获取策略。例如,你可以选择对某些数据进行懒加载,也可以将一个复杂的对象图只用一条联合查询SQL语句就同时加载完毕,从而避免严重的性能问题。

以上所说的这些似乎好像在自我推销了。那么,既然我们已经进入了这种状态,为何不继续深入研究一下你需要使用iBATIS的理由呢?我们会在2.3节做这件事情。并且为了公平起见,在2.4节中,我们还会讨论一些你不应该使用iBATIS的情况。

3              为何使用iBATIS

我们有充足的理由说明,你几乎可以在任何系统中使用iBATIS。正如你在本章之前的内容中所学到的,像iBATIS这样一个框架能够使你的应用从架构级别上开始受益。本节中我们就将讨论这些益处以及使这些益处成为可能的iBATIS特性。

3.1     简单性

iBATIS被广泛认为是当今可用的最简单的持久化框架之一。简单性的理念根植于iBATIS开发团队,它在iBATIS的所有开发目标中居于首位。这种简单性的取得是因为iBATIS直接构建于JDBCSQL之上。iBATIS对于Java开发者来说非常简单,因为它除了不用编写那么多代码外与JDBC的工作机制非常相像。几乎你知道的关于JDBC的一切都对iBATIS同样适用。你几乎可以这样认为,iBATIS就是以XML的形式来描述的JDBC代码。有人说,iBATIS拥有许多JDBC所没有的架构级的优点,这些优点我们随后就将讨论。iBATIS对于数据库管理员以及SQL程序员来说也非常容易理解。iBATIS配置文件几乎人人都能读懂,只要他有SQL编程的经验。

3.2     生产效率

任何一个优秀的框架,其目的都是使得框架的使用者能够获得更高的生产效率。一般情况下,框架负责处理公共的任务,减少编写那些像样板一样的重复代码,以及解决复杂的架构级的问题。iBATIS在给开发人员带来更高的开发效率方面做得非常成功。在意大利的Java用户组(Java Users Group)所做的一个案例调查中(参见www.jugsardegna.org/vqwiki/jsp/Wiki?IBatisCaseStudy),Fabrizio Gianneschi发现iBATIS减少了持久层大约62%的代码量。所以能减少如此之多的代码量,究其原因是开发者再不需要编写繁琐的JDBC代码了。SQL语句仍然是硬编码的,不过就像你在本章之前所看到的,SQL不是问题——问题在于JDBC API,对于ADO.NET而言也是如此。

3.3     性能

性能这个话题无疑会引起框架开发者、框架使用者以及商业软件开发商之间的一场激烈争论。事实是,从一个较低的级别来看这个问题,无疑所有的框架都会带来一定的性能损失。一般来说,如果你比较硬编码的JDBC代码和iBATIS代码,做一个1,000,000次的for循环,你会发现JDBC在性能上的确有一些优势。幸运的是,在现代应用开发中,以上这样的for循环带来的性能损失并不重要。真正重要的是,你如何从数据库中获取数据,何时获取,获取的频率又是多少。例如,分页查询之所以能大大提高应用的性能就在于,它不会将可能成千上万条的记录从数据库中一次取出。同样地,使用像懒加载这样的特性可以避免加载那些在当前情况下并不需要的数据。另一方面,如果你确定需要加载一份复杂的对象图,涉及到来自多个表的大量数据,如果能仅用一条SQL语句就完成所有对象的加载显然可以大大提高效率,iBATIS支持使用这样的SQL语句。iBATIS支持许多性能优化措施,我们将在本章之后的章节中详细讨论。就目前而言,最重要的是要知道iBATIS总是能通过一种简单的方式来配置和使用,其性能与JDBC相当,甚至更好。另一个需要重点考虑的问题就是,并不是所有的JDBC代码都是编写良好的。JDBC API非常复杂,编写正确的代码需要非常小心。不幸的是,大量的JDBC代码都编写得相当糟糕,因此从较低层次上看甚至还没有iBATIS工作得好。

3.4     关注点分离

在典型的JDBC代码中,在应用的各个层中都能找到与数据库相关(例如获取连接、遍历结果集)的代码,这并不稀奇。相信大家都见过甚至开发过这样糟糕的应用——所有的逻辑都在一个JSP页面中完成,连接数据库、执行SQL语句,遍历结果集,当然还有散落其间的HTML代码。这样的代码简直是一个噩梦。在第一章中我们已经讨论了对应用分层的重要性。我们已经看到了如何从一个较高的角度对应用分层,以及持久层内部又是如何进一步分层的。iBATIS通过帮助管理所有这些持久化相关的资源来支持分层,这些资源包括数据库连接(database connection)、prepared statement以及结果集(result set)。iBATIS提供了一组数据库无关的接口以及API,使得应用的其它部分能够通过使用这组接口和API保持与数据库相关资源的独立性。使用iBATIS,你的代码总是在直接与严格的对象打交道,再也不用管那些随意的结果集了。iBATIS实际上使得你难以违背对应用分层这样的最佳实践。

3.5     明确分工

一些数据库管理员是如此热爱他们的数据库,以至于不愿意让任何其他人来为数据库编写SQL。而有一些人又是如此擅长编写SQL,以至于其他人都想让他们来做这项工作。无论是什么原因,在你的开发团队中实现人尽其才总是有利的。如果你的团队中有人尤其擅长于编写SQL,但是对于JavaC#却不那么在行,那么就可以让他们专门编写SQLiBATIS使得这种分工成为可能。因为在iBATIS中,SQL语句在很大程度上同应用的源代码是分离的,SQL程序员可以按照SQL原本的方式来编写它,而不必担心有关SQL字符串连接的问题。即使由相同的开发者来编写Java代码和SQL,在数据库性能调整(performance tune)的过程中也会有一个来自DBA的常见请求,即“显示出SQL”。用JDBC来完成此事并不容易,因为SQL隐藏得很深,是连接一堆分散的字符串得到的,甚至还可能是在递归语句和条件语句中动态地创建的。如果使用对象关系映射,情况就更加复杂了,因为你通常必须运行应用然后才能log出这些SQL语句,即使你找到了它们,你也根本不能改变它们。iBATIS则提供了充分的自由,使得任何人都可以开发、观察并且修改在数据库中执行的SQL语句。

3.6     可移植性:Java、.NET或者其他

iBATIS是可移植的。由于它相对简单的设计,它几乎可以用任何一种语言在任何一个平台上实现。书写本书时,iBATIS支持3种最受欢迎的开发平台:JavaRuby和微软.NETC#

当前,配置文件并不完全跨平台兼容,但是我们正在计划使它们更加兼容。重要的是,概念方法都是可移植的。这就使你能够保证你所有的应用在设计上是一致的。和其他框架相比,iBATIS可以处理更多语言以及更多类型的应用,而无需考虑应用设计的不同。如果应用的一致性对于你来说很重要的话,使用iBATIS就可以很好地帮你达到一致性。

3.7     开源和诚实

开始时我们说本节有“自吹自擂”之嫌。事实上,iBATIS是免费的开源软件。无论你是否使用它,我们都不会因此而获得哪怕是一分钱的利益。你已经买了本书,所以我们已经获得了我们该得的利益。开源软件的最大的优点之一就是诚实。我们没有任何理由对你撒谎。因此下面我们将要做的事在商业软件文档中就是非常罕见的:我们将要讨论一些你可能不应该使用iBATIS的情况,并且提出一些合适的替代方案。

4             何时不该使用iBATIS

每个框架都是建立在一定的规则和约束之上的。底层的框架,如JDBC,提供了一个灵活且完备的功能集合,但是它们使用起来却更难也更繁琐。高层的框架,如对象/关系映射工具,使用就更加方便并且可以帮你节省大量工作,但是它们是建立在更多的假设和约束的基础上的,这使得它们只能适用于较少的应用。

iBATIS是一个中层的框架。它比JDBC更高层一些,但是相对于对象/关系映射工具,又要更底层一些。这使得iBATIS处于一个很独特的位置上,使它能够适用于一些较为特别的应用。在本书前面的部分,我们讨论了为什么iBATIS对于多种应用类型都是有用的,包括小的桌面应用,和较大的、企业级的Web应用——以及任何介于这两者之间的应用类型。那么,iBATIS不适用的场合是什么呢?下面几节就将详细描述不适合使用iBATIS的各种情况,并且给出有关替代方案的建议。

4.1     当你能永远拥有完全控制权时

如果你能够保证拥有对应用设计和数据库设计的完全控制权,那你一定是一个非常幸运的人。这在商业环境或者任何一个核心工作不是软件开发的行业中都是非常少见的。然而,如果你在一个软件公司工作,并且在开发一个你拥有完全设计控制权的且由你发售的产品时,那么你可能恰好处于这种情况。

当你具有完全控制权时,就有充分理由使用一个完全的对象/关系映射方案,如Hibernate。你可以充分利用对象/关系映射工具所能提供的设计优势并提高生产率。可能根本没有来自企业数据库组的干扰,也不需要与遗留系统整合。此外,数据库可能是与应用一同部署的,这使得它属于应用数据库的范畴(参见第1章)。使用Hibernate的应用的一个很好的例子就是,AtlassianJIRA。它们提供了一个问题跟踪软件,作为一个他们可以完全控制的发布产品。

然而,还需要考虑应用未来的发展。如果数据库有可能超出应用开发者的控制,那么你就必须仔细考虑一下使用对象/关系映射将对你的持续化策略带来怎样的影响。

4.2     当你的应用需要完全动态的SQL

如果你的应用的核心功能是动态生成SQL,那么iBATIS就是错误的选择。iBATIS支持非常强大的动态SQL特性,这些特性又反过来支持高级查询能力,甚至一些动态更新功能。然而,如果你的系统中每个语句都是动态地生成的,那么你最好使用原始的JDBC,甚至可以创建你自己的框架。

iBATIS的强大功能之一就是它允许你拥有完全的自由,可以手工编写和直接操作SQL。当应用中大部分的SQL都是从某些SQL生成类动态地生成时,这种优势就会很快丧失。

4.3     当你并没有使用关系数据库时

对于关系数据库之外的其他数据库,也存在可用的JDBC驱动。对于平板文件,微软Excel电子数据表,XML,以及其他类型的数据存储平台,都有相应的JDBC驱动。虽然一些人在iBATIS中使用这些驱动也获得了成功,但是对于大多数用户我们并不推荐使用这些驱动。

iBATIS并不会对你的环境做出任何假设。但是它确实期望你使用的是真正的关系数据库,即支持事务、相对典型的SQL、和存储过程这些语义的关系数据库。即使一些非常著名的数据库也可能不支持关系数据库的某些重要特性。如MySQL的早期版本就不支持事务,因此iBATIS不能很好地处理MySQL。幸运的是,当前的MySQL已支持事务并且还有一个非常符合规范的JDBC驱动。

如果你使用的不是真正的关系数据库,我们推荐你最好使用原始的JDBC,甚至更底层的文件I/O API

4.4     iBATIS不起作用时

随着社区提出的需求越来越多,iBATIS也不断地实现了许多非常好的特性。然而,iBATIS是有其自己的发展方向和设计目标的,这样一来它有时候就有可能会同一些应用的需求发生冲突。人们在软件帮助下可以完成很多神奇的事情,但是有时候由于需求过于复杂,软件可能会完全不起作用,iBATIS也是如此。虽然我们也可以在iBATIS中添加特性以支持这些复杂的需求,但是这么做可能会极大地提高复杂性,甚至可能会改变iBATIS框架的适用范围。因此,我们不会修改框架。为了解决以上问题,我们将提供可扩展的接口,这样你就可以扩展iBATIS以满足几乎任何需求。事实上,有时候iBATIS就是不起作用。此时,最好另寻一个更好的解决方案,而不是“霸王硬上弓”地将iBATIS(或者其他任何框架)用在不合适的地方。

下面我们不再讨论使用iBATIS的是是非非了,还是来看一个简单的例子吧。

5           5分钟内用iBATIS创建应用

iBATIS框架实际上非常简单,要开始使用它也同样非常简单。那么究竟有多简单呢?事实上,它是如此简单,以至于你可以用iBATIS5分钟之内创建一个完整的应用——不是一个大型的企业资源规划(Enterprise Resource PlanningERP)方案或者一个大型电子商务网站,而是一个简单的命令行工具,用于执行一个SQL映射文件中的一条SQL语句并且向控制台输出执行结果。下面将要给出的例子将配置一条简单的静态SQL语句,用于查询一个简单的数据库表格,并且把它以如下形式输出到控制台:

这种数据输出方式并不是最漂亮的,但是由此你可以了解该应用到底要做些什么。在下面几节中,我们将逐步从无到有地实现此功能。

5.1     安装数据库

为了满足示例应用的目的,我们将使用MySQL数据库。iBATIS框架可以使用任何数据库,只要该数据库具有符合规范的JDBC驱动。你只需要在配置文件中提供驱动的全限定类名以及一个JDBC URL即可。

安装数据库服务器超出了本书所讨论的范围,因此我们假设数据库服务器已经安装好并且可用了,然后告诉你在此基础之上需要做些什么。以下的MySQL脚本用于构造了我们将要使用的表格,并且在其中添加了一些示例数据:

如果你已经安装了一个不同的数据库服务器,其中包含一些其他数据,并且你想要在这些数据上执行某些SQL查询,你可以大胆地在本例子中使用它。你只需要修改SqlMap.xml文件中的查询语句,以包含你的SQL,同时还需要修改SqlMapConfig.xml文件以配置iBATIS使用你的数据库。为使整个例子成功运行,你还需要知道驱动的名称、JDBC URL、以及连接时的用户名和密码。

5.2     编写代码

由于本例是我们给出的第一个完整的例子,同时也只是对使用iBATIS的一个介绍,因此它的代码将会比真正的应用要简单得多。类型安全和异常处理我们以后将会详细讨论,因而在此处对于这些话题我们将不予考虑。代码清单2-4给出了完整的代码:

代码清单2-4 Main.java

就是这些了!我们在大约10Java代码中就完成了对iBATIS的配置,执行了SQL语句,并且打印了结果。以上就是一个功能完整的iBATIS应用所需要的全部Java代码。稍后,我们将对其进行改进,但是现在我们将继续讨论有关iBATIS配置的基础知识。

5.3     配置iBATIS(预览)

考虑到我们将在下一章中深入介绍如何配置iBATIS,所以此处我们将只是简单介绍一下。此处你不会找到有关配置选项的过多解释,但是我们将给出最重要的信息。

首先,让我们来研究SqlMapConfig.xml文件。它是使用iBATIS的起点,负责把所有的SQL映射文件组合在一起。代码清单2-5给出了我们的简单应用中使用的SqlMapConfig.xml文件。

代码清单2-5 最简单的iBATIS应用中的SQL映射配置

 

 

你可能已经猜到,我们正是在此配置文件中告诉iBATIS如何连接数据库,以及获取哪些SQL映射文件。由于这是一个XML文件,我们需要提供doctypeDTD用于验证(1)。SIMPLE是一个iBATIS内置事务处理器的别名(2)。我们需要为这个事务处理器提供JDBC驱动的名称、JDBC URL、以及允许你连接到数据库的用户名(username)和密码(password)。然后你将提供你的SQL映射文件(3)。此例中,我们只有一个SQL映射文件,但是你可以想要多少就提供多少个。该文件中你还可以做一些其他设置,我们将在下一章中详细介绍。

现在你已经看到了主配置文件,下面我们来看一下SqlMap.xml文件(代码清单2-6)。这个文件包含了我们将要运行的SQL语句。

代码清单2-6 最简单的SQL映射

代码清单2-6XML中,我们接受了一个String类型的参数(parameterClass)作为GROUPNAME列的值,并且把结果类(resultClass)映射到了一个HashMap上。

警告:

我们并不推荐使用Map(例如,HashMapTreeMap)作为域模型,但是这样做确实显示了iBATIS所提供的映射的灵活性。你并不总是需要映射到JavaBean——你可以直接映射到Map或者基本类型。

 

无论你是否相信,你现在已经看到了使用iBATIS所需要的所有代码和配置。我们故意将它分散开以便于印刷,但是即使如此,这些代码总共也只有大约50行,包括JavaXML。而且更重要的一点是,这50行代码中有45行都是有关配置的,这些配置在一个应用中只需要编写一次即可,而不需要针对每个语句都写一次。正如你在本章之前的例子中所见到的那样,使用JDBC最终需要对每条SQL语句都编写50行或者更多的代码。

5.4   构造应用

通常当构建一个大型应用时,你都会使用一些类似于Ant的工具来简化构建过程。由于本例中我们只有一个类,因此就不需要麻烦地为它创建一个Ant脚本了。为了编译此应用,你只需要在classpath上添加两个JAR文件:ibatis-common-2.jaribatis-sqlmap-2.jar,因此我们只要用命令行把它们输入给Java编译器即可:

javac -classpath <your-path>ibatis-common-2.jar;

<your-path>ibatis-sqlmap-2.jar Main.java

当然了,以上的代码应该在同一行中输入,并且你应该用JAR文件的实际路径来代替<your-path>。如果一切都顺利的话,编译器应该不会产生任何输出到屏幕上,而只是在当前目录上创建一个Main.class文件。

5.5     运行应用

在执行此应用时,我们还需要另外一些JAR文件,但也不是那么多。为了运行我们的应用,我们只需要在classpath上添加以下JAR文件即可:ibatis-common-2.jaribatis-sqlmap-2.jarcommons-logging.jar以及JDBC驱动(在此例中,这个驱动器就是mysql-connector-java.jar),因而下面应该输入以下这个命令:

java -classpath <your-path>;mysql-connector.jar;commons-logging.jar;

             ibatis-common-2.jar;ibatis-sqlmap-2.jar;.Main

同样地,在编辑时以上代码必须在同一行中,同时应该用系统中的实际路径来代替<your-path>

这个程序运行之后会告诉你选择出了多少条记录,然后将它们以一种以较粗糙的格式输出,类似如下:

 

 

iBATIS框架被设计得非常灵活。它可以是一个非常轻量简单的框架,只执行SQL并且返回数据,当然也可以被用来做其他更多的工作。

这种灵活性的关键之一在于对框架的正确配置。在下一章中,我们将讨论两种主要的配置文件类型,然后研究通过使用配置来处理复杂情况的一些模式。

注意:

配置文件都是标准的XML文档。这意味着如果你有一个时髦的XML编辑器,就可以用DTDDocument Type Definition)来验证你的文档是否存在语法错误,有时候甚至可以在编辑过程中提供代码提示和自动完成功能。

 

现在你已经看到了最简单形式的iBATIS。在我们继续讨论其他内容之前,先来讨论一下iBATIS的未来发展方向,这样你在使用它时就可以更加的有信心。

6      iBATIS未来的发展方向?

在过去几个月中,iBATIS获得了巨大的发展动力。作为结果,iBATIS小组发展壮大了,产品也得到了改进,我们也开始讨论支持新平台的问题了。下面我们将详细地讨论iBATIS未来的发展方向。

6.1 Apache软件基金会

近期,iBATIS已经成为了Apache软件基金会的一部分。我们之所以选择转向Apache,是因为我们相信他们的使命并且尊重他们的态度。Apache绝不仅仅是一堆服务器和基础设施的组合,它是一个系统,是开源软件真正的家。Apache更关注软件周边社区(即使用者社区),而不是软件背后的技术,因为如果没有社区,软件就是一个死的工程。

这对于iBATIS用户来说意味着,iBATIS并不是由某个单独的团体来指导,也不是依赖于某个单独的团体。iBATIS不属于任何个人——它属于整个社区。Apache能够始终保护iBATIS,并且确保它维持正确的方向。然而,Apache许可并没有像GPL might许可那样,限制对开源软件的使用。Apache许可并不是一个viral许可,这意味着你可以在商业环境中自由地使用这些软件,而不用担心需要遵守许多不合理的条件。

虽然Apache并不关注基础设施,但是它们确实拥有一些非常好的基础设施。目前iBATIS使用Subversion source controlSVN)进行版本控制,用AtlassianJIRA来跟踪问题,用AtlassianConfluence来协作撰写wiki文档,并且使用Apache的邮件列表服务器进行开发小组、用户以及一般社区之间的交流。

Apache拥有保护iBATIS所需要的一切,并且可以确保:只要仍然有人想要使用iBATIS,它就会在那儿。

6.2 更简单、更小、依赖性更少

和其他框架不同的是,iBATIS工程并不期望分枝出新的领域,也没有任何野心要解决所有问题。iBATIS是一个目标非常集中的工程,每一次发布新版本,我们都期望能使它更小、更简单,并且更少地依赖第三方库。

我们相信iBATIS还有许多创新的空间。iBATIS可以从很多新的技术和设计方法中获益,以便使其配置更加简练,也更容易使用。例如,C#Java都内置了元数据(attribute,也可称为“标注(annotation)”)功能。在未来的版本中,iBATIS就有可能利用此功能来减少配置框架时所需的XML代码的数量。

在为iBATIS开发支持工具方面也还有许多事情可做。iBATIS的设计使得为其开发像IDE这样的图形化工具非常地容易。也可考虑创建支持从数据库框架(database schema)中直接生成iBATIS配置文件的工具,其实这一点上已经有相应的工具可以用了。你可以在我们的网页http://ibatis.apache.org上看到一些工具的示例。

6.3 更多的扩展点和插件

iBATIS已经有了许多扩展点。我们将在第12章中深入探讨有关扩展的问题。你可以实现你自己的事务处理器、数据源、缓存控制器(cache controller),以及其他。但是我们期望使得iBATIS更易于扩展。我们希望将JDBC架构的几乎每一层都设计为可扩展的,这将意味着你可以实现自己的ResultSet处理器和SQL执行引擎。这将帮助我们支持更复杂的系统,或者遗留系统以及私有系统。它也将使开发者能够更充分地利用特定数据库或应用服务器的定制特性。

6.4 支持更多的平台和语言

正如你在第1章和第2章中所看到的那样,我们已经在.NETJava中讨论了iBATIS。本书的其余部分将主要关注JavaiBATISAPI,但是大部分的信息都是可以被转化成.NET平台的。另外,我们还将在附录中较详细讨论iBATIS.NET。实际上iBATIS也已经有Ruby实现了,但是Ruby是一种完全不同的语言,因此用Ruby实现的iBATIS也有很大的不同。本书中我们就不讨论其Ruby实现了。

除了JavaC#之外,iBATIS小组还在讨论用其他的语言来实现iBATIS,这些语言包括PHP 5Python。我们相信iBATIS对于几乎任意一种无法使用或不愿使用底层数据库API和高层对象/关系映射工具的平台,都可以做出巨大的贡献。iBATIS可以帮助你找到折中的办法,并且允许你始终用一致的方式来实现所有的应用。

我们也曾经讨论过要起草一份规范,使用户可以更容易地把iBATIS移植到不同的平台上,并且确保合理的一致性。当然了,我们既希望iBATIS能够充分利用特定语言和平台的特性,也期望它们能有一定程度的相似性,以便确保它们都能被称为iBATIS,并且能够被熟悉另一种语言中的iBATIS的开发者一眼就辨认出来。

7      小结

在本章中,你已经了解到iBATIS是一个独特的数据映射器,它使用一种被称为SQL映射的方式将对象持久化到关系数据库中。iBATISJava.NET两个平台上的实现始终是一致的,而在应用中始终采用一种一致的持久化方式也具有非常重要的意义。

在本章中,你还学习了iBATIS是如何工作的。通常,在后台iBATIS将运行编写好了的JDBCADO.NET代码,如手工编写这些代码将非常难以维护。我们发现,和JDBC相比,iBATIS代码更加简洁,也更易于编写。

我们还讨论了为什么iBATIS对于小型和大型商业应用来说都是非常合适的框架,尽管它的设计很简单。iBATIS具有很多支持企业级持久化需求的特性。如行处理器等特性可以允许高效地处理大型数据集,每次处理一条记录,这样就保证了不会耗尽系统的内存。

我们还讨论了iBATIS所以区别于其他竞争对手的一些很重要的特性,同时我们还构造了一个使用iBATIS的简单示例。这些特性包括:

l     简单性——iBATIS被广泛认为是最简单的一种持久化框架。

l     生产效率——简洁的代码和简单的配置使得使用iBATIS所需的代码量可以减少到了相应JDBC代码的62%

l     性能——架构级性能增强,如联合查询的使用等,加速了数据的处理。

l     关注点的分离——iBATIS改进了应用的设计方式以确保未来的可维护性。

l     细化分工——iBATIS可以帮助细化分工,使得工作团队能够充分利用个人的专长。

l     可移植性——iBATIS可以用任何具有完备功能的编程语言来实现。

宣传了iBATIS的优点之后,我们也承认iBATIS并不是一颗“银弹”,因为没有任何框架是万能的。我们讨论了在哪些情况下使用iBATIS可能不是最完美的方案。例如,当你始终拥有对应用和数据库的完全控制权时,采用一个成熟的对象/关系映射工具可能就是更好的选择。另一方面,如果你的应用主要处理动态生成的SQL代码,直接使用JDBC可能就是更好的选择了。我们也提到了iBATIS主要是为关系数据库而设计的,因此如果你使用的是平板文件、XMLExcel表单,或者其他任何非关系数据库技术存储数据,那么你最好还是使用另一种完全不同的API

最后,我们以对iBATIS未来发展的讨论结束本章。关于iBATIS的未来,开发小组有很多非常好的设计理念,同时Apache软件基金会将会确保始终有一个充满活力的社区能够支持iBATIS未来的发展。

posted @ 2008-07-19 15:52 庞滨 阅读(308) 评论(0) 编辑

2008年7月8日

尊敬的闵书记、许校长,各位老师,各位家长,亲爱的学弟学妹们,大家上午好。

今天,站在各位同学毕业典礼的讲台上,我最大的感受就是觉得非常的荣幸,在各位生命中最值得纪念的时刻与你们在一起,让我百感交集。我仿佛找回了十七年前,坐在你们中间,对这个再熟悉不过的校园感到万分的留恋,也对即将展开的新的生活有期待、有迷茫甚至有所畏惧。  

说实话,我今天除了荣幸之外,还有一些紧张。因为我知道,在座的不仅有我十分尊敬的师长,更多的是未来中国最有影响力的一群人。你们中一定会有未来中国最杰出的科学家、最成功的企业家、最优秀的政治家、外交家。

如果我这个曾经住在43楼522的北大男生今天和大家交流的内容,能够为各位即将铺展开的未来有些许帮助的话,那我也会觉得,这是经历了2005年百度在纳斯达克的辉煌上市后,我所经历的又一个光荣时刻。
今天回忆十多年前,我走入社会的感觉,那是让视野顿时豁然开朗的一步,走出校园后看到的是一个充满机会、日新月异的新天地。大家今天所面对的中国与世界,与十年前我所见到的华尔街和硅谷,当然会有很大的不同。但以我在美国八年、回到中国八年多的经历,我更感受到今天,社会经济文化生活各个方面都充满了活力,你们面对的是更广阔的天地,一定将大有所为。

今天我想给大家分享一些我的经历和对生活的感悟。

第一,是关于选择的故事

进北大前我就非常喜欢计算机,我相信未来的计算机肯定会被应用广泛,而单纯的学计算机恐怕不如把计算机和某项应用结合起来有前途,于是我选择了北大的信息管理系,而不是计算机系。

我有个姐姐先我五年考上了北大,她告诉我北大的学生出国都很容易,她告诉我外面的世界很精彩。上了北大之后,我却发现我的情报学专业出国并不容易,而最先进的计算机技术那时候在美国。我被迫开始思考自己的下一步,并通过不断参与各种活动来丰富自己的视野。

李彦宏说,自己的火炬手名额是联想推荐的。据说李彦宏陈绍鹏关系不错。

我去学了不少计算机系的课,我翻阅了很多美国有关情报学的论文,希望能够在国际学术期刊上找到自己的机会,我作为那时唯一的理科生参加了学校的五四辩论赛,我听了各种各样的讲座:气功、哲学、电影,我参加了合唱团,还在国庆的时候到天安门广场去跳集体舞,我尽情的享受着北大带给我的各种机会,我接触到了各种各样的人,每个人都有他们自己的思路,每个人都不一样,每个人都很精彩。这让我逐渐形成了不轻信,不跟风的思维方式。对于我未来人生道路的选择,北大四年让我具备了独立思考的能力。

我在美国读计算机的时候,本来是读博士的,后来选择了放弃。原因是发现我更希望我做的东西能够被很多很多人使用,而不喜欢去研究一个别人已经研究了10年的命题。

1997年我离开自己奋斗了三年多的华尔街,前往当时在硅谷很著名的搜索引擎公司Infoseek。在硅谷,我亲见了当时最成功的搜索技术公司如何在股市上呼风唤雨,见识了每天支持上千万流量的大型工业界信息系统是怎样工作运转,我也见证了Infoseek后来的每况愈下和惨淡经营。但最重要的是,在Infoseek,我找到了我一生的兴趣所在—互联网搜索引擎。那时,是北大所学的信息检索方面的理论,让我比任何计算机系科班出身的工程师都更能够理解普通用户习惯于怎样的信息获取方式。我意识到搜索能让每个人与所需信息的距离只有鼠标的点击一下那么远,这种感觉是那么的美妙。从那以后,我从来没有离开搜索引擎超过24小时,不是因为我是工作狂,而是因为我喜欢。

百度公司走过了8年的历程,今天已经成为一个市值超过100亿美元的公司,为越来越多的人提供服务。我最大的心得就是要选择做自己喜欢做的事情,我们需要从自己真正的心里面去作选择,并不是你认为社会期望你这样做,父母期望你这样做,朋友期望你这样做。只有这样,你才会越工作越开心,在遇到困难遇到挫折的时候,不会被沮丧击败,而全身心的去享受整个过程。
 
第二,是关于专注的认识

我一生有两个最大的幸运,一是找到我的太太,二是从事一份自己喜欢的工作。但太太与工作唯一的不同就是:太太只有一个,而工作每时每刻都充满了诱惑。很多人都会专注于一个妻子,但很多人都会喜欢上多个不同的工作。

在百度上市之前,百度只做一件事情就是中文搜索。在创业初期,搜索在美国硅谷并不是炙手可热的概念,当时更热的是门户,是电子商务,以及后来在中国火起来的无线、网游等等。百度在招第一批职员的时候,碰到一位我特别希望他能加盟,他技术很好,可惜他对我说如果我们不做e-Commerce他就不来了.。2001年,曾经有一位百度的工程师找到我,很认真地说他想做网上购物,结果被我拒绝了,并为此离开了百度。百度上市后,也有一些共事多年的老同事先后离开了百度去尝试更多的业务。

很多时候,我感到百度能一直坚持做搜索是因为我对专注有宗教一般的信仰。普通人很难想象对于一个有2亿的用户的公司,每天要面对多少诱惑。百度可以做一百件事,最后我们只选择了一件,并一做就是8年,而且还会再做下去。

人一生中可以完成的事情是有限的。只有专注才能让自己变得足够优秀。所以说:“有所不为,才能有所为”。

第三,是关于视野的感悟

回头望望自己走过的路,我会发现,这个世界的广阔是自己很难想象的。很多当时觉得非常大的困难,现在看来不过是一些小事,很多当时感觉到很棘手的事,现在也只是茶余饭后的话题罢了。

百度在2000年成立时,并不直接为网民提供搜索服务,我们只为门户网站输出搜索引擎技术,而当时只有门户需要搜索服务。2001年夏天,我做了这样一个决定,从一个藏在门户网站后面技术服务商,转型做一个拥有自己品牌的独立搜索引擎。这是百度发展历程中唯一的一次转型,会得罪几乎所有的客户,所以当时遭到很多投资者反对。但当我把视线投向若干年以后时,我不得不坚持自己的观点。大家知道,后来我说服了投资者,所以才有了大家今天看到的百度。

百度从后台走向了前台,加上我们的专注与努力,今天运营着东半球最大的网站。
 
事实上,从创立百度的第一天,我的理想就是“让人们最便捷的获取信息”。这个理想不局限于中文,不局限于互联网。做为一名北大信息管理系的学生,我很幸运在前互联网时代,在大学时就理解了信息与人类的关系和重要性。所以,百度从第一天起,就胸怀远大理想:我们希望为所有中国人,以至亚洲,以至全世界的人类,寻求人与信息之间最短的距离,寻求人与信息的相亲相爱。

所以说:视野有多远,世界就有多大。

最后,我在这里衷心祝贺你们顺利完成在北京大学的学习,祝愿你们未来的道路越走越宽广,世界在你手中。也让我们一起祝福我们的母校传承历史、继往开来、再攀高峰。
谢谢大家!

posted @ 2008-07-08 10:07 庞滨 阅读(25) 评论(0) 编辑

2008年6月30日

 今天雨好大,大家都湿身了吧?
 
 
 
 
曾几何时,我们不再是孩子
 

黑漆漆的隧道尽头隐隐透着一丝亮光,你挣扎着,亮光越来越近,越来越亮,然后随着随着一声激昂的呐喊,来到这个世界。面对绚丽的光亮和缤纷的色彩,你眯上来眼睛。是的,你离开了母亲温暖的子宫,来到这个世界,这是多么崭新的经历。你惊奇于你所看到听到的每一样事物,那么新鲜、那么令人激动,并且那么美妙。
        这便是孩子眼中的一切,当你逐渐长大,新奇而激动的感觉也逐渐离你而去。直至最终,风儿不在低语,鸟儿也不再歌唱,所以的色彩在你眼中都是一样的灰暗,生活成了日复一日的习惯,于是,你便成了大人。
        成人的标志既不是婚姻,也不是财富,而是你是否已失去探寻世界的能力,是你是否已不会再问“为什么”。
        孩子永不厌倦地问着:“天为什么这样蓝?”“我是怎么来这儿的?”“人真的是猴子变的吗?”这是人一生中最可贵的阶段,有幻想、有渴望、有梦一般的感觉。可惜的是,这些清纯的梦往往被成人平淡的回答打破:“别问了,本来就是这样的。”
        某一天,当你认为天之所以那么蓝,是因为“本来就是这样的”的时候,你便已长大成人。
         人们常说,那些伟大的发明是由伟大的人创造的,其实不然,应该说,那些伟大的发明是由尚未失去童心的人们创造的。他们没有丧失探寻的能力,他们还在问:“为什么”。试想,如果牛顿没有一颗童心,便不会对“苹果落到地上”这样一个众人熟视无睹的现象产生兴趣,他会想当然地认为苹果从来都是落到地上的。因为“本来就是这样的”,于是世界上最伟大的发现之一便不会产生了。
        古希腊哲学家苏格拉底曾说:“我更愿意同一个孩子探讨哲学。”这首因为孩子的思想单纯开放,他们有寻求问题答案的好奇心,他们有新的思维、新的着眼点,从而能给你新的启示。
        但是,人在成长的过程中总在不可避免地积累经验,这是否意味着终有一天他会“成人”,他会失去那可贵的好奇心?不,不是的,经验并不是好奇心的“杀手”。一只苹果从树上落下,不应该得到“本来就是这样的”的结论,相反,正因为苹果不断地从树上落下,人们才应该提出疑问,寻求答案。
        那么,究竟是什么导致人的感觉变得粗糙麻木呢?
        其实,永远保持好奇心,永远在问为什么,是很累的,也是很难的,而绕过问题却容易得多,于是大多数人便选择了这条捷径。然而提出问题,钻研问题,比绕路而行的做法有价值得多。
        大多数人选择走向成人的道路,是因为这条路更实际、更安全。另一些人选择了保持童心的道路,对他们来说,生活中充满了神秘和刺激。他们中有的留名青史,因为他们敢问“为什么”,并且找到了与“本来就是这样的”截然不同的答案。
        人们常说成长是一种奇妙的经历。实际上,“成人”意味着丧失激情和思想狭隘,他们通常无暇也无法体验生活的美妙。因此,对大多数人来说,童年时很短暂的,也是很值得回忆的。只有在童年,他们才真正用心感受过生活,有过纯真的快乐。当戴上了成人的面具后,整天忙忙碌碌的他们实际上远离了生活的真谛。
         我在生命的路上不停地走着,每次转弯的时候,我都听到了童年的呼唤。

 


~~~~~~~~~委屈的男人~~~~~~
 
一女走夜路,突然看到一男张开双臂向她走来,
做拥抱状,遂上去一脚,男子倒地大哭,
曰:“都第三块了,我招谁惹谁了,带块玻璃就这么难吗??”

posted @ 2008-06-30 11:17 庞滨 阅读(20) 评论(0) 编辑

2008年6月13日

posted @ 2008-06-13 17:05 庞滨 阅读(55) 评论(0) 编辑

posted @ 2008-06-13 14:26 庞滨 阅读(771) 评论(0) 编辑

posted @ 2008-06-13 14:25 庞滨 阅读(887) 评论(0) 编辑