DataBase는 데이터를 영구적으로 저장 할 수 있는 저장소
Data Base를 이용하면 서로 다른 시스템 간에 데이터를 공휴 할 수 있다
Data Base를 관리하는 시스템을 DBMS라고 한다
많은 종류의 DBMS들이 존재 ↓
Controller는 받은 일을 직접 처리하거나 다른 Model에 전달한다
Model은 Controller로 부터 요청받은 일을 처리하고 통지한다
이때 DB에 access할 일이 생길 경우 DB 처리를만을 위한 class를 생성한다 ( = DAO )
위 내용을 모두 수행을 해줄 수 있는 JDBC(Java Data Base Connectivity)
DB에 접속해서 Data를 꺼내온다는 것은 창고에서 물건을 꺼내 오는것과 같다
Connection 객체는 DB에서 하는 모든 일을 대신 DB에 접속해서 수행 해 준다.
// pom.xml에 라이브러리 추가
<!-- JDBC -->
<!-- https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-client -->
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
<version>2.7.3</version>
</dependency>
// Controller
@Controller
public class HomeController {
Logger logger = LoggerFactory.getLogger(HomeController.class);
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Model model) {
logger.info("컨트롤러 접근");
// DB에 접근해서 접속이 성공 되었는지 확인
// 1. Service에게 일을 시킨다
HomeService service = new HomeService();
boolean success = service.dbConnect();
model.addAttribute("result", success);
// 성공여부를 VIEW에 전송
return "home";
}
}
// Service
public class HomeService {
// logger에 log를 찍는 대상 class를 지정해 주어야 한다
Logger logger = LoggerFactory.getLogger(HomeService.class);
public boolean dbConnect() {
logger.info("서비스가 명령 받음 !");
HomeDAO dao = new HomeDAO();
// 서비스에서는 DAO에게 접속후 성공여부 반환받아 Controller에게 던진다.
return dao.dbConnect();
}
}
// DAO
public class HomeDAO {
Logger logger = LoggerFactory.getLogger(HomeDAO.class);
public boolean dbConnect() {
logger.info("DAO 접근");
boolean success = false;
// 1. context.xml 에서 정보를 가져온다.
// 우리가 찾는 정보는 context.xml의 Resource태그 안에 있다. (문제는 xml을 어떻게 읽어오나?0
try {
// 1-1. context.xml 자체를 java 객체로 변환
Context ctx = new InitialContext();
// 1-2 context.xml안에 있는 Resource 태그를 찾아온다.(이름을 이용해서)
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/MariaDB");
// 2. 그걸로 DB접속 요청( 커넥션 가져오기 )
Connection conn = ds.getConnection();
// 3. 커넥션이 있으면 성공
if(conn != null) {
success = true;
logger.info("connection : "+ conn); // logger는 문자열이 아니면 찍을 수 없다
// 4.자원 반납
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return success;
}
// context.xml 일부
<Resource
name="jdbc/MariaDB"
auth="Container"
type="javax.sql.DataSource"
driverClassName="org.mariadb.jdbc.Driver"
url="jdbc:mariadb://localhost:3306/gdj70"
username="web_user"
password="pass"
/>
Connection Pool의 장점
connection pool을 사용하기 위해서는 몇가지 작업이 필요하다
1. project의 pom.xml에 common-dbcp 라이브러리 추가
2. Server 폴더 내 context.xml의 Resource 속성 추가
// Controller
@Controller
public class HomeController {
Logger logger = LoggerFactory.getLogger(HomeController.class);
@RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Model model) {
logger.info("컨트롤러 접근");
HomeService service = new HomeService();
boolean success = service.dbConnect();
model.addAttribute("result", success);
return "home";
}
}
// Service
public class HomeService {
Logger logger = LoggerFactory.getLogger(HomeService.class);
public boolean dbConnect() {
logger.info("서비스 접근");
HomeDAO dao = new HomeDAO();
return dao.dbConnect();
}
}
// DAO
public class HomeDAO {
Logger logger = LoggerFactory.getLogger(HomeDAO.class);
public boolean dbConnect() {
logger.info("DAO 접근");
boolean success = false;
// 1. context.xml 에서 정보를 가져온다.
// 우리가 찾는 정보는 context.xml의 Resource태그 안에 있다. (문제는 xml을 어떻게 읽어오나?)
try {
// 1-1. context.xml 자체를 java 객체로 변환
Context ctx = new InitialContext();
// 1-2 context.xml안에 있는 Resource 태그를 찾아온다.(이름을 이용해서)
DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/MariaDB");
// 2. 그걸로 DB접속 요청( 커넥션 가져오기 )
Connection conn = ds.getConnection();
// 3. 커넥션이 있으면 성공
if(conn != null) {
success = true;
logger.info("connection : "+ conn); // logger는 문자열이 아니면 찍을 수 없다
// 4.자원 반납
conn.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return success;
}
}
DataBase와 접속이 성공했다면 Data를 처리하는 Query문을 실행해야 한다.
Connection 인터페이스에서 Query문을 실행 할 수 있는 두가지 방법을 제공한다.
Statement
: create / drop 과 같은 최초한번만 실행되는 쿼리에 유리
Prepared Statement
: select / update와 같은 반복사용할 쿼리에 유리
executeQuery()
: 반환되는 데이터가 있을경우 사용 ( SELECT)executeUpdate()
: DB에 변화를 줄 경우 사용 ( UPDATE / DELETE / INSERT )executeUpdate()
사용시 반환되는 int는 적용에 성공한 데이터 행(row)개수이다.public class HomeDAO {
Logger logger = LoggerFactory.getLogger(HomeDAO.class);
Connection conn = null;
// DAO가 호출이 된다는것은 곧 DB와 연결하겠다는 것을 의미
// 그래서 DB연결 과정을 생성자 초기화시에 해주기
public HomeDAO() {
try {
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("java:comp/env/jdbc/MariaDB");
conn = ds.getConnection();
} catch (Exception e) {
e.printStackTrace();
}
}
public boolean connect() {
boolean success = false;
if(conn!=null) {
success = true;
}
return success;
}
public boolean stmt() {
boolean success = false;
// 1. DB접속
if(conn != null) {
// 2. QUERY문 준비
String sql = "create table member("
+ "ID VARCHAR(50) primary KEY"
+ ",PW VARCHAR(100) not NULL"
+ ",NAME VARCHAR(50) not NULL"
+ ",AGE INT(3)"
+ ",GENDER VARCHAR(4)"
+ ",EMAIL VARCHAR(100)"
+ ")";
// 3. QUERY문 실행
try {
// 3-1 Statement 준비
Statement stmt = conn.createStatement();
// 3-2 QUERY문 실행
int row = stmt.executeUpdate(sql); // Updated Row를 반환
// 4. 결과값 확인
// INFO : kr.co.gudi.dao.HomeDAO - 영향을 받은 데이터 개수 : 0
// 테이블 생성은 데이터에 영향이 없으므로 0
logger.info("영향을 받은 데이터 개수 : "+ row);
success = true;
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
conn.close();
}catch (Exception e) {
}
}
}
return success;
}
}
// HomeDAO
public boolean join(HashMap<String, String> param) throws SQLException {
boolean success = false;
logger.info("join(DAO) 요청");
// 1. DB접속 - 생성자에서 이미 처리
// 2. Query문 준비
String sql = "insert into member(id, pw, name, age, gender, email)"
+ "values(?,?,?,?,?,?)";
// 3. Query문 실행
try {
// 3-1. PreparedStatement 준비
PreparedStatement pstmt = conn.prepareStatement(sql);
// 3-2. ? 인자 지정
pstmt.setString(1, param.get("id"));
pstmt.setString(2, param.get("pw"));
pstmt.setString(3, param.get("name"));
pstmt.setInt(4, Integer.parseInt(param.get("age")));
pstmt.setString(5, param.get("gender"));
pstmt.setString(6, param.get("email"));
// 3-3. Query문 실행
int row = pstmt.executeUpdate();
// 4. 결과값 반환
if(row == 1) {
success = true;
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
// 5. 자원반납
conn.close();
}
return false;
}
// HomeConroller
// @RequestParam 으로 parameter를 받는 방식
// -> 이미 parameter를 뽑아온 후이기 때문에 소스내에서 한글 인코딩 처리불가
@RequestMapping(value = "/join", method = RequestMethod.POST)
public String join( Model model, @RequestParam HashMap<String, String> param ) {
logger.info("join(controller) 요청");
logger.info("params : "+param);
HomeService service = new HomeService();
String msg = service.join(param);
model.addAttribute("msg", msg);
return "home";
}
위와 같이 POST방식으로 요청을 받아올때 parameter값의한글이 깨지는 현상이 일어난다. @RequestParam
을 이용해 parameter의 값을 가져오게 되면 소스내에서 인코딩 처리를 할 수 없다.
이럴때 Project의 WEB-INF폴더 안의 web.xml파일에 filter를 추가해주어야 한다.
executeQuery()는 값을 조회하는 query를 실행 해 주는 메서드이다.
executeQuery()
의 반환 타입을 보면 ResultSet인걸 확인 할 수있다.
executeQuery()
의 결과값을 ResultSet 객체로 반환하는것이다.
그리고 ResultSet 객체에 대한 MetaData를 뽑아 낼 수 있다
// HomeDAO
public void list() throws Exception {
// 1. 쿼리문 준비
String sql = "SELECT * FROM member";
// 2. PreparedStatement 준비 - ? 가 있으면 뭐가 들어갈지 설정(이번 메서드에서는 사용 '?' 없음
PreparedStatement pstmt = conn.prepareStatement(sql);
// 3. 쿼리문 실행 - executeQuery()의 결과값은 ResultSet을 반환한다.
ResultSet rs = pstmt.executeQuery(sql);
// 4. Result Set을 하나씩 뽑아내기
while (rs.next()) { // 값이 있는지 확인 , 있으면 해당 데이터로 커서가 이동
logger.info("id : "+rs.getString("id")); // 컬럼명으로 가져오기
logger.info("pw : "+rs.getString(2)); // 컬럼 인덱스 번호로 가져오기
logger.info("name : "+rs.getString("name"));
logger.info("age : "+rs.getInt("age"));
logger.info("gender : "+rs.getString("gender"));
logger.info("email : "+rs.getString("email"));
logger.info("____________________________________");
}
// 5. 자원 반납
pstmt.close();
rs.close();
conn.close();
}