我正在使用Apache2.2(worker)/ mod_perl 2.0.4 / Apache :: DBI / CGI :: Session和Firebird RDBMS。
我还编写了CGI :: Session :: Driver :: firebird.pm来使用Firebird RDBMS。数据库连接由Apache :: DBI汇集,并提供CGI :: Session {Handle => $ dbh}的连接句柄。
DB连接数等于工作进程数。
我在3个月前发布了Programming with Apache::DBI and firebird. Get Stucked httpd on exception。我找到了这个问题的原因,并想知道如何解决它。
$dbh = DBI->connect("dbi:Firebird:db=$DBSERVER:/home/cdbs/xxnet.fdb;
ib_charset=UTF8;ib_dialect=3",$DBUSER,$DBPASS,{
AutoCommit=>1,
LongReadLen=>8192,
RaiseError=>1
});
my $session = new CGI::Session('dbi:firebird',$sessid,{Handle=>$dbh});
my $ses_p1 = $session->param('p1');
eval { $dbh->begin_work()
my $sql = "SELECT * FROM SAMPLETABLE"
my $st = $dbh->prepare($sql);
$st->execute();
while (my $R = $st->fetchrow_hashref()) {
...
}
$st->finish();
}; warn $@ if $@;
if ($@) {
$dbh->rollback();
}else{
$dbh->commit();
}
$session->flush();
发生sql错误时,eval块会捕获异常和回滚事务。之后,CGI :: Session不再检索会话对象。
因为prepare_cached语句在CGI :: Session :: DBI.pm处失败。 CGI :: Session :: DBI.pm使用prepare_cached($ sql,undef,3)。 '3'是使用缓存语句的最安全的方法,但在这种情况下它永远不会找到破坏的语句。
如何解决这个问题?提出更改CGI :: Session :: DBI.pm的请求以使用prepare()语句?在firebird.pm中写store(),retrieve(),traverse()函数来使用prepare()语句?
捕获异常后,其他prepare_cached()可能会失败...
1)我在CGI :: Session-> errstr()上添加了die语句我收到错误“new():failed:load():无法检索数据:retrieve():$ sth-> execute failed with error message “2)如果$ session有效,我会在session-> load()之后刷新会话对象,更改存储到DB。 3)我将begin_work()替换为{AutoCommit} = 0结果相同。我可以在捕获异常和回滚后正常使用$ dbh,但是新的CGI :: Session返回错误。 ------------------------------------------已添加2017/07/26 18: 47 JST
请给我你的建议。
谢谢。
在请求更改为CGI :: Session :: Driver :: DBI.pm之前,您可以尝试各种各样的事情......
首先,更改调用new CGI::Session
的方式,以便在创建或加载会话时诊断问题是否发生:
my $session = CGI::Session->new('dbi:firebird',$sessid,{Handle=>$dbh}) or die CGI::Session->errstr();
方法param
或delete
将更改存储在$session
句柄内的会话中,而不是DB中。 flush
在DB中存储会话句柄内的更改。仅在session-> param set / update或session delete之后使用$session->flush()
:
$session->param('p1','someParamValue');
$session->flush() or die 'Unable to update session storage!';
# OR
$session->delete();
$session->flush() or die 'Unable to update session storage!';
方法flush
不会破坏$session
手柄(你可以在冲洗后调用$session->param('p1')
)。在某些情况下,mod_perl会缓存$session
,导致下次尝试加载同一会话时出现问题。在这些情况下,它需要在不再需要时销毁:
undef($session)
我可以建议的最后一件事是避免使用begin_work
方法,用AutoCommit
控制交易行为(因为DBD::Firebird documentation说应该控制交易的方式)和eval块内的commit
:
eval {
# Setting AutoCommit to 0 enables transaction behavior
$dbh->{AutoCommit} = 0;
my $sql = "SELECT * FROM SAMPLETABLE"
my $st = $dbh->prepare($sql);
$st->execute();
while (my $R = $st->fetchrow_hashref()) {
...
}
$st->finish();
$dbh->commit();
};
if ($@) {
warn "Tansaction aborted! $@";
$dbh->rollback();
}
# Remember to set AutoCommit to 1 after the eval
$dbh->{AutoCommit} = 1;
你说你为Firebird编写了自己的会话驱动程序......你应该看看CGI / Driver / sqlite.pm或CGI / Driver / mysql.pm是如何制作的,也许你需要写一些你想念的提取方法......
希望这可以帮助!!