Changeset 256

Show
Ignore:
Timestamp:
02/11/07 12:33:23 (1 year ago)
Author:
mikey
Message:

Stubbles Database Persistence API (still experimental):

  • reworked serializer
  • added finder
  • moved querybuilder package one down, is now part of rdbms instead of rdbms.persistence

for example usage see experiments/people/mikey/persistence

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/experiments/people/mikey/persistence/MyNewsArticle.php

    r195 r256  
    1212     * @var  int 
    1313     */ 
    14     protected $id
     14    protected $id           = null
    1515    /** 
    1616     * switch whether news is stored persistent or not 
     
    5454     * @var  int 
    5555     */ 
    56     protected $lastChanged  = 0
     56    protected $lastChanged  = null
    5757    /** 
    5858     * status of article 
     
    6060     * @var  int 
    6161     */ 
    62     protected $status       = 0
     62    protected $status       = null
    6363     
    6464    /** 
     
    217217    public function setStatus($status) 
    218218    { 
    219         $this->lastChanged = $status; 
     219        $this->status = $status; 
    220220    } 
    221221     
     
    256256        return $this->isPersistent; 
    257257    } 
     258     
     259    /** 
     260     * returns a list of all instances with status $status 
     261     * 
     262     * @param   stubDatabaseFinder  $finder  the finder to use 
     263     * @param   int                 $status  status that the instances must have 
     264     * @return  array<MyNewsArticle> 
     265     */ 
     266    public static function findByStatus(stubDatabaseFinder $finder, $status) 
     267    { 
     268        $equalCriterion = new stubEqualCriterion('status', $status, 'news'); 
     269        return $finder->findByCriterion($equalCriterion, __CLASS__); 
     270    } 
    258271} 
    259272?> 
  • trunk/experiments/people/mikey/persistence/createTable.php

    r196 r256  
    44stubClassLoader::load('net.stubbles.rdbms.rdbms', 
    55                      'net.stubbles.rdbms.persistence.stubPersistable', 
    6                       'net.stubbles.rdbms.persistence.annotations.DBTable', 
    7                       'net.stubbles.rdbms.persistence.annotations.DBColumn', 
    8                       'net.stubbles.rdbms.persistence.querybuilder.stubDatabaseMySQLQueryBuilder', 
    9                       'net.stubbles.reflection.stubReflectionClass' 
     6                      'net.stubbles.rdbms.persistence.querybuilder.stubDatabaseQueryBuilderFactory' 
    107); 
    118require_once 'MyNewsArticle.php'; 
     9require_once 'connection.php'; 
    1210 
    13 $refClass = new stubReflectionClass('MyNewsArticle'); 
    14 if ($refClass->hasAnnotation('DBTable') == false) { 
    15     throw new stubException('Class ' . $refClass->getName() . ' is missing the DBTable annotation.'); 
    16 
    17  
    18 $query       = 'CREATE TABLE `' . $refClass->getAnnotation('DBTable')->getName() . "` (\n"; 
    19 $methods     = $refClass->getMethods(); 
    20 $primaryKeys = array(); 
    21 $keys        = array(); 
    22 foreach ($methods as $method) { 
    23     if ($method->hasAnnotation('DBColumn') == false || $method->getDeclaringClass()->equals($refClass) == false) { 
    24         continue; 
    25     } 
    26      
    27     $dbColumn = $method->getAnnotation('DBColumn'); 
    28     $query   .= '  ' . $dbColumn->getName() . ' ' . $dbColumn->getType(); 
    29     if (strtoupper($dbColumn->getType()) != 'TEXT') { 
    30         $query   .= '(' . str_replace('_', ',', $dbColumn->getSize()) . ')'; 
    31     } 
    32      
    33     if ($dbColumn->isUnsigned() == true) { 
    34         $query .= ' UNSIGNED'; 
    35     } 
    36      
    37     if ($dbColumn->isNullable() == true && $dbColumn->getDefaultValue() == null) { 
    38         $query .= ' DEFAULT NULL'; 
    39     } else { 
    40         if ($dbColumn->isNullable() == false) { 
    41             $query .= ' NOT NULL'; 
    42         } 
    43          
    44         if ($dbColumn->getDefaultValue() !== null) { 
    45             $query .= " DEFAULT '" . $dbColumn->getDefaultValue() . "'"; 
    46         } 
    47     } 
    48      
    49     if ($dbColumn->isPrimaryKey() == true) { 
    50         $query        .= ' AUTO_INCREMENT'; 
    51         $primaryKeys[] = $dbColumn->getName(); 
    52     } elseif ($dbColumn->isKey() == true) { 
    53         $keys[] = $dbColumn->getName(); 
    54     } 
    55      
    56     $query .= ",\n"; 
    57 
    58 if (count($primaryKeys) > 0) { 
    59     if (count($primaryKeys) > 1) { 
    60         $query = str_replace(' AUTO_INCREMENT', '', $query); 
    61     } 
    62      
    63     $query .= '  PRIMARY KEY (`' . join('`, `', $primaryKeys) . '`)'; 
    64 
    65 if (count($keys) > 0) { 
    66     foreach ($keys as $key) { 
    67         $query .= ",\n  KEY (`" . $key . '`)'; 
    68     } 
    69 
    70 $query .= "\n) TYPE=" . $refClass->getAnnotation('DBTable')->getType(); 
    71 echo $query; 
     11$connection       = stubDatabaseConnectionPool::getConnection(); 
     12$queryBuilder     = stubDatabaseQueryBuilderFactory::create($connection); 
     13var_dump($connection->exec($queryBuilder->createTable('MyNewsArticle'))); 
    7214?> 
  • trunk/src/main/php/net/stubbles/rdbms/persistence/EXPERIMENTAL

    r182 r256  
    1 Warning: The stuff in this package is highly experimental. It just contains  
    2 thoughts about how this could look like. Neither has it been tested nor  
    3 executed. 
     1Warning: The stuff in this package is experimental. Use at your own risk. 
     2For examples how it can be used see experiments/people/mikey/persistence. 
  • trunk/src/main/php/net/stubbles/rdbms/persistence/serializer/stubDatabaseSerializer.php

    r182 r256  
    55 * @author      Frank Kleine <mikey@stubbles.net> 
    66 * @package     stubbles 
    7  * @subpackage  rdbms_persistence 
     7 * @subpackage  rdbms_persistence_serializer 
    88 */ 
    99stubClassLoader::load('net.stubbles.rdbms.stubDatabaseConnection', 
    10                       'net.stubbles.rdbms.querybuilder.stubDatabaseQueryBuilder', 
     10                      'net.stubbles.rdbms.criteria.stubCriterion', 
     11                      'net.stubbles.rdbms.criteria.stubAndCriterion', 
     12                      'net.stubbles.rdbms.criteria.stubEqualCriterion', 
    1113                      'net.stubbles.rdbms.persistence.stubPersistable', 
    12                       'net.stubbles.rdbms.persistence.stubDatabaseSerializedValue', 
    13                       'net.stubbles.rdbms.persistence.annotations.DBTable', 
    14                       'net.stubbles.rdbms.persistence.annotations.DBColumn' 
     14                      'net.stubbles.rdbms.persistence.stubSetterMethodFactory', 
     15                      'net.stubbles.rdbms.persistence.annotations.stubDBTableAnnotation', 
     16                      'net.stubbles.rdbms.persistence.annotations.stubDBColumnAnnotation', 
     17                      'net.stubbles.rdbms.persistence.serializer.stubDatabaseSerializerException', 
     18                      'net.stubbles.rdbms.querybuilder.stubDatabaseQueryBuilderFactory' 
    1519); 
    1620/** 
     
    1822 * 
    1923 * @package     stubbles 
    20  * @subpackage  rdbms_persistence 
     24 * @subpackage  rdbms_persistence_serializer 
    2125 */ 
    2226class stubDatabaseSerializer extends stubBaseObject 
    2327{ 
     28    /** 
     29     * list of serializer instances 
     30     * 
     31     * @var  array<string,stubDatabaseSerializer> 
     32     */ 
     33    protected static $instances = array(); 
    2434    /** 
    2535     * the connection to use for making the object persistent 
     
    2838     */ 
    2939    protected $connection; 
    30     /** 
    31      * the querybuilder to use 
    32      * 
    33      * @var  stubDatabaseQueryBuilder 
    34      */ 
    35     protected $queryBuilder; 
    36     /** 
    37      * information about the single primary key 
    38      * 
    39      * @var  array 
    40      */ 
    41     protected $singlePrimaryKey = null; 
    4240     
    4341    /** 
     
    4543     * 
    4644     * @param  stubDatabaseConnection    $connection 
    47      * @param  stubDatabaseQueryBuilder  $queryBuilder 
    4845     */ 
    49     public function __construct(stubDatabaseConnection $connection, stubDatabaseQueryBuilder $queryBuilder
     46    protected function __construct(stubDatabaseConnection $connection
    5047    { 
    5148        $this->connection   = $connection; 
    52         $this->queryBuilder = $queryBuilder; 
    5349    } 
    5450     
    5551    /** 
    56      * takes a stubPersistable and serializes it into a stubDatabaseSerializedValue 
     52     * method to return instances of the finder depending of the connection 
     53     *  
     54     * Because the finder itself is stateless and only bound to the connection  
     55     * this factory methods prevents that a finder for a specific connection is 
     56     * created more than once. 
    5757     * 
    58      * @param   stubPersistable                  $stubObject 
    59      * @return  stubDatabaseSerializedValue 
    60      * @throws  stubDatabaseSerializerException 
     58     * @param   stubDatabaseConnection  $connection  connection to use for finding the data 
     59     * @return  stubDatabaseFinder 
    6160     */ 
    62     public function serialize(stubPersistable $stubPersistable
     61    public function getInstance(stubDatabaseConnection $connection
    6362    { 
    64         $refClass  = $stubPersistable->getClass(); 
    65         $baseTable = null; 
    66         if ($refClass->hasAnnotation('DBTable') == true) { 
    67             $baseTable = $refClass->getAnnotation('DBTable')->getName(); 
     63        if (isset(self::$instances[$connection->hashCode()]) == false) { 
     64            self::$instances[$connection->hashCode()] = new self($connection); 
    6865        } 
    6966         
    70         $serializedValue  = new stubDatabaseSerializedValue(); 
    71         $this->processProperties($refClass->getProperties(), $baseTable, $stubPersistable, $serializedValue); 
    72         $this->processMethods($refClass->getMethods(), $baseTable, $stubPersistable, $serializedValue); 
    73          
    74  
    75         try { 
    76             $this->processQueries($this->getQueries($serializedValue, $stubPersistable->isPersistent())); 
    77         } catch (stubDatabaseException $dbe) { 
    78             throw new stubDatabaseSerializerException('Can not persist ' . $stubPersistable->getClassName() . ': a database error occured.', $dbe); 
    79         } 
     67        return self::$instances[$connection->hashCode()]; 
    8068    } 
    8169     
    8270    /** 
    83      * process the properties of the persistable object 
     71     * cloning is forbidden 
    8472     * 
    85      * @param  array<stubReflectionProperty>  $refProperties 
    86      * @param  string                         $baseTable 
    87      * @param  stubPersistable                $stubPersistable 
    88      * @param  stubDatabaseSerializedValue    $serializedValue 
     73     * @throws  stubDatabaseFinderException 
    8974     */ 
    90     protected function processProperties(array $refProperties, $baseTable, stubPersistable $stubPersistable, stubDatabaseSerializedValue $serializedValue
     75    protected final function __clone(
    9176    { 
    92         foreach ($refProperties as $refProperty) { 
    93             if ($refProperty->isPublic() == false || $refProperty->hasAnnotation('DBColumn') == false) { 
     77        throw new stubDatabaseFinderException('Cloning ' . $this->getClassName() . ' is not allowed.'); 
     78    } 
     79     
     80    /** 
     81     * takes a stubPersistable and serializes it into the database 
     82     * 
     83     * @param   stubPersistable                  $persistable 
     84     * @throws  stubDatabaseSerializerException 
     85     * @throws  stubPersistenceException 
     86     */ 
     87    public function serialize(stubPersistable $persistable) 
     88    { 
     89        $refObject = $persistable->getClass(); 
     90        if ($refObject->hasAnnotation('DBTable') == false) { 
     91            throw new stubDatabaseQueryBuilderException('No database table defined for  ' . $refObject->getFullQualifiedClassName() . ', class is missing the DBTable annotation.'); 
     92        } 
     93         
     94        $baseTableName    = $refObject->getAnnotation('DBTable')->getName(); 
     95        $methods          = $refObject->getMethods(); 
     96        $serializedValue  = array(); 
     97        $singlePrimaryKey = null; 
     98        foreach ($methods as $method) { 
     99            if ($method->isPublic() == false || $method->hasAnnotation('DBColumn') == false) { 
    94100                continue; 
    95101            } 
    96102             
    97             $table = $baseTable; 
    98             if (null === $table) { 
    99                 if ($refProperty->hasAnnotation('DBTable') == false) { 
    100                     throw new stubDatabaseSerializerException('No database table defined for property ' . $stubPersistable->getClassName() . '::' . $refProperty->getName()); 
     103            $tableName = (($method->hasAnnotation('DBTable') == true) ? ($method->getAnnotation('DBTable')->getName()) : ($baseTableName)); 
     104            if (isset($serializedValue[$tableName]) == false) { 
     105                $serializedValue[$tableName] = array('criterion' => new stubAndCriterion(), 'columns' => array()); 
     106            } 
     107             
     108            try { 
     109                $value = $method->invoke($persistable); 
     110            } catch (ReflectionException $re) { 
     111                throw new stubDatabaseSerializerException('Can not get return value of ' . $refObject->getFullQualifiedClassName() . '::' . $method->getName() . '(), invokation failed.', $re); 
     112            } 
     113             
     114            $dbColumn = $method->getAnnotation('DBColumn'); 
     115            if (null === $value && $dbColumn->isPrimaryKey() == true) { 
     116                if ($persistable->isPersistent() == true) { 
     117                    throw new stubDatabaseSerializerException('Data inconsistent: persistable object is already persistent, but the primary key ' . $refObject->getFullQualifiedClassName() . '::' . $method->getName() . '() is null.'); 
    101118                } 
    102119                 
    103                 $table = $refProperty->getAnnotation('DBTable')->getName(); 
     120                if (null !== $singlePrimaryKey) { 
     121                    throw new stubDatabaseSerializerException('Persistence error: only one primary key can be null, but at least two primary keys are null: ' . $singlePrimaryKey['propertyName'] . ' and ' . $method->getName()); 
     122                } 
     123                 
     124                $singlePrimaryKey = array('propertyName' => $method->getName(), 
     125                                          'columnName'   => $dbColumn->getName(), 
     126                                          'tableName'    => $tableName 
     127                                    ); 
     128                continue; 
     129            } elseif (null !== $value && $dbColumn->isPrimaryKey() == true) { 
     130                $serializedValue[$tableName]['criterion']->addCriterion(new stubEqualCriterion($dbColumn->getName(), $value, $tableName)); 
     131            } elseif (null === $value && $persistable->isPersistent() == false) { 
     132                $value = $dbColumn->getDefaultValue(); 
     133                $setterMethod = stubSetterMethodFactory::create($dbColumn, $refObject, $method->getName()); 
     134                 
     135                $setterMethod->invoke($persistable, $value); 
    104136            } 
    105137             
    106             $value = $refProperty->getValue($stubPersistable); 
    107             if (null == $value && $refProperty->getAnnotation('DBColumn')->isPrimaryKey() == true) { 
    108                 if ($stubPersistable->isPersistent() == true) { 
    109                     throw new stubDatabaseSerializerException('Data inconsistent: persistable object is already persistent, but the primary key ' . $stubPersistable->getClassName() . '::' . $refProperty->getName() . ' is null.'); 
    110                 } 
    111                  
    112                 $this->setSinglePrimaryKey($refProperty->getName(), $refProperty->getAnnotation('DBColumn')->getName(), $table); 
    113             } 
    114              
    115             $serializedValue->setColumnValue($table, $refProperty->getAnnotation('DBColumn')->getName(), $value, $refProperty->getAnnotation('DBColumn')->isPrimaryKey()); 
     138            $serializedValue[$tableName]['columns'][$dbColumn->getName()] = $value; 
    116139        } 
     140         
     141        try { 
     142            $this->processQueries($this->getQueries($serializedValue, $persistable->isPersistent()), $persistable, $singlePrimaryKey); 
     143        } catch (stubDatabaseException $dbe) { 
     144            throw new stubDatabaseSerializerException('Can not persist ' . $refObject->getFullQualifiedClassName() . ': a database error occured.', $dbe); 
     145        } 
     146         
     147        $persistable->setPersistent(true); 
    117148    } 
    118149 
    119150    /** 
    120      * process the methods of the persistable object 
    121      * 
    122      * @param array <stubReflectionMethod>  $refMethods 
    123      * @param string                        $baseTable 
    124      * @param stubPersistable               $stubPersistable 
    125      * @param stubDatabaseSerializedValue   $serializedValue 
    126      */ 
    127     protected function processMethods(array $refMethods, $baseTable, stubPersistable $stubPersistable, stubDatabaseSerializedValue $serializedValue) 
    128     { 
    129         foreach ($refMethods as $refMethod) { 
    130             if ($refMethod->isPublic() == false || $refMethod->hasAnnotation('DBColumn') == false) { 
    131                 continue; 
    132             } 
    133              
    134             $table = $baseTable; 
    135             if (null === $table) { 
    136                 if ($refMethod->hasAnnotation('DBTable') == false) { 
    137                     throw new stubDatabaseSerializerException('No database table defined for return value of ' . $stubPersistable->getClassName() . '::' . $refMethod->getName() . '()'); 
    138                 } 
    139                  
    140                 $table = $refMethod->getAnnotation('DBTable')->getName(); 
    141             } 
    142              
    143             try { 
    144                 $value = $refMethod->invoke($stubPersistable); 
    145             } catch (ReflectionException $re) { 
    146                 throw new stubDatabaseSerializerException('Can not get return value of ' . $stubPersistable->getClassName() . '::' . $refMethod->getName() . '(), invokation failed.', $re); 
    147             } 
    148              
    149             if (null == $value && $refMethod->getAnnotation('DBColumn')->isPrimaryKey() == true) { 
    150                 if ($stubPersistable->isPersistent() == true) { 
    151                     throw new stubDatabaseSerializerException('Data inconsistent: persistable object is already persistent, but the primary key ' . $stubPersistable->getClassName() . '::' . $refMethod->getName() . '() is null.'); 
    152                 } 
    153                  
    154                 $this->setSinglePrimaryKey($refMethod->getName(), $refMethod->getAnnotation('DBColumn')->getName(), $table); 
    155             } 
    156              
    157             $serializedValue->setColumnValue($table, $refMethod->getAnnotation('DBColumn')->getName(), $value, $refMethod->getAnnotation('DBColumn')->isPrimaryKey()); 
    158         } 
    159     } 
    160      
    161     /** 
    162      * sets the single primary key which may be null 
    163      * 
    164      * @param  string  $propertyName  name of the property or method 
    165      * @param  string  $columnName    name of the column where primary key is stored 
    166      * @param  string  $tableName     name of the table where primary key is stored 
    167      */ 
    168     protected function setSinglePrimaryKey($propertyName, $columnName, $tableName) 
    169     { 
    170         if (null != $this->singlePrimaryKey) { 
    171             throw new stubDatabaseSerializerException('Persistence error: only one primary key can be null, but at least two primary keys are null: ' . $$this->singlePrimaryKey['propertyName'] . ' and ' . $propertyName); 
    172         } 
    173  
    174         $this->singlePrimaryKey = array('propertyName' => $propertyName, 
    175                                         'columnName'   => $columnName, 
    176                                         'tableName'    => $tableName 
    177                                   ); 
    178     } 
    179      
    180     /** 
    181151     * build the queries out of the serialized value 
    182152     * 
    183      * @param   stubDatabaseSerializedValue  $value 
    184      * @param   bool                         $isPersistent 
    185      * @return  array<string,string>         tableName => query 
     153     * @param   array                     $values 
     154     * @param   bool                      $isPersistent 
     155     * @return  array(tableName => query) 
    186156     */ 
    187     protected function getQueries(stubDatabaseSerializedValue $value, $isPersistent) 
     157    protected function getQueries(array $values, $isPersistent) 
    188158    { 
     159        $queryBuilder = stubDatabaseQueryBuilderFactory::create($this->connection); 
    189160        if (true == $isPersistent) { 
    190             return $this->queryBuilder->createUpdate($value); 
     161            return $queryBuilder->createUpdate($values); 
    191162        } 
    192163         
    193         return $this->queryBuilder->createInsert($value); 
     164        return $queryBuilder->createInsert($values); 
    194165    } 
    195166     
     
    197168     * process the queries 
    198169     * 
    199      * @param   array<string,string>  $queries  tableName => query 
     170     * @param   array(tableName => query)  $queries           list of queries to process 
     171     * @param   stubPersistable            $persistable       the persistable object 
     172     * @param   array                      $singlePrimaryKey  information about the single primary key 
    200173     * @throws  stubDatabaseException 
    201174     */ 
    202     protected function processQueries(array $queries, stubPersistable $stubPersistable
     175    protected function processQueries(array $queries, stubPersistable $persistable, array $singlePrimaryKey = null
    203176    { 
    204177        foreach ($queries as $tableName => $query) { 
    205178            $this->connection->exec($query); 
    206             if (null != $this->singlePrimaryKey && $this->singlePrimaryKey['table'] == $tableName) { 
    207                 $stubPersistable->setId($this->connection->getLastInsertID()); 
     179            if (null !== $singlePrimaryKey && $singlePrimaryKey['tableName'] == $tableName) { 
     180                $persistable->setId($this->connection->getLastInsertID()); 
    208181            } 
    209182        } 
    210183    } 
     184     
     185    /** 
     186     * returns a unique hash code for the class 
     187     *  
     188     * Two serializers are equal if they use the same connection. 
     189     * 
     190     * @return  string 
     191     */ 
     192    public function hashCode() 
     193    { 
     194        return 'serializer:' . $this->connection->hashCode(); 
     195    } 
    211196} 
    212197?> 
  • trunk/src/main/php/net/stubbles/rdbms/querybuilder/stubDatabaseMySQLQueryBuilder.php

    r195 r256  
    55 * @author      Frank Kleine <mikey@stubbles.net> 
    66 * @package     stubbles 
    7  * @subpackage  rdbms_persistence_querybuilder 
     7 * @subpackage  rdbms_querybuilder 
    88 */ 
    9 stubClassLoader::load('net.stubbles.rdbms.persistence.querybuilder.stubDatabaseQueryBuilder'); 
     9stubClassLoader::load('net.stubbles.rdbms.querybuilder.stubDatabaseQueryBuilder'); 
    1010/** 
    1111 * Class for creating MySQL specific queries. 
    1212 * 
    1313 * @package     stubbles 
    14  * @subpackage  rdbms_persistence_querybuilder 
     14 * @subpackage  rdbms_querybuilder 
    1515 */ 
    1616class stubDatabaseMySQLQueryBuilder extends stubBaseObject implements stubDatabaseQueryBuilder 
    1717{ 
    1818    /** 
     19     * creates a select query 
     20     * 
     21     * @param   stubCriterion   $criterion  condition for query 
     22     * @param   array           $tables     list of tables to use in query 
     23     * @return  string 
     24     */ 
     25    public function createSelect(stubCriterion $criterion, array $tables) 
     26    { 
     27        return 'SELECT * FROM `' . join('`, `', $tables) . '` WHERE ' . $criterion->toSQL(); 
     28    } 
     29     
     30    /** 
    1931     * creates insert queries from a serialized value 
    2032     * 
    21      * @param   stubDatabaseSerializedValue  $value 
    22      * @return  array<string,string>         tableName => query 
     33     * @param   array(tableName => array(columns => array(columnName => columnValue)))  $values 
     34     * @return  array(tableName => query) 
    2335     */ 
    24     public function createInsert(stubDatabaseSerializedValue $value
     36    public function createInsert(array $values
    2537    { 
    2638        $queries    = array(); 
    27         $tableNames = $value->getTableNames(); 
     39        $tableNames = array_keys($values); 
    2840        foreach ($tableNames as $tableName) { 
    29             $columns = $value->getColumnsForTable($tableName); 
    30             $queries[$tableName] = 'INSERT INTO `' . $tableName . '` (`' . join('`, `', array_keys($columns)) . "`) VALUES ('" . join("', '", array_values($columns)) . "')"; 
     41            $queries[$tableName] = 'INSERT INTO `' . $tableName . '` (`' . join('`, `', array_keys($values[$tableName]['columns'])) . '`) VALUES ('; 
     42            $counter = 0; 
     43            foreach ($values[$tableName]['columns'] as $columnValue) { 
     44                if (0 < $counter) { 
     45                    $queries[$tableName] .= ', '; 
     46                } 
     47                 
     48                if (null === $columnValue) { 
     49                    $queries[$tableName] .= 'NULL'; 
     50                } else { 
     51                    $queries[$tableName] .= "'" . $columnValue . "'"; 
     52                } 
     53                 
     54                $counter++; 
     55            } 
     56             
     57            $queries[$tableName] .= ')'; 
    3158        } 
    3259         
     
    3764     * creates update queries from a serialized value 
    3865     * 
    39      * @param   stubDatabaseSerializedValue  $value 
    40      * @return  array<string,string>         tableName => query 
     66     * @param   array(tableName => array(criterion => stubCrition, columns => array(columnName => columnValue)))  $values 
     67     * @return  array(tableName => query) 
    4168     */ 
    42     public function createUpdate(stubDatabaseSerializedValue $value
     69    public function createUpdate(array $values
    4370    { 
    4471        $queries    = array(); 
    45         $tableNames = $value->getTableNames(); 
     72        $tableNames = array_keys($values); 
    4673        foreach ($tableNames as $tableName) { 
    47             $columns = $value->getColumnsForTable($tableName); 
    4874            $query   = 'UPDATE `' . $tableName . '` SET '; 
    4975            $where   = array(); 
    5076            $counter = 0; 
    51             foreach ($columns as $columnName => $columnValue) { 
     77            foreach ($values[$tableName]['columns'] as $columnName => $columnValue) { 
    5278                if (0 < $counter) { 
    5379                    $query .= ', '; 
    5480                } 
    5581                 
    56                 $query .= '`' . $columnName . "` = '" . $columnValue . "'"; 
     82                $query .= '`' . $columnName . '` = '; 
     83                if (null === $columnValue) { 
     84                    $query .= 'NULL'; 
     85                } else { 
     86                    $query .= "'" . $columnValue . "'"; 
     87                } 
    5788                $counter++; 
    58                  
    59                 if ($value->isPrimaryKey($tableName, $columnName) == true) { 
    60                     $where[] = '`' . $columnName . "` = '" . $columnValue . "'"; 
    61                 } 
    6289            } 
    6390             
    64             $queries[$tableName] = $query . ' WHERE ' . join(' AND ', $where); 
     91            $queries[$tableName] = $query . ' WHERE ' . $values[$tableName]['criterion']->toSQL(); 
    6592        } 
    6693         
    6794        return $queries; 
    6895    } 
     96     
     97    /** 
     98     * creates the query to create a table for the given class 
     99     * 
     100     * @param   string  $fqClassName  the full qualified classname of the class to create the table for 
     101     * @throws  stubDatabaseQueryBuilderException 
     102     * @todo    put all reflection related stuff to persistence package,  
     103     *          introduce a class that represents a table description 
     104     */ 
     105    public function createTable($fqClassName) 
     106    { 
     107        $nqClassName = stubClassLoader::getNonQualifiedClassName($fqClassName); 
     108        if (class_exists($nqClassName, false) == false) { 
     109            stubClassLoader::load($fqClassName); 
     110        } 
     111         
     112        $refClass = new stubReflectionClass($nqClassName); 
     113        if ($refClass->hasAnnotation('DBTable') == false) { 
     114            throw new stubDatabaseQueryBuilderException('Class ' . $refClass->getName() . ' is missing the DBTable annotation.'); 
     115        } 
     116         
     117        $dbTable     = $refClass->getAnnotation('DBTable'); 
     118        $tableName   = $dbTable->getName(); 
     119        $query       = 'CREATE TABLE `' . $tableName . "` (\n"; 
     120        $methods     = $refClass->getMethods(); 
     121        $primaryKeys = array(); 
     122        $keys        = array(); 
     123        foreach ($methods as $method) { 
     124            if ($method->hasAnnotation('DBColumn') == false || $method->getDeclaringClass()->equals($refClass) == false) { 
     125                continue; 
     126            } elseif ($method->hasAnnotation('DBTable') == true && $method->getAnnotation('DBTable')->getName() != $tableName) { 
     127                continue; 
     128            } 
     129             
     130            $dbColumn = $method->getAnnotation('DBColumn'); 
     131            $query   .= '  ' . $dbColumn->getName() . ' ' . $dbColumn->getType(); 
     132            if (strtoupper($dbColumn->getType()) != 'TEXT') { 
     133                $query   .= '(' . str_replace('_', ',', $dbColumn->getSize()) . ')'; 
     134            } 
     135             
     136            if ($dbColumn->isUnsigned() == true) { 
     137                $query .= ' UNSIGNED'; 
     138            } 
     139             
     140            if ($dbColumn->isNullable() == true && $dbColumn->getDefaultValue() == null) { 
     141                $query .= ' DEFAULT NULL'; 
     142            } else { 
     143                if ($dbColumn->isNullable() == false) { 
     144                    $query .= ' NOT NULL'; 
     145                } 
     146                 
     147                if ($dbColumn->getDefaultValue() !== null) { 
     148                    $query .= " DEFAULT '" . $dbColumn->getDefaultValue() . "'"; 
     149                } 
     150            } 
     151             
     152            if ($dbColumn->isPrimaryKey() == true) { 
     153                $query        .= ' AUTO_INCREMENT'; 
     154                $primaryKeys[] = $dbColumn->getName(); 
     155            } elseif ($dbColumn->isKey() == true) { 
     156                $keys[] = $dbColumn->getName(); 
     157            } 
     158             
     159            $query .= ",\n"; 
     160        } 
     161        if (count($primaryKeys) > 0) { 
     162            if (count($primaryKeys) > 1) { 
     163                $query = str_replace(' AUTO_INCREMENT', '', $query); 
     164            } 
     165             
     166            $query .= '  PRIMARY KEY (`' . join('`, `', $primaryKeys) . '`)'; 
     167        } 
     168        if (count($keys) > 0) { 
     169            foreach ($keys as $key) { 
     170                $query .= ",\n  KEY (`" . $key . '`)'; 
     171            } 
     172        } 
     173         
     174        $query .= "\n) TYPE=" . $dbTable->getType(); 
     175        if ($dbTable->hasCharacterSet() == true) { 
     176            $query .= ' CHARACTER SET=' . $dbTable->getCharacterSet(); 
     177        } 
     178         
     179        if ($dbTable->hasCollation() == true) { 
     180            $query .= ' COLLATE=' . $dbTable->getCollation(); 
     181        } 
     182         
     183        if ($dbTable->hasComment() == true) { 
     184            $query .= ' COMMENT=' . $dbTable->getComment(); 
     185        } 
     186         
     187        return $query; 
     188    } 
    69189} 
    70190?> 
  • trunk/src/main/php/net/stubbles/rdbms/querybuilder/stubDatabaseQueryBuilder.php

    r182 r256  
    55 * @author      Frank Kleine <mikey@stubbles.net> 
    66 * @package     stubbles 
    7  * @subpackage  rdbms_persistence_querybuilder 
     7 * @subpackage  rdbms_querybuilder 
    88 */ 
    9 stubClassLoader::load('net.stubbles.rdbms.persistence.stubDatabaseSerializedValue'); 
     9stubClassLoader::load('net.stubbles.rdbms.criteria.stubCriterion', 
     10                      'net.stubbles.rdbms.querybuilder.stubDatabaseQueryBuilderException', 
     11                      'net.stubbles.rdbms.persistence.annotations.stubDBTableAnnotation', 
     12                      'net.stubbles.rdbms.persistence.annotations.stubDBColumnAnnotation', 
     13                      'net.stubbles.reflection.reflection' 
     14); 
    1015/** 
    1116 * Interface for database specific query builders. 
    1217 * 
    1318 * @package     stubbles 
    14  * @subpackage  rdbms_persistence_querybuilder 
     19 * @subpackage  rdbms_querybuilder 
    1520 */ 
    1621interface stubDatabaseQueryBuilder 
    1722{ 
    1823    /** 
     24     * creates a select query 
     25     * 
     26     * @param   stubCriterion   $criterion  condition for query 
     27     * @param   array           $tables     list of tables to use in query 
     28     * @return  string 
     29     */ 
     30    public function createSelect(stubCriterion $criterion, array $tables); 
     31     
     32    /** 
    1933     * creates insert queries from a serialized value 
    2034     * 
    21      * @param   stubDatabaseSerializedValue  $value 
    22      * @return  array<string,string>         tableName => query 
     35     * @param   array(tableName => array(columns => array(columnName => columnValue)))  $values 
     36     * @return  array(tableName => query) 
    2337     */ 
    24     public function createInsert(stubDatabaseSerializedValue $value); 
     38    public function createInsert(array $values); 
    2539     
    2640    /** 
    2741     * creates update queries from a serialized value 
    2842     * 
    29      * @param   stubDatabaseSerializedValue  $value 
    30      * @return  array<string,string>         tableName => query 
     43     * @param   array(tableName => array(criterion => stubCrition, columns => array(columnName => columnValue)))  $values 
     44     * @return  array(tableName => query) 
    3145     */ 
    32     public function createUpdate(stubDatabaseSerializedValue $value); 
     46    public function createUpdate(array $values); 
     47     
     48    /** 
     49     * creates the query to create a table for the given class 
     50     * 
     51     * @param   string  $fqClassName  the full qualified classname of the class to create the table for 
     52     * @throws  stubDatabaseQueryBuilderException 
     53     */ 
     54    public function createTable($fqClassName); 
    3355} 
    3456?> 
  • trunk/src/main/php/net/stubbles/rdbms/stubDatabaseConnection.php

    r243 r256  
    1616 * @subpackage  rdbms 
    1717 */ 
    18 interface stubDatabaseConnection 
     18interface stubDatabaseConnection extends stubObject 
    1919{ 
    2020    /** 
  • trunk/src/main/php/net/stubbles/rdbms/stubDatabaseResult.php

    r250 r256  
    1414 * @subpackage  rdbms 
    1515 */ 
    16 interface stubDatabaseResult 
     16interface stubDatabaseResult extends stubObject 
    1717{ 
    1818    /** 
  • trunk/src/main/php/net/stubbles/rdbms/stubDatabaseStatement.php

    r250 r256  
    1414 * @subpackage  rdbms 
    1515 */ 
    16 interface stubDatabaseStatement 
     16interface stubDatabaseStatement extends stubObject 
    1717{ 
    1818    /**