#php #oracle #oop #pdo #connection-pooling
#php #Oracle #ооп #pdo #объединение пулов соединений
Вопрос:
В настоящее время я использую комбинацию PHP, ORACLE, PDO и JNDI для подключения моего приложения к базе данных. У меня возникли проблемы с пониманием наилучшего подхода к управлению пулами соединений в объектно-ориентированном подходе. В результате я получаю предупреждения о максимальном подключении при попытке выполнить массовые вставки (все, что превышает 32 вставки (на которые установлен мой максимальный размер пула)).
Рассмотрим этот пример:
Main File:
//User uploads excel document which I parse into an array
$car = array();
foreach($array as $index => $data){
$car[$index] = new Car(null,$data["make"],$data["model"]);
$car[$index]->insert();
}
//return car array of objects
О классе:
//Car Class
class Car{
protected $pkey;
protected $make;
protected $model;
protected $db;
public function __construct($pkey,$make,$model){
$this->pkey = $pkey;
if(isset($make) amp;amp; ($make != '')){
$this->make = $make;
}else{
throw new Exception("Car must have make");
}
if(isset($model) amp;amp; ($model != '')){
$this->model = $model;
}else{
throw new Exception("Car must have model");
}
$this->db = new Database();
}
public function insert(){
$sql = "INSERT INTO TABLE (...) VALUES (..)";
$data = array(
":make"=>$this->make,
":model"=>$this->model,
);
try{
$this->pkey = $this->db->insert($sql,$data);
return true;
}catch(Exception $err){
//catch errors
return false;
}
}
}
В этом примере, предполагая, что max-pool имеет значение 32, любой массив, больший 32, приведет к превышению максимального размера пула, поскольку каждый объект car хранится с активным подключением к БД. Чтобы исправить это, я попытался внедрить следующие исправления в класс.
//Car Class
class Car{
protected $pkey;
protected $make;
protected $model;
protected $db;
public function __construct($pkey,$make,$model){
$this->pkey = $pkey;
if(isset($make) amp;amp; ($make != '')){
$this->make = $make;
}else{
throw new Exception("Car must have make");
}
if(isset($model) amp;amp; ($model != '')){
$this->model = $model;
}else{
throw new Exception("Car must have model");
}
//$this->db = new Database(); //Moved out of the constructor
}
public function insert(){
$this->establishDBConn();
$sql = "INSERT INTO TABLE (...) VALUES (...)";
$data = array(
":make"=>$this->make,
":model"=>$this->model,
);
try{
$this->pkey = $this->db->insert($sql,$data);
$this->closeDBConn();
return true;
}catch(Exception $err){
//catch errors
$this->closeDBConn();
return false;
}
}
protected function establishDBConn(){
if(!$this->db){
$this->db = new Database();
}
}
public function closeDBConn(){
if($this->db){
$this->db->close();
$this->db = null;
}
}
}
Теоретически это изменение должно было обеспечить только поддержание активного соединения во время фактического процесса вставки. Однако с этим изменением я продолжаю достигать максимального предела пула соединений. В качестве последней попытки я удалил всю логику вставки из класса car и создал функцию массовой вставки. Эта функция игнорирует концепцию объекта, вместо этого она просто получает массив данных, который она перебирает и вставляет в одно подключение к данным. Это работает, но я хотел бы найти способ решить мою проблему с помощью ограничений объектно-ориентированного программирования.
Есть предложения о том, как я могу улучшить свой код, чтобы более эффективно использовать объекты и подключения к базе данных?
Для справки вот как выглядит мой класс базы данных:
class Database {
protected $conn;
protected $dbstr;
public function __construct() {
$this->conn = null;
$this->dbstr = "jndi connection string";
$this->connect();
}
public function connect(){
try{
$this->conn = new PDO($this->dbstr); // Used with jndi string
} catch (PDOException $e){
// print $e->getMessage();
}
return "";
}
public function insert($query, $data){
try{
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
/* Execute a prepared statement by passing an array of values */
$sth = $this->conn->prepare($query, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$count = $sth->execute($data);
return $this->oracleLastInsertId($query);
}catch(PDOException $e){
throw new Exception($e->getMessage());
}
}
public function oracleLastInsertId($sqlQuery){
// Checks if query is an insert and gets table name
if( preg_match("/^INSERT[tn ] INTO[tn ] ([a-z0-9_-] )/is", $sqlQuery, $tablename) ){
// Gets this table's last sequence value
$query = "select ".$tablename[1]."_SEQ.currval AS last_value from dual";
try{
$temp_q_id = $this->conn->prepare($query);
$temp_q_id->execute();
if($temp_q_id){
$temp_result = $temp_q_id->fetch(PDO::FETCH_ASSOC);
return ( $temp_result ) ? $temp_result['LAST_VALUE'] : false;
}
}catch(Exception $err){
throw new Exception($err->getMessage());
}
}
return false;
}
public function close(){
$this->conn = null;
}
}
Комментарии:
1. Просто прочитайте введение, но действительно ли вы используете отдельные соединения для каждой отдельной вставки? (Или почему у вас закончились соединения после 32 вставок?) Это … дерьмо ^^
Ответ №1:
Правильный подход, по-видимому, заключается в использовании класса базы данных на основе singleton как такового:
class Database {
protected $conn;
protected $dbstr;
// keep the one and only instance of the Database object in this variable
protected static $instance;
// visibility changed from public to private to disallow dynamic instances
private function __construct() {
$this->conn = null;
$this->dbstr = "jndi connection string";
$this->connect();
}
// added this method
public static function getInstance() {
if (!isset(self::$instance)) {
self::$instance = new Database();
}
return self::$instance;
}
// everything below this comment is as it was; I made no changes here
public function connect(){
try{
$this->conn = new PDO($this->dbstr); // Used with jndi string
} catch (PDOException $e){
// print $e->getMessage();
}
return "";
}
public function insert($query, $data){
try{
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
/* Execute a prepared statement by passing an array of values */
$sth = $this->conn->prepare($query, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$count = $sth->execute($data);
return $this->oracleLastInsertId($query);
}catch(PDOException $e){
throw new Exception($e->getMessage());
}
}
public function oracleLastInsertId($sqlQuery){
// Checks if query is an insert and gets table name
if( preg_match("/^INSERT[tn ] INTO[tn ] ([a-z0-9_-] )/is", $sqlQuery, $tablename) ){
// Gets this table's last sequence value
$query = "select ".$tablename[1]."_SEQ.currval AS last_value from dual";
try{
$temp_q_id = $this->conn->prepare($query);
$temp_q_id->execute();
if($temp_q_id){
$temp_result = $temp_q_id->fetch(PDO::FETCH_ASSOC);
return ( $temp_result ) ? $temp_result['LAST_VALUE'] : false;
}
}catch(Exception $err){
throw new Exception($err->getMessage());
}
}
return false;
}
public function close(){
$this->conn = null;
}
}