본문 바로가기
Java/JSP & Servlet

[JSP&Servlet] 8. Servlet DB 연동 및 쿼리 실행

by Rosmary 2022. 12. 29.
728x90
반응형

 

 

 

 

이번 포스팅에서는 웹 페이지 제작 시 필연적으로 진행해야하는 Database, DB와의 연동을 진행해보려한다. DB 연동을 위한 환경 구성은 이전의 포스팅 내용을 참고하면 된다. 

 

사실, DB 연동은 일반 Java 파일에서도 진행할 수 있는 부분이라 반드시 Servlet 페이지에서 코드를 작성하지 않아도 된다. DB와 연동하는 Java 파일만 클래스로 컴파일하고 Servlet에서 이 클래스 파일을 Import하여 사용하는 방식을 많이 사용한다. 따라서 이번 포스팅에서는 굳이 Tomcat 서버를 구동시키지 않고 CMD를 통해 콘솔로 결과값을 확인해보려한다. 

 

 

1. Java와 DB 연동 과정 / 코드

 

직전의 포스팅에서 환경 구성을 진행하면서, Java에서 설치된 DB와 연동하기 위해 dbConnector라는 Jar파일을 하나 설치했었다. 이 파일은 설치된 DB의 종류에 맞춰 연동이 가능하도록 클래스 파일을 압축해놓은 것이라 보면 된다.

 

 

 

dbConnector jar 파일에서 직접적으로 사용하는 클래스 파일은 Driver라는 이름의 파일이다. 이 클래스 파일로부터 생성된 인스턴스 객체를 DB 연동을 위한 DriverMananer에 등록하는 것이 DB 연동의 가장 첫 단계다. 

 

Java에서는 DB와 관련된 클래스를 java.sql 패키지를 통해 제공하고 있다. 이 패키지 내에는 DriverMananger라는 클래스 파일이 하나 존재하는데, 이 파일을 통해 dbConnector의 Driver 객체를 등록하면 된다. Driver를 등록하는 매서드는 registerDriver()이며, static 매서드기 때문에 DriverManager로부터 인스턴스를 생성할 필요는 없다.

 

이 코드 작성 시 예외 처리를 많이 해줘야하는데, 테스트 중이라 귀찮으신 분들은 Exception 하나만 catch문으로 작성하자.

 

Driver가 등록되었기 때문에 현재 설치된 DB와의 연동이 가능한 상태다. 설치된 DB와의 연결은 DriverManager에서 getConnection()이라는 매서드를 사용한다. 이 매서드 역시 static 제어자가 적용되어 DriverManager 클래스로부터 직접 사용이 가능한데, 매개인자는 연결할 DB와 Table의 전체 주소, DB 계정 ID, DB 계정 PW 세 가지를 String 형태로 작성하면 된다. 매서드는 Connection이라는 객체를 반환하는데, 이 객체는 java.sql 패키지 내에 존재한다.

 

 

DB URL 주소 형태는 "jdbc:myql://DB서버_IP주소/데이터베이스명"을 띈다. 컴파일 클래스를 실행했을 때, 아래와 같이 CommunicationsException이 나타난다면 이는 DB 서비스가 실행이 되지 않은 상태라는 것이다. 클래스 파일 실행 전에 DB 서비스가 실행되는지 확인하자. 

 

!! 데이터베이스의 생성은 CREATE DATABASE {데이터베이스명};  명령어로 생성하면 된다.

 

 

모든 것이 정확하게 입력되었다면, 클래스 파일 실행 시 아무것도 나타나지 않는다. 이 결과까지 다다른 상태라면 DB와의 연동은 정상적으로 진행된 것이라 보면 된다. 

 

 

 

2. Statement 객체 생성 및 쿼리 전송

 

jdbc에서 DB와 연결이 완료되었다면, 쿼리를 날리기 위해 statement 객체를 생성해주어야 한다. 이 statement는 쿼리를 날릴 수 있는 executeQuery() 등의 매서드를 가지는데, 보통 하나의 statement는 둘 이상의 쿼리를 동시에 날릴 수 없게 설계되어 있다. 만약 두 개 이상의 쿼리를 동시에 날리고자 한다면, Statement 객체를 쿼리 수에 맞게 생성해주어야 한다.

 

Statement 클래스는 java.sql 패키지에 존재하며, 앞서 생성한 Connection 객체의 createStatment() 매서드를 사용하여 Statment 인스턴스화를 진행한다.

 

 

statement 객체를 createStatement() 매서드로 생성할 때, createStatement() 매서드 매개인자에 따라 성격이 달라지게 된다. createStatment의 매개인자는 쿼리의 결과를 저장하는 ResultSet의 열거형 값을 입력하게 되어 있는데, 기본값은 아래와 같다.

 

Connection.createStatement() =>

Connection.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY) 

 

즉, executeQuery() 매서드로 쿼리는 읽기(SELECT)만 가능하고(CONCUR_READ_ONLY), 여러 행의 결과가 나타났을 때 단순히 순차적으로 결과값을 볼 수 있게(TYPE_FORWARD_ONLY) 되어 있다. 이 부분은 조금 뒤에서 다시 살펴볼 예정이다. 

 

하여간, executeQuery로 테이블 조회를 진행해보자. 필자는 test라는 이름의 데이터베이스에 table1이라는 테이블을 만들고, 사람의 인적 사항 일부를 이 곳에 추가하였다.

 

 

저장된 모든 정보를 보기 위해 SELECT * FROM table1; 쿼리를 사용한다. 이를 executeQuery()의 매개인자로 추가하자.

 

 

 

executeQuery()의 결과는 java.sql 패키지에 정의된 ResultSet 클래스 객체 형태로 반환된다. 

이제 ResultSet을 통해 조회한 결과를 확인해보자.

 

 

3. ResultSet 결과 조회

 

ResultSet은 방금 필자가 전송한 쿼리의 결과가 저장된 객체다. 객체 형태이기 때문에 직접 출력을 진행하면 해당 객체의 주소만 반환되기 때문에 의미있는 결과가 나타나지 않는다. 대신 ResultSet은 몇몇 매서드를 통해 내부 결과를 확인할 수 있도록 설계되어 있다.

 

(1) next()

ResultSet.next() 매서드는 반환받은 쿼리의 결과값이 존재하는지를 Boolean 형태로 반환하는 기능을 가지고 있다. 현재 필자가 조회한 쿼리는 세 줄의 결과를 반환하기 때문에 ResultSet.next()의 결과를 loop로 돌려보면 세 번의 True 값이 반환된다.

 

 

즉, ResultSet의 결과를 조회하려면 일종의 커서로 위치를 이동해야하는데, next()는 이 커서를 다음 결과로 이동시키는 역할을 한다고 보면 된다.

 

 

 

(2) getXXX() 매서드

 

특정 결과에 커서가 놓이게 되면 커서가 놓인 라인까지는 접근이 가능한 상태다. 하지만 한 행에 기록된 결과를 개별 컬럼 값에 대해 접근하기 위해서는 행 결과에 저장된 각 값을 호출하는 매서드를 별도로 사용해야한다. 

 

이 기능을 하는 것이 get으로 시작되는 매서드들이다. get 뒤에는 각 컬럼이 가지는 데이터형에 맞게 Java 데이터 타입을 명시해주면 된다. 예를 들어, firstname의 경우 문자열(VARCHAR) 형태로 저장되어 있기 때문에 getString() 매서드를, age는 숫자(INT) 형태로 저장되어 있기 때문에 getInt() 매서드를 사용하면 된다. 

 

각 매서드 매개인자로 확인하고자 하는 컬럼의 이름을 넣어주면 된다.

 

 

 

4. 데이터 추가, 수정 및 삭제 

 

위에서 createStatement() 매서드로 생성한 Statement 객체의 경우, executeQuery로 SELECT 외의 다른 쿼리(UPDATE, INSERT, DELETE)가 매개인자로 존재하는 경우, 아래와 같이 에러가 발생한다. DB 테이블에서 alex의 나이가 0으로 잘못 입력되어 있어 이를 executeQuery()로 변경해보려한다. 결과는 아래와 같다.

 

 

createStatement에서 매개인자의 기본값인 CONCUR_READ_ONLY가 적용되어 Statement가 생성되었기 때문에, executeQuery()로 데이터의 내용을 변경하는 것이 불가능하다. 이 때문에 ResultSet은 데이터의 추가, 수정, 삭제를 위한 별도의 매서드를 제공하는데 바로 executeUpdate()다.

 

 

executeUpdate()로 쿼리 변경을 수행해보자. 단, executeUpdate는 별도의 반환값이 없다는 점을 주의하자.

 

 

 

데이터의 수정을 수행할 수 있는 방법이 하나 더 있기는 하다. createStatment() 매서드로 Statement 객체 생성 시, 두 번 째 매개인자를 ResultSet.CONCUR_UPDATE)로 지정하는 것이다. 이렇게 되는 경우, ResultSet에서 제공하는 updateXXX() 매서드를 통해 특정 컬럼의 값만 업데이트를 진행하는 것이 가능해진다. 

 

우선 예시를 보자.

 

 

 

updateXXX() 매서드는 ResultSet에 종속된 매서드이기 때문에 반드시 사용 전에 executeQuery 등으로 조회 데이터가 ResultSet에 남아 있어야 한다. 더불어서, 쿼리를 수행하는 statement 또한 READ ONLY가 아니라 업데이트가 가능하도록 매개변수를 바꿔 주어야 한다. 마지막으로 updateXXX() 매서드는 최종적으로 DB의 값을 변화시키지 않고, 단순히 값을 찾아 참조만 하는 개념이며, 실제 데이터를 업데이트 하기 위해 updateRow()라는 매서드를 최종 단계에서 사용해주어야만 실질적인 변화가 일어나게 된다. 

 

 

 

Fin.

반응형

댓글