PHP: управление пулами соединений в объектно-ориентированном подходе

#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; 
    } 
}