让ThinkPHP3.2.3 支持对Oracle LOB数据的操作 – Fighting! – CSDN博客

来源: 让ThinkPHP3.2.3 支持对Oracle LOB数据的操作 – Fighting! – CSDN博客

让ThinkPHP3.2.3 支持对Oracle LOB数据的操作

1. 问题由来

直接用PHP存取oracle的clob也是会自动截断。当需要插入的数据很大,会直接报错。ThinkPHP 3.2.3 封装的CURD也不能解决这个问题。
这个时候就需要使用PHP OCI8的拓展函数库。

2. 配置PHP:OCI8

首先,原理是利用PHP OCI8的拓展函数库。所以,必须先把OCI8,和php_oci8和php_oci8_11 功能开启。
可以wampserver上直接进行设置。如下图:
setconfig.png
设置成功后,可以通过phpinfo() 查看,显示enabled即开启成功,如下图。
phpinfo.png

2. PHP:OCI8的CURD

PHP对Clob文件是以resource的类型存取的, 所以PDO对Clob资源文件的读取应该这样操作:

$arr = $pdo -> fetch();
is_resource($arr[‘clob’]) && $arr[‘clob’]=stream_get_contents($arr[‘clob’]);
  • 1
  • 2

可以对照参考:PHP:OCI8 手册
更多CURD的Examples可以参考 官方范例

$c1 = oci_connect("hr", "welcome", 'localhost/XE');
$c2 = oci_connect("hr", "welcome", 'localhost/XE');

// Both $c1 and $c2 show the same PHP resource id meaning they use the
// same underlying database connection
echo "c1 is $c1<br>\n";
echo "c2 is $c2<br>\n";

function create_table($conn)
{
    $stmt = oci_parse($conn, "create table hallo (test varchar2(64))");
    oci_execute($stmt);
    echo "Created table<br>\n";
}

function drop_table($conn)
{
    $stmt = oci_parse($conn, "drop table hallo");
    oci_execute($stmt);
    echo "Dropped table<br>\n";
}

function insert_data($connname, $conn)
{
    $stmt = oci_parse($conn, "insert into hallo
              values(to_char(sysdate,'DD-MON-YY HH24:MI:SS'))");
    oci_execute($stmt, OCI_DEFAULT);
    echo "$connname inserted row without committing<br>\n";
}

function rollback($connname, $conn)
{
    oci_rollback($conn);
    echo "$connname rollback<br>\n";
}

function select_data($connname, $conn)
{
    $stmt = oci_parse($conn, "select * from hallo");
    oci_execute($stmt, OCI_DEFAULT);
    echo "$connname ----selecting<br>\n";
    while (oci_fetch($stmt)) {
        echo "    " . oci_result($stmt, "TEST") . "<br>\n";
    }
    echo "$connname ----done<br>\n";
}

create_table($c1);

insert_data('c1', $c1);   // Insert a row using c1
sleep(2);                 // sleep to show a different timestamp for the 2nd row
insert_data('c2', $c2);   // Insert a row using c2

select_data('c1', $c1);   // Results of both inserts are returned
select_data('c2', $c2);   // Results of both inserts are returned

rollback('c1', $c1);      // Rollback using c1

select_data('c1', $c1);   // Both inserts have been rolled back
select_data('c2', $c2);

drop_table($c1);

// Closing one of the connections makes the PHP variable unusable, but
// the other could be used
oci_close($c1);
echo "c1 is $c1<br>\n";
echo "c2 is $c2<br>\n";


// Output is:
//    c1 is Resource id #5
//    c2 is Resource id #5
//    Created table
//    c1 inserted row without committing
//    c2 inserted row without committing
//    c1 ----selecting
//        09-DEC-09 12:14:43
//        09-DEC-09 12:14:45
//    c1 ----done
//    c2 ----selecting
//        09-DEC-09 12:14:43
//        09-DEC-09 12:14:45
//    c2 ----done
//    c1 rollback
//    c1 ----selecting
//    c1 ----done
//    c2 ----selecting
//    c2 ----done
//    Dropped table
//    c1 is 
//    c2 is Resource id #5
?>

注意
1.[oci_connect()]连接数据库时,为了防止乱码,建立连接的时候就指定字符编码。

// OCILogon 是oci_connect的别名, 不建议使用OCILogon
oci_connect($username, $password, $dbsid, $charset);
OCILogon($username, $password, $dbsid, $charset);

2.oci_execute() 执行一条之前被解析过的语句(见 oci_parse())。可选参数 mode 允许定义执行模式(默认是 OCI_COMMIT_ON_SUCCESS)。如果不需要将语句自动提交,则需要把 mode 设为 OCI_DEFAULT。
用 OCI_DEFAULT 模式时,将建立一个事务。事务会在关闭连接或脚本结束时(看哪个先)自动回卷。需要明确调用 oci_commit() 来提交事务,或者 oci_rollback() 中止事务。
成功时返回 TRUE, 或者在失败时返回 FALSE。

3. ThinkPHP 的Oracle Clob操作类:

[引用自ThinkPHP社区《分享自己的oracel类,支持多CLOB操作及主键自增加》]


class Ociclob{

    var $conn;
    var $table;
    var $seqname;
    var $where;//查询条件,字符串
    var $lob;//lob字段名,数组
    var $data;//数据数组

    //架构函数
    public function Ociclob($table='',$data=''){
        //取TP框架的数据库配置
        $this->connect(C('DB_USER'),C('DB_PWD'),C('DB_NAME'));

        if(!empty($table)) $this->table=$table;
        if(!empty($table)&&!empty($data)){
            $this->checkfield($table,$data);
        }
    }

    //检测字段属性
    public function checkfield($table,$data){

        if(!empty($table)&&!empty($data)){
            $fields=$this->getFields($table);
            foreach ($data as $key=>$value){
                //检测lob字段
                if(strtolower($fields[strtolower($key)]['type'])=='clob')  $lob[]=$key;
                //检测PK字段并获取SEQ
                if(strtolower($fields[strtolower($key)]['primary'])==1){
                    $this->seqname=$value;
                    $this->data[$key]=$this->getseq();//根据自动填充主键值
                    $pk=$key;//主键被设置标志
                }
            }
            $this->lob=$lob;
            //如果没有在DATA中的设置主键值,则根据SEQNAME自动填充
            if(!isset($pk)&&!empty($this->seqname)) {
                $this->data[$fields['pk']]=$this->getseq();
            }
            unset($lob);
            unset($pk);
        }

    }

    /**
    +----------------------------------------------------------
     * 连接ORACLE
    +----------------------------------------------------------
     */
    public function connect($user, $password, $SID){
        // 创建连接,并设置字符编码为'UTF8'.
        $this->conn = OCILogon($user, $password, $SID, 'UTF8');
    }

    /**
    +----------------------------------------------------------
     * 设置ORACLE字符集
    +----------------------------------------------------------
     */
    public function charset($code='UTF8'){
        $sql="ALTER DATABASE CHARACTER SET $code";
        $stmt = oci_parse($this->conn, $sql);
        oci_execute($stmt);
        oci_commit($this->conn);
        // Free resources
        oci_free_statement($stmt);

    }

    /**
    +----------------------------------------------------------
     * 添加包含有CLOB字段的记录
    +----------------------------------------------------------
     */
    public function insert (){

        //检测字段属性
        if(empty($this->lob)) $this->checkfield($this->table,$this->data);

        //字段整理
        $f=strtoupper(join(',',array_keys($this->data)));
        //数据整理
        foreach ($this->data as $key=>$val){
            $f_v_arr[]=!in_array($key,$this->lob)?"'".$val."'":"EMPTY_CLOB()";
        }
        $f_v=join(',',$f_v_arr);

        //lob字段清理并赋值LOB数据到绑定变量
        for ($i=0;$i<count($this->lob);$i++){
            $lob_str.=":".$this->lob[$i]."_loc,";
        }
        $returning_str.="  RETURNING ".join(',',$this->lob)." INTO ".rtrim($lob_str,',');

        //组装SQL
        $sql = "INSERT INTO  $this->table ($f) VALUES (".$f_v.")".$returning_str ;
        $stmt = oci_parse($this->conn, $sql);

        for ($i=0;$i<count($this->lob);$i++){
            // 创建一个“空”的OCI LOB对象绑定到定位器
            $$this->lob[$i] = oci_new_descriptor($this->conn, OCI_D_LOB);
            $lob_str=":".$this->lob[$i]."_loc";
            // 将Oracle LOB定位器绑定到PHP LOB对象
            oci_bind_by_name($stmt, $lob_str, $$this->lob[$i], -1, OCI_B_CLOB);
        }

        // 执行该语句的使用,oci_default -作为一个事务
        oci_execute($stmt, OCI_DEFAULT)    or die ("Unable to execute query\n");

        // 保存LOB对象数据
        for ($i=0;$i<count($this->lob);$i++){
            if(!$$this->lob[$i]->save($this->data[$this->lob[$i]])){
                $result=false;
                break;
            }
        }

        if ( isset($result)&&$result==false ) {
            // 如果错误,则回滚事务
            oci_rollback($this->conn);
            $ret=false;
        } else {
            // 如果成功,则提交
            oci_commit($this->conn);
            $ret=true;
        }

        // 释放资源
        oci_free_statement($stmt);
        for ($i=0;$i<count($this->lob);$i++){
            $$this->lob[$i]->free();
        }

        return $ret;

    }

    /**
    +----------------------------------------------------------
     * 更新CLOB字段的内容
    +----------------------------------------------------------
     */
    public function update (){
        //检测字段属性
        if(empty($this->lob)) $this->checkfield($this->table,$this->data);

        //数据整理
        foreach ($this->data as $key=>$val){
            $set_arr[]=!in_array($key,$this->lob)?strtoupper($key)."='".$val."'":$key."=EMPTY_CLOB()";
        }
        $set_str=join(',',$set_arr);

        //lob字段清理并赋值LOB数据到绑定变量
        for ($i=0;$i<count($this->lob);$i++){
            $lob_str.=":".$this->lob[$i]."_loc,";
        }
        $returning_str.="  RETURNING ".join(',',$this->lob)." INTO ".rtrim($lob_str,',');
        $where_str=strtoupper($this->where);
        //组装SQL
        $sql = "UPDATE  $this->table SET   $set_str  WHERE   $where_str  ".$returning_str;
        $stmt = OCIParse($this->conn, $sql);

        for ($i=0;$i<count($this->lob);$i++){
            // 创建一个“空”的OCI LOB对象绑定到定位器
            $$this->lob[$i] = OCINewDescriptor($this->conn, OCI_D_LOB);
            $lob_str=":".$this->lob[$i]."_loc";
            // 将Oracle LOB定位器绑定到PHP LOB对象
            OCIBindByName($stmt, $lob_str, $$this->lob[$i], -1, OCI_B_CLOB);
        }

        // 执行该语句的使用,oci_default -作为一个事务
        OCIExecute($stmt, OCI_DEFAULT)    or die ("Unable to execute query\n");

        // 保存LOB对象数据
        for ($i=0;$i<count($this->lob);$i++){
            if(!$$this->lob[$i]->save($this->data[$this->lob[$i]])){
                $result=false;
                break;
            }
        }
        if ( isset($result)&&$result==false ) {
            OCIRollback($this->conn);
            $ret=false;

        }else $ret=true;

        // 提交事务
        OCICommit($this->conn);
        //释放资源
        for ($i=0;$i<count($this->lob);$i++){
            $$this->lob[$i]->free();
        }
        OCIFreeStatement($stmt);
        return $ret;
    }

    public function getseq(){
        $sql="select $this->seqname.currval from dual";

        $stmt = oci_parse($this->conn, strtoupper($sql));

        oci_execute($stmt);
        $data = array();
        while ( $row = oci_fetch_array($stmt, OCI_ASSOC+OCI_RETURN_LOBS) ) {
            if($row == null) continue;
            array_push($data, $row);
        }
        // 释放资源
        oci_free_statement($stmt);
        array_filter($data);
        return $data[0]['CURRVAL'];
    }

    /**
    +----------------------------------------------------------
     * 查询包含有CLOB字段的记录
    +----------------------------------------------------------
     */
    public function select ($sql=''){
        $sql = empty($sql) ? "SELECT * FROM  $this->table  WHERE $this->where ":$sql;

        $stmt = oci_parse($this->conn, strtoupper($sql));

        oci_execute($stmt);

        while ( $row = oci_fetch_array($stmt, OCI_ASSOC+OCI_RETURN_LOBS) ) {
            $data[]=$row;
        }
        // 释放资源
        oci_free_statement($stmt);
        return $data;
    }

    /**
     * 取得数据表的字段信息
     * @access public
     */
    public function getFields($tableName) {
        $sql="select a.column_name,data_type,decode(nullable,'Y',0,1) notnull,data_default,decode(a.column_name,b.column_name,1,0) pk "
            ."from user_tab_columns a,(select column_name from user_constraints c,user_cons_columns col "
            ."where c.constraint_name=col.constraint_name and c.constraint_type='P'and c.table_name='".strtoupper($tableName)
            ."') b where table_name='".strtoupper($tableName)."' and a.column_name=b.column_name(+)";

        $result=  $this->select ($sql);
        $info   =   array();
        if($result) {
            foreach ($result as $key => $val) {
                $info[strtolower($val['COLUMN_NAME'])] = array(
                    'name'    => strtolower($val['COLUMN_NAME']),
                    'type'    => strtolower($val['DATA_TYPE']),
                    'notnull' => $val['NOTNULL'],
                    'default' => $val['DATA_DEFAULT'],
                    'primary' => $val['PK'],
                    'autoinc' => $val['PK'],
                );
                if($val['PK']==1) $info['pk']=$val['COLUMN_NAME'];
            }
        }
        return $info;
    }
}
?>

Ociclob 的调用样例
假设已经存在一个表名为’tb_post’的表(表的主键为’postid’,并且是自增的),以及一个名为’SEQ_POST’的序列名

/**
 * $data 要插入到表的数据
 * 返回值 插入数据的ID
 */
function insertPostWithClob($data) {
    $clog_obj=new Ociclob();
    $clog_obj->table='tb_post';
    $clog_obj->seqname='SEQ_POST';
    $clog_obj->data = $data;
    if($clog_obj->insert() == false) return -1;
    $ret = $clog_obj->getseq();
    return $ret;
}
/**
 * $data 要插入到表的数据
 * 返回值 是否修改成功
 */
function updatePostWithClob($data) {
    $postid = $data['postid'];
    $clog_obj=new Ociclob();
    $clog_obj->table='tb_post';
    $clog_obj->where="postid='$postid'";
    $clog_obj->data = $data;
    return $clog_obj->update();
}
/**
 * $postid
 * 返回值 查询得到的list
 */
function selectPostWithClob($postid) {
    $clog_obj=new Ociclob();
    $clog_obj->table='tb_post';
    $clog_obj->where="postid='$postid'";
    $ret=$clog_obj->select();
    return $ret;
}

4. 参考资料:

  1. ThinkPHP社区《分享自己的oracel类,支持多CLOB操作及主键自增加》
  2. [PHP Manual OCI8函数介绍]
  3. [oracle 官网的解释]
分享到:更多 ()