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(저장)
- 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 |