33、MySQL数据库的索引、外键与面向对象编程应用

MySQL数据库的索引、外键与面向对象编程应用

1. 索引的使用

在数据库操作中,一个两列索引也可当作单列索引使用,以第一列作为索引依据。例如,在一个 joke_category 表中,若有 jokeid 和其他列构成的两列索引,当执行以下查询时,会利用该索引列出笑话ID为1所属的类别:

SELECT * FROM joke_category WHERE jokeid = 1
2. 外键的概念与约束
  • 外键的定义 :在数据库设计里,一个表中的某列值与另一个表中的列值相匹配,这列就被称为外键。比如, joke 表中的 authorId 列指向 author 表中的 id 列,用来记录每个笑话的作者,此时 authorId 就是一个引用 author id 列的外键。
  • 外键约束的引入 :之前设计表时虽考虑了外键关系,但MySQL并未强制这些关系。例如,若不小心插入一个在 author 表中无对应记录的 authorId 值,MySQL不会阻止。不过,MySQL支持外键约束功能,可明确记录表间关系并强制实施。
    • 在创建表时添加外键约束
CREATE TABLE joke (
    id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
    joketext TEXT,
    jokedate DATE NOT NULL,
    authorId INT,
    FOREIGN KEY (authorId) REFERENCES author (id)
) DEFAULT CHARACTER SET utf8 ENGINE=InnoDB
- **对现有表添加外键约束**:
ALTER TABLE joke
ADD FOREIGN KEY (authorId) REFERENCES author (id)
  • 使用MySQL Workbench创建外键约束的步骤
    1. 确保外键列(如 authorId )有索引。若使用上述两个查询语句,MySQL会自动创建索引,但在MySQL Workbench中需手动操作。
    2. joke 表的 Foreign Keys 选项卡中,双击 Foreign Key Name 列的第一行,会自动生成一个名称,此名称仅供参考。
    3. 将该外键命名为 fk_joke_author ,以表明是在 joke author 表间创建外键。
    4. 选择 author 表作为引用表, authorId 列作为列, id 列作为引用列。

添加外键约束后,MySQL会拒绝向 joke 表插入无对应 author 表记录的 authorId 值,并且在删除 author 表中的记录前,需先删除指向该记录的 joke 记录。

3. 引用操作
  • 引用操作的方式 :除了拒绝删除或更新有外键指向的记录,还可进行引用操作。可通过配置MySQL的外键约束自动解决冲突,方式有两种:一是级联操作,即删除作者时同时删除其相关的笑话;二是将受影响的外键列值设为 NULL ,如将作者笑话的 authorId 设为 NULL ,这对应MySQL Workbench中外键约束的 ON RESTRICT ON UPDATE 选项。
  • 不建议使用引用操作的原因 :使用此功能虽方便,但会将网站逻辑分散在PHP代码和外键约束两处。例如,删除笑话时,无法仅通过查看PHP控制器了解所有操作。因此,多数有经验的PHP开发者倾向避免使用外键约束中的引用操作,甚至有人完全避免使用外键约束。
4. 数据库关系与面向对象编程
  • SQL JOIN的局限性 :在构建互联网笑话数据库网站时,常需从多个表获取数据,SQL JOIN是一种解决方案,虽有性能优势,但与面向对象编程不兼容。数据库的关系型方法与面向对象编程的嵌套结构通常不匹配。在面向对象编程中,对象以层次结构存储,如作者对象封装其笑话列表,类别对象封装类别内的笑话列表。
  • SQL查询示例 :若要获取作者及其所有笑话的信息,SQL查询如下:
SELECT author.name, joke.id, joke.joketext
FROM author
INNER JOIN joke ON joke.authorId = author.id
WHERE authorId = 123
  • 面向对象编程的实现方式 :使用面向对象方法获取笑话列表,代码如下:
// Find the author with the id `123`
$author =  $authors->findById(123);
// Get all the jokes by this author
$jokes = $author->getJokes();
// Print the text of the first joke by that author.
echo $jokes[0]->joketext;

此代码无需SQL,数据从数据库获取但在后台完成。
- 关系型与面向对象处理方式对比
- 关系型处理方式 :获取作者及其笑话信息需执行两个独立的 SELECT 查询:

// Find the author with the ID 123
$author = $this->authors->findById(123);
// Now find all the jokes posted by the author with that ID
$jokesByAuthor = $this->jokes->find('authorId', $authorId);

插入笑话时,代码如下:

public function saveEdit() {
    $author = $this->authentication->getUser();
    $joke = $_POST['joke'];
    $joke['jokedate'] = new \DateTime();
    $joke['authorId'] = $author['id'];
    $this->jokesTable->save($joke);
    header('location: /joke/list');
}

编写此代码的人需了解数据库的底层结构和表间的关系存储方式。
- 面向对象处理方式

public function saveEdit() {
    $author = $this->authentication->getUser();
    $joke = $_POST['joke'];
    $joke['jokedate'] = new \DateTime();
    $author->addJoke($joke);
    header('location: /joke/list');
}

这种方式隐藏了底层实现,编写代码的人无需了解数据库中关系的建模方式,只需知道数据会被存储在作者实例中。

5. 面向对象编程的优势
  • 实现隐藏 :面向对象编程采用层次化方法,利用数据结构建模关系,具有实现隐藏的优点。不同人可负责代码的不同部分,如编写 saveEdit 方法的开发者无需了解 addJoke 方法的具体实现,只需知道其能保存数据且后续可检索。
  • 分离关注点 :这种方式将逻辑分离,保存笑话的过程和向数据库写入数据的过程是不同的关注点。分离后增加了灵活性, addJoke 方法可在任何地方调用,且其实现方式可更改而不影响调用代码。
  • 对测试驱动开发(TDD)的帮助 :这种灵活性对TDD很有用。在TDD中,可编写一个 DatabaseTable 类的版本作为测试占位符,而无需使用包含相关测试数据的真实数据库。通过考虑关注点分离,可更轻松地编写自动化测试代码。
6. 对象关系映射器(ORM)
  • ORM的概念 :逐步构建的 DatabaseTable 类属于对象关系映射器(ORM)。市面上有很多ORM实现,如Doctrine、Propel和ReadBeanPHP,它们的主要作用是为关系型数据库提供面向对象的接口,弥合关系型数据库的SQL查询和网站其他PHP代码间的差距。
  • 从数组到对象的转变 :使用 DatabaseTable 类查找作者并打印其名称时, $author 变量是一个数组:
$author = $authors->findById(123);
echo $author['name'];

由于数组不能包含函数,无法在 $author 实例上实现 addJoke 方法。若要调用方法,需将 $author 变量设为对象。
- 实体类的创建 :创建一个代表作者的实体类:

namespace Ijdb\Entity;
class Author {
    public $id;
    public $name;
    public $email;
    public $password;
}

此类将直接映射到数据库中的记录,通常称为实体类。
- 实体类的公共属性 :通常类变量设为私有,可避免意外破坏类的功能。但在实体类中,应使用公共属性,因为实体类的主要目的是提供数据,若无法读取作者姓名,类就失去了意义。
- 实体类中的方法 :为 Author 类添加 getJokes addJoke 方法:

<?php
namespace Ijdb\Entity;
class Author
{
    public $id;
    public $name;
    public $email;
    public $password;
    private $jokesTable;
    public function __construct(\Ninja\DatabaseTable $jokesTable)
    {
        $this->jokesTable = $jokesTable;
    }
    public function getJokes()
    {
        return $this->jokesTable->find('authorId', $this->id);
    }
    public function addJoke($joke) {
        $joke['authorId'] = $this->id;
        $this->jokesTable->save($joke);
    }
}

使用示例:

$jokesTable = new \Ninja\DatabaseTable($pdo, 'joke', 'id');
$author = new \Ijdb\Entity\Author($jokesTable);
$author->id = 123;
$jokes = $author->getJokes();

Joke 控制器的 saveEdit 方法中使用新类保存笑话:

public function saveEdit() {
    $author = $this->authentication->getUser();
    $authorObject = new \Ijdb\Entity\Author($this->jokesTable);
    $authorObject->id = $author['id'];
    $authorObject->name = $author['name'];
    $authorObject->email = $author['email'];
    $authorObject->password = $author['password'];
    $joke = $_POST['joke'];
    $joke['jokedate'] = new \DateTime();
    $authorObject->addJoke($joke);
    header('location: /joke/list');
}

综上所述,在数据库开发中,合理运用索引和外键约束可提高数据操作的准确性和效率,而结合面向对象编程和ORM能让代码更具灵活性和可维护性,同时为测试驱动开发提供便利。

MySQL数据库的索引、外键与面向对象编程应用

7. 实体类方法的详细分析
  • getJokes 方法 :该方法用于获取指定作者的所有笑话。它依赖于 jokesTable 实例来执行查询操作。具体实现如下:
public function getJokes()
{
    return $this->jokesTable->find('authorId', $this->id);
}

在这个方法中, $this->id 代表当前作者的ID,通过调用 jokesTable find 方法,以 authorId 为条件进行查询,返回该作者的所有笑话记录。
- addJoke 方法 :此方法用于为指定作者添加新的笑话。它会自动设置笑话的 authorId 为当前作者的ID,并将笑话数据保存到数据库中。代码如下:

public function addJoke($joke) {
    $joke['authorId'] = $this->id;
    $this->jokesTable->save($joke);
}

在调用该方法时,只需传入包含笑话信息的数组 $joke ,方法会自动处理 authorId 的设置和数据保存操作。

8. 面向对象编程在数据库操作中的优势总结
优势 描述
实现隐藏 开发者无需了解数据存储的底层细节,只需要关注方法的调用和返回结果。例如,在调用 $author->addJoke($joke) 时,不需要知道数据是如何写入数据库的,只需要知道该方法可以完成添加笑话的功能。
分离关注点 将不同的功能逻辑分离,使得代码的维护和扩展更加容易。如保存笑话和写入数据库是两个不同的关注点,通过面向对象编程可以将它们分开处理,提高代码的灵活性。
便于测试 在测试驱动开发中,由于逻辑的分离,可编写简单的测试代码来验证各个功能模块。例如,可以模拟 DatabaseTable 类的行为,对 Author 类的方法进行单元测试。
9. 数据库表与实体类的映射关系

数据库表和实体类之间存在着紧密的映射关系,这种映射关系使得我们可以通过操作实体类来间接操作数据库。以下是一个简单的流程图,展示了这种映射关系:

graph LR
    classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px

    A[数据库表]:::process -->|映射| B[实体类]:::process
    B -->|操作| C[数据库操作]:::process
    C -->|结果| B
    B -->|展示| D[应用程序]:::process

在这个流程中,数据库表的数据通过映射关系存储在实体类中,开发者可以通过操作实体类的方法来执行数据库操作,操作结果又会更新实体类的数据,最终展示在应用程序中。

10. 外键约束对数据库完整性的保障

外键约束在维护数据库的完整性方面起着至关重要的作用。以下是外键约束的主要作用和相关操作的总结:
- 防止无效数据插入 :通过外键约束,MySQL会拒绝插入在引用表中没有对应记录的外键值。例如,在 joke 表中插入 authorId 时,如果该 authorId author 表中不存在,插入操作将被拒绝。
- 级联操作控制 :可以通过配置外键约束的级联操作,如级联删除或级联更新,来确保数据的一致性。例如,当删除一个作者时,可以同时删除该作者的所有笑话记录。
- 数据一致性维护 :外键约束确保了表之间的关系始终保持一致,避免了数据的不一致性问题。例如,在更新 author 表中的 id 时,如果有外键约束,相关的 joke 表中的 authorId 也会相应更新。

11. 索引对查询性能的提升

索引在数据库查询中起着关键作用,它可以显著提高查询的性能。以下是索引的使用场景和优势的总结:
- 快速定位数据 :通过索引,数据库可以快速定位到满足查询条件的记录,减少了全表扫描的时间。例如,在执行 SELECT * FROM joke_category WHERE jokeid = 1 时,使用 jokeid 索引可以快速找到相关记录。
- 提高查询效率 :对于经常进行查询操作的列,添加索引可以大大提高查询的效率。例如,在 joke 表中,经常根据 authorId 进行查询,为 authorId 添加索引可以加快查询速度。

12. 总结与展望

在数据库开发中,合理运用索引、外键约束、面向对象编程和ORM等技术,可以提高数据库的性能、数据的完整性和代码的可维护性。未来,随着数据库技术和软件开发方法的不断发展,我们可以进一步探索这些技术的应用场景和优化方法。例如,可以研究更高效的索引策略、更灵活的外键约束配置和更强大的ORM框架,以适应不断变化的业务需求。同时,在开发过程中,要始终关注代码的可测试性和可维护性,为项目的长期发展奠定坚实的基础。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值