본문 바로가기
백엔드/자바

자바 - 23

by study_yeon 2023. 6. 1.

2023.05.30

☆ 이것이 자바다 참고

* JDBC(Java Database Connectivity)
- import는 java.sql로 하기
- 데이터베이스와 연결해서 데이터 입출력 작업을 할 수 있도록 하는 라이브러리

- 자바가 데이터베이스와 통신할 수 있게 해주는 다리

* DBMS(데이터베이스 관리시스템) : Oracle, MySQL, MariaDB, SQL Server

 

* 운송객체(SQL문 전달)
1. Statement : 입문용
Statement 인터페이스는 SQL의 DDL과 DML을 실행할 때 사용 
주로 변경되지 않는 정적 SQL 문을 실행할 때 사용

2. PreparedStatement : 실무용(Statement의 상위버전)
Statement와 동일하게 SQL의 DDL, DML 문을 실행할 때 사용
매개변수화된 SQL 문을 사용할 수 있기 때문에 편리성과 보안성이 좋음

3. CallableStatement : 절차적 프로그래밍시 선택사항
DB에 저장되어 있는 프로시저procuder와 함수function를 호출할 때 사용

* Client Tool 
- 디비버 : 모든 데이터베이스에 적용 가능
- MySQL : MySQL Workbench
- Oracle : SQL Developer / Oracle Instant Client

Connector/J 8.0.25 : JDBC드라이버

포트번호가 다르면 다른 프로그램으로 인식
-> MySQL(3307)과 MariaDB(3306) 으로 설정하면 둘 다 설치 가능 


● 데이터베이스

 

▶ sts로 데이터베이스 만들기

- 연결프로그램 MariaDB
- name : thisisjava


 테이블 만들기
1. 사용자정보 테이블 만들기
- Create New Table로 만들기

- name : user

컬럼입력(하단의 버튼 오른쪽마우스)

 

- 원하는 컬럼의 값 입력하기

- 프라이머리키 설정 

오른쪽마우스 > New Constraint from Selection > Type 설정

- 저장 : preview가 뜸 - persist(저장)

preview

- CONSTRAINT users_pk 생략가능
- DEFAULT CHARSET=utf8mb4 생략가능(DB에서 상속)
- COLLATE=utf8mb4_general_ci 생략가능(DB에서 상속)

 

* 생성확인



2. 게시물 정보가 저장될 boards 테이블
- 새SQL편집기로 만들기
- 어느 데이터베이스를 사용할 것인지 명시해주기 : `thisisjava`.`boards` 

create table `thisisjava`.`boards` ( 
	bno       int primary key auto_increment,
	btitle    varchar(100) not null,
	bcontent  longtext     not null,
	bwriter   varchar(50)  not null,
	bdate     datetime     default now(),
	bfilename varchar(50) null,
	bfiledata longblob null
);

 

auto_increment : 숫자인 경우에 데이터를 추가할때마다 자동으로 증가시켜라
longtext : 큰 문자열을 집어넣을때 사용(20억자)
datetime now() : 현재 시간을 가져와라
longblob : 큰 파일을 집어넣을때 사용(4기가)

3. 계좌정보가 저장될 accounts 테이블을 생성 

- 어느 데이터베이스를 사용할 것인지 명시해주기 : use thisisjava;

use thisisjava;
create table accounts (
	ano     varchar(20) primary key,
	owner   varchar(20) not null,
	balance numeric     not null
);


3-1. 사용자계정 추가 
insert into

insert into accounts (ano, owner, balance)
	values 
	('111-111-1111', '하여름', 1000000),
	('222-222-2222', '한겨울', 0);


- Refresh 하여 테이블 3개 생성 확인 




● 자바(DB 연결)


* 클라이언트 프로그램에서 DB와 연결하려면 해당 DBMS의 JDBC Driver가 필요
- 포트를 통해 어떤 프로그램인지 알 수 있음

▶ JDBC Driver 설치
1) MySQL을 설치하지 않고 원격 MySQL을 사용한다면 JDBC Driver만 별도로 다음 URL에서 다운로드
https://mvnrepository.com/
2) MariaDB 검색
3) MariaDB Java Client » 3.0.10 
4) jar 다운 C:\app\java\lib에 저장 


5) 빌드패스 

-> 새로운 프로젝트를 만들때마다 실행(버전은 상황에 맞게 변경)

빌드패스 연결 끊기


▶  thisisjava DB에 연결하는 클래스

- 클래스 만들기 ConnectionExampleApp

 

* Connection 하기 위해 필요한 것
1. IP주소
2. port번호
3. 사용자 계정/비밀번호
4. DB명

	String JDBC_DRIVER = "org.mariadb.jdbc.Driver";
	String JDBC_URL = "jdbc:mariadb://localhost:3306/thisisjava";
	String USER = "root";
	String PASSWORD = "mariadb";

 

* Class.forName() 

- 문자열로 주어진 JDBC Driver 클래스를 Build Path에서 찾고, 메모리로 로딩

Class.forName(JDBC_DRIVER);

* Connection conn = DriverManager.getConnection("연결 문자열", "사용자", "비밀번호");

- JDBC Driver 클래스의 static 블록이 실행되면서 DriverManager에 JDBC Driver 객체를 등록하게 된다. 

- Build Path에서 JDBC Driver 클래스를 찾지 못할 수 있어서 ClassNotFoundException 예외 처리
- DriverManager에 JDBC Driver가 등록되면 getConnection() 메소드로 DB와 연결을 할 수 있다.

- 연결이 실패하면 SQLException이 발생하므로 예외처리

conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);


* 인터넷 네트워크에서 컴퓨터를 찾기 위해 유일한 주소인 IP가 필요
IP는 사용범위에 따라 크게 3가지 타입으로 구분
public IP(공안 IP) - 실제 인터넷에 연결된 주소
private IP(사설 IP) - 공유기 밑에 연결된 주소
local IP(로컬IP)- 컴퓨터 한대에 부여되어 자체 네트워크를 구성
-> Loop Back(루프백)주소  : 127.0.0.1(IP) 또는 localhost(DNS)라고 함


▶ 데이터 저장

* 값을 ?(물음표)로 대체한 매개변수화된 INSERT 문

INSERT INTO users (userid, username, userpassword, userage, useremail)
VALUES (?, ?, ?, ?, ?)

* String 타입변수 sql에 문자열로 대입

String sql = new StringBuilder()
.append("INSERT INTO users (userid, username, userpassword, userage, useremail) ")
.append("VALUES (?, ?, ?, ?, ?)")
.toString();
//또는
String sql = "" +
"INSERT INTO users (userid, username, userpassword, userage, useremail) " +
"VALUES (?, ?, ?, ?, ?)";

* String 객체는 한번 값이 할당되면 공간은 변하지 않는다(=불변성)

- String변수에 +=로 문자열을 더하는 경우 기존 a와 더해진 ab의 주소(메모리 영역)가 다름

 

* StringBuilder() : 기존의 자료를 수정 등에 사용(임시객체) - 가변성을 가짐
- 새로운 객체를 생성하는 것이 아니라 기존의 데이터에 더하는 방식(주소가 변하지 않)

- 문자열을 바로 추가할 수 있어 시간 단축 효과 - 내용이 적으면 시간은 비슷함

- 메소드 : .append() : 문자열을 더하는 역할


* 빌더패턴
- 복잡한 객체를 생성하는 클래스와 표현하는 클래스를 분리하여, 동일한 절차에서도 서로 다른 표현을 생성하는 방법을 제공
- 생성자를 통해 객체를 생성하는데 몇 가지 단점이 있어 객체를 생성하는 별도 builder를 두는 방법이 있다. 이를 빌더 패턴이라고 한다.
- 계속해서 새로운 것을 만든다
- 객체지향언어의 특징
- .(점) 활용

 

※ StringBuilder와 String 실행시간 측정(비교)해보기

 

* 매개변수화된 SQL 문
PreparedStatement

- 값이 필요할때 집어넣음

- 매개변수화된 SQL 문을 실행할때 필요

PreparedStatement pstmt = conn.prepareStatement(sql);
// 만들어져서 과거형           // 만들거라 현재형

* ?에 들어갈 값을 지정
- ?는 순서에 따라 1번부터 번호가 부여
- Setter 메소드를 선택한 후 첫 번째에는 ? 순번, 두 번째에는 값을 지정
예시)

pstmt.setString(1, "12345");
pstmt.setInt(2, 25);

▷ users 테이블에 사용자 정보를 저장
클래스  UserInsertExampleApp

package mariadb;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class UserInsertExampleApp {

	public static void main(String[] args) {
		String JDBC_DRIVER = "org.mariadb.jdbc.Driver";
		String JDBC_URL = "jdbc:mariadb://localhost:3306/thisisjava";
		String USER = "root";
		String PASSWORD = "mariadb";
		Connection conn = null;
		
		try {
			Class.forName(JDBC_DRIVER);
			System.out.println("드라이버 로딩에 성공했습니다.");
			// 연결하기
			conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
			System.out.println("마리아디비에 연결했습니다.");
			
			// 실제 JDBC를 사용하여 쿼리 처리할때 PreparedStatement 사용
			String sql = """
					INSERT INTO users 
						(userid, username, userpassword, userage, useremail)
						VALUES (?, ?, ?, ?, ?);""";
			// PreparedStatement 생성 및 인수 지정
			// 실행할 쿼리에 필요한 인수들
			PreparedStatement pstmt = conn.prepareStatement(sql); // pstmt는 미리 준비가 되어있음
			pstmt.setString(1, "winter");
			pstmt.setString(2, "한겨울");
			pstmt.setString(3, "12345");
			pstmt.setInt(4, 25);
			pstmt.setString(5, "winter@mycompany.com");
			// SQL문 실행
			int insertCount = pstmt.executeUpdate(); // sql을 이미 실어서 안담아도 됨
			System.out.println("저장된 행 수 : " + insertCount);
			
			// 반드시 명령객체(실행객체)를 닫아야 합니다.
			pstmt.close(); // try에서 사용하는거라 여기서 닫음
			
		} catch(ClassNotFoundException e) {
			e.printStackTrace();
		} catch(SQLException e) {
			e.printStackTrace();
		} finally {
			
			try {
				if (conn != null) { // 연결된 것이 있으므로 끊기(끊기를 시도하면 예외 발생 가능)
					conn.close();
				}
			} catch(SQLException e) {
				
			}
			
		}
	}

}

 

* prepareStatement 
- 매개변수로 넣지 않음(?로 대체)
- 생성시 sql을 보내야함
- 데이터 값 포함 하는가 안하는가(stmt와 차이점)

▷ boards 테이블에 게시물 정보를 저장

클래스 BoardInsertExampleApp

package mariadb;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class BoardInsertExampleApp {

	public static void main(String[] args) {
		Connection conn = null;
		
		String JDBC_DRIVER = "org.mariadb.jdbc.Driver";
		String JDBC_URL = "jdbc:mariadb://localhost:3306/thisisjava";
		String USER = "root";
		String PASSWORD = "mariadb";
		
		try {
			Class.forName(JDBC_DRIVER);
			System.out.println("DRIVER 불러오기 성공!");
			conn = DriverManager.getConnection(JDBC_URL, USER, PASSWORD);
			System.out.println("MariaDB 연결 성공!");
			
			// 여기서부터 추가 질의 
			String sql = "" +
					"INSERT INTO boards ( " +
					"       btitle, bcontent, bwriter, bdate, bfilename, bfiledata )" +
					"VALUES ( ?, ?, ?, now(), ?, ? );";
					  	
			// preparedStatement 얻기 및 값 지정
			PreparedStatement pstmt = conn.prepareStatement(
					sql, Statement.RETURN_GENERATED_KEYS);
					// GENERATED_KEYS : auto_increment로 되어 있음
			// * void setString(int parameterIndex, String x) throws SQLException;
			pstmt.setString(1, "눈 오는 날");
			pstmt.setString(2, "함박눈이 내려요.");
			pstmt.setString(3, "winter");
			pstmt.setString(4,"snow.jpg");
			pstmt.setBlob(
					5, new FileInputStream("src/mariadb/images/snow.jpg"));
			
			// SQL문 실행
			int rows = pstmt.executeUpdate();
			System.out.println("현재 DB에 저장된 행 수 : " + rows);
			
			// 마지막 추가된 레코드의 마지막 primary key 일련번호 ( = bno )
			if ( rows == 1 ) {
				// 현재 DB에 저장된 행 수가 1이므로, 1로 설정함
				ResultSet rs = pstmt.getGeneratedKeys();
				// * ResultSet getGeneratedKeys() throws SQLException;
				// 가장 최근에 추가된 id값을 리턴해줌
				if(rs.next()) {
					int bno = rs.getInt(1);
					// * int getInt(int columnIndex) throws SQLException;
					System.out.println("저장된 bno : " + bno);
					rs.close();
				}
			}
			
			// 다 실행했으므로 preparedStatement 종료
			pstmt.close();
			
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} finally {
			try {
				if ( conn != null ) {
					conn.close();
				} 
			} catch(SQLException e) {}	
		}

	}

}


- bno는 자동 증가 컬럼이므로 생략되고, now()는 현재 시간
-> bno는 게시판의 글번호. 시퀀스로 생성되는 기본키

* prepareStatement( ) 메소드로부터 PreparedStatement를 얻는데 두 번째 매개값이 있음

PreparedStatement pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);

 

- bno에 값 없이 DB에 member를 insert 해봐야 bno가 만들어짐 - bno를 DB가 자동생성하기 때문
이렇게 DB가 생성한 bno값이 필요하면 DB에서 새로 생성된 bno값을 읽어와야 하고 그 기능이 바로 RETURN_GENERATED_KEYS

* ResultSet에서 자동 생성된 키의 데이터 유형은 해당 도메인의 데이터 유형에 상관 없이 DECIMAL이다.



'백엔드 > 자바' 카테고리의 다른 글

자바 - 24  (0) 2023.06.02
자바 - 24 (lombok설치하기)  (0) 2023.06.01
자바 - 22 (List - 리스트)  (0) 2023.05.31
자바 - 21 (노래 관리 프로그램 분리하기)  (0) 2023.05.31
자바 - 20 (노래 관리 프로그램 만들기)  (0) 2023.05.25