前回のデータの永続化機構(データベースやデータファイル)とビジネスロジック間にDAO:Data Access Objectを配し、データの永続化機構をビジネスロジックから隠蔽する方法を説明しました。今回はDAOを使う場合の汎用的で現実的な構造を考えます。
前回のログイン処理(Daoを使った改良版)は、プログラムを簡潔にし保守性、拡張性を高めるためにデータベースアクセスを隠蔽する方法として説明しました。
しかし、実際のデータ永続化機構を使ったアプリケーションの開発では、ビジネスロジックの開発を始めようとするときには利用するデータ永続化機構が決定していない事があります。この場合、何らかのデータ永続化機構を使ったDAOをスタブとして用意しビジネスロジックの開発を進め、データ永続化機構が最終決定した段階でスタブを最終的なDAOに置き換えます。
そのためにはDAOをインターフェースとして用意し、スタブとしてのDAOや最終版のDAOはそれを実装する形にすれば、ビジネスロジックを全く修正することなくこの置き換えが可能です。
ビジネスロジックはインターフェースであるUserDaoを使って構築し、ビジネスロジックの単体テストまでにスタブとしてのUserDaoImplを作って、UserDaoFactoryのcreateDaoメソッドを使って各機能ごとのDAOを用意します。こうすることによってcreateDaoメソッドとUserDaoを実装するJavaプログラムを書き換えるだけで各種のデータの永続化機構に対応することが出来ます。
package com.example; import java.util.ArrayList; public interface UserDao { public UserDto findUser(String id); public ArrayList<UserDto> getUserList(); public int regUser(String id, String password); public int delUser(String id, String password); }
ビジネスロジックが必要としているインターフェースだけが記述されています。
package com.example; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; public class UserDaoImpl implements UserDao { private static Connection getConnection() { try { Class.forName("org.postgresql.Driver"); return DriverManager.getConnection("jdbc:postgresql:login","postgres", "root"); } catch (Exception e) { throw new IllegalArgumentException(e); } } private static void allClose(PreparedStatement statement, Connection connection) { if (statement != null) { try { statement.close(); } catch (SQLException e) { e.printStackTrace(); } } if (connection != null) { try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } static Connection connection = null; static PreparedStatement statement = null; public UserDto findUser(String id) { UserDto user = new UserDto(); try { connection = getConnection(); statement = connection.prepareStatement("SELECT * FROM userinf WHERE userid = ?"); statement.setString(1, id); ResultSet resultSet = statement.executeQuery(); if (!resultSet.next()) { return null; } user.setUserid(resultSet.getString("userid")); user.setPassword(resultSet.getString("password")); } catch (SQLException e) { e.printStackTrace(); } finally { allClose(statement, connection); } return user; } public ArrayList<UserDto> getUserList() { ArrayList<UserDto> list = new ArrayList<UserDto>(); try { connection = getConnection(); statement = connection.prepareStatement("SELECT * FROM userinf"); ResultSet resultSet = statement.executeQuery(); while (resultSet.next()) { UserDto ud = new UserDto(); ud.setNo(resultSet.getInt(1)); ud.setUserid(resultSet.getString(2)); ud.setPassword(resultSet.getString(3)); list.add(ud); } } catch (SQLException e) { e.printStackTrace(); } finally { allClose(statement, connection); } return list; } public int regUser(String id, String password) { int result = 0; try { connection = getConnection(); statement = connection.prepareStatement("INSERT INTO userinf (userid, password) VALUES (?, ?)"); statement.setString(1, id); statement.setString(2, password); result = statement.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { allClose(statement, connection); } return result; } public int delUser(String id, String password) { int result = 0; try { connection = getConnection(); statement = connection.prepareStatement("DELETE FROM userinf WHERE userid = ? AND password = ?"); statement.setString(1, id); statement.setString(2, password); result = statement.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { allClose(statement, connection); } return result; } }
この実装は、利用しているデータベースが同じなので前回のログイン処理(Daoを使った改良版)で紹介したDAOと内容が全く同じです。
package com.example; public class UserDaoFactory { public static UserDaoImpl createDao() { return new UserDaoImpl(); } }
唯一の機能は実装したUserDaoのインスタンスを返す事です。
一つの例としてユーザのログイン機能:UserLoginServlet.javaを見てみましょう。
package com.example; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; /** * Servlet implementation class LoginServlet */ @WebServlet("/UserLoginServlet") public class UserLoginServlet extends HttpServlet { private static final long serialVersionUID = 1L; /** * @see HttpServlet#HttpServlet() */ public UserLoginServlet() { super(); // TODO Auto-generated constructor stub } /** * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response) */ @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("UTF-8"); String id = request.getParameter("id"); String password = request.getParameter("password"); UserDao dao = UserDaoFactory.createDao(); UserDto user = dao.findUser(id); boolean isLogin = (user != null && id.equals(user.getUserid()) && password.equals(user.getPassword())); HttpSession session = request.getSession(); session.setAttribute("isLogin", isLogin); if (isLogin) { request.setAttribute("username", user.getUserid()); request.getRequestDispatcher("/user_login_success.jsp").forward(request, response); } else { request.setAttribute("error", "IDかパスワードが間違っています。\n再入力してください。"); request.getRequestDispatcher("/user_login.jsp").forward(request, response); } } }
37行目でUserDaoFactoryのcreateDao()を呼んでいるのは前回のログイン処理(Daoを使った改良版)のUserLoginServlet.javaの37行目で直接UserDaoをnewしてインスタンスを作っているのと同じです。
他の機能も同じようにUserDaoFactoryのcreateDao()を呼んでUserDaoのインスタンスを取得すればデータ永続化機構を利用することが出来ます。
以上でDAOを使ったビジネスロジックに対するデータ永続化機構の隠蔽の実現方法についての説明を終わります。
DAOをプログラミングの定石として活用できるように、本ページの説明だけでなくDAOを使わなかった場合、単独のDAOを使ってコードを簡潔に記述する場合の説明も合わせて参考にして頂くと良いでしょう。