EasyDBAccess

出自小鱼工作室

在2008年11月15日 (六) 16:15由Root (对话 | 贡献)所做的修订版本
(差异) ←上一修订 | 当前修订 (差异) | 下一修订→ (差异)
跳转到: 导航, 搜索

You can get the latest document from http://search.cpan.org/perldoc?EasyDBAccess

目录

NAME

EasyDBAccess - Perl Database Access Interface

SYNOPSIS

加载模块
use EasyDBAccess;

判断模块是否已经被加载
if(defined(&EasyDBAccess::foo)){
  print "lib is included";
}else{
  print "lib is not included";
}

通过TCP/IP连接数据库
my $dba=EasyDBAccess->new({host=>'127.0.0.1',usr=>'root',pass=>'passwd',database=>'test_db'});
通过unix socket连接数据库
my $dba=EasyDBAccess->new({socket=>'/tmp/mysql.sock',usr=>'root',pass=>'passwd',database=>'test_db'});
连接的时候设定字符集(SET NAMES),如果不设定,默认是utf8
my $dba=EasyDBAccess->new({host=>'127.0.0.1',usr=>'root',pass=>'passwd',database=>'test_db',encoding=>'gbk'});
 
EasyDBAccess->once();#once函数让下面一条数据库操作就算出错也不会"DIE"掉,方便我们进行错误处理
my ($dba,$err_code)=EasyDBAccess->new({host=>'127.0.0.1',usr=>'root',pass=>'passwd',database=>'test_db'});
if($err_code==3){
  print "Connect Error";
}elsif($err_code==0){
  print "Connect Succ";
}else{
  CORE::die 'BUG';
}

#把错误记录到LOG文件中
my $dba=EasyDBAccess->new({host=>'127.0.0.1',usr=>'root',pass=>'passwd',database=>'test_db'},{err_file=>'\var\log\logfile'});

#编写自己的"DIE"事件的处理函数

my $dbh=$dba->dbh();#得到dbh,你自己可以用DBI的函数进行操作

EasyDBAccess->once();
$dba->once();

$re=$dba->select_one('select id from person limit 0,1');
#$re=1
($re,$err_code)=$dba->select_one('select id from person limit 0,1'); 
#$re=1,$err_code=0
($re,$err_code)=$dba->select_one('select id from person2 limit 0,1');
#table person2 doesn't exist, will die
 
$dba->once();
($re,$err_code,$err_detail)=$dba->select_one('select id from person2 limit %start_pos,%count',{start_pos=>0,count=>1});
#won't die, because we have do "$dba->once()" before it 
if($err_code==0){
    print "no error, id is $re";
}elsif($err_code==5){
    #execute error
    if($dba->err_code()==1146){
        print 'table not exist';
    }else{
        #other error
        CORE::die $err_detail;
    }
}
 
($re,$err_code)=$dba->execute('insert into person values (?,?)',[3,'Bob']);
#1, affected_rows
($re,$err_code)=$dba->select('select * from person');
#[{id=>1,name=>'tom'},{id=>2,name=>'gates'}]
($re,$err_code)=$dba->select_array('select * from person');
#[[1,'tom'],[2,'gates']]
($re,$err_code)=$dba->select_row('select * from person');
#{id=>1,name=>'tom'}
($re,$err_code)=$dba->select_col('select id from person');
#[1,2]
($re,$err_code)=$dba->select_one('select id from person');
#1, first line first column
 
($re,$err_code)=$dba->select_one('select name from person where id=3');
#select_row, select_one can cause NO_LINE error
if($err_code==0){
   print "no error, name is $re";
}elsif($err_code==1){
   #no line
   print "there is 0 row in result set";
}else{
   print "other error";
}

$id=$dba->id('key1');#1
$id=$dba->id('key2');#1
$id=$dba->id('key1');#2
$id=$dba->id('key1');#3
$id=$dba->id('key3',100);#100,一次取多个ID

$sid=$dba->sid();
#446d40ffd9890184
$sid_info=$dba->sid_info('446d40ffd9890184');
#{"sid" => 3649634692, "comment" => undef, "record_time" => 1148010751}
 
#will insert a record to note table
$sid->note('hello world');

文档约定

true & false

下文中所提到的true一般是指1 下文中所提到的false一般是指''或 0, 两者均可,若在返回值中,返回的是''

ERR_CODE(错误代码)

程序有五种错误代码,4种错误,其中NO_LINE是不会触发"DIE"的,其他几个错误都会触发"DIE"

NO_ERR 0

0表示程序执行成功,没有错误

NO_LINE 1

某些函数($dba->serlect_row or $dba->select_one)假设性的认为,返回值中至少有一条记录 但是如果结果集中一条记录都没有,那么程序不会"DIE"掉,但是会把ERR_CODE设置成 1

PARAM_ERR 2

参数错误,通常是编程中的错误,比如SQL string is null

CONN_ERR 3

连接数据库的时候发生错误

PREPARE_ERR 4

Prepare SQL 错误,实际上,Perl DBI设计的原因,这个错误是几乎不太可能发生的(换句话说,从来没发生过)

EXEC_ERR 5

SQL执行错误

"DIE" & "DIE_HANDLER"

某些程序的错误会触发我们称之为"DIE"事件的发生,"DIE"时间发生后,由"DIE_HANDLER"程序来处理 程序默认的"DIE_HANDLER"是CORE::die,即遇到错误时会die掉,我们可以自己编写程序的"DIE_HANDLER" 之所以"DIE"需要加引号是为了与 CORE::die 区分开来,"DIE"未必是真的die 用once函数可以使下一个数据库操作就算发生了错误也不会"DIE"

全局选项

你可以在运行的时候设定全局选项,比如

EasyDBAccess::$_DEBUG=0;

大多数时候你不需要改变全局设定,强烈建议你不要改变全局设定

选项$_DEBUG

$_DEBUG的默认值是true

如果$_DEBUG设置成false, 那么当发生错误时,不会触发"DIE"的动作(不建议)

虽然这个选项名字叫DEBUG,但实际上在生产环境(Release)中也是把次选项设定为true,之所以还叫这个名字是因为历史的原因,兼容性方面的考虑

$_SETNAMES

$_SETNAMES的默认值是true

如果$_SETNAMES设置成false,那么数据库连接成功后不会执行"SET NAMES"命令来指定字符集

$_HIDE_CONN_PARAM

$_HIDE_CONN_PARAM的默认值是true

如果$_HIDE_CONN_PARAM设置成false,当程序遇到错误时,错误信息中可能会包含敏感的帐户及数据库地址信息,如果你的网站错误处理得有问题,那么这些信息可能会被网站用户看到,所以 非常不建议改变$_HIDE_CONN_PARAM的默认值

函数的返回方式设计

函数的返回值返回方式分为scalar和array两种模式

scalar模式

$re=$dba->select_one('select 1'); #$re=1

array全模式(不常用)

($re,$err_code,$err_detail,$_pkg_name)=$dba->select_one('select 1'); 
#$re=1,$err_code=0,$err_detail=undef,$_pkg_name=EasyDBAccess

array简化模式

#if you don't need all result
($re,$err_code)=$dba->select_one('select 1');

其他约定: if $err_code!=0 then $re=undef

几乎所有主要函数都采用了这种函数返回方式设计


函数参数设计

$dba->select($sql_str,$bind_param,$inline_param);
$dba->select($sql_str,$bind_param);
$dba->select($sql_str);

使用案例

$dba->select('select * from person where id=?,name=? limit %start_pos,%count',[10,'qian'],{start_pos=>10,count=>20});

参数$sql_str

SQL字符串,可能其中含有 '?','%'字符,这些字符是有特殊含义的

'?' 将会按顺序绑定 $bind_param 中的值 '%' 将会结合$inline_param做字符串替换

参数$bind_param

是一个array_ref(数组引用)

内部实现方式

$sth->execute(@$bind_param)

参数$inline_param

是一个hash_ref(哈希引用)

内部实现方式:

while(my($k,$v)=each %{$inline_param}){
    if(!defined($v)){return 0;}
    $_[0]=~s/\Q%$k\E/$v/g;
}  

函数参考

核心函数

foo - 判断模块是否已经加载

if(defined(&EasyDBAccess::foo)){
    print "lib is included";
}else{
    print "lib is not included";
}

new - 连接数据库

$dba/($dba,$err_code)=EasyDBAccess->new($conn_param,$ext_option);
$dba/($dba,$err_code)=EasyDBAccess->new($param);
$param = $conn_param + $ext_option

$param= merge of $conn_param & $ext_option, sometimes you need to put param in separate hash_ref,so do this design

$param is a hash_ref has below option

type: only support 'mysql' yet, default 'mysql'
host: mysql server address, default'127.0.0.1'
port: mysql server service port, default is 3306
socket: use socket to connect mysql, set socket path to this
usr : mysql user name, default 'root'
pass: mysql auth password, default 
encoding: mysql charset (when mysql ver>=4.1), default 'UTF8', please check ther reference of 'SET NAMES'
version:  mysql database version, default auto detect, if set, then please at least specify one digit after  '.', e.g. '3.23','4.1'
database: default database, if not set, then no default database
     

the below option is to set what should do when error occur(default is CORE::die)

die_handler: set a EasyHandler to this to handler error, will do this handler when 
err_file: an internal die_handler, set file name to this, then will log into file when die
#normal use
my $dba=EasyDBAccess->new({host=>'127.0.0.1',usr=>'root',pass=>'passwd',database=>'test_db'});
   
#use socket
my $dba=EasyDBAccess->new({socket=>'/tmp/mysql.sock',usr=>'root',pass=>'passwd',database=>'test_db'});
#use other encoding than utf8
my $dba=EasyDBAccess->new({host=>'127.0.0.1',usr=>'root',pass=>'passwd',database=>'test_db',encoding=>'gbk'});
    
#判断连接数据库是否成功
把错误写道LOG文件中
#编写自己的"DIE"事件的处理函数

close - 关闭数据库连接

关闭数据库连接

$dba->close();

delete $dba 也能达到同样的效果

dbh - 得到dbh

得到dbh

my $dbh=$dba->dbh();

type - 返回数据库类型

返回数据库类型,因为目前只支持mysql,所以只能返回'mysql'

print $dba->type();

once - 关闭下一条语句中的"DIE"

EasyDBAccess->once();
$dba->once();

#判断连接数据库是否成功

err_code, err_str - 得到数据库的错误代码和错误信息

 $dba->err_code();#1146,if no error, return undef
 $dba->err_str();
   
 #结合MYSQL ERR_CODE进行处理

execute - 执行SQL语句

$rc/($rc,$err_code,$err_detail,$_pkg_name)=$dba->execute($sql_str,$bind_param,$inline_param);

return result of $dbh->do if succ, in most case ,this will be "affected rows"

if execute error, $rc return undef

select - 执行select语句,取得结果集

$rc/($rc,$err_code,$err_detail,$_pkg_name)=$dba->select($sql_str,$bind_param,$inline_param);
 

return result as array_ref of hash_ref ([{id=>1,name=>'hello'},...])

select_array - 执行select语句,用数组的形式返回结果集

执行select语句,用数组的形式返回结果集,相对用哈希的形式能节省不少内存,速度也会变快一点点

$rc/($rc,$err_code,$err_detail,$_pkg_name)=$dba->select_select_array($sql_str,$bind_param,$inline_param);

return result as array_ref of array_ref ([[1,'hello'],...])

select_row - 执行select语句,取得第一行

$rc/($rc,$err_code,$err_detail,$_pkg_name)=$dba->select_row($sql_str,$bind_param,$inline_param);
   

return first row of result set as hash ({id=>1,name=>'hello'})

if no row in result set, then $rc=undef, $err_code=1 but won't cause a die

select_col - 执行select语句,取得第一列

$rc/($rc,$err_code,$err_detail,$_pkg_name)=$dba->select_row($sql_str,$bind_param,$inline_param);
 

return first column of result set as array_ref( [1,2,3,...] )

select_one - 执行select语句,取得第一行第一列

$rc/($rc,$err_code,$err_detail,$_pkg_name)=$dba->select_one($sql_str,$bind_param,$inline_param);

return first row first column of result set scalar( 1 ) if no row in result set, then $rc=undef, $err_code=1 but won't cause a "DIE"

额外的函数

batch_insert - 一次插入很多条记录

一次插入数据库很多条记录,采取了一条SQL插入多行的技术,插入速度大大提高

$rc/($rc,$err_code,$err_detail,$_pkg_name)=$dba->batch_insert($sql_str,$values_tmpl,$values,$max_count);
   

$max_count: max record insert per time, the default value for $max_count is 1

return result of $dbh->do if succ, in most case ,this will be "affected rows"

if execute error, $rc return undef

$dba->batch_insert('insert into person values %V','(?,?)',[[1,'tom'],[2,'gates'],[3,'bush']],100);

build_array - 根据一些设置生成一个数组

根据一些 $filter 和 哈希 $rh,数组 $ra 生成一个数组,生成出来的数组主要用于insert

$filter是一个数组,数组可能是一般的字符串,或者是 '?',如果是一般的字符串$k,那么把$rh->{$k}放入结果集中,如果是'?',那么从数组中拿一个,具体看下面的例子

($ra,$err_code)/$ra = EasyDBAccess::build_array($filter,$rh,$ra);
 

when you want to find a value from hash, but there is no such key in hash, then will use value undef instead, and will set $err_code=1

my $param={name=>'tom',age=>23,other_key=>'hello'};
my $filter=[qw/? name age/];
my $id=$dba->id('person');
my $record=&EasyDBAccess::build_array($filter,$param,[$id]);#$record=[$id,'tom','23']
$dba->execute('insert into person values (?,?,?)',$record);

build_update - 为update函数准备数据

($str2,$ra_bind_param,$count,$str)/$str=EasyDBAccess::build_update($filter,$hash);
my $param={name=>'jack',other_key=>'hello'};
my $filter=[qw/name age/];
my ($str2,$ra_bind_param,$count,$str)=EasyDBAccess::build_update($filter,$param);  
# $str2='name=?',$ra_bind_param=['jack'],$count=1,$str='name=?,'
if($count>0){
    $dba->execute("update person set $str2 where id=?",[@$ra_bind_param,3]);
}
if($count>0){
  $dba->execute("update person set ".$str."gender=1 where id=?",[@$ra_bind_param,3]);
}

insert_one_row - build_array 与execute的整合

整合了build_array和execute的一个函数

$rc/($rc,$err_code,$err_detail,$_pkg_name)==EasyDBAccess::insert_one_row($sql, $filter, $rh_data, $ra);
my $sql='insert into person values (?,?)';
my $filter=[qw/name ?/];
my $param={name=>'tom',age=>23,other_key=>'hello'};
$ra=[23]
my $record=&EasyDBAccess::insert_one_row($sql,$filter,$record,$ra);

update - build_update 和 execute的整合

build_update 和 execute的整合

$rc/($rc,$err_code,$err_detail,$_pkg_name)==EasyDBAccess::update($sql, $filter, $rh_data, $ra_param);
my $sql='update person set %ITEM, SCORE=\'A\' where name=?';
my $sql='update person set %COMMAITEM SCORE=\'A\' where name=?';
#COMMAITEM is ITEM with a comma
my $filter=[qw/age/];
my $rh_data={name=>'tom',age=>24};
my $ra_param=['tom'];
my $record=&EasyDBAccess::update($sql, $filter, $rh_data, $ra_param);

数据库应用工具函数

id - id 生成器

要使用这个函数,你必须使用下面语句在数据库中创建一个合适的表

CREATE TABLE RES(ATTRIB VARCHAR(255) NOT NULL,ID INT NOT NULL ,PRIMARY KEY (ATTRIB))
$id=$dba->id('key1');#1
$id=$dba->id('key2');#1
$id=$dba->id('key1');#2
$id=$dba->id('key1');#3
$id=$dba->id('key3',100);#100,一次生成多个ID,返回最大的一个ID

sid, sid_info - session id 生成器

要使用这个函数,你必须使用下面语句在数据库中创建一个合适的表

CREATE TABLE SID(RECORD_TIME INT UNSIGNED NOT NULL, SID INT UNSIGNED NOT NULL,COMMENT VARCHAR(255) DEFAULT NULL,PRIMARY KEY(RECORD_TIME,SID))
$sid=$dba->sid();    #446d40ffd9890184
$sid_info=$dba->sid_info('446d40ffd9890184'); #{"sid" => 3649634692, "comment" => undef, "record_time" => 1148010751}

内部实现原理: sid_string=hex(2^16*record_time+ ramdon_number)

sid_string : return value of $dba->sid()

record_time: record_time in $sid_info

ramdon_number: sid in $sid_info

note - 往数据库中写入一些纪要

要使用这个函数,你必须使用下面语句在数据库中创建一个合适的表

CREATE TABLE NOTE(TEXT TEXT NOT NULL, RECORD_TIME INT UNSIGNED NOT NULL)
$dba->note('something to note');

错误处理综述

不处理错误

如果你不显示的写出错误处理代码,程序在遇到严重错误(除NO_LINE外的错误时),会"DIE"出错误字符串

EasyDBAccess->new({host=>'127.0.0.1',usr=>'root',pass=>'wrong passwd',database=>'test_db'});

但是下面这种情况,程序不会"DIE",但会将$err_code设置成1

($row,$err_code)=$dba->select_row("select * from empty_table");
if($err_code==0){
    printf("execute success");
}elsif($err_code==1){
    printf("table is empty");
}else{
    printf("other error");
}

处理错误

判断连接数据库是否成功

EasyDBAccess->once();  #disable "DIE" in next database operation 
my ($dba,$err_code)=EasyDBAccess->new({host=>'127.0.0.1',usr=>'root',pass=>'passwd',database=>'test_db'});
if($err_code==0){
    printf("connect database success");
}elsif($err_code==3){
    printf("connect database fail");
}else{
    printf("other error");
}

结合MYSQL ERR_CODE进行处理

$dba->once();#disable "DIE" in next database operation 
($rc,$err_code,$err_detail)=$dba->execute('insert into hello values(1,2,3)');
if($err_code==5){#execute error
    if($dba->err_code()==1146){
        print 'table not exist';
    }else{
        CORE::die $err_detail;#other error
    }
}

编写自己的"DIE"事件的处理函数

sub die_to_file{
    my $file_path= shift;
    my ($err_pkg,$err_code,$err_detail,$record_time)=(undef,undef,undef,CORE::time);
    my $param_count=scalar(@_);
    if($param_count==1){
        ($err_detail)=@_;
    }elsif($param_count==3){
        ($err_code,$err_detail,$err_pkg)=@_;
    }elsif($param_count==4){
        ($err_code,$err_detail,$err_pkg,$record_time)=@_;
    }else{
        CORE::die($_pkg_name.'::die_to_file: param error');
    }

    $_=[localtime($record_time)];
    my $prefix="#####".sprintf('%04s-%02s-%02s %02s:%02s:%02s',$_->[5]+1900,$_->[4]+1,$_->[3],$_->[2],$_->[1],$_->[0])."\n";
 
    my $result=append_file($file_path,$prefix.$err_detail."\n");
    if($result){
        #log succ
        CORE::die $err_detail;
    }else{
        CORE::die($_pkg_name.'::die_to_file: append to file failed');
    }
}
my $die_handler=_EasyDBAccess_EasyHandler->new(\&die_to_file,['\var\log\logfile']);
my $dba=EasyDBAccess->new({host=>'127.0.0.1',usr=>'root',pass=>'passwd',database=>'test_db'},{die_handler=>$die_handler});

内置的另一个DIE_HANDLER

程序内置了另外一个DIE_HANDLER,可以在错误的同时写一下LOG文件,你可以用下面这种方式来开启这个功能

my $dba=EasyDBAccess->new({host=>'127.0.0.1',usr=>'root',pass=>'passwd',database=>'test_db'},{err_file=>'\var\log\logfile'});

COPYRIGHT

The EasyDBAccess module is Copyright (c) 2003-2005 QIAN YU. All rights reserved.

You may distribute under the terms of either the GNU General Public License or the Artistic License, as specified in the Perl README file.


首页 >> Perl 模块
小猪的知识库
小猪实验室(LABS)