Spring - DataBase [IT 국비지원/구디 아카데미/김지훈 강사님]

hooon__dii·2023년 9월 14일
0

Spring

목록 보기
2/5
post-thumbnail

DB

Data Base

DataBase는 데이터를 영구적으로 저장 할 수 있는 저장소
Data Base를 이용하면 서로 다른 시스템 간에 데이터를 공휴 할 수 있다

DBMS(Data Base Management System)

Data Base를 관리하는 시스템을 DBMS라고 한다

많은 종류의 DBMS들이 존재 ↓


DBMS와 연결

DAO DTO

Controller는 받은 일을 직접 처리하거나 다른 Model에 전달한다
Model은 Controller로 부터 요청받은 일을 처리하고 통지한다
이때 DB에 access할 일이 생길 경우 DB 처리를만을 위한 class를 생성한다 ( = DAO )

Java와 Data Base를 연결 하기 위해 필요한것 생각해 보기

  1. Java와 DB 연결 프로그래밍
  2. DB 쿼리 실행 프로그래밍
  3. DB 쿼리 반환 값 추출 프로그래밍

위 내용을 모두 수행을 해줄 수 있는 JDBC(Java Data Base Connectivity)

DB에 접속해서 Data를 꺼내온다는 것은 창고에서 물건을 꺼내 오는것과 같다
Connection 객체는 DB에서 하는 모든 일을 대신 DB에 접속해서 수행 해 준다.

JDBC를 활용하여 DBMS와 연결 해보기

// 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의 장점

  • Pool 속에 미리 커넥션이 생성되어 있기 때문에 connection을 생성하는데 시간이 소비되지 않는다.
  • connection을 재사용하기 때문에 생성되는 connection 수가 많지 않다.
  • connectiond을 생성하고 제거하는데 필요한 시간이 소요 되지 않기 때문에 application의 실행 속도가 빨리지며 한번에 생성될 수 있는 connection 수를 제어하기 때문에 동시 접속자 수가 많아도 쉽게 다운 되지 않는다.

connection pool을 사용하기 위해서는 몇가지 작업이 필요하다
1. project의 pom.xml에 common-dbcp 라이브러리 추가

2. Server 폴더 내 context.xml의 Resource 속성 추가

Connection Pool을 활용하여 DBMS와 연결해보기

// 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;
	}
}

Query

DataBase와 접속이 성공했다면 Data를 처리하는 Query문을 실행해야 한다.
Connection 인터페이스에서 Query문을 실행 할 수 있는 두가지 방법을 제공한다.

Statement / Prepared Statement

Statement : create / drop 과 같은 최초한번만 실행되는 쿼리에 유리
Prepared Statement : select / update와 같은 반복사용할 쿼리에 유리

  • executeQuery() : 반환되는 데이터가 있을경우 사용 ( SELECT)
  • executeUpdate() : DB에 변화를 줄 경우 사용 ( UPDATE / DELETE / INSERT )
  • executeUpdate() 사용시 반환되는 int는 적용에 성공한 데이터 행(row)개수이다.

Statement를 이용하여 테이블 생성해보기

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;
	}
}

Prepared Statement를 이용하여 데이터 입력해보기

// 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;
	}

ps. Controller에 post로 전송시 한글깨짐 처리방법

// 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를 추가해주어야 한다.


Result Set

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();
}

Pool장에 빠지고 싶다

0개의 댓글