活动记录模式


该模式属于数据源体系结构模式目录,此目录属于企业应用程序体系结构模式
目的
将行包装在数据库表或视图中、封装数据库访问并在该数据上添加域逻辑的对象。
说明
每个Active Record负责保存和加载到数据库以及任何作用于数据的域逻辑。对象包含数据和行为。大部分数据都是持久性的,需要存储在数据库中。 Active Record使用最明显的方法,将数据访问逻辑放在域对象中。这样所有人都知道如何在数据库中读写他们的数据。
行数据网关和活动模式之间的主要区别是行数据网关模式不包含域逻辑方法,而活动模式对象包含域逻辑方法。举个例子,假设我们需要一些针对StudentGateway的域逻辑方法确定学生是否及格或是留校察看:
public boolean passes() {
  return grade!='F';
 }
 public boolean onProbation() { // a grade of D or F
     // results in being put on probation…
  return grade>='D';
 }

如何运作
Active Record的本质是一个域模型,其中类与底层数据库的记录结构非常接近。每个Active Record负责保存和加载到数据库以及任何作用于数据的域逻辑。 
Active Record类通常具有执行以下操作的方法:

  • 从SQL结果集行构造Active Record的实例

  • 构造一个新实例以便以后插入表中

  • 静态查找程序方法,用于包装常用的SQL查询并返回Active Record对象

  • 更新数据库并在其中插入Active Record中的数据

  • 获取并设置字段

  • 实现一些业务逻辑

Active Record与Row Data Gateway非常相似。主要区别在于,行数据网关仅包含数据库访问,而活动记录包含数据源和域逻辑。
何时使用
对于不太复杂的域逻辑,Active Record是一个很好的选择,例如创建,读取,更新和删除。基于单个记录的派生和验证在此结构中运行良好。
示例代码
示例:一个简单的人(Java)。
让我们为Person 类的  示例代码创建一个类图  来演示这种模式。

这是一个非常简单的示例,用于说明Active Record是如何工作的。我们从一个基本的Person类开始。
 

public class Person {
   private int id;
   private String lastName;
   private String firstName;
   private int numberOfDependents;
   // getter and setter methods
}

数据库设置具有相同的结构。

create table people (ID int primary key, lastname varchar,
                     firstname varchar, number_of_dependents int

要加载对象,Person类充当查找程序并执行加载。它在person类上使用静态方法。
public class Person{
  private final static String findStatementString =
         "SELECT id, lastname, firstname, number_of_dependents" +
         
"  FROM people" +
         
"  WHERE id = ?";
   public static Person find(Long id) {
      Person result = (Person) Registry.getPerson(id);
      if (result != null) return result;
      PreparedStatement findStatement = null;
      ResultSet rs = null;
      try {
         findStatement = DB.prepare(findStatementString);
         findStatement.setLong(1, id.longValue());
         rs = findStatement.executeQuery();
         rs.next();
         result = load(rs);
         return result;
      } catch (SQLException e) {
         throw new ApplicationException(e);
      } finally {
         DB.cleanUp(findStatement, rs);
      }
   }
   public static Person find(long id) {
      return find(new Long(id));
   }
   public static Person load(ResultSet rs) throws SQLException {
      Long id = new Long(rs.getLong(1));
      Person result = (Person) Registry.getPerson(id);
      if (result != null) return result;
      String lastNameArg = rs.getString(2);
      String firstNameArg = rs.getString(3);
      int numDependentsArg = rs.getInt(4);
      result = new Person(id, lastNameArg, firstNameArg, numDependentsArg);
      Registry.addPerson(result);
      return result;
   }
}

让我们将更新方法添加到Person类。

public class Person{
   private final static String updateStatementString =
         "UPDATE people" +
         
"  set lastname = ?, firstname = ?, number_of_dependents = ?" +
         
"  where id = ?";
   public void update() {
      PreparedStatement updateStatement = null;
      try {
         updateStatement = DB.prepare(updateStatementString);
         updateStatement.setString(1, lastName);
         updateStatement.setString(2, firstName);
         updateStatement.setInt(3, numberOfDependents);
         updateStatement.setInt(4, getID().intValue());
         updateStatement.execute();
      } catch (Exception e) {
         throw new ApplicationException(e);
      } finally {
         DB.cleanUp(updateStatement);
      }
   }
}

让我们将插入方法添加到Person类。

public class Person{
   private final static String insertStatementString =
         "INSERT INTO people VALUES (?, ?, ?, ?)";
   public Long insert() {
      PreparedStatement insertStatement = null;
      try {
         insertStatement = DB.prepare(insertStatementString);
         setID(findNextDatabaseId());
         insertStatement.setInt(1, getID().intValue());
         insertStatement.setString(2, lastName);
         insertStatement.setString(3, firstName);
         insertStatement.setInt(4, numberOfDependents);
         insertStatement.execute();
         Registry.addPerson(this);
         return getID();
      } catch (Exception e) {
         throw new ApplicationException(e);
      } finally {
         DB.cleanUp(insertStatement);
      }
   }
}

任何业务逻辑(例如计算豁免)都直接位于Person类中。

public class Person{
   public Money getExemption() {
      Money baseExemption = Money.dollars(1500);
      Money dependentExemption = Money.dollars(750);
      return baseExemption.add(dependentExemption.multiply(this.getNumberOfDependents()));
   }
   
   public boolean isFlaggedForAudit() {
      // Business logic 
   return true;
   }
   
   public String getTaxableEarnings() {
     
// Business logic
   }
}