Nel secondo modo però dovresti perdere lo stack trace della prima eccezione, nel senso che lanciando la CategoryModelException la DbException si perde perchè intercettata dal blocco try del metodo insert.

Nel caso della insert il problema non ti si dovrebbe porre, in quanto è solo il metodo query che lancia la DbException (escape non penso lanci eccezioni ), non so poi in altri casi. In un codice più complesso potresti perdere la provenienza dell' eccezione DbException.
Potresti in questo caso specializzare le eccezioni (tipo CategoryModelException) overloadando il costruttore dell' eccezione in modo che gli passi come parametro anche lo stack trace.

Codice PHP:
public function insert($title,$description){
        try{
            
$querysprintf("INSERT INTO categories (category_ID, category_title, category_description) VALUES ('12','%s','%s');",
            
$this->db->escape($title),$this->db->escape($description));
            
$this->db->query($query);
        }
        catch(
DbException $e){
            throw new 
CategoryModelException($e->getTrace(), $e->getMessage().' in class [b]['.__CLASS__.'][/b]');
        }
    } 
la classe CategoryModelException
Codice PHP:
class CategoryModelException extends Exceptions{
        private 
$prevTrace;
        public 
__construct($trace=array(), $message null$code=0){
                
$this->prevStrace $trace;
                
parent:__construct($message$code);
        }

        public 
getPrevTrace(){
                return 
$this->prevTrace;
        }

In questo modo (ovviamente puoi riportarti tutti i valori che vuoi nell' eccezione) puoi mantenere traccia dell' eccezione padre.

Ciao