두번째 필수강의 ch3. 14강
Spring으로 DB에 정보를 저장하고 수정해보는 작업을 하기 위해 User 모델을 생성한다. user_info 테이블과 동일한 멤버변수들을 가지고, getter, setter, 생성자, toString, equals와 hashcode 메서드들을 가진다.
public class User {
private String id;
private String pwd;
private String name;
private String email;
private Date birth;
private String sns;
private Date reg_date;
...
Test 클래스에 그대로 insert 메서드를 만든다. 먼저 Connection 객체를 만들고, PreparedStatement를 사용해 sql문을 작성해준다. 이 때 sql문을 전부 string에 저장하는 것이 아니라, 입력받을 값 부분은 ?로 표시하고, PreparedStatement의 setter들을 사용하여 매개변수로 받은 객체에서 정보값을 받아온다.
sql문을 그대로 작성했던 Statement와 달리 PreparedStatement는 SQL Injection 공격을 방어할 수 있고, 쌍따옴표와 따옴표를 사용할 필요가 없어 훨씬 가독성이 좋다. 또, 재사용성이 개선되어 성능이 올라간다는 장점이 있다.
public int insertUser(User user) throws Exception{
Connection conn = ds.getConnection();
// String sql = "insert into user_info values ('asdf', '1111', 'kim', 'aaa@aaa.com', '2022-12-02', 'twitter', now())";
String sql = "insert into user_info values (?,?,?,?,?,?,now())";
PreparedStatement preparedStatement = conn.prepareStatement(sql);
preparedStatement.setString(1, user.getId());
preparedStatement.setString(2, user.getPwd());
preparedStatement.setString(3,user.getName());
preparedStatement.setString(4, user.getEmail());
preparedStatement.setDate(5, new java.sql.Date(user.getBirth().getTime()));
preparedStatement.setString(6,user.getSns());
int rowCount = preparedStatement.executeUpdate(); // insert, delete, update에 사용
return rowCount;
}
따로 예외를 try-catch 문으로 잡거나 connection을 닫는 작업까지는 포함하지 않았다.
다음으로 insertUser를 테스트하는 메서드를 작성한다.
@Test
public void testInsertUser() throws Exception{
User user = new User("asdf", "1111","kim","aaa@aaa.com",new Date(),"twitter",new Date());
int rowCount = insertUser(user);
assertTrue(rowCount==1);
}
테스트를 순조롭게 통과한 것을 볼 수 있다.
실제로 테이블에도 잘 저장되었다.
테이블에 저장된 데이터를 전부 지우는 deleteAll도 만들어본다.
public void deleteAll() throws Exception{
Connection conn = ds.getConnection();
String sql = "delete from user_info ";
PreparedStatement preparedStatement = conn.prepareStatement(sql);
preparedStatement.executeUpdate();
}
이 deleteAll을 testInsertUser에 넣어, 같은 user 객체를 계속 입력했을 때 테스트가 통과하는지를 통해 deleteAll 메서드가 잘 작동하는지 확인할 수 있다.
연속으로 user 객체를 수정하지 않고 테스트를 돌려도 통과하는 것을 통해 deleteAll이 잘 작동하는 것을 확인하였다.
다음으로 selectUser를 만들어본다.
Select 쿼리는 excuteUpdate가 아닌 executeQuery를 사용한다.
public User selectUser(String id) throws Exception{
Connection conn = ds.getConnection();
String sql = "select * from user_info where id= ? ";
PreparedStatement preparedStatement = conn.prepareStatement(sql);
preparedStatement.setString(1,id);
ResultSet resultSet = preparedStatement.executeQuery();
if(resultSet.next()){
User user = new User();
user.setId(resultSet.getString(1));
user.setPwd(resultSet.getString(2));
user.setName(resultSet.getString(3));
user.setEmail(resultSet.getString(4));
user.setBirth(new Date(resultSet.getDate(5).getTime()));
user.setSns(resultSet.getString(6));
user.setReg_date(new Date(resultSet.getTimestamp(7).getTime()));
return user;
}
return null;
}
Primary Key인 id를 받아 일치하는 id를 가진 row를 ResultSet으로 전달한다. ResultSet에 내용이 있으면 User 객체에 정보값을 set하여 해당 user 객체를 반환하고, 없으면 null을 반환한다.
@Test
public void testSelectUser() throws Exception{
User user = selectUser("asdf");
assertTrue(user.getId().equals("asdf"));
}
테스트 클래스 내부에 있는 DataSource ds는 인스턴스 변수로, 클래스 내부의 인스턴스 매서드들은 해당 인스턴스를 공유할 수 있다. 그러나 JUnit은 메서드 하나하나를 별도의 객체에서 실행을 시키기 때문에 테스트 메서드들은 같은 ds를 공유하지 않는다.
"asdf" 아이디를 가진 User 객체를 select하고, 그 user의 id가 "asdf"와 일치하는지 테스트를 돌려본다.
그런데 db에 "asdf"라는 id의 user가 없으면 테스트는 실패한다. 따라서 deleteAll을 진행하고, insertUser로 "asdf" user를 입력한 다음 selectUser를 실행해보도록 한다.
@Test
public void testSelectUser() throws Exception{
deleteAll();
insertUser(new User(
"asdf", "1111","kim","aaa@aaa.com",new Date(),"twitter",new Date()));
User user = selectUser("asdf");
assertTrue(user.getId().equals("asdf"));
}
테스트가 성공한 것을 확인할 수 있었다.