web.xml의 기본페이지를 수정.
WEB-INF/web.xml
<welcome-file-list>
<welcome-file>login</welcome-file>
</welcome-file-list>
header.jsp의 로그인 링크 "<%=request.getContextPath()%>/login"로 수정해 LoginController서블릿으로 가도록 수정.
LoginController서블릿의 doGet메서드 수정
LoginController.java(servlet)
HttpSession session = request.getSession();
// get방식으로 넘어왔을땐 user창과 message창을 비워줌
session.setAttribute("user", "");
session.setAttribute("message", "");
// 로그인 페이지로 이동
response.sendRedirect("login/login.jsp");
// login폴더 안의 login.jsp로 이동. forward가 아니라 단순히 페이지만 이동함
=> 이제 맨 처음 방문시 로그인페이지의 메시지창과 id창에 null값이 안뜸
TodoDao 인터페이스. 메서드와 매개변수, 리턴형을 지정한다.
구현은 안되어있으며 TodoDaoImpl클래스에서 이 인터페이스를 구현해서 기능을 완성해야함.
todoApp.dao/TodoDao.java(interface)
void insertTodo(Todo todo); // Create 입력 => 할 일을 DB에 입력
Todo selectTodo(long todoId); // Read => id로 할일을 검색
List<Todo> selectAllTodos(); // Read => 모든 할일을 검색 (DB에서 <Todo>의 list타입으로 결과를 리턴받음)
boolean deleteTodo(long todoId); // Delete => id로 할일을 삭제 (제대로 삭제되었는지 true, false로 리턴)
boolean updateTodo(Todo todo); // Update => 할일을 업데이트 (제대로 업데이트되었는지 true, false로 리턴)
TodoDao 인터페이스를 상속받아 TodoDaoImpl 클래스를 구현한다.
DB와 직접 연결되어 각 기능(CRUD)을 수행하는 작업을 할 예정.
앞서만든 TodoDao인터페이스에 메서드와 매개변수, 리턴형이 지정되어 있으므로 그대로 구현하면 된다.
todoApp.dao/TodoDaoImpl.class insert메서드
String INSERT_USER_SQL = "INSERT INTO todos(title, username, description, target_date, is_done) "
+ "VALUES (?, ?, ?, ?, ?)";
try {
// 우변은 JDBCUtils클래스에 미리 만들어둔 getConnection()메서드를 말함
Connection conn = JDBCUtils.getConnection();
PreparedStatement pstmt = conn.prepareStatement(INSERT_USER_SQL);
// 아래 속성들은 Todo클래스에서 가져와야함
pstmt.setString(1, todo.getTitle());
pstmt.setString(2, todo.getUsername());
pstmt.setString(3, todo.getDescription());
pstmt.setDate(4, JDBCUtils.getSQLDate(todo.getTargetDate()));
// JDBCUtils에서 만들어둔 SQLDate메서드로 java날짜를 SQL날짜로 변경해 4번째 ?에 넣어줌
pstmt.setBoolean(5, todo.isStatus()); // pstmt 준비완료 (모든 ?를 채움)
pstmt.executeUpdate(); // 입력은 리턴값이 없음
} catch (SQLException e) {
System.out.println("SQL todo입력 에러..");
} // insert 작업이므로 리턴값 없음
todoApp.dao/TodoDaoImpl.class delete메서드
// id로 검색해 삭제
String DELETE_TODO = "DELETE FROM todos WHERE id = ? ";
boolean rowdeleted = false;
try {
Connection conn = JDBCUtils.getConnection();
PreparedStatement pstmt = conn.prepareStatement(DELETE_TODO);
pstmt.setLong(1, todoId);
rowdeleted = pstmt.executeUpdate() > 0; // 한 행이상 삭제되면 true
} catch (SQLException e) {
System.out.println("SQL todo삭제 에러..");
}
System.out.println("삭제 완료");
return rowdeleted;
todoApp.dao/TodoDaoImpl.class update메서드
// 같은 id의 모든 데이터 업데이트
String UPDATE_TODO = "UPDATE todos set title=?, username=?, description=?, target_date=?, is_done=? WHERE id=?";
Boolean rowupdate = false;
try {
Connection conn = JDBCUtils.getConnection();
PreparedStatement pstmt = conn.prepareStatement(UPDATE_TODO);
// 아래 속성들은 Todo클래스에서 가져와야함
pstmt.setString(1, todo.getTitle());
pstmt.setString(2, todo.getUsername());
pstmt.setString(3, todo.getDescription());
pstmt.setDate(4, JDBCUtils.getSQLDate(todo.getTargetDate()));
pstmt.setBoolean(5, todo.isStatus());
pstmt.setLong(6, todo.getId());
rowupdate = pstmt.executeUpdate() > 0;
} catch (SQLException e) {
System.out.println("SQL todo업데이트 에러..");
return false;
}
System.out.println("업데이트 완료");
return rowupdate;
todoApp.dao/TodoDaoImpl.class selectTodo메서드
// 할일을 하나 검색했을 때. id로 검색함. 리턴값이 있음.
Todo todo = null;
String SELECT_TODO_BY_ID = "SELECT id, title, username, description, target_date, is_done FROM todos WHERE id = ?" ;
try {
Connection conn = JDBCUtils.getConnection(); // DB연결함
PreparedStatement pstmt = conn.prepareStatement(SELECT_TODO_BY_ID);
pstmt.setLong(1, todoId);
ResultSet rs = pstmt.executeQuery(); // 쿼리문 실행한 값을 ResultSet의 객체 rs에 저장
if(rs.next()) { // 결과가 있으면 값을 저장. 없을때 저장하면 에러발생.
long id = rs.getLong("id"); // 리턴값을 long타입의 id에 저장
// 우변은 DB의 속성이름, 좌변은 리턴받은 우변의 데이터를 저장하는 변수의 이름
String title = rs.getString("title");
String username = rs.getString("username");
String description = rs.getString("description");
LocalDate targetDate = rs.getDate("target_date").toLocalDate();
// 그냥 rs.getDate("target_date")하면 java날짜와 SQL날짜형이 달라 에러나므로 로컬데이트(자바날짜)로 변환
Boolean status = rs.getBoolean("is_done");
todo = new Todo(id, title, username, description, targetDate, status);
}
} catch (SQLException e) {
System.out.println("SQL todo검색 에러..");
}
return todo;
todoApp.dao/TodoDaoImpl.class selectAllTodos메서드
// 모든 할일을 검색했을 때
List<Todo> todos = new ArrayList<>(); // 빈 ArrayList를 생성
Connection conn = JDBCUtils.getConnection(); // DB연결함
String SELECT_ALL_TODOS = "SELECT * FROM todos"; // todos테이블 전체검색
// try-catch문은 PreparedStatement객체를 만들때 생기는 예외를 처리하는 작업이므로 Connection의 위치는 상관없음.
// 원래는 Connection객체를 선언할때도 예외처리가 필요하지만 JDBCUtils클래스에서 getConnection메서드의 예외처리를 이미 해주었으므로 여기선 다시 안해줘도 됨
try {
PreparedStatement pstmt = conn.prepareStatement(SELECT_ALL_TODOS); // 쿼리문에 ?가 없으므로 바로실행
ResultSet rs = pstmt.executeQuery(); // 쿼리실행 후 결과저장
// 결과가 여러행일 경우 while()을 사용하여 처리함. 한 행일땐 if문 사용.
while(rs.next()) { // 결과가 있을 경우 값을 저장. 없는데 저장하면 에러발생
long id = rs.getLong("id");
String title = rs.getString("title");
String username = rs.getString("username");
String description = rs.getString("description");
LocalDate targetDate = rs.getDate("target_date").toLocalDate();
// 그냥 rs.getDate("target_date")하면 java날짜와 SQL날짜형이 달라 에러나므로 로컬데이트(자바날짜)로 변환
Boolean status = rs.getBoolean("is_done");
// 리스트에 담기 (todo객체로 입력)
todos.add(new Todo(id, title, username, description, targetDate, status));
} // while문은 rs.next(한 행이 끝나면 다음행으로 넘어감)에 값이 있을 경우 반복해서 실행됨
} catch (SQLException e) {
System.out.println("SQL todo ALL검색 에러..");
return null;
}
return todos;
=> selectAllTodos메서드 잘모르겠으니 체크.
try-catch문은 PreparedStatement객체를 만들때 생기는 예외를 처리하는 작업이므로 Connection의 위치가 try-catch문 안이던 밖이건 관계없음.
원래는 Connection객체를 선언할때도 예외처리가 필요하지만 JDBCUtils클래스에서 getConnection메서드의 예외처리를 이미 해주었으므로 여기선 다시 안해줘도 된다.
test패키지 아래 test클래스를 만들어 main메서드에서 각 메서드의 기능이 DB에 작동되는지 테스트한다.
test/test.class
TodoDaoImpl dao = new TodoDaoImpl();
// 입력테스트용
Todo todo = new Todo(1L, "할일1", "hong", "처음 할일", LocalDate.parse("2022-01-20"), false);
Todo todo2 = new Todo(2L, "할일2", "son", "두번째 할일", LocalDate.parse("2022-01-21"), false);
Todo todo3 = new Todo(3L, "할일3", "kim", "세번째 할일", LocalDate.parse("2022-01-22"), true);
dao.insertTodo(todo);
dao.insertTodo(todo2);
dao.insertTodo(todo3);
// 검색테스트용
Todo t1 = dao.selectTodo(1);
System.out.println(t1.toString());
// 삭제테스트용
Boolean t2 = dao.deleteTodo(3);
// 업데이트 테스트용
Todo todo4 = new Todo(2L,"할일_수정", "park", "수정한 할일", LocalDate.parse("2022-01-31"), true);
dao.updateTodo(todo4);
// 전체검색 테스트용
List<Todo> todos = dao.selectAllTodos();
for(Todo todo5 : todos) {
System.out.println(todo5.toString());
}
입력 결과
검색 결과
전체검색 결과
업데이트 결과
삭제 결과
모든 메서드 정상동작 확인.