1. 자바와 마리아DB 연결하기 +@Test 어노테이션
전체 코드 및 간단한 설명
DBConnection
package db; import java.sql.Connection; import java.sql.DriverManager; public class DBConnection { public static Connection getInstance() { //싱글톤 디자인 패턴 String username = "root"; String password = "1234"; String url = "jdbc:mariadb://localhost:3306/cosdb"; //프로토콜이 적용된 소켓 try { Connection conn = DriverManager.getConnection(url, username, password); System.out.println("db connect success"); return conn; } catch (Exception e) { e.printStackTrace(); } return null; } }
getConnection() 메서드를 사용하여 Connection 타입의 객체를 얻을 수 있다.
그래서 getInstance() (심지어 애는 싱글톤)가 아니라 getConnection 임
* getConnection을 보니 매개변수로 url, user, password 받게 되어있음.
(순서 맞춰서 작성하자!)
* InputStream이랑 getInputStream 기억하지? 그것처럼 get
[ 왜 리턴을 하는지? ]
싱글톤이기 때문! 싱글톤 패턴에서는 해당 클래스의 인스턴스를 오직 하나만 생성하고, 그 인스턴스를 반환해야 한다. 그래서 return conn;은 getInstance() 메소드에서 생성된 Connection 객체를 반환하는 역할을 한다. 변수에 담으려면 return 해야해서
[ 싱글톤인데 왜 new를 안 쓰는지? ]
Connection은 인터페이스이기 때문에 직접 인스턴스화할 수 없다. 따라서, Connection 인터페이스를 구현한 클래스의 인스턴스를 생성해야 한다. 그래서 DriverManager.getConnection() 메서드를 호출하여 Connection 인터페이스를 구현한 클래스의 인스턴스를 반환받을 수 있는 것.
BankApp
import db.DBConnection; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class BankApp { public static void main(String[] args) { Connection conn = DBConnection.getInstance(); try { PreparedStatement pstmt = conn.prepareStatement("insert into account_tb(password, balance, created_at) values(?, ?, now())"); pstmt.setString(1,"1234"); pstmt.setInt(2,1000); int num = pstmt.executeUpdate(); System.out.println(num); } catch (Exception e) { throw new RuntimeException(e); } } }
쿼리문 코드
import db.DBConnection; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; public class BankApp { public static void main(String[] args) { Connection conn = DBConnection.getInstance(); try { String insert = "insert into account_tb(password, balance, created_at) values(?, ?, now())"; String delete = "delete from account_tb where number = ?"; String update = "update account_tb set balance = balance + ? where number = ?"; //위험한 코드 PreparedStatement pstmt = conn.prepareStatement(update); pstmt.setString(1,"5000"); pstmt.setInt(2,1); int num = pstmt.executeUpdate(); System.out.println(num); } catch (Exception e) { throw new RuntimeException(e); } } }
쿼리문을 변수에 저장한 후, 매개변수에 변수명만 넣어서 사용하면 편하다
@Test 어노테이션
@Test 어노테이션을 붙이니까 main 없이도 해당 메소드를 실행할 수 있게 됨! * @Teset를 메소드 위에 붙이면 해당 메소드를 테스트 메소드로 인식하고 실행할 수 있다. * 저 세모 버튼을 누르면 해당 메소드를 실행 가능!
@Test 어노테이션 특징 3가지
1. void 밖에 못쓴다. return 불가! = 메소드가 값을 반환하지 않고 실행 결과를 확인하기 위한 용도로만 사용 2. 매개 변수를 적을 수 없다. 넣을 수가 없다! 3. @Test 어노테이션을 붙인 메소드는 개별적으로 실행 가능! = 테스트 메소드 간의 독립성을 보장
@Test는 메소드 위에 기입하고, 메소드에 void, 매개 변수 없죠?
@Test 어노테이션을 왜 사용할 수 있나?
처음에 프로젝트를 Gradle을 사용하여 만들었기 때문! Gradle!!! 덕분에 JUnit과 같은 테스트 프레임워크를 쉽게 사용할 수 있다.
JUnit = 자바로 테스트할 수 있는 도구.
메서드를 독립적으로 실행할 수 있게 해준다
1-1. test 폴더에 먼저 test 해보자 (디버깅까지 다 해보자)
원래, 하나의 프로젝트에 main은 하나만 있어야한다. 때문에 @Test 를 사용!
본코드 : main 파일 / test코드 : test 파일 나눠서 코딩을 하자
1. test폴더에 클래스와 메소드를 작성하자
test에는 본코드에 쓴 것과 동일한 클래스명 등을 작성하고, 뒤에 _test 라고만 붙인다.
이게 바로 test코드의 컨벤션! 개발자들끼리의 약속!
이렇게 본코드에 DBConnection을 쓰고, getInstance 메소드를 작성했다. 그러면!
test폴더에 클래스를 만들 때, DBConnectionTest 라고 적는다. 본코드와 이름은 똑같은데 test만 붙인다 메서드는 _test 이렇게. 컨벤션! (@Test 어노테이션은 메소드 위에 붙이기)
2. 통신을 하기 위한 필드값 작성
네트워크 통신의 기본 5가지 !! (인증 받아야 하는 경우에)
★ IP, PORT, ID, PASSWORD, 프로토콜 ★ 을 넣어준다!
인증이 필요한 네트워크 통신에서는 사용자의 신원을 확인하고 인증 절차를 거쳐야 하기 때문에 ID, PASSWORD도 함께 필요하다.
url에 프로토콜, ip, 포트 번호가 다 들어있기에 url을 만들어준다. * 마리아DB의 프로토콜 jdbc:mariadb: -> 이런걸 챗GPT에게 물어보는 것! * ID와 Password는 url에 포함되지 않아서 따로 만들어주기. (필드 = 클래스 내부에 선언된 변수)
[ jdbc:mariadb://본인아이피:포트/데이터베이스명 ] * 내 컴퓨터에 접속할거면 ip란에 "localhost"나 루프백ip를 기입 * 데이터베이스명 = DB안의 데이터베이스 중 어떤 데이터베이스에 접근할 건지 설정할 수 있음 * URL에 데이터베이스명을 명시하면 해당 데이터베이스에 접근할 수 있다 * 접속 후에 SQL 문을 사용하여 다른 데이터베이스로 전환할 수도 있다. : "USE 데이터베이스명;" 자세한건... 해당 문서를 읽어보면 안다.
* 파일 서버는 주로 파일 전송을 위해 FTP(File Transfer Protocol) 프로토콜을 사용
* 웹 서버는 HTTP(Hypertext Transfer Protocol) 프로토콜을 사용
* 오라클 데이터베이스는 오라클 프로토콜을 사용
* 마리아DB는 마리아DB의 고유한 프로토콜을 사용
= DB마다 프로토콜이 다르구나!!
+) 원래라면…
이렇게, 메소드에 리턴값이 들어가는데, test폴더에선 불가능하니 패스 아무튼, Connection은 프로토콜이 적용된 소켓(Socket)이다!!!
디버깅 하기
catch 저거는 Exception으로 바꿔주자. 불안하니까... 저 throw는 jvm 한테 해결하라고 던지는 것이다
이건 오류가 아니다.. 붉은색이라고 다 오류라고 생가하지 말자. 오류 안나면 끝! 이 아니라, 유저네임을 틀려본다거나… 계속 시도해보기
오류 모음
테스트 코드 완료
package db; import org.junit.jupiter.api.Test; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; public class DBConnectionTest { @Test public void getInstance_test() { String username = "root"; String password = "1234"; String url = "jdbc:mariadb://localhost:3306/cosdb"; //프로토콜이 적용된 소켓 try { Connection conn = DriverManager.getConnection(url, username, password); //여기에 원래는 return이 있는데 애는 void라 return ㄴㄴ 안됨 그래서 없음 } catch (Exception e) { throw new RuntimeException(e); } } }
1-2. main 폴더에 작성하자 ✓
test 폴더에서 코드가 정상적으로 실행되는 걸 확인하면, 해당 코드를 main에 복붙한다.
프로토콜이 적용된 소켓 (=Connection) 만들기 (DBConnection)
Connection이 바로 프로토콜이 적용된 소켓을 만드는 객체다.
Connection 객체는 데이터베이스 연결을 설정하고 처리하는 역할을 담당한다. 여기에 URL, ID, PASSWORD를 전달하여 DriverManager.getConnection() 메소드를 호출하면 Connection 객체가 반환(리턴)된다.
getConnection = CheckedException이 발생! 컴퓨터 안 켜졌거나... 그런 Exception들이 발생할 수 있으니 try-catch를 하자!
`DriverManager` 쓰는 이유.. 알 수 가 없다.
문서에 있는 대로 쓰는 것. 왜 이 코드를 이렇게 쓰는지 이해하려고 하지 말자.
데이터 베이스를 연결할 땐 '프로토콜이 적용된 소켓'을 연결해야하고,
그게 바로 Connection 객체이며, DriverManager 클래스를 쓴다. 는 것만 이해하자.
버퍼 만들기 (BankApp) (=PreparedStatement)
메인 메소드를 BankApp에 만들어준다.
getInstance() 메서드를 호출하여 데이터베이스 연결을 수행하고, Connection 객체를 얻어온다. 여기서 실행했을 때 success가 뜨면 연결 된 것 (static이라 클래스명.메소드로 가져올 수 있죠?)
소켓 달았으니 버퍼도 달아야겠죠?
그러나… 우리가 직접 버퍼를 만들 수는 없다. 그러면 구현할게 너무 많다.
그래서 이 라이브러리가 만든 버퍼를 사용할 것!
(소켓 ♥ 버퍼 둘은 한 세트라는 걸 기억하자^^)
[ PreparedStatement ] = 버퍼
PreparedStatement는 물음표(?)를 이용하여 값이 들어갈 자리를 미리 지정해놓고, 나중에 그 자리에 값을 넣어서 SQL 문을 실행하는 것 데이터베이스 시스템에서 PreparedStatement는 일종의 버퍼다.
* PreparedStatement는 SQL 문과 관련된 meta데이터를 캐싱할 수 있다.
* ? 를 사용하기 때문에, 보안성이 있다.
메타 데이터란?
데이터에 대한 설명서나 태그. 데이터가 어떤 내용을 담고 있는지,
어떻게 구성되어 있는지, 어떤 속성을 갖고 있는지 등을 설명하는 정보
애가 버퍼여 연결된 선(conn)에다가 preparestatement 버퍼를 달았다.
문법만 지켜서 날리면 메세지가 날아갈 것이다. 애도 당연히 (syntex 오류날 수 있으니) try-catch를 해줘야함.
쿼리문 작성하기 (insert)
now() = 현재 시간을 넣을 때 사용
PreparedStatement에 쿼리문을 작성한다. ?(물음표) 때문에 아직 완성되지 않은 쿼리다. 그러니.... ?(물음표)에 들어갈 데이터를 넣어줘야겠지?
[ ? (물음표) 작성법 ]
?를 완성하기 위해서는 int parameterIndex 값을 사용한다. parameterIndex는 PreparedStatement에서 ? 플레이스홀더의 순서를 지정하는 역할! (즉, 몇 번째 ?를 완성할거냐는 말) 첫번째 물음표를 완성할거면 1, 2번째는 2, 3번째는 3. 이런 식으로 숫자를 지정해준다 (parameterIndex는 0이 없다. 1부터 시작한다.)
버퍼만 완성 완료! 버퍼에 완성된 쿼리만 되어 있다. * setString, setInt 이런건 데이터베이스 테이블을 보고, 테이블이 무슨 타입으로 만들어졌는지를 확인한 후, 거기에 맞춰서 작성하라 이제 마지막으로 전송(flush) 하면 끝!
flush 하기 = 데이터베이스에선 executeUpdate()
//execute… 실행하다
이 쿼리문을 실행하면 1이 뜨겠지. 적용된 쿼리문이 1개니까 신텍스가 잘못되면 -1이 되고... 안나온다 그 전에 터져버림
이렇게, num 변수에 담아서 출력하면 자바에서도 숫자가 뜨는 걸 확인할 수 있다.
pstmt.executeUpdate();
데이터베이스에서 SQL 문을 실행하고, 영향을 받은 행의 수를 반환하는 기능
INSERT, UPDATE, DELETE와 같은 데이터 변경 작업에 주로 사용. SELECT는 다르다.
그냥… flush 라고 생각하자.
빈 메소드네? = 동적바인딩 된다는 말이구나? Exception도 throws고.
flush까지 다 끝냈다. 마리아db에 확인해보면 이렇게!! 자바에서 입력한 데이터가 들어간 것 확인 완료!!!
[ select 문은 다른 쿼리문과는 다르다! ]
int num = pstmt.executeUpdate(); 이게 다르다. 테이블 데이터를 받아서 executeQuery() 메서드를 사용한다.
select 는 테이블 데이터 형식으로 반환하지만
write, delete, update는 int 형식으로 반환한다.
Share article