简体   繁体   English

如何在带有 Yii 框架的 oracle 11g 中使用 CLOB

[英]How to use CLOB in oracle 11g with Yii framework

I have object in Yii mapped to a table witch contains one CLOB column.我将 Yii 中的对象映射到包含一个 CLOB 列的表。

How I can insert with save() method call an ~54k char length string into a row?如何使用 save() 方法调用一个 ~54k 字符长度的字符串插入一行?

Yii has beforeSave() and afterSave() event methods in it's ActiveRecord models. Yii 在它的 ActiveRecord 模型中有 beforeSave() 和 afterSave() 事件方法。 I would use those instead of overriding the save() method.我会使用它们而不是覆盖 save() 方法。 Put all of the necessary Oracle methods in there.将所有必要的 Oracle 方法放在那里。 Basically: null out the fields in the beforeSave(), then in the afterSave() write the values to the DB with OCIParse(), etc.基本上:清空 beforeSave() 中的字段,然后在 afterSave() 中使用 OCIParse() 等将值写入 DB。

Here is a good blog article about it using the Cake PHP framework, which is very similar (MVC with before and after Save methods).这是一篇关于使用 Cake PHP 框架的很好的博客文章,它非常相似(MVC 与前后保存方法)。 You will need to modify this code to work with Yii, obviously, but it should get you on the right track:显然,您将需要修改此代码以使用 Yii,但它应该让您走上正轨:

http://nik.chankov.net/2008/01/03/cakephp-and-oracle-handling-clob-fields/ http://nik.chankov.net/2008/01/03/cakephp-and-oracle-handling-clob-fields/

The newest version of Yii has a query builder now too, which might be of some help in the afterSave() code you will need to write:最新版本的 Yii 现在也有一个查询构建器,这可能对您需要编写的 afterSave() 代码有一些帮助:

http://www.yiiframework.com/doc/guide/1.1/en/database.query-builder http://www.yiiframework.com/doc/guide/1.1/en/database.query-builder

Good luck!祝你好运!

I work it out just now.我刚刚解决了。 I also need to use BLOB in oracle 11g, I want to insert image into oracle table.我还需要在 oracle 11g 中使用 BLOB,我想将图像插入到 oracle 表中。

I search it all around the net, finally I found a probably solution at enter link description here .我在网上搜索它,最后我在此处输入链接描述找到了一个可能的解决方案。

please click it out.请点击它。

I follow the example #2我按照示例 #2

<?php
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');
$stmt = $db->prepare("insert into images (id, contenttype, imagedata) values (?, ?, ?)");
$id = get_new_id(); // some function to allocate a new ID

// assume that we are running as part of a file upload form
// You can find more information in the PHP documentation

$fp = fopen($_FILES['file']['tmp_name'], 'rb');

$stmt->bindParam(1, $id);
$stmt->bindParam(2, $_FILES['file']['type']);
$stmt->bindParam(3, $fp, PDO::PARAM_LOB);

$db->beginTransaction();
$stmt->execute();
$db->commit();
?>

I get the $db by我得到 $db

$db = Yii::app()->db->getPdoInstance() 

instead by代替

$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');

and other things are all the same.其他的都一样。

Good luck!祝你好运!

This has worked for me, but I have little praise, I am starting the adaptation of MySQL to ORACLE.这对我有用,但我几乎没有表扬,我正在开始 MySQL 到 ORACLE 的适配。 Crazy!!!疯狂的!!!

It is a beta, if one can improve it will help.这是一个测试版,如果可以改进它将会有所帮助。

overwrite the __get function in Model.覆盖Model中的__get函数。

class Entidad extends \common\myclass\MyActiveRecord {....... Entidad 类扩展 \common\myclass\MyActiveRecord {......

public function __get($name) {

    $columna = $this->getTableSchema()->getColumn($name);
    $current = parent::__get($name); 
    if (isset($columna->dbType) && $columna->dbType=='CLOB') {          
        if (is_string($current)) {
            return $current;
        } 
        else if (is_null($current)) {
            return $current;
        }
        else if ($this->isAttributeChanged($name)) {
            return $this->getAttribute($name);
        }
        else {
            $valor = stream_get_contents($current);   
            $this->setAttribute($name,$valor);
            return $this->getAttribute($name);                
        }
    } else {            
        return $current;
    }

}

.....

} }

Our project also had problems with reading and writing to LOB fields when using yii2 and Oracle DB.我们的项目在使用 yii2 和 Oracle DB 时也存在读写 LOB 字段的问题。 As a result, at the moment, I came to the decision to write a class overrides some methods of the ActiveRecord class in yii2, and to inherit some models not from the ActiveRecord, but from it.因此,此刻,我决定写一个类来覆盖 yii2 中 ActiveRecord 类的一些方法,并继承一些模型,而不是从 ActiveRecord 继承。 I made it relatively universal, you can take as a basis for your improvements https://github.com/pivasikkost/yii2-improvements/blob/master/models/ActiveRecordOciLob.php .我让它比较通用,你可以作为你改进的基础https://github.com/pivasikkost/yii2-improvements/blob/master/models/ActiveRecordOciLob.php

<?php

namespace app\models\system;

use Yii;
use \yii\db\ActiveRecord;
use \yii\db\Query;

/**
 * ActiveRecordOciLob is the base class for classes representing relational data in terms of objects,
 * that should work correctly with LOB data in Oracle DB. Used Oci8Connection (in my case - neconix\src\Oci8Connection).
 * 
 * Just inherit your gii generated model class not from ActiveRecord, but from this class,
 * set $clob_attributes, $blob_attributes and $dbOciLobName to work.
 * 
 * @property array $clob_attributes an array of attribute names of the form "['name_1', 'name_2', ...]"
 * @property array $blob_attributes an array of attribute names of the form "['name_1', 'name_2', ...]"
 * @property array $primaryKeyOciLob an array of attribute names of the form "['name_1', 'name_2', ...]"
 * @property string $dbOciLobName Oci8Connection name
 * 
 * @author Konstantin Zosimenko <pivasikkost@gmail.com>
 * @since 2.0
 */
class ActiveRecordOciLob extends ActiveRecord
{
    // METHOD 4. Almost completed, it works!
    
    public static $clob_attributes = [];
    public static $blob_attributes = [];
    public static $dbOciLobName;
    public $primaryKeyOciLob; // Not static! for some reason, being static, when saving one descendant of this class from another descendant of this class, this variable is overwritten and takes the wrong value
    
    /** 
     * @inheritdoc
     */
    public function init()
    {
        parent::init();
        $this->primaryKeyOciLob = static::primaryKey();
    }
    
    /**
     * @return \yii\db\Connection the database connection used by this AR class.
     */
    public static function getDbOciLob()
    {
        return static::$dbOciLobName 
                ? Yii::$app->get(static::$dbOciLobName)
                : static::getDb()
        ;
    }
    
    /**
     * Override ActiveRecord afterFind() method to fix LOB data receiveing
     * Maybe it's better to do this in populateRecord() method
     * 
     * @inheritdoc
     */
    public function afterFind ()
    {
        // Get lob fields as string, because standardly ActiveRecord gets them as resource
        $lob_attributes = array_merge(static::$clob_attributes, static::$blob_attributes);
        
        $where = [];
        foreach ($this->primaryKeyOciLob as $attribute) {
            $where[$attribute] = $this->$attribute;
        }

        foreach ($lob_attributes as $lob_attribute) {
            //$this->$lob_attribute = stream_get_contents($this->$lob_attribute); // Does not work, for some reason always returns 1 value if you try to get multiple records
            $this->$lob_attribute = (new Query())
                ->select($lob_attribute)
                ->from(static::tableName())
                ->where($where)
                ->createCommand(static::getDbOciLob())
                ->queryScalar();
            $this->setOldAttribute($lob_attribute, $this->$lob_attribute);
        }
        
        parent::afterFind ();
    }
    
    /**
     * Override ActiveRecord update() method to fix LOB data update
     * 
     * @inheritdoc
     */
    public function update($runValidation = true, $attributeNames = null)
    {   
        if ($runValidation && !$this->validate($attributeNames)) {
            Yii::info('Model not updated due to validation error.', __METHOD__);
            return false;
        }
        
        if (!$this->beforeSave(false)) {
            return false;
        }
        
        $db = static::getDbOciLob()->getDbh();
        $fields_blob = static::$blob_attributes;
        $fields_clob = static::$clob_attributes; // array with the fields to be updated
        $fields_dirty = $this->getDirtyAttributes($attributeNames); // changed fields with values
        $exist_fields_dirty_clob = array_values(
                array_intersect(array_keys($fields_dirty), $fields_clob)
        );

        
        if (empty($fields_dirty)) {
            $this->afterSave(false, $fields_dirty);
            return 0;
        }
        
        $set = [];
        $into = [];
        foreach ($fields_dirty as $name => $value) {
            if (in_array($name, $fields_clob)) {
                $set[] = $name . " = EMPTY_CLOB()";
                $into[] = ":" . $name;
            /*} elseif (in_array($name, $fields_blob)) {
                $set[] = $name . " = EMPTY_BLOB()";
                $into[] = ":" . $name;*/
            } else {
                $set[] = $name . " = :" . $name;
            }
        }
        $set_stmt = implode(", ", $set); // array to string to fill 'set' clause in the sql
        $where = [];
        foreach ($this->primaryKeyOciLob as $attribute) {
            $where[] = $attribute . "=" .$this->$attribute;
        }
        $where_stmt = implode(" AND ", $where);
        //$returning = implode(", ", array_merge($fields_clob, $fields_blob));
        $returning = implode(", ", $exist_fields_dirty_clob); // array to string to fill 'returning' clause in the sql
        $into_stmt = implode(", ", $into); // array to string to fill 'into' clause in the sql

        $sql = "UPDATE " . static::tableName() . "
                    SET " . $set_stmt . "
                    WHERE " . $where_stmt
        ;
        if ($returning && $into_stmt) {
            $sql .= " RETURNING " . $returning . "
                    INTO " . $into_stmt
            ;
        }
        
        $stmt  = oci_parse($db, $sql);
        $my_lob = [];
        // just see http://www.oracle.com/technology/pub/articles/oracle_php_cookbook/fuecks_lobs.html
        // you'll get it i'm sure
        foreach ($into as $key => $value) {
            $my_lob[$key] = oci_new_descriptor($db, OCI_D_LOB);
            oci_bind_by_name($stmt, $value, $my_lob[$key], -1, OCI_B_CLOB);
            //oci_bind_by_name($stmt, $value, $my_lob[$key], -1, OCI_B_BLOB);
        }
        foreach ($fields_dirty as $name => $value) { // don't use $value in oci_bind_by_name! the link inside this variable is changing
            if (!in_array($name, $fields_clob)) {
                oci_bind_by_name($stmt, ":".$name, $fields_dirty[$name]);
            }
        }
        $result = oci_execute($stmt, OCI_DEFAULT); // or die ("Unable to execute query\n"); //echo oci_error()
        if ($result === false) {
            oci_rollback($db);
            return false;
        }
        if ($exist_fields_dirty_clob) {
            foreach ($exist_fields_dirty_clob as $key => $name) {
                //if (!$my_lob[$key]->savefile( Yii::getAlias('@webroot/uploads/') . $model->files[0]->name )) {
                if (!$my_lob[$key]->save($this->$name)) {
                    oci_rollback($db);
                    return false; //die("Unable to update clob\n");
                }
            }
        }
        oci_commit($db);
        //$my_lob[$key]->free();
        oci_free_statement($stmt);
        oci_close($db); // not sure
        
        $changedAttributes = [];
        $oldArrtibutes = $this->getOldAttributes();
        foreach ($fields_dirty as $name => $value) {
            $changedAttributes[$name] = isset($oldArrtibutes[$name]) ? $oldArrtibutes[$name] : null;
            $this->setOldAttribute($name, $value);
        }
        $this->afterSave(false, $changedAttributes);
        
        return $result;
    }
    
    /**
     * Override ActiveRecord insert() method to fix LOB data insertion
     * 
     * @inheritdoc
     */
    public function insert($runValidation = true, $attributes = null)
    {
        //return parent::insert($runValidation, $attributes);
        
        if ($runValidation && !$this->validate($attributes)) {
            Yii::info('Model not inserted due to validation error.', __METHOD__);
            return false;
        }
        
        if (!$this->beforeSave(true)) {
            return false;
        }
        
        $db = static::getDbOciLob()->getDbh();
        //$fields_blob = static::$blob_attributes;
        $fields_clob = static::$clob_attributes; // array with the fields to be updated
        $fields_dirty = $this->getDirtyAttributes($attributes); // changed fields with values
        $fields_dirty_names = array_keys($fields_dirty);
        $exist_fields_dirty_clob = array_intersect(array_keys($fields_dirty), $fields_clob);
        
        $values = [];
        $into = [];
        foreach ($fields_dirty as $name => $value) {
            if (in_array($name, $fields_clob)) {
                $values[] = "EMPTY_CLOB()";
                $into[] = ":" . $name;
            /*} elseif (in_array($name, $fields_blob)) {
                $values[] = "EMPTY_BLOB()";
                $into[] = ":" . $name;*/
            } else {
                $values[] = ":" . $name;
            }
        }
        $fields_stmt  = implode(", ", $fields_dirty_names);
        $values_stmt  = implode(", ", $values);
        //$returning = implode(", ", $fields_clob + $fields_blob);
        $returning = implode(", ", $fields_clob);
        $into_stmt = implode(", ", $into);

        $sql = "INSERT INTO " . static::tableName() . "
                    (" . $fields_stmt . ")
                    VALUES(" . $values_stmt . ")
        ";
        if ($returning && $into_stmt) {
            $sql .= " RETURNING " . $returning . "
                    INTO " . $into_stmt
            ;
        }

        $stmt  = oci_parse($db, $sql);
        $my_lob = [];
        // just see http://www.oracle.com/technology/pub/articles/oracle_php_cookbook/fuecks_lobs.html
        // you'll get it i'm sure
        foreach ($into as $key => $value) {
            $my_lob[$key] = oci_new_descriptor($db, OCI_D_LOB);
            oci_bind_by_name($stmt, $value, $my_lob[$key], -1, OCI_B_CLOB);
            //oci_bind_by_name($stmt, $value, $my_lob[$key], -1, OCI_B_BLOB);
        }
        foreach ($fields_dirty as $name => $value) { // don't use $value in oci_bind_by_name!
            if (!in_array($name, $fields_clob)) {
                oci_bind_by_name($stmt, ":".$name, $fields_dirty[$name]);
            }
        }
        
        $result = oci_execute($stmt, OCI_DEFAULT); //or die ("Unable to execute query\n"); //echo oci_error()
        if ($result === false) {
            oci_rollback($db);
            return false;
        }
        if ($exist_fields_dirty_clob) {
            foreach ($fields_clob as $key => $name) {
                //if (!$my_lob[$key]->savefile( Yii::getAlias('@webroot/uploads/') . $model->files[0]->name )) {
                if (!$my_lob[$key]->save($this->$name)) {
                    oci_rollback($db);
                    return false; //die("Unable to update clob\n");
                }
            }
        }
        oci_commit($db);
        //$my_lob[$key]->free();
        oci_free_statement($stmt);
        oci_close($db); // not sure

        // Set primary key for new record
        $orderBy = [];
        foreach ($this->primaryKeyOciLob as $attribute) {
            $orderBy[$attribute] = SORT_DESC;
        }
        foreach ($this->primaryKeyOciLob as $attribute) {
            $fields_dirty[$attribute] = $this->find()
                ->orderBy($orderBy)
                ->limit(1)
                ->one()
                ->$attribute;
        }
        
        foreach ($fields_dirty as $name => $value) {
            $id = static::getTableSchema()->columns[$name]->phpTypecast($value);
            $this->setAttribute($name, $id);
            $fields_dirty[$name] = $id;
        }

        $changedAttributes = array_fill_keys(array_keys($fields_dirty), null);
        $this->setOldAttributes($fields_dirty);
        $this->afterSave(true, $changedAttributes);
        
        return $result;
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM