Changeset 1066

Show
Ignore:
Timestamp:
11/26/07 16:55:12 (11 months ago)
Author:
mikey
Message:

implemented enhancement #101: stubDatabaseSerializer must support direct insert/update

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/src/main/php/net/stubbles/rdbms/persistence/serializer/stubDatabaseSerializer.php

    r1051 r1066  
    8585 
    8686    /** 
    87      * takes an entity and serializes it into the database 
     87     * takes an entity and inserts it into the database 
    8888     * 
    8989     * @param   object                           $entity 
     
    9292     * @throws  stubPersistenceException 
    9393     */ 
    94     public function serialize($entity) 
     94    public function insert($entity) 
    9595    { 
    9696        if (is_object($entity) === false) { 
     
    103103        } 
    104104         
    105         $tableRow         = new stubDatabaseTableRow($this->getTableDescription($entityClass)->getName()); 
    106         $methods          = $entityClass->getMethods(); 
    107         $singlePrimaryKey = null; 
    108         $defaultValues    = array(); 
     105        $stuff = $this->processEntity($entityClass, $entity, self::INSERT); 
     106        try { 
     107            $this->processInsertQueries($this->getInsertQuery($stuff['tableRow'], $entity, $stuff['defaultValues']), $entity, array_shift($stuff['primaryKeys'])); 
     108        } catch (stubDatabaseException $dbe) { 
     109            throw new stubDatabaseSerializerException('Can not persist ' . $entityClass->getFullQualifiedClassName() . ': a database error occured.', $dbe); 
     110        } 
     111         
     112        return self::INSERT; 
     113    } 
     114 
     115    /** 
     116     * takes an entity and updates its database entry 
     117     * 
     118     * @param   object                           $entity 
     119     * @return  string 
     120     * @throws  stubDatabaseSerializerException 
     121     * @throws  stubPersistenceException 
     122     */ 
     123    public function update($entity) 
     124    { 
     125        if (is_object($entity) === false) { 
     126            throw new stubIllegalArgumentException('Can only serialize objects.'); 
     127        } 
     128         
     129        $entityClass = (($entity instanceof stubObject) ? ($entity->getClass()) : (new stubReflectionObject($entity))); 
     130        if ($entityClass->hasAnnotation('Entity') === false) { 
     131            throw new stubPersistenceException('Class ' . $entityClass->getFullQualifiedClassName() . ' is not an entity.'); 
     132        } 
     133         
     134        $stuff = $this->processEntity($entityClass, $entity, self::UPDATE); 
     135        try { 
     136            $this->processUpdateQueries($this->getUpdateQuery($stuff['tableRow'], $stuff['defaultValues'])); 
     137        } catch (stubDatabaseException $dbe) { 
     138            throw new stubDatabaseSerializerException('Can not persist ' . $entityClass->getFullQualifiedClassName() . ': a database error occured.', $dbe); 
     139        } 
     140             
     141        return self::UPDATE; 
     142    } 
     143 
     144    /** 
     145     * takes an entity and serializes it into the database 
     146     * 
     147     * @param   object                           $entity 
     148     * @return  string 
     149     * @throws  stubDatabaseSerializerException 
     150     * @throws  stubPersistenceException 
     151     */ 
     152    public function serialize($entity) 
     153    { 
     154        if (is_object($entity) === false) { 
     155            throw new stubIllegalArgumentException('Can only serialize objects.'); 
     156        } 
     157         
     158        $entityClass = (($entity instanceof stubObject) ? ($entity->getClass()) : (new stubReflectionObject($entity))); 
     159        if ($entityClass->hasAnnotation('Entity') === false) { 
     160            throw new stubPersistenceException('Class ' . $entityClass->getFullQualifiedClassName() . ' is not an entity.'); 
     161        } 
     162         
     163        $stuff = $this->processEntity($entityClass, $entity); 
     164        if (count($stuff['primaryKeys']) > 1) { 
     165            throw new stubDatabaseSerializerException('Persistence error for ' . $entityClass->getFullQualifiedClassName() . ': only one primary key can be null, but at least two primary keys are null: ' . join(', ', array_keys($stuff['primaryKeys']))); 
     166        } 
     167         
     168        if ($stuff['tableRow']->hasCriterion() === true) { 
     169            try { 
     170                $this->processUpdateQueries($this->getUpdateQuery($stuff['tableRow'], $stuff['defaultValues'])); 
     171            } catch (stubDatabaseException $dbe) { 
     172                throw new stubDatabaseSerializerException('Can not persist ' . $entityClass->getFullQualifiedClassName() . ': a database error occured.', $dbe); 
     173            } 
     174             
     175            return self::UPDATE; 
     176        } 
     177         
     178        try { 
     179            $this->processInsertQueries($this->getInsertQuery($stuff['tableRow'], $entity, $stuff['defaultValues']), $entity, array_shift($stuff['primaryKeys'])); 
     180        } catch (stubDatabaseException $dbe) { 
     181            throw new stubDatabaseSerializerException('Can not persist ' . $entityClass->getFullQualifiedClassName() . ': a database error occured.', $dbe); 
     182        } 
     183         
     184        return self::INSERT; 
     185    } 
     186 
     187    /** 
     188     * processes the entity: create another presentation of data 
     189     * 
     190     * @param   stubBaseReflectionClass  $entityClass 
     191     * @param   object                   $entity 
     192     * @param   string                   $type         optional 
     193     * @return  array 
     194     */ 
     195    protected function processEntity(stubBaseReflectionClass $entityClass, $entity, $type = null) 
     196    { 
     197        $tableRow      = new stubDatabaseTableRow($this->getTableDescription($entityClass)->getName()); 
     198        $methods       = $entityClass->getMethods(); 
     199        $primaryKeys   = array(); 
     200        $defaultValues = array(); 
    109201        foreach ($methods as $method) { 
    110202            $column = $this->getTableColumn($method); 
     
    120212             
    121213            if ($column->isPrimaryKey() === true) { 
    122                 if (null === $value) { 
    123                     if (null !== $singlePrimaryKey) { 
    124                         throw new stubDatabaseSerializerException('Persistence error for ' . $entityClass->getFullQualifiedClassName() . ': only one primary key can be null, but at least two primary keys are null: ' . $singlePrimaryKey['propertyName'] . ' and ' . $method->getName()); 
    125                     } 
    126                      
    127                     $singlePrimaryKey = array('setterMethod' => stubSetterMethodHelper::create($column, $entityClass, $method->getName()), 
    128                                               'tableName'    => $tableRow->getTableName() 
    129                                         ); 
    130                     continue; 
     214                if (null === $value && self::UPDATE === $type) { 
     215                    throw new stubDatabaseSerializerException('Persistence error for ' . $entityClass->getFullQualifiedClassName() . ': should be updated, but one primary key column is null: ' . $method->getName()); 
     216                } elseif (null === $value) { 
     217                    $primaryKeys[$method->getName()] = array('setterMethod' => stubSetterMethodHelper::create($column, $entityClass, $method->getName()), 
     218                                                             'tableName'    => $tableRow->getTableName() 
     219                                                       ); 
     220                } elseif (self::INSERT === $type) { 
     221                    $tableRow->setColumn($column->getName(), $value); 
    131222                } else { 
    132223                    $tableRow->addCriterion(new stubEqualCriterion($column->getName(), $value, $tableRow->getTableName())); 
     
    148239        } 
    149240         
    150         try { 
    151             $this->processQueries($this->getQuery($tableRow, $entity, $defaultValues), $entity, $singlePrimaryKey); 
    152         } catch (stubDatabaseException $dbe) { 
    153             throw new stubDatabaseSerializerException('Can not persist ' . $entityClass->getFullQualifiedClassName() . ': a database error occured.', $dbe); 
    154         } 
    155          
    156         return (($tableRow->hasCriterion() === true) ? (self::UPDATE) : (self::INSERT)); 
    157     } 
    158  
    159     /** 
    160      * build the query out of the serialized value 
     241        return array('tableRow'      => $tableRow, 
     242                     'defaultValues' => $defaultValues, 
     243                     'primaryKeys'   => $primaryKeys 
     244               ); 
     245    } 
     246 
     247    /** 
     248     * creates the queries required to process the insert 
    161249     * 
    162250     * @param   stubDatabaseTableRow  $tableRow 
     251     * @param   object                $entity 
    163252     * @param   array                 $defaultValues 
    164      * @return  array<string,string
    165      * @throws   
    166      */ 
    167     protected function getQuery(stubDatabaseTableRow $tableRow, $entity, array $defaultValues) 
     253     * @return  array<string
     254     * @throws  stubDatabaseSerializerException 
     255     */ 
     256    protected function getInsertQuery(stubDatabaseTableRow $tableRow, $entity, array $defaultValues) 
    168257    { 
    169258        $queryBuilder = stubDatabaseQueryBuilderFactory::create($this->connection); 
    170259        try { 
    171             // criterion for update exists => update 
    172             if ($tableRow->hasCriterion() === true) { 
    173                 // set any null values 
    174                 foreach ($defaultValues as $defaultValue) { 
    175                     $tableRow->setColumn($defaultValue['column'], $defaultValue['value']); 
    176                 } 
    177                  
    178                 return $queryBuilder->createUpdate(array($tableRow->getTableName() => $tableRow)); 
    179             } 
    180              
    181260            // fill default values into entity and table row 
    182261            foreach ($defaultValues as $defaultValue) { 
     
    192271 
    193272    /** 
    194      * process the queries 
     273     * creates the queries required to process the update 
     274     * 
     275     * @param   stubDatabaseTableRow  $tableRow 
     276     * @param   array                 $defaultValues 
     277     * @return  array<string> 
     278     * @throws  stubDatabaseSerializerException 
     279     */ 
     280    protected function getUpdateQuery(stubDatabaseTableRow $tableRow, array $defaultValues) 
     281    { 
     282        $queryBuilder = stubDatabaseQueryBuilderFactory::create($this->connection); 
     283        try { 
     284            foreach ($defaultValues as $defaultValue) { 
     285                $tableRow->setColumn($defaultValue['column'], $defaultValue['value']); 
     286            } 
     287             
     288            return $queryBuilder->createUpdate(array($tableRow->getTableName() => $tableRow)); 
     289        } catch (stubIllegalArgumentException $iae) { 
     290            throw new stubDatabaseSerializerException('Creating the queries failed.', $iae); 
     291        } 
     292    } 
     293 
     294    /** 
     295     * process insert queries 
    195296     * 
    196297     * @param   array<string,string>   $queries           list of queries to process 
     
    199300     * @throws  stubDatabaseException 
    200301     */ 
    201     protected function processQueries(array $queries, $entity, array $singlePrimaryKey = null) 
     302    protected function processInsertQueries(array $queries, $entity, array $singlePrimaryKey = null) 
    202303    { 
    203304        foreach ($queries as $tableName => $query) { 
     
    210311 
    211312    /** 
     313     * process update queries 
     314     * 
     315     * @param   array<string,string>   $queries           list of queries to process 
     316     * @param   object                 $entity            the entity to process the queries for 
     317     * @param   array<string,string>   $singlePrimaryKey  optional  information about the single primary key 
     318     * @throws  stubDatabaseException 
     319     */ 
     320    protected function processUpdateQueries(array $queries) 
     321    { 
     322        foreach ($queries as $tableName => $query) { 
     323            $this->connection->exec($query); 
     324        } 
     325    } 
     326 
     327    /** 
    212328     * returns a unique hash code for the class 
    213329     *  
  • trunk/src/test/php/net/stubbles/rdbms/persistence/serializer/stubDatabaseSerializerTestCase.php

    r997 r1066  
    100100     * check that a non-object throws an exception 
    101101     */ 
     102    public function testInsertNonObject() 
     103    { 
     104        $this->expectException('stubIllegalArgumentException'); 
     105        $this->dbSerializer->insert('foo'); 
     106    } 
     107 
     108    /** 
     109     * test that trying to find a class that does not have an entity annotation throws an exception 
     110     */ 
     111    public function testInsertNonEntity() 
     112    { 
     113        $this->expectException('stubPersistenceException'); 
     114        $this->dbSerializer->insert(new MockNoEntityAnnotationEntity()); 
     115    } 
     116 
     117    /** 
     118     * check that a non-object throws an exception 
     119     */ 
     120    public function testUpdateNonObject() 
     121    { 
     122        $this->expectException('stubIllegalArgumentException'); 
     123        $this->dbSerializer->update('foo'); 
     124    } 
     125 
     126    /** 
     127     * test that trying to find a class that does not have an entity annotation throws an exception 
     128     */ 
     129    public function testUpdateNonEntity() 
     130    { 
     131        $this->expectException('stubPersistenceException'); 
     132        $this->dbSerializer->update(new MockNoEntityAnnotationEntity()); 
     133    } 
     134 
     135    /** 
     136     * check that a non-object throws an exception 
     137     */ 
    102138    public function testSerializeNonObject() 
    103139    { 
     
    119155     */ 
    120156    public function testInsertWithSinglePrimaryKey() 
     157    { 
     158        $singlePrimaryKeyEntity = new MockSinglePrimaryKeyEntity(); 
     159        $this->mockConnection->setReturnValue('getLastInsertId', 'mockId'); 
     160        $this->mockConnection->expectCallcount('exec', 1); 
     161        $this->mockQueryBuilder->setInsertQueries(array('foo' => 'foo')); 
     162        $this->assertEqual($this->dbSerializer->insert($singlePrimaryKeyEntity), stubDatabaseSerializer::INSERT); 
     163        $this->assertEqual($this->mockQueryBuilder->getCallCount('createInsert'), 1); 
     164        $this->assertEqual($this->mockQueryBuilder->getCallCount('createUpdate'), 0); 
     165        $tableRows = $this->mockQueryBuilder->getInsertTableRows(); 
     166        $this->assertEqual(count($tableRows), 1); 
     167        $this->assertTrue(isset($tableRows['foo'])); 
     168        $this->assertEqual($singlePrimaryKeyEntity->getId(), 'mockId'); 
     169        $this->assertEqual($tableRows['foo']->getColumns(), array('bar' => 'this is bar', 'default' => 'example')); 
     170        $this->assertFalse($tableRows['foo']->hasCriterion()); 
     171    } 
     172 
     173    /** 
     174     * test insert with a single primary key 
     175     */ 
     176    public function testInsertWithSinglePrimaryKeyAlreadySet() 
     177    { 
     178        $singlePrimaryKeyEntity = new MockSinglePrimaryKeyEntity(); 
     179        $singlePrimaryKeyEntity->setId('mockId'); 
     180        $this->mockConnection->expectNever('getLastInsertId'); 
     181        $this->mockConnection->expectCallcount('exec', 1); 
     182        $this->mockQueryBuilder->setInsertQueries(array('foo' => 'foo')); 
     183        $this->assertEqual($this->dbSerializer->insert($singlePrimaryKeyEntity), stubDatabaseSerializer::INSERT); 
     184        $this->assertEqual($this->mockQueryBuilder->getCallCount('createInsert'), 1); 
     185        $this->assertEqual($this->mockQueryBuilder->getCallCount('createUpdate'), 0); 
     186        $tableRows = $this->mockQueryBuilder->getInsertTableRows(); 
     187        $this->assertEqual(count($tableRows), 1); 
     188        $this->assertTrue(isset($tableRows['foo'])); 
     189        $this->assertEqual($singlePrimaryKeyEntity->getId(), 'mockId'); 
     190        $this->assertEqual($tableRows['foo']->getColumns(), array('id' => 'mockId', 'bar' => 'this is bar', 'default' => 'example')); 
     191        $this->assertFalse($tableRows['foo']->hasCriterion()); 
     192    } 
     193 
     194    /** 
     195     * test update with a single primary key 
     196     */ 
     197    public function testUpdateWithSinglePrimaryKey() 
     198    { 
     199        $singlePrimaryKeyEntity = new MockSinglePrimaryKeyEntity(); 
     200        $singlePrimaryKeyEntity->setId('mockId'); 
     201        $singlePrimaryKeyEntity->setDefaultValue('anotherExample'); 
     202        $this->mockConnection->expectNever('getLastInsertId'); 
     203        $this->mockConnection->expectCallcount('exec', 1); 
     204        $this->mockQueryBuilder->setUpdateQueries(array('foo' => 'foo')); 
     205        $this->assertEqual($this->dbSerializer->update($singlePrimaryKeyEntity), stubDatabaseSerializer::UPDATE); 
     206        $this->assertEqual($this->mockQueryBuilder->getCallCount('createInsert'), 0); 
     207        $this->assertEqual($this->mockQueryBuilder->getCallCount('createUpdate'), 1); 
     208        $tableRows = $this->mockQueryBuilder->getUpdateTableRows(); 
     209        $this->assertEqual(count($tableRows), 1); 
     210        $this->assertTrue(isset($tableRows['foo'])); 
     211        $this->assertEqual($singlePrimaryKeyEntity->getId(), 'mockId'); 
     212        $this->assertEqual($tableRows['foo']->getColumns(), array('bar' => 'this is bar', 'default' => 'anotherExample')); 
     213        $this->assertTrue($tableRows['foo']->hasCriterion()); 
     214        $this->assertEqual($tableRows['foo']->getCriterion()->toSQL(), "(`foo`.`id` = 'mockId')"); 
     215    } 
     216 
     217    /** 
     218     * test insert with a single primary key 
     219     */ 
     220    public function testSerializeInsertWithSinglePrimaryKey() 
    121221    { 
    122222        $singlePrimaryKeyEntity = new MockSinglePrimaryKeyEntity(); 
     
    138238     * test update with a single primary key 
    139239     */ 
    140     public function testUpdateWithSinglePrimaryKey() 
     240    public function testSerializeUpdateWithSinglePrimaryKey() 
    141241    { 
    142242        $singlePrimaryKeyEntity = new MockSinglePrimaryKeyEntity();