我将 Yii 中的对象映射到包含一个 CLOB 列的表。
如何使用 save() 方法将 ~54k 字符长度的字符串插入一行?
Yii 在它的 ActiveRecord 模型中有 beforeSave() 和 afterSave() 事件方法。我会使用那些而不是覆盖 save() 方法。将所有必要的 Oracle 方法放在那里。基本上:清空 beforeSave() 中的字段,然后在 afterSave() 中使用 OCIParse() 等将值写入数据库。
这是一篇关于它使用 Cake PHP 框架的很好的博客文章,它非常相似(MVC 与之前和之后的 Save 方法)。显然,您需要修改此代码以使用 Yii,但它应该让您走上正确的轨道:
http://nik.chankov.net/2008/01/03/cakephp-and-oracle-handling-clob-fields/
Yii 的最新版本现在也有一个查询构建器,它可能对你需要编写的 afterSave() 代码有一些帮助:
http://www.yiiframework.com/doc/guide/1.1/en/database.query-builder
祝你好运!
我现在才解决。我还需要在 oracle 11g 中使用 BLOB,我想将图像插入到 oracle 表中。
我在网上搜索了它,最后我在enter link description here找到了一个可能的解决方案。
请点击它。
我按照示例 #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();
?>
我得到 $db 通过
$db = Yii::app()->db->getPdoInstance()
而是由
$db = new PDO('odbc:SAMPLE', 'db2inst1', 'ibmdb2');
其他的都一样。
祝你好运!
这对我有用,但我没有什么好评,我正在开始将MySQL适应ORACLE。疯狂的!!!
这是一个测试版,如果有人可以改进它会有所帮助。
覆盖模型中的 __get 函数。
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;
}
}
.....
}
我们的项目在使用 yii2 和 Oracle DB 时也存在读写 LOB 字段的问题。结果,此刻,我决定写一个类覆盖yii2中ActiveRecord类的一些方法,并继承一些模型,而不是从ActiveRecord,而是从它。我使它相对通用,您可以将其作为改进的基础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;
}