跳转到内容

主动记录

本页使用了标题或全文手工转换
维基百科,自由的百科全书
(重定向自Active Record)

在软件工程中,主动记录模式(active record pattern)是一种架构模式,可见于在关系数据库中存储内存中对象的软件中。它在Martin Fowler的2003年著《企业应用架构的模式》书中命名[1]。符合这个模式的对象的接口将包括函数比如插入、更新和删除,加上直接对应于在底层数据库表格中列的或多或少的属性。

主动记录模式是访问在数据库中的数据的一种方式。数据库表或视图被包装入类。因此,对象实例被连结到这个表格的一个单一行。在一个对象建立之后,在保存时将一个新行增加到表格中。加载的任何对象都从数据库得到它的信息。在一个对象被更新的时候,在表格中对应的行也被更新。包装类为在表格或视图中的每个列都实现访问器方法或属性。

这个模式常用于对象持久化工具和对象关系映射(ORM)之中。典型的,外键联系也通过一个属性而被显露为适当类型的一个对象实例。

实现

[编辑]

这个概念的实现可以在很多编程环境中的各种框架中找到。例如,如果在数据库中有一个表格parts,它具有列name(字符串类型)和price(数值类型),而主动记录模式用类Part来实现,伪码为:

part = new Part()
part.name = "Sample part"
part.price = 123.45
part.save()

它在parts表格中建立有给定值的一个新行,并且粗略的等价于SQL命令:

INSERT INTO parts (name, price) VALUES ('Sample part', 123.45);

反过来,这个类可以被用来查询这个数据库:

b = Part.find_first("name", "gearbox")

这会找到一个新Part对象,基于在parts表格中其name列的值为"gearbox"的第一个匹配行。使用的SQL命令可能类似于下面所列,具体依赖于这个数据库的SQL实现细节:

SELECT * FROM parts WHERE name = 'gearbox' LIMIT 1; -- MySQL or PostgreSQL

例子

[编辑]

下面的例子是Java中的一个主动记录对象User。这里的User类是模型和数据访问对象二者,并且提供save()、find()和delete()方法。

package org.wikipedia.examples;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class User {
    private int id;
    private String name;
    private String email;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public void save() {
        try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password")) {
            if (this.id == 0) {
                String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
                try (PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
                    stmt.setString(1, this.name);
                    stmt.setString(2, this.email);
                    stmt.executeUpdate();
                    ResultSet rs = stmt.getGeneratedKeys();
                    if (rs.next()) {
                        this.id = rs.getInt(1);
                    }
                }
            } else {
                String sql = "UPDATE users SET name = ?, email = ? WHERE id = ?";
                try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                    stmt.setString(1, this.name);
                    stmt.setString(2, this.email);
                    stmt.setInt(3, this.id);
                    stmt.executeUpdate();
                }
            }
        } catch (SQLException e) {
            System.err.printf("A SQLException occurred: %s%n", e.getMessage());
            e.printStackTrace();
        }
    }

    public static User find(int id) {
        try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password")) {
            String sql = "SELECT * FROM users WHERE id = ?";
            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                stmt.setInt(1, id);
                ResultSet rs = stmt.executeQuery();
                if (rs.next()) {
                    User user = new User(rs.getString("name"), rs.getString("email"));
                    user.setId(rs.getInt("id"));
                    return user;
                }
            }
        } catch (SQLException e) {
            System.err.printf("A SQLException occurred: %s%n", e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    public void delete() {
        try (Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password")) {
            String sql = "DELETE FROM users WHERE id = ?";
            try (PreparedStatement stmt = conn.prepareStatement(sql)) {
                stmt.setInt(1, this.id);
                stmt.executeUpdate();
            }
        } catch (SQLException e) {
            System.err.printf("A SQLException occurred: %s%n", e.getMessage());
            e.printStackTrace();
        }
    }
}

主动记录框架

[编辑]

主动记录框架一般兼有ORM框架的功能,但主动记录不是简单的ORM。关系型数据库往往通过外键来表述实体的联系,主动记录在数据源层面上也将这种联系映射为对象的关联和聚集(英语:Object composition#Aggregation)。著名的例子是解决方案堆栈Web开发框架Ruby on Rails,其默认使用一个纯Ruby写成的主动记录框架来驱动MVC中的模型层。此外还有:

类似模式

[编辑]

主动记录和行数据门路(英语:Row Data Gateway)十分相似,但前者是领域模型模式,后者只是一种数据源模式。主动记录适合非常简单的领域需求,尤其在领域模型和数据库模型十分相似的情况下。如果遇到更加复杂的领域模型结构(例如用到继承、策略的领域模型),往往需要使用分离数据源的领域模型,结合数据映射器使用。

参见

[编辑]

资料来源

[编辑]
  1. ^ Fowler, Martin. Patterns of enterprise application architecture. Addison-Wesley. 2003 [2020-11-07]. ISBN 978-0-321-12742-6. (原始内容存档于2021-11-24). 承载数据和行为二者的对象。多数这种数据是持久化的并需要被存储在数据库中。主动记录使用最明显的方式,将数据访问逻辑放置在领域对象中。这种方式下所有人都知道如何读和写他们的数据进出数据库。 
  2. ^ Orator - An ActiveRecord ORM for Python. [2020-11-08]. (原始内容存档于2020-12-16). 
  3. ^ ObjectiveSql (页面存档备份,存于互联网档案馆)
  4. ^ JAVA 极速WEB+ORM框架 JFinal. [2020-11-08]. (原始内容存档于2020-11-21). 

外部链接

[编辑]