【PofEAA】DataMapperパターン

DataMapperパターン

 * モデルとデータベースの間を取りなすMapperという中間的な構造を持っている

サンプル

DBテーブル/PostgreSQL

person

CREATE TABLE person
(
  id character(8) NOT NULL,
  name character varying(100),
  sex character(1),
  updatedate timestamp without time zone,
  CONSTRAINT person_pkey PRIMARY KEY (id)
)

Javaソース

BaseDbObject.java

public class BaseDbObject {
   protected String id;

   public String getId() {
      return this.id;
   }

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

Person.java

import java.sql.Timestamp;

public class Person extends BaseDbObject {
   private String name;
   private String sex;
   private Timestamp updatedate;

   public Person(String id, String name, String sex, Timestamp updatedate) {
      this.id = id;
      this.name = name;
      this.sex = sex;
      this.updatedate = updatedate;
   }

   public String getName() {
      return name;
   }

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

   public String getSex() {
      return sex;
   }

   public void setSex(String sex) {
      this.sex = sex;
   }

   public Timestamp getUpdatedate() {
      return updatedate;
   }

   public void setUpdatedate(Timestamp updatedate) {
      this.updatedate = updatedate;
   }
}

PersonMapper.java

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.List;

public class PersonMapper extends BaseMapper<Person> {
   private static final String STATEMENT_FIND = "SELECT * FROM person WHERE id = ?";
   private static final String STATEMENT_FIND_BY_NAME = "SELECT * FROM person WHERE name = ?";
   private static final String STATEMENT_INSERT = "INSERT INTO person VALUES ( ?, ?, ?, ? )";
   private static final String STATEMENT_UPDATE = "UPDATE person SET name = ?, sex = ?, updatedate = ? WHERE id = ?";
   private static final String STATEMENT_DELETE = "DELETE FROM person WHERE id = ?";

   public PersonMapper() throws ClassNotFoundException, SQLException {
      super();
   }
   
   public List<Person> findByName(String name) {
      try {
         PreparedStatement pstmt = this.connection.prepareStatement(STATEMENT_FIND_BY_NAME);
         pstmt.setString(1, name);
         ResultSet rs = pstmt.executeQuery();
         return loadAll(rs);
      } catch (SQLException e) {
         throw new RuntimeException(e);
      }
   }

   public void update(Person subject) {
      try {
         PreparedStatement pstmt = this.connection.prepareStatement(STATEMENT_UPDATE);
         pstmt.setString(1, subject.getName());
         pstmt.setString(2, subject.getSex());
         pstmt.setTimestamp(3, subject.getUpdatedate());
         pstmt.setString(4, subject.getId());
         pstmt.executeUpdate();
      } catch (SQLException e) {
         throw new RuntimeException(e);
      }
   }

   public void delete(Person subject) {
      try {
         PreparedStatement stmt = this.connection.prepareStatement(STATEMENT_DELETE);
         stmt.setString(1, subject.getId());
         stmt.executeUpdate();
      } catch (SQLException e) {
         throw new RuntimeException(e);
      }
   }

   @Override
   protected String findStatement() {
      return STATEMENT_FIND;
   }

   @Override
   protected String insertStatement() {
      return STATEMENT_INSERT;
   }

   @Override
   protected Person doLoad(String id, ResultSet rs) throws SQLException {
      String name = rs.getString(2);
      String sex = rs.getString(3);
      Timestamp updatedate = rs.getTimestamp(4);
      return new Person(id, name, sex, updatedate);
   }

   @Override
   protected void doInsert(Person subject, PreparedStatement pstmt)
         throws SQLException {
      pstmt.setString(2, subject.getName());
      pstmt.setString(3, subject.getSex());
      pstmt.setTimestamp(4, subject.getUpdatedate());
   }
}

BaseMapper.java

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class BaseMapper<E extends BaseDbObject> implements
      IFinder<E> {
   protected Map<String, E> loadedMap = new HashMap<String, E>();

   protected Connection connection;
   
   abstract protected String findStatement();

   abstract protected String insertStatement();

   abstract protected E doLoad(String id, ResultSet rs) throws SQLException;

   abstract protected void doInsert(E domainObject, PreparedStatement pstmt)
         throws SQLException;

   public BaseMapper() throws ClassNotFoundException, SQLException {
      Class.forName("org.postgresql.Driver");
      this.connection = DriverManager.getConnection(
            "jdbc:postgresql://localhost:5432/Sample", "user", "password");
   }
   @Override
   public E find(String id) {
      E domainObject = loadedMap.get(id);
      if (domainObject != null) {
         return domainObject;
      }

      try {
         PreparedStatement pstmt = this.connection.prepareStatement(findStatement());
         pstmt.setString(1, id);
         ResultSet rs = pstmt.executeQuery();
         rs.next();
         domainObject = load(rs);
         return domainObject;
      } catch (SQLException e) {
         throw new RuntimeException(e);
      }
   }

   public void insert(E subject) {
      try {
         PreparedStatement pstmt = this.connection.prepareStatement(insertStatement());
         pstmt.setString(1, subject.getId());
         doInsert(subject, pstmt);
         pstmt.executeUpdate();
         loadedMap.put(subject.getId(), subject);
      } catch (SQLException e) {
         throw new RuntimeException(e);
      }
   }

   protected E load(ResultSet rs) throws SQLException {
      String id = rs.getString(1);
      if (loadedMap.containsKey(id)) {
         return loadedMap.get(id);
      }
      E domainObjcet = doLoad(id, rs);
      loadedMap.put(id, domainObjcet);
      return domainObjcet;
   }

   protected List<E> loadAll(ResultSet rs) throws SQLException {
      List<E> results = new ArrayList<E>();
      while (rs.next()) {
         results.add(load(rs));
      }
      return results;
   }
}

IFinder.java

public interface IFinder<E> {
   E find(String id);
}

Main.java

* 使用者
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.List;

public class Main {

   public static void main(String[] args) throws ClassNotFoundException, SQLException {
      PersonMapper personMapper = new PersonMapper();
      Person mike = new Person("X0000034", "Mike", "m", new Timestamp(System.currentTimeMillis()));

      // 追加
      personMapper.insert(mike);
      
      Person foundMike = personMapper.find("X0000034");
      Main.print(foundMike);
      
      List<Person> foundMikes = personMapper.findByName("Mike");
      for (Person person : foundMikes) {
         Main.print(person);
      }
      
      Person tom = new Person("X0000032", "Tom", "m", new Timestamp(System.currentTimeMillis()));

      // 更新
      personMapper.update(tom);
      
      Person foundTom = personMapper.find("X0000032");
      Main.print(foundTom);
   }
   
   private static void print(Person person) {
      System.out.println("ID " + person.getId());
      System.out.println("name " + person.getName());
      System.out.println("Sex " + person.getSex());
      System.out.println("Update date " + person.getUpdatedate());
      System.out.println();
   }
}