该模式属于数据源架构模式目录,此目录属于企业应用程序体系结构模式。
目的
一个映射器层,在对象和数据库之间移动数据,同时保持它们彼此和映射器本身独立。映射器(Mapper )是指在两个独立对象之间建立通信的对象。
适用性
在以下任何情况下使用Data Mapper
说明
- Data Mapper是一个软件层,用于将内存中对象与数据库分开。
- 使用Data Mapper,即使存在数据库,内存中的对象也不需要知道; 他们不需要SQL接口代码,当然也不需要了解数据库模式。
如何运作
域和数据源之间的分离是Data Mapper的主要功能,但要实现这一点,需要处理大量的细节。
一个简单的案例要有一个Person和Person 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); }
}
|