数据映射器模式(Data Mapper Pattern)

19-03-21 jdon
         

该模式属于数据源架构模式目录,此目录属于企业应用程序体系结构模式

目的

一个映射器层,在对象和数据库之间移动数据,同时保持它们彼此和映射器本身独立。映射器(Mapper )是指在两个独立对象之间建立通信的对象。

适用性

在以下任何情况下使用Data Mapper

  • 当您想要从DB访问层分离数据对象时

  • 当您想要编写多个数据检索/持久性实现时

说明

  • Data Mapper是一个软件层,用于将内存中对象与数据库分开。

  • 它的职责是在两者之间传输数据,并将它们彼此隔离。 

  • 使用Data Mapper,即使存在数据库,内存中的对象也不需要知道; 他们不需要SQL接口代码,当然也不需要了解数据库模式。 

如何运作

域和数据源之间的分离是Data Mapper的主要功能,但要实现这一点,需要处理大量的细节。

一个简单的案例要有一个PersonPerson Mapper类。若要从数据库加载人员,客户端将在mapper上调用find方法。映射器使用Identity Map模式来查看此人是否已加载; 如果没有,它加载它。

示例代码

让我们为StudentMapper的  示例代码创建一个类图,  以演示这种模式。

第1步:创建Student域类。

public final class Student implements Serializable {

  private static final long serialVersionUID = 1L;

  private int studentId;
  private String name;
  private char grade;


  /**
   * Use this constructor to create a Student with all details
   *
   * @param studentId as unique student id
   * @param name as student name
   * @param grade as respective grade of student
   */
  public Student(final int studentId, final String name, final char grade) {
    this.studentId = studentId;
    this.name = name;
    this.grade = grade;
  }

  /**
   *
   * @return the student id
   */
  public int getStudentId() {
    return studentId;
  }

  /**
   *
   * @param studentId as unique student id
   */
  public void setStudentId(final int studentId) {
    this.studentId = studentId;
  }

  /**
   *
   * @return name of student
   */
  public String getName() {
    return name;
  }

  /**
   *
   * @param name as 'name' of student
   */
  public void setName(final String name) {
    this.name = name;
  }

  /**
   *
   * @return grade of student
   */
  public char getGrade() {
    return grade;
  }

  /**
   *
   * @param grade as 'grade of student'
   */
  public void setGrade(final char grade) {
    this.grade = grade;
  }

  /**
 *
 */
  @Override
  public boolean equals(final Object inputObject) {

    boolean isEqual = false;

    /* Check if both objects are same */
    if (this == inputObject) {

      isEqual = true;
    } else if (inputObject != null && getClass() == inputObject.getClass()) {

      final Student inputStudent = (Student) inputObject;

      /* If student id matched */
      if (this.getStudentId() == inputStudent.getStudentId()) {

        isEqual = true;
      }
    }

    return isEqual;
  }

  /**
 *
 */
  @Override
  public int hashCode() {

    /* Student id is assumed to be unique */
    return this.getStudentId();
  }

  /**
 *
 */
  @Override
  public String toString() {
    return "Student [studentId=" + studentId + ", name=" + name + ", grade=" + grade + "]";
  }
}

第2步:让我们使用Runtime Exception创建,以避免依赖于实现异常。这有助于解耦。

public final class DataMapperException extends RuntimeException {

  private static final long serialVersionUID = 1L;

  /**
   * Constructs a new runtime exception with the specified detail message. The cause is not
   * initialized, and may subsequently be initialized by a call to {@link #initCause}.
   *
   * @param message the detail message. The detail message is saved for later retrieval by the
   *        {@link #getMessage()} method.
   */
  public DataMapperException(final String message) {
    super(message);
  }
}

第3步:创建StudentDataMapper接口 - 接口列出了所有可能的学生映射器的可能行为。

public interface StudentDataMapper {

  Optional<Student> find(int studentId);

  void insert(Student student) throws DataMapperException;

  void update(Student student) throws DataMapperException;

  void delete(Student student) throws DataMapperException;
}

第4步:让我们实现上面的接口 - 对学生数据执行动作。这个实现是在内存中,你可以在这里使用数据库连接。

public final class StudentDataMapperImpl implements StudentDataMapper {

  /* Note: Normally this would be in the form of an actual database */
  private List<Student> students = new ArrayList<>();

  @Override
  public Optional<Student> find(int studentId) {

    /* Compare with existing students */
    for (final Student student : this.getStudents()) {

      /* Check if student is found */
      if (student.getStudentId() == studentId) {

        return Optional.of(student);
      }
    }

    /* Return empty value */
    return Optional.empty();
  }

  @Override
  public void update(Student studentToBeUpdated) throws DataMapperException {


    /* Check with existing students */
    if (this.getStudents().contains(studentToBeUpdated)) {

      /* Get the index of student in list */
      final int index = this.getStudents().indexOf(studentToBeUpdated);

      /* Update the student in list */
      this.getStudents().set(index, studentToBeUpdated);

    } else {

      /* Throw user error after wrapping in a runtime exception */
      throw new DataMapperException("Student [" + studentToBeUpdated.getName() + "] is not found");
    }
  }

  @Override
  public void insert(Student studentToBeInserted) throws DataMapperException {

    /* Check with existing students */
    if (!this.getStudents().contains(studentToBeInserted)) {

      /* Add student in list */
      this.getStudents().add(studentToBeInserted);

    } else {

      /* Throw user error after wrapping in a runtime exception */
      throw new DataMapperException("Student already [" + studentToBeInserted.getName() + "] exists");
    }
  }

  @Override
  public void delete(Student studentToBeDeleted) throws DataMapperException {

    /* Check with existing students */
    if (this.getStudents().contains(studentToBeDeleted)) {

      /* Delete the student from list */
      this.getStudents().remove(studentToBeDeleted);

    } else {

      /* Throw user error after wrapping in a runtime exception */
      throw new DataMapperException("Student [" + studentToBeDeleted.getName() + "] is not found");
    }
  }

  public List<Student> getStudents() {
    return this.students;
  }
}

第5步:让我们测试一下这种模式。下面的Client类演示了基本的CRUD操作:创建,读取,更新和删除。

public final class Client {

  private static Logger log = Logger.getLogger(App.class);

  /**
   * Program entry point.
   * 
   * @param args command line args.
   */
  public static void main(final String... args) {

    /* Create new data mapper for type 'first' */
    final StudentDataMapper mapper = new StudentDataMapperImpl();

    /* Create new student */
    Student student = new Student(1, "Adam", 'A');

    /* Add student in respectibe store */
    mapper.insert(student);

    log.debug("App.main(), student : " + student + ", is inserted");

    /* Find this student */
    final Optional<Student> studentToBeFound = mapper.find(student.getStudentId());

    log.debug("App.main(), student : " + studentToBeFound + ", is searched");

    /* Update existing student object */
    student = new Student(student.getStudentId(), "AdamUpdated", 'A');

    /* Update student in respectibe db */
    mapper.update(student);

    log.debug("App.main(), student : " + student + ", is updated");
    log.debug("App.main(), student : " + student + ", is going to be deleted");

    /* Delete student in db */
    mapper.delete(student);
  }

}