Ch. 03 Spring DB연결, DAO
2023. 1. 13. 12:46
1. DB 연결하기
<JDBC api>
Maven repository에서 MySQL connector maven 복사해서 pom.xml에 붙혀넣기
// 스키마의 이름(springbasic)이 다른 경우 알맞게 변경
String DB_URL = "jdbc:mysql://localhost:3306/springbasic?useUnicode=true&characterEncoding=utf8";
// DB의 userid와 pwd를 알맞게 변경
String DB_USER = "root";
String DB_PASSWORD = "1234";
String DB_DRIVER = "com.mysql.jdbc.Driver";
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName(DB_DRIVER);
ds.setUrl(DB_URL);
ds.setUsername(DB_USER);
ds.setPassword(DB_PASSWORD);
<Spring jdbc>
public static void main(String[] args) throws Exception{
ApplicationContext ac = new GenericXmlApplicationContext("file:src/main/webapp/WEB-INF/spring/**/root-context.xml");
DataSource ds = ac.getBean(DataSource.class);
Connection conn = ds.getConnection(); // 데이터베이스의 연결을 얻는다.
System.out.println("conn = " + conn);
// assertTrue(conn!=null);
}
<TDD> - JUnit Test Framework 사용
- TDD(Test Drivien Development) 테스트 주도 개발
- 우리가 실행결과를 직접 눈으로 확인하는게 아니고 JUnit으로 Test 자동화해서 Test 성공여부만 볼 수 있음.
- 메소드 위에 @Test붙이고 메소드는 public void 여야 한다.
- assert문을 통해 테스트 결과 확인
public class DBConnectionTest2Test {
@Test
public void main() throws Exception{
ApplicationContext ac = new GenericXmlApplicationContext("file:src/main/webapp/WEB-INF/spring/**/root-context.xml");
DataSource ds = ac.getBean(DataSource.class);
Connection conn = ds.getConnection(); // 데이터베이스의 연결을 얻는다.
System.out.println("conn = " + conn);
assertTrue(conn!=null); // 괄호 안의 조건식이 true면, 테스트 성공 아니면 실패
}
}
<@Autowired를 통한 자동 bean 연결>
- @RunWith(SpringJUnit4ClassRunner.class)이 ac를 자동으로 만들어 줌 (성능도 더 좋음)
- @ContextConfiguration(locations = {"file:src/main.webapp/WEB-INF/spring/**/root-context.xml"})로 위치 설정
- ApplicationContext도 주입받아 사용가능 (@Autowired ApplicationContext ac)
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/**/root-context.xml"})
public class DBConnectionTest2Test {
@Autowired DataSource ds;
@Test
public void main() throws Exception{
Connection conn = ds.getConnection(); // 데이터베이스의 연결을 얻는다.
System.out.println("conn = " + conn);
assertTrue(conn!=null); // 괄호 안의 조건식이 true면, 테스트 성공 아니면 실패
}
}
2. 구현해보기
<insertUser>
// 사용자 정보를 user_info 테이블에 저장하는 메서드
public int insertUser(User user) throws Exception{
Connection conn = ds.getConnection(); // 데이터베이스의 연결을 얻음
String sql = "insert into user_info values (?,?,?,?,?,?,now())";
// PreparedStatement를 통해 values 안에 ? 채우고 밑에서 setString (",' 복잡하지 않게)
// SQL Injection 공격방지, 성능향상
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1,user.getId());
pstmt.setString(2,user.getPwd());
pstmt.setString(3,user.getName());
pstmt.setString(4,user.getEmail());
pstmt.setDate(5,new java.sql.Date(user.getBirth().getTime()));
pstmt.setString(6,user.getSns());
int rowCnt = pstmt.executeUpdate(); // insert,delete,update
return rowCnt;
}
<selectUser>
public User selectUser(String id) throws Exception{
Connection conn = ds.getConnection(); // 데이터베이스의 연결을 얻음
String sql = "select * from user_info where id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1,id);
ResultSet rs = pstmt.executeQuery(); // insert,delete,update
if(rs.next()){
User user = new User();
user.setId(rs.getString(1));
user.setPwd(rs.getString(2));
user.setName(rs.getString(3));
user.setEmail(rs.getString(4));
user.setBirth(new Date(rs.getDate(5).getTime()));
user.setSns(rs.getString(6));
user.setReg_date(new Date(rs.getTimestamp(7).getTime()));
return user;
}
return null;
}
<deleteUser> 랑 <deleteAll>
public int deleteUser(String id) throws Exception{
Connection conn = ds.getConnection(); // 데이터베이스의 연결을 얻음
String sql = " delete from user_info where id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1,id);
int rowCnt = pstmt.executeUpdate(); // insert,delete,update
return rowCnt;
}
private void deleteAll() throws Exception{
Connection conn = ds.getConnection(); // 데이터베이스의 연결을 얻음
String sql = "delete from user_info";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.executeUpdate(); // insert,delete,update
}
<Test>
@Test
public void insertUserTest() throws Exception{
User user = new User("aadasdca","1234","abc","aaa@aaa.com",new java.util.Date(),
"fb",new java.util.Date());
deleteAll();
int rowCnt = insertUser(user);
assertTrue(rowCnt==1);
}
@Test
public void selectUserTest() throws Exception{
deleteAll();
User user = new User("aadasdca","1234","abc","aaa@aaa.com",new java.util.Date(),
"fb",new java.util.Date());
int rowCnt = insertUser(user);
User user2 = selectUser("aadasdca");
assertTrue(user2.getId().equals("aadasdca"));
}
@Test
public void deleteUserTest() throws Exception{
deleteAll();
int rowCnt = deleteUser("aadasdca");
assertTrue(rowCnt == 0);
User user = new User("aadasdca","1234","abc","aaa@aaa.com",new java.util.Date(),
"fb",new java.util.Date());
rowCnt = insertUser(user);
assertTrue(rowCnt == 1);
rowCnt = deleteUser(user.getId());
assertTrue(rowCnt == 1);
assertTrue(selectUser(user.getId()) == null);
}
3. DAO의 작성과 적용
<DAO(Data Access Object)>
- 데이터에 접근하기 위한 객체
- DAO가 CRUD를 통해 DB에 접근함 (DB 테이블당 하나의 DAO를 작성)
<계층(Layer)의 분리>
- Controller에서 DAO를 이용해서 DB를 다룬다 (Controller가 바로 DB에 접근할려고하면 반드시 중복이 생김)
- DAO를 영속계층(Persistence Layer, Data Access Layer) , Controller를 Presentation Layer라고 함 (계층 분리)
- 인터페이스를 이용해 UserDao를 MySQLUserDao, OracleUserDao로 사용하면 편함
[참고] - 제일 좋은 예외처리 방법
- AutoCloseable을 구현한 객체만 사용 가능
- 수동으로 안해도 자동으로 close 된다.
// try-with-resources - since jdk7
try (
Connection conn = ds.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql); // SQL Injection공격, 성능향상
){
pstmt.setString(1, user.getPwd());
pstmt.setString(2, user.getName());
pstmt.setString(3, user.getEmail());
pstmt.setDate(4, new java.sql.Date(user.getBirth().getTime()));
pstmt.setString(5, user.getSns());
pstmt.setTimestamp(6, new java.sql.Timestamp(user.getReg_date().getTime()));
pstmt.setString(7, user.getId());
rowCnt = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
return FAIL;
}
<UserDao interface>
- UserDaoImpl에서는 실제 구현체인 UserDaoImpl을 쓰는게 아니고 @Autowired UserDao userDao;로 인터페이스를 사용
public interface UserDao {
int deleteUser(String id);
User selectUser(String id) throws Exception;
// 사용자 정보를 user_info테이블에 저장하는 메서드
int insertUser(User user);
// 매개변수로 받은 사용자 정보로 user_info테이블을 update하는 메서드
int updateUser(User user);
void deleteAll() throws Exception;
}
<test>
@Test
public void updateUser() throws Exception {
// Date의 시간정보 삭제 년,월,일 만
Calendar cal = Calendar.getInstance();
cal.clear();
cal.set(2021,1,1);
userDao.deleteAll();
User user = new User("asdf","1234","abc","aaa@aaa.com", new Date(cal.getTimeInMillis()),"fb",new Date(cal.getTimeInMillis()));
int rowCnt = userDao.insertUser(user);
assertTrue(rowCnt==1);
user.setPwd("4321");
user.setEmail("bbb@bbb");
rowCnt = userDao.updateUser(user);
assertTrue(rowCnt == 1);
User user2 = userDao.selectUser(user.getId());
System.out.println("user = " + user);
System.out.println("user2 = " + user2);
assertTrue(user.equals(user2));
}
'Spring > 스프링의 정석' 카테고리의 다른 글
Ch. 03 Spring 서비스 계층의 분리와 @Transactional (0) | 2023.01.14 |
---|---|
Ch. 03 Spring Transaction, AOP (0) | 2023.01.14 |
Ch. 03 Spring DI (0) | 2023.01.12 |
Ch. 03 Spring DI 흉내내기 (0) | 2023.01.11 |
Ch. 02 Spring DispatcherServlet, 데이터변환과 검증 (0) | 2023.01.10 |