避免与学说的唯一约束碰撞的最佳方法是什么?

时间:2015-10-10 10:43:51

标签: php sql math doctrine-orm

SQL表的senquenced标识符的内在缺点是最终用户可以轻松遍布整个表。有时这是一个问题。

一种解决方案是创建一个非序列的id,这对于每一行都是不可猜测的。

显然,这个id必须是一个独特的字段。我可以使用随机函数为每一行生成独特的id,但它有可能与先前设置的id冲突。如果它发生碰撞,最终用户会将其视为随机错误......

这是解决此问题的一个简单解决方案:

$keyValid = false;
while(!$keyValid) {
    // Create a new random key
    $newKey = generateKey();

    // Check if the key already exists in database
    $existingPotato = $em->getRepository('Potato')->findOneBy(array('key' => $newKey));
    if (empty($existingPotato)) {
        $keyValid = true;
    }
}

// Hooray, key is unique!

它强迫我每次想要一个新的id时至少生成一个SELECT语句。

那么,这个问题有更好的,被广泛接受的解决方案吗?

或者,是否存在id的优化长度,通过使碰撞概率可忽略(对于3,000,000行表)来使该问题无关紧要?

1 个答案:

答案 0 :(得分:3)

您可以添加Custom id generation strategy来执行此操作。您可以通过创建扩展AbstractIdGenerator的类来实现它:

use Doctrine\ORM\Id\AbstractIdGenerator;

class NonSequencedIdGenerator extends AbstractIdGenerator
{
    public function generate(\Doctrine\ORM\EntityManager $em, $entity)
    {
        $class  = $em->getClassMetadata(get_class($entity));
        $entityName = $class->getName();

        do {
            // You can use uniqid(), http://php.net/manual/en/function.uniqid.php
            $id = generateKey();
        } while($em->find($entityName, $id));


        return $id;
    }
}

然后在实体类中使用注释添加它:

/**
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="CUSTOM")
 * @ORM\CustomIdGenerator(class="NonSequencedIdGenerator")
 */
private $id;

但是,如果您的generateKey没有返回唯一标识符,则应检查它是否已存在。为避免这种情况,您也可以将UUID生成器用于实体中的主键。

/**
 * @ORM\Id
 * @ORM\Column(type="guid", unique=true)
 * @ORM\GeneratedValue(strategy="UUID")
 */
private $id;

如果你不喜欢这样,你可以创建一个使用UUID_SHORT的新自定义ID代码,并使用this之类的功能缩短它。

use Doctrine\ORM\Id\AbstractIdGenerator;

class UuidShortGenerator extends AbstractIdGenerator
{
    public function generate(EntityManager $em, $entity)
    {
        $conn = $em->getConnection();
        return $conn->query('SELECT UUID_SHORT()')->fetchColumn(0);
    }
}

这里的问题是我不认为它提供了完全的可移植性。