Failed Conditions
Push — 2.6 ( d8212e...6e93f5 )
by Luís
23s queued 18s
created

Query::setLockMode()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 5
nc 3
nop 1
dl 0
loc 11
ccs 0
cts 6
cp 0
crap 12
rs 10
c 0
b 0
f 0
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM;
21
22
use Doctrine\Common\Collections\ArrayCollection;
23
use Doctrine\DBAL\LockMode;
24
use Doctrine\ORM\Mapping\ClassMetadata;
25
use Doctrine\ORM\Query\Exec\AbstractSqlExecutor;
26
use Doctrine\ORM\Query\Parameter;
27
use Doctrine\ORM\Query\ParameterTypeInferer;
28
use Doctrine\ORM\Query\Parser;
29
use Doctrine\ORM\Query\ParserResult;
30
use Doctrine\ORM\Query\QueryException;
31
use Doctrine\ORM\Utility\HierarchyDiscriminatorResolver;
32
use function array_keys;
33
use function assert;
34
35
/**
36
 * A Query object represents a DQL query.
37
 *
38
 * @since   1.0
39
 * @author  Guilherme Blanco <[email protected]>
40
 * @author  Konsta Vesterinen <[email protected]>
41
 * @author  Roman Borschel <[email protected]>
42
 */
43
final class Query extends AbstractQuery
44
{
45
    /**
46
     * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
47
     */
48
    const STATE_CLEAN  = 1;
49
50
    /**
51
     * A query object is in state DIRTY when it has DQL parts that have not yet been
52
     * parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart
53
     * is called.
54
     */
55
    const STATE_DIRTY = 2;
56
57
    /* Query HINTS */
58
59
    /**
60
     * The refresh hint turns any query into a refresh query with the result that
61
     * any local changes in entities are overridden with the fetched values.
62
     *
63
     * @var string
64
     */
65
    const HINT_REFRESH = 'doctrine.refresh';
66
67
    /**
68
     * @var string
69
     */
70
    const HINT_CACHE_ENABLED = 'doctrine.cache.enabled';
71
72
    /**
73
     * @var string
74
     */
75
    const HINT_CACHE_EVICT = 'doctrine.cache.evict';
76
77
    /**
78
     * Internal hint: is set to the proxy entity that is currently triggered for loading
79
     *
80
     * @var string
81
     */
82
    const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity';
83
84
    /**
85
     * The forcePartialLoad query hint forces a particular query to return
86
     * partial objects.
87
     *
88
     * @var string
89
     * @todo Rename: HINT_OPTIMIZE
90
     */
91
    const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad';
92
93
    /**
94
     * The includeMetaColumns query hint causes meta columns like foreign keys and
95
     * discriminator columns to be selected and returned as part of the query result.
96
     *
97
     * This hint does only apply to non-object queries.
98
     *
99
     * @var string
100
     */
101
    const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
102
103
    /**
104
     * An array of class names that implement \Doctrine\ORM\Query\TreeWalker and
105
     * are iterated and executed after the DQL has been parsed into an AST.
106
     *
107
     * @var string
108
     */
109
    const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers';
110
111
    /**
112
     * A string with a class name that implements \Doctrine\ORM\Query\TreeWalker
113
     * and is used for generating the target SQL from any DQL AST tree.
114
     *
115
     * @var string
116
     */
117
    const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker';
118
119
    //const HINT_READ_ONLY = 'doctrine.readOnly';
120
121
    /**
122
     * @var string
123
     */
124
    const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration';
125
126
    /**
127
     * @var string
128
     */
129
    const HINT_LOCK_MODE = 'doctrine.lockMode';
130
131
    /**
132
     * The current state of this query.
133
     *
134
     * @var integer
135
     */
136
    private $_state = self::STATE_CLEAN;
137
138
    /**
139
     * A snapshot of the parameter types the query was parsed with.
140
     *
141
     * @var array
142
     */
143
    private $_parsedTypes = [];
144
145
    /**
146
     * Cached DQL query.
147
     *
148
     * @var string
149
     */
150
    private $_dql = null;
151
152
    /**
153
     * The parser result that holds DQL => SQL information.
154
     *
155
     * @var \Doctrine\ORM\Query\ParserResult
156
     */
157
    private $_parserResult;
158
159
    /**
160
     * The first result to return (the "offset").
161
     *
162
     * @var integer
163
     */
164
    private $_firstResult = null;
165
166
    /**
167
     * The maximum number of results to return (the "limit").
168
     *
169
     * @var integer|null
170
     */
171
    private $_maxResults = null;
172
173
    /**
174
     * The cache driver used for caching queries.
175
     *
176
     * @var \Doctrine\Common\Cache\Cache|null
177
     */
178
    private $_queryCache;
179
180
    /**
181
     * Whether or not expire the query cache.
182
     *
183
     * @var boolean
184
     */
185
    private $_expireQueryCache = false;
186
187
    /**
188
     * The query cache lifetime.
189
     *
190
     * @var int
191
     */
192
    private $_queryCacheTTL;
193
194
    /**
195
     * Whether to use a query cache, if available. Defaults to TRUE.
196
     *
197
     * @var boolean
198
     */
199
    private $_useQueryCache = true;
200
201
    /**
202
     * Gets the SQL query/queries that correspond to this DQL query.
203
     *
204
     * @return mixed The built sql query or an array of all sql queries.
205
     *
206
     * @override
207
     */
208 340
    public function getSQL()
209
    {
210 340
        return $this->_parse()->getSqlExecutor()->getSqlStatements();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_parse()->...r()->getSqlStatements() returns the type array which is incompatible with the return type mandated by Doctrine\ORM\AbstractQuery::getSQL() of string.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
211
    }
212
213
    /**
214
     * Returns the corresponding AST for this DQL query.
215
     *
216
     * @return \Doctrine\ORM\Query\AST\SelectStatement |
217
     *         \Doctrine\ORM\Query\AST\UpdateStatement |
218
     *         \Doctrine\ORM\Query\AST\DeleteStatement
219
     */
220 2
    public function getAST()
221
    {
222 2
        $parser = new Parser($this);
223
224 2
        return $parser->getAST();
225
    }
226
227
    /**
228
     * {@inheritdoc}
229
     */
230 461
    protected function getResultSetMapping()
231
    {
232
        // parse query or load from cache
233 461
        if ($this->_resultSetMapping === null) {
234 38
            $this->_resultSetMapping = $this->_parse()->getResultSetMapping();
235
        }
236
237 458
        return $this->_resultSetMapping;
238
    }
239
240
    /**
241
     * Parses the DQL query, if necessary, and stores the parser result.
242
     *
243
     * Note: Populates $this->_parserResult as a side-effect.
244
     *
245
     * @return \Doctrine\ORM\Query\ParserResult
246
     */
247 786
    private function _parse()
248
    {
249 786
        $types = [];
250
251 786
        foreach ($this->parameters as $parameter) {
252
            /** @var Query\Parameter $parameter */
253 183
            $types[$parameter->getName()] = $parameter->getType();
254
        }
255
256
        // Return previous parser result if the query and the filter collection are both clean
257 786
        if ($this->_state === self::STATE_CLEAN && $this->_parsedTypes === $types && $this->_em->isFiltersStateClean()) {
258 39
            return $this->_parserResult;
259
        }
260
261 786
        $this->_state = self::STATE_CLEAN;
262 786
        $this->_parsedTypes = $types;
263
264
        // Check query cache.
265 786
        if ( ! ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver()))) {
266 172
            $parser = new Parser($this);
267
268 172
            $this->_parserResult = $parser->parse();
269
270 168
            return $this->_parserResult;
271
        }
272
273 614
        $hash   = $this->_getQueryCacheId();
274 614
        $cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
275
276 614
        if ($cached instanceof ParserResult) {
277
            // Cache hit.
278 124
            $this->_parserResult = $cached;
279
280 124
            return $this->_parserResult;
281
        }
282
283
        // Cache miss.
284 561
        $parser = new Parser($this);
285
286 561
        $this->_parserResult = $parser->parse();
287
288 543
        $queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL);
289
290 543
        return $this->_parserResult;
291
    }
292
293
    /**
294
     * {@inheritdoc}
295
     */
296 478
    protected function _doExecute()
297
    {
298 478
        $executor = $this->_parse()->getSqlExecutor();
299
300 471
        if ($this->_queryCacheProfile) {
301 8
            $executor->setQueryCacheProfile($this->_queryCacheProfile);
302
        } else {
303 465
            $executor->removeQueryCacheProfile();
304
        }
305
306 471
        if ($this->_resultSetMapping === null) {
307 429
            $this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
308
        }
309
310
        // Prepare parameters
311 471
        $paramMappings = $this->_parserResult->getParameterMappings();
312 471
        $paramCount = count($this->parameters);
313 471
        $mappingCount = count($paramMappings);
314
315 471
        if ($paramCount > $mappingCount) {
316 2
            throw QueryException::tooManyParameters($mappingCount, $paramCount);
317
        }
318
319 470
        if ($paramCount < $mappingCount) {
320 1
            throw QueryException::tooFewParameters($mappingCount, $paramCount);
321
        }
322
323
        // evict all cache for the entity region
324 469
        if ($this->hasCache && isset($this->_hints[self::HINT_CACHE_EVICT]) && $this->_hints[self::HINT_CACHE_EVICT]) {
325 2
            $this->evictEntityCacheRegion();
326
        }
327
328 469
        list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
329
330 468
        $this->evictResultSetCache(
331 468
            $executor,
332 468
            $sqlParams,
333 468
            $types,
334 468
            $this->_em->getConnection()->getParams()
335
        );
336
337 468
        return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
338
    }
339
340 468
    private function evictResultSetCache(
341
        AbstractSqlExecutor $executor,
342
        array $sqlParams,
343
        array $types,
344
        array $connectionParams
345
    ) {
346 468
        if (null === $this->_queryCacheProfile || ! $this->getExpireResultCache()) {
347 468
            return;
348
        }
349
350 2
        $cacheDriver = $this->_queryCacheProfile->getResultCacheDriver();
351 2
        $statements  = (array) $executor->getSqlStatements(); // Type casted since it can either be a string or an array
352
353 2
        foreach ($statements as $statement) {
354 2
            $cacheKeys = $this->_queryCacheProfile->generateCacheKeys($statement, $sqlParams, $types, $connectionParams);
355
356 2
            $cacheDriver->delete(reset($cacheKeys));
357
        }
358 2
    }
359
360
    /**
361
     * Evict entity cache region
362
     */
363 2
    private function evictEntityCacheRegion()
364
    {
365 2
        $AST = $this->getAST();
366
367 2
        if ($AST instanceof \Doctrine\ORM\Query\AST\SelectStatement) {
0 ignored issues
show
introduced by
$AST is always a sub-type of Doctrine\ORM\Query\AST\SelectStatement.
Loading history...
368
            throw new QueryException('The hint "HINT_CACHE_EVICT" is not valid for select statements.');
369
        }
370
371 2
        $className = ($AST instanceof \Doctrine\ORM\Query\AST\DeleteStatement)
372 1
            ? $AST->deleteClause->abstractSchemaName
373 2
            : $AST->updateClause->abstractSchemaName;
374
375 2
        $this->_em->getCache()->evictEntityRegion($className);
376 2
    }
377
378
    /**
379
     * Processes query parameter mappings.
380
     *
381
     * @param array $paramMappings
382
     *
383
     * @return array
384
     *
385
     * @throws Query\QueryException
386
     */
387 469
    private function processParameterMappings($paramMappings)
388
    {
389 469
        $sqlParams = [];
390 469
        $types     = [];
391
392 469
        foreach ($this->parameters as $parameter) {
393 170
            $key = $parameter->getName();
394
395 170
            if ( ! isset($paramMappings[$key])) {
396 1
                throw QueryException::unknownParameter($key);
397
            }
398
399 169
            [$value, $type] = $this->resolveParameterValue($parameter);
400
401 169
            foreach ($paramMappings[$key] as $position) {
402 169
                $types[$position] = $type;
403
            }
404
405 169
            $sqlPositions = $paramMappings[$key];
406
407
            // optimized multi value sql positions away for now,
408
            // they are not allowed in DQL anyways.
409 169
            $value = [$value];
410 169
            $countValue = count($value);
411
412 169
            for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
413 169
                $sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)];
414
            }
415
        }
416
417 468
        if (count($sqlParams) != count($types)) {
418
            throw QueryException::parameterTypeMismatch();
419
        }
420
421 468
        if ($sqlParams) {
422 169
            ksort($sqlParams);
423 169
            $sqlParams = array_values($sqlParams);
424
425 169
            ksort($types);
426 169
            $types = array_values($types);
427
        }
428
429 468
        return [$sqlParams, $types];
430
    }
431
432
    /** @return mixed[] tuple of (value, type) */
433 169
    private function resolveParameterValue(Parameter $parameter) : array
434
    {
435 169
        if ($parameter->typeWasSpecified()) {
436 5
            return [$parameter->getValue(), $parameter->getType()];
437
        }
438
439 165
        $key           = $parameter->getName();
440 165
        $originalValue = $parameter->getValue();
441 165
        $value         = $originalValue;
442 165
        $rsm           = $this->getResultSetMapping();
443
444 165
        assert($rsm !== null);
445
446 165
        if ($value instanceof ClassMetadata && isset($rsm->metadataParameterMapping[$key])) {
447
            $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]);
448
        }
449
450 165
        if ($value instanceof ClassMetadata && isset($rsm->discriminatorParameters[$key])) {
451 3
            $value = array_keys(HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($value, $this->_em));
452
        }
453
454 165
        $processedValue = $this->processParameterValue($value);
455
456
        return [
457 165
            $processedValue,
458 165
            $originalValue === $processedValue
459 153
                ? $parameter->getType()
460 165
                : ParameterTypeInferer::inferType($processedValue),
461
        ];
462
    }
463
464
    /**
465
     * Defines a cache driver to be used for caching queries.
466
     *
467
     * @param \Doctrine\Common\Cache\Cache|null $queryCache Cache driver.
468
     *
469
     * @return Query This query instance.
470
     */
471 6
    public function setQueryCacheDriver($queryCache)
472
    {
473 6
        $this->_queryCache = $queryCache;
474
475 6
        return $this;
476
    }
477
478
    /**
479
     * Defines whether the query should make use of a query cache, if available.
480
     *
481
     * @param boolean $bool
482
     *
483
     * @return Query This query instance.
484
     */
485 175
    public function useQueryCache($bool)
486
    {
487 175
        $this->_useQueryCache = $bool;
488
489 175
        return $this;
490
    }
491
492
    /**
493
     * Returns the cache driver used for query caching.
494
     *
495
     * @return \Doctrine\Common\Cache\Cache|null The cache driver used for query caching or NULL, if
496
     *                                           this Query does not use query caching.
497
     */
498 614
    public function getQueryCacheDriver()
499
    {
500 614
        if ($this->_queryCache) {
501 9
            return $this->_queryCache;
502
        }
503
504 605
        return $this->_em->getConfiguration()->getQueryCacheImpl();
505
    }
506
507
    /**
508
     * Defines how long the query cache will be active before expire.
509
     *
510
     * @param integer $timeToLive How long the cache entry is valid.
511
     *
512
     * @return Query This query instance.
513
     */
514 1
    public function setQueryCacheLifetime($timeToLive)
515
    {
516 1
        if ($timeToLive !== null) {
0 ignored issues
show
introduced by
The condition $timeToLive !== null is always true.
Loading history...
517 1
            $timeToLive = (int) $timeToLive;
518
        }
519
520 1
        $this->_queryCacheTTL = $timeToLive;
521
522 1
        return $this;
523
    }
524
525
    /**
526
     * Retrieves the lifetime of resultset cache.
527
     *
528
     * @return int
529
     */
530
    public function getQueryCacheLifetime()
531
    {
532
        return $this->_queryCacheTTL;
533
    }
534
535
    /**
536
     * Defines if the query cache is active or not.
537
     *
538
     * @param boolean $expire Whether or not to force query cache expiration.
539
     *
540
     * @return Query This query instance.
541
     */
542 7
    public function expireQueryCache($expire = true)
543
    {
544 7
        $this->_expireQueryCache = $expire;
545
546 7
        return $this;
547
    }
548
549
    /**
550
     * Retrieves if the query cache is active or not.
551
     *
552
     * @return bool
553
     */
554
    public function getExpireQueryCache()
555
    {
556
        return $this->_expireQueryCache;
557
    }
558
559
    /**
560
     * @override
561
     */
562 212
    public function free()
563
    {
564 212
        parent::free();
565
566 212
        $this->_dql = null;
567 212
        $this->_state = self::STATE_CLEAN;
568 212
    }
569
570
    /**
571
     * Sets a DQL query string.
572
     *
573
     * @param string $dqlQuery DQL Query.
574
     *
575
     * @return \Doctrine\ORM\AbstractQuery
576
     */
577 979
    public function setDQL($dqlQuery)
578
    {
579 979
        if ($dqlQuery !== null) {
0 ignored issues
show
introduced by
The condition $dqlQuery !== null is always true.
Loading history...
580 979
            $this->_dql = $dqlQuery;
581 979
            $this->_state = self::STATE_DIRTY;
582
        }
583
584 979
        return $this;
585
    }
586
587
    /**
588
     * Returns the DQL query that is represented by this query object.
589
     *
590
     * @return string DQL query.
591
     */
592 924
    public function getDQL()
593
    {
594 924
        return $this->_dql;
595
    }
596
597
    /**
598
     * Returns the state of this query object
599
     * By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL
600
     * part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY.
601
     *
602
     * @see AbstractQuery::STATE_CLEAN
603
     * @see AbstractQuery::STATE_DIRTY
604
     *
605
     * @return integer The query state.
606
     */
607
    public function getState()
608
    {
609
        return $this->_state;
610
    }
611
612
    /**
613
     * Method to check if an arbitrary piece of DQL exists
614
     *
615
     * @param string $dql Arbitrary piece of DQL to check for.
616
     *
617
     * @return boolean
618
     */
619
    public function contains($dql)
620
    {
621
        return stripos($this->getDQL(), $dql) !== false;
622
    }
623
624
    /**
625
     * Sets the position of the first result to retrieve (the "offset").
626
     *
627
     * @param integer $firstResult The first result to return.
628
     *
629
     * @return Query This query object.
630
     */
631 223
    public function setFirstResult($firstResult)
632
    {
633 223
        $this->_firstResult = $firstResult;
634 223
        $this->_state       = self::STATE_DIRTY;
635
636 223
        return $this;
637
    }
638
639
    /**
640
     * Gets the position of the first result the query object was set to retrieve (the "offset").
641
     * Returns NULL if {@link setFirstResult} was not applied to this query.
642
     *
643
     * @return integer The position of the first result.
644
     */
645 671
    public function getFirstResult()
646
    {
647 671
        return $this->_firstResult;
648
    }
649
650
    /**
651
     * Sets the maximum number of results to retrieve (the "limit").
652
     *
653
     * @param integer|null $maxResults
654
     *
655
     * @return Query This query object.
656
     */
657 247
    public function setMaxResults($maxResults)
658
    {
659 247
        $this->_maxResults = $maxResults;
660 247
        $this->_state      = self::STATE_DIRTY;
661
662 247
        return $this;
663
    }
664
665
    /**
666
     * Gets the maximum number of results the query object was set to retrieve (the "limit").
667
     * Returns NULL if {@link setMaxResults} was not applied to this query.
668
     *
669
     * @return integer|null Maximum number of results.
670
     */
671 671
    public function getMaxResults()
672
    {
673 671
        return $this->_maxResults;
674
    }
675
676
    /**
677
     * Executes the query and returns an IterableResult that can be used to incrementally
678
     * iterated over the result.
679
     *
680
     * @param ArrayCollection|array|null $parameters    The query parameters.
681
     * @param string|int                 $hydrationMode The hydration mode to use.
682
     *
683
     * @return \Doctrine\ORM\Internal\Hydration\IterableResult
684
     */
685 10
    public function iterate($parameters = null, $hydrationMode = self::HYDRATE_OBJECT)
686
    {
687 10
        $this->setHint(self::HINT_INTERNAL_ITERATION, true);
688
689 10
        return parent::iterate($parameters, $hydrationMode);
690
    }
691
692
    /**
693
     * {@inheritdoc}
694
     */
695 468
    public function setHint($name, $value)
696
    {
697 468
        $this->_state = self::STATE_DIRTY;
698
699 468
        return parent::setHint($name, $value);
700
    }
701
702
    /**
703
     * {@inheritdoc}
704
     */
705 369
    public function setHydrationMode($hydrationMode)
706
    {
707 369
        $this->_state = self::STATE_DIRTY;
708
709 369
        return parent::setHydrationMode($hydrationMode);
710
    }
711
712
    /**
713
     * Set the lock mode for this Query.
714
     *
715
     * @see \Doctrine\DBAL\LockMode
716
     *
717
     * @param int $lockMode
718
     *
719
     * @return Query
720
     *
721
     * @throws TransactionRequiredException
722
     */
723
    public function setLockMode($lockMode)
724
    {
725
        if (in_array($lockMode, [LockMode::NONE, LockMode::PESSIMISTIC_READ, LockMode::PESSIMISTIC_WRITE], true)) {
726
            if ( ! $this->_em->getConnection()->isTransactionActive()) {
727
                throw TransactionRequiredException::transactionRequired();
728
            }
729
        }
730
731
        $this->setHint(self::HINT_LOCK_MODE, $lockMode);
732
733
        return $this;
734
    }
735
736
    /**
737
     * Get the current lock mode for this query.
738
     *
739
     * @return int|null The current lock mode of this query or NULL if no specific lock mode is set.
740
     */
741
    public function getLockMode()
742
    {
743
        $lockMode = $this->getHint(self::HINT_LOCK_MODE);
744
745
        if (false === $lockMode) {
746
            return null;
747
        }
748
749
        return $lockMode;
750
    }
751
752
    /**
753
     * Generate a cache id for the query cache - reusing the Result-Cache-Id generator.
754
     *
755
     * @return string
756
     */
757 614
    protected function _getQueryCacheId()
758
    {
759 614
        ksort($this->_hints);
760
761 614
        $platform = $this->getEntityManager()
762 614
            ->getConnection()
763 614
            ->getDatabasePlatform()
764 614
            ->getName();
765
766 614
        return md5(
767 614
            $this->getDQL() . serialize($this->_hints) .
768 614
            '&platform=' . $platform .
769 614
            ($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') .
770 614
            '&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults .
771 614
            '&hydrationMode=' . $this->_hydrationMode . '&types=' . serialize($this->_parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT'
772
        );
773
    }
774
775
     /**
776
     * {@inheritdoc}
777
     */
778 28
    protected function getHash()
779
    {
780 28
        return sha1(parent::getHash(). '-'. $this->_firstResult . '-' . $this->_maxResults);
781
    }
782
783
    /**
784
     * Cleanup Query resource when clone is called.
785
     *
786
     * @return void
787
     */
788 142
    public function __clone()
789
    {
790 142
        parent::__clone();
791
792 142
        $this->_state = self::STATE_DIRTY;
793 142
    }
794
}
795