728x90

1. 다중 서버 세션 관리를 위한 핵심 컴포넌트

SessionRegistry

  • Spring Security에서 현재 활성화된 세션을 추적하고 관리하는 인터페이스
  • 동시 로그인 세션 관리 및 강제 로그아웃을 위해 사용
  • SessionRegistryImpl 또는 SpringSessionBackedSessionRegistry 구현체 활용 가능

FindByIndexNameSessionRepository

  • Spring Session의 세션 저장소 역할을 하는 인터페이스
  • Redis, JDBC, MongoDB 등 다양한 저장소 추상화 지원
  • sessionId 외에도 username과 같은 특정 속성으로 세션 검색 가능
  • Spring Security와 연동하여 로그인 사용자의 세션 처리

SpringSessionBackedSessionRegistry

  • Spring Session 기반의 SessionRegistry 구현체
  • FindByIndexNameSessionRepository를 활용한 세션 관리
  • 단일/다중 서버 환경 모두 지원
  • Redis와 같은 공유 저장소를 통한 일관된 세션 관리 가능

 

 

 


2. SessionRegistryImpl vs SpringSessionBackedSessionRegistry 비교

기존 코드에서는 SessionRegistryImpl을 사용해서 세션을 관리했으나, 다중서버에서 Redis로 세션을 공통으로 관리하기 위해서 SpringSessionBackedSessionRegistry로 변경함

 

  SessionRegistryImpl SpringSessionBackedSessionRegistry 
설명 기본적인 SessionRegistry 구현체 Spring Session과 연동된 SessionRegistry
저장 방식 서버 메모리(In-Memory)에 세션 저장 Redis, JDBC, MongoDB 등 외부 저장소를 활용
단일 서버 환경 ✅ 사용 가능 ✅ 사용 가능
다중 서버 환경 사용 불가 (서버마다 세션 정보가 다름) 사용 가능 (세션이 공유 저장소에 저장됨)
장점 - 간단한 설정으로 사용 가능
- 별도 저장소 필요 없음
- Redis 같은 공유 저장소를 활용하여 서버 간 세션 공유 가능
단점 - 단일 서버에서만 정상 동작
- 서버 재시작 시 세션 정보 유실
- 서버 확장 시 세션 정보 공유 안 됨
- 추가적인 Redis/JDBC 설정 필요

단일 서버 환경 → SessionRegistryImpl 사용 가능
다중 서버 환경 → SpringSessionBackedSessionRegistry 사용 필수

 

 

 


3. 세션 클러스터링(Session Clustering)

1. Sticky Session

https://www.imperva.com/learn/availability/sticky-session-persistence-and-cookies/

 

Sticky session은 세션을 사용해 트래픽을 분산하는 기능으로 특정 세션의 요청을 최초 처리한 서버로만 전송하는 것을 의미

 

예를 들어 회원 1이 서버 A에서 세션을 생성한다면, 이후 회원 1이 보내는 모든 요청은 서버 A로만 전송하게 됩니다.

 

이러한 Sticky session은 아래와 같은 단점이 존재

  • 동일한 세션은 동일한 서버로만 전송 => 특정 서버에 요청이 몰려 과부하가 발생 할 수 있음
  • 특정 서버에 장애가 발생할 시 => 해당 서버에 연결되어 있는 세션들이 모두 소실될 수 있음

위와 같은 단점을 해결하기 위해 Session Clustering 방식이 존재


2. Session Clustering (세션 클러스터링)

https://smjeon.dev/web/sticky-session/

 

Session Clustering 은 여러 WAS가 세션을 공유하여 동일하게 세션을 관리하는 방식을 의미

 

세션 클러스터링 방식은 Sticky Session에서 발생했던 1. 특정 서버에만 트래픽이 많이 발생하거나, 2. 장애 발생 시 세션 소실의 문제를 해결할 수 있음

 

하지만 세션 클러스터링은 세션 데이터가 생성될 때마다 모든 서버에 세션 정보를 추가해야 하기 때문에 서버의 메모리에 상당히 부담이 된다는 치명적인 단점이 존재


3. Redis Session Clustering

Redis를 이용한 세션 클러스터링 방식은 세션을 관리할 저장소를 Redis로 따로 두어 사용하는 방식입니다.

 

RDB가 아닌 Redis에서 세션을 관리하면 좋은 점

  • 세션은 데이터 모델이 관계형 데이터베이스처럼 복잡하지 않다
  • 세션은 보통 만료시간이 존재하기에 영속성이 필요하지 않다
  • 세션은 사용자가 로그인 후 요청을 할 때마다 확인이 필요하기 때문에 그만큼 I/O가 많이 발생하고, RDB를 사용하는 경우에는 디스크 I/O로 인해 성능 이슈가 발생할 수 있다

 

- 세션 클러스터 내용 출처 https://zzang9ha.tistory.com/442

 

Spring 분산 환경에서 세션 관리하기 (Redis Session Clustering)

🔗 Spring 분산 환경에서 세션 관리하기(세션 클러스터링) 안녕하세요, 이번 포스팅에서는 여러 서버 환경에서 세션을 관리하는 방법에 대해 살펴보겠습니다. (코드는 깃허브에서 확인하실 수 있

zzang9ha.tistory.com

 

 

 


4. 다중 서버에서 로그인 테스트 시 설정이 동일해야 하는 이유

  • Sticky Session이 아니면, 사용자의 요청이 여러 서버로 분산
  • 따라서, 모든 서버가 동일한 세션 저장소(Redis 등)를 공유해야 세션 정보가 유지
  • 설정이 다르면 특정 서버에서 로그인했어도, 다른 서버에서는 해당 세션을 찾을 수 없어 로그아웃된 상태가 될 수 있음

 

 


5. Dependencies

1. spring-boot-starter-data-redis

  • Spring Boot에서 Redis를 사용하기 위한 의존성
  • Redis 클라이언트인 Lettuce 또는 Jedis와의 연결을 설정하고 관리하는 기본적인 기능을 제공
  • Redis 데이터 처리와 관련된 기본적인 기능을 제공하며, 이를 통해 애플리케이션에서 Redis를 캐시 저장소나 메시지 큐, 다른 용도로 사용할 수 있음
  • 이 라이브러리를 사용하면 Redis를 다양한 용도로 연결하고 활용할 수 있음

기능

  • Redis에 대한 연결 설정 및 관리
  • Redis에 데이터를 저장하고 조회하는 기능 (기본적인 Redis 키-값 저장소)
  • 캐시와 같은 일반적인 Redis 사용을 위한 설정
=> 이 의존성만 사용하면 Redis와의 연결은 관리하지만 세션 관리 기능을 제공하지 않음

2. spring-session-data-redis

  • Spring Session을 Redis와 통합하여 세션 관리를 할 수 있도록 돕는 라이브러리
  • Spring Session은 애플리케이션의 세션 관리를 외부 저장소(Redis 등)로 분리하여 관리하는 기능을 제공
  • 이 라이브러리는 Redis를 세션 저장소로 사용하게 해 주며, 세션 데이터가 클러스터링 된 환경에서도 유지될 수 있도록 지원

기능

  • Redis를 세션 저장소로 설정
  • Spring Security와 연동하여 로그인 상태를 Redis에 저장하고 공유
  • 세션 클러스터링을 통해 다중 서버 환경에서 세션을 유지
  • Redis에서 세션 만료 및 관리

=> 이 의존성만 사용하면 세션 관리와 관련된 기능만 제공

 

 

 

 

 


SpringSecurty + Redis 사용시의 흐름

1. 익명 사용자 세션 생성

  • 사용자가 로그인 페이지에 처음 접근할 때, 익명 사용자로서 세션이 자동으로 생성
  • 이때 SecurityContext에는 인증되지 않은 상태로 익명 사용자 정보가 저장

2. 로그인 시도 (인증 과정)

  • 사용자가 로그인 폼에 아이디와 비밀번호를 입력하고 로그인 요청을 보냄
  • Spring Security는 Authentication 필터AuthenticationManager를 사용해 인증을 진행
  • 인증이 성공하면, SecurityContext에 인증된 사용자 정보(예: username, roles, authorities 등)가 저장
  • 로그인된 세션이 현재의 HTTP 세션에 바인딩

3. 세션 관리 (SpringSessionBackedSessionRegistry)

  • SpringSessionBackedSessionRegistry는 세션의 유효성 검사, 세션 만료, maximumSessions 등의 정책을 적용하여 세션 관리를 수행
  • 이 과정에서, FindByIndexNameSessionRepository와 결합하여 세션 데이터를 Redis와 같은 외부 저장소에 저장 => 이때 Redis에는 사용자 세션의 정보 (예: 세션 ID, 인증된 사용자 정보 등)가 저장

4. 로그아웃

  • 사용자가 로그아웃을 요청하면, SecurityContext에서 현재 사용자의 인증 정보를 clear합니다. 즉, 인증 정보가 삭제되고, 이로써 인증되지 않은 상태로 돌아감
  • 세션도 무효화되며, Redis에 저장된 해당 세션 정보도 삭제 => 이때 세션 ID가 Redis에서 제거

5. 로그인 페이지로 리디렉션

  • 로그아웃 후, 사용자는 자동으로 로그인 페이지로 리디렉션

정리 :

  • 세션 생성 (익명 사용자) → 로그인 → 인증 → SecurityContext에 인증 정보 저장 → SpringSessionBackedSessionRegistry 세션 관리 및 Redis 저장 → 로그아웃 → Redis에서 세션 삭제 → 로그인 페이지로 리디렉션
728x90

'DBMS > REDIS' 카테고리의 다른 글

[Redis]Redis를 활용한 Spring Security 세션 관리 동작 원리  (0) 2025.02.10
728x90

 

 1. Redis에서 세션이 저장되는 방식

Spring Session과 Redis를 사용하면 세션 정보를 Redis에 저장하여 분산 환경에서도 세션을 공유할 수 있음
기본적으로 세션 데이터는 아래와 같은 4가지 키-값 형태로 Redis에 저장됨

🔹 로그인 후 Redis에 저장되는 데이터

예를 들어, U0001097이라는 유저가 로그인하면 Redis에 다음과 같은 데이터가 생성됨

Key(Redis Key)

spring:session:expirations:<timestamp> 특정 시간에 만료되는 세션 ID 리스트
spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:세션ID 사용자 ID(U0001097)와 관련된 세션 ID 리스트
spring:session:sessions:expires:<세션 ID> 특정 세션 ID의 만료 시간
spring:session:sessions:<세션 ID> 실제 세션 데이터 (유저 정보, 속성 등 포함)

 


 2. Redis 세션 저장 구조

🔹 Redis에 저장된 데이터

cf)

spring:session:expirations:1739162460000 → [ "32cf118e-9c76-4550-b7ee-c5d6abc3c520" ]
spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:SESSIONID → { "32cf118e-9c76-4550-b7ee-c5d6abc3c520" }
spring:session:sessions:expires:32cf118e-9c76-4550-b7ee-c5d6abc3c520 → 1739162460
spring:session:sessions:32cf118e-9c76-4550-b7ee-c5d6abc3c520 → { "sessionAttr:SpringContext": {...}, "creationTime": 1739162460000, ... }

 


 3. 로그인 및 세션 저장 과정

1️⃣ 사용자가 로그인한 경우

  1. Spring Security가 사용자 인증(Authentication)을 수행함
  2. 세션이 생성되고 Redis에 저장됨
  3. Redis에 spring:session:sessions:<세션 ID>와 관련된 여러 키가 저장됨

2️⃣ 사용자가 로그아웃한 경우

🚀 로그아웃 후 Redis에서 삭제될 데이터

spring:session:sessions:<세션 ID> ✅ 삭제됨 현재 세션에 대한 데이터
spring:session:sessions:expires:<세션 ID> ✅ 삭제됨 현재 세션의 만료 정보
spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:세션ID ❌ 남아 있음 이게 남아 있어서 동시 접속 허용됨
spring:session:expirations:<timestamp> ❌ 남아 있음 다른 세션이 있을 경우 남아 있음

 4. 동시 로그인(멀티 디바이스 로그인) 관리

✅ 동시 로그인을 허용하려면

  • Redis의 사용자 ID 인덱스(spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:세션ID)를 삭제하지 않아야 함
  • 즉, 하나의 세션만 로그아웃하고 다른 세션을 유지해야 함

❌ 동시 로그인을 차단하려면

  • 로그아웃 시, 사용자 ID에 연관된 모든 세션을 삭제해야 함
  • sessionRepository.findByPrincipalName(userId)를 사용해 해당 사용자의 모든 세션 ID를 삭제하면 됨.
 

 


5. 정리: 로그아웃 시 남겨야 할 정보와 삭제해야 할 정보

🔹 로그아웃 시 삭제할 정보 (현재 세션만 삭제)

  • spring:session:sessions:<세션 ID> (세션 데이터)
  • spring:session:sessions:expires:<세션 ID> (만료 정보)

🔹 로그아웃 시 남겨야 할 정보 (다른 기기의 세션 유지)

  • spring:session:index:org.springframework.session.FindByIndexNameSessionRepository.PRINCIPAL_NAME_INDEX_NAME:<사용자 ID> (사용자의 다른 세션을 유지해야 함)
  • spring:session:expirations:<timestamp> (다른 세션이 있으면 유지)

🚀 결론

Redis는 Spring Security 세션을 관리하기 위해 4가지 주요 키를 사용함
로그아웃 시 현재 세션만 삭제하면 동시 접속이 유지됨
동시 로그인을 차단하려면 사용자 ID에 연관된 모든 세션을 삭제하면 됨
세션 무효화 (session.invalidate();) 없이 Redis에서 세션을 삭제하지 않으면 불완전한 로그아웃이 될 수 있음

🔥 즉, "동시 로그인 허용"과 "완전한 로그아웃"을 구분해서 설계해야 한다! 

728x90
728x90

if-then은 주로 단일 조건을 다루는 데 사용,

예를 들어, "만약 A라면 B를 실행하고, 그렇지 않으면 C를 실행" 하는 경우에 사용

 

case-when-then은 여러 조건을 처리하거나 여러 값을 반환해야 하는 경우에 사용, 여러 조건에 따라 다른 결과를 반환하는 복잡한 분기 처리에 유용

 

간단한 단일 조건을 다룰 때는 if-then을 사용하고, 여러 조건이나 값에 따라 다른 결과를 반환해야 할 때는 case-when-then을 사용하는 것이 좋다

If-then

use khw;

set @var1=10;
set @var2=20;

select @var1 from emp;
select @var1+@var2;

-- IF문 
delimiter //
create procedure pr1()
begin
	if 5=5 then
		select '5는 5와 같다';
	end if;
    end //
delimiter ;

call pr1();

delimiter //
create procedure pr2()
begin
	declare num int; -- 변수선언
    set num =100; -- 변수에 값 대입
    if num=100 then
		select '100이다';
    else
		select '100아니다';
    end if;
end //
delimiter ;
call pr2();    

Case-When-Then

-- case when then

delimiter //
create procedure pr3()
begin
	declare num int; -- 변수선언
    declare grade char(1); -- 변수선언
    set num=80; -- 값 대입
case 
	when num >= 90 then
		set grade = 'A';
	when num >= 80 then
		set grade = 'B';
	else
		set grade = 'F';
end case;
	select num,grade;
end //
delimiter ;

call pr3();

Set-While-do

delimiter //
create procedure pr4()
begin
	declare i int;
    declare sum int;
    set i=1;
    set sum=0;
    
    while(i<=10) do
		set sum=sum+i;
        set i=i+1;
    end while;
    
    select sum;
end //
delimiter ;
    
call pr4();
728x90

'DBMS > MySQL' 카테고리의 다른 글

[MySQL] 커서(Cursor)  (0) 2024.03.20
[MySQL] 트리거(Trigger)  (0) 2024.03.20
[MySQL] 쿼리 종합예시  (0) 2024.03.19
[MySQL] 스토어드 프로시저  (0) 2024.03.18
[MySQL] 뷰(View)  (0) 2024.03.18
728x90

 

import java.sql.Connection;
import java.sql.DriverManager;

public class Test1{
    public static Connection get() {
        Connection con=null;

        try{
            String id="아이디";
            String pw="패스워드";
            String url="jdbc:mysql://localhost:호스트넘버/스키마이름";

            Class.forName("com.mysql.cj.jdbc.Driver");
            //Class 클래스로 mysql 드라이버를 로딩하는 코드 =>DriverManager에 등록됨

            con= DriverManager.getConnection(url,id,pw);
            //Connection객체를 얻음
            //con은 실제로 데이터베이스와 연결하여 작업을 수행할 수 있는 통로로 작용하는 중요한 객체 변수로 사용됨

            System.out.println("DB에 연결됐다.");
        }
        catch (Exception e){
            System.out.println("로딩실패");
        }
        return con;
    }
}
import java.sql.*;

public class Test2 {
    public static void main(String[] args) throws SQLException {

        Connection con = null; //DB와 연결하는 인터페이스
        PreparedStatement psmt=null;
        ResultSet rs=null; //sql에 대한 반환 (쿼리 실행에 대한 결과값 저장)

        try{
            String que="select *from emp";

            con=Test1.get(); // DB연결

            psmt=con.prepareStatement(que);
            rs=psmt.executeQuery();

            //select -> executeQuery()
            //DML(insert, update, delete..) -> excuteUpdate();
            while(rs.next()){
                //DB에 있는 값들을 가져옴(행 기준)
                String empno=rs.getString(1);
                String ename=rs.getString(2);
                String job=rs.getString(3);
                int mgr=rs.getInt(4);
                Date hiredate=rs.getDate(5);
                int sal=rs.getInt(6);
                int comm=rs.getInt(7);
                int deptno=rs.getInt(8);

                System.out.println(empno+" "+ename+" "+job);
            }
        }catch(Exception e){
            e.printStackTrace();
        }
        rs.close();
        psmt.close();
        con.close();

    }
}

 


 

테이블 2개 생성

use 스키마이름;

create table users(
userid varchar(20) primary key,
username varchar(20) not null,
password varchar(20) not null,
age int(3) not null,
email varchar(40) not null);

create table boards(
bno int primary key auto_increment,
btitle varchar(100) not null,
bcontent longtext not null,
bwriter varchar(30) not null,
bdate datetime default now(),
bfilename varchar(50) null,
bfiledata longblob null); -- binary large object

desc boards;

 

JDBC 연결하기

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

public class Jdbc1 {
	public static void main(String[] args) throws SQLException {
		
		Connection conn=null;
		
		try {
			Class.forName("com.mysql.cj.jdbc.Driver");
			//Class클래스로 JDBC드라이버를 로딩되어지면서 DriverManager에 등록된다
			
			conn=DriverManager.getConnection(
			"jdbc:mysql://localhost:3306/스키마이름",
			"아이디",
			"비밀번호");
			//Connection객체 생성하여 데이터베이스와 연결이 이루어지도록 한다
		}catch(Exception e) {}

		conn.close();  //연결끊기
	}

}

 

MySQL 데이터베이스에 새로운 사용자 정보를 삽입

package jdbc;

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

public class jdbc1 {
    public static void main(String[] args) throws SQLException {

        Connection conn = null;

        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            //Class클래스로 JDBC드라이버를 로딩되어 지면서 DriverManager에 등록된다

            conn= DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/khw","root","0000");
            //  Connection 객체 생성하여 DB와 연결이 이루어지도록 함

            String sql="insert into users(userid,username,password,age,email)"
                        +"values(?,?,?,?,?)";

            PreparedStatement pstmt = conn.prepareStatement(sql);
            // ? : 바인드 변수 -> 매번 값이 바뀔 수 있기 때문에 미리 정해 놓지 않는다
            // setXXX (순서, 데이터)
            pstmt.setString(1, "com");
            pstmt.setString(2, "tom");
            pstmt.setString(3, "1234");
            pstmt.setInt(4, 49);
            pstmt.setString(5,"aa@naver.com");

            int r = pstmt.executeUpdate();
            //executeXXX : 쿼리문 실행되기 위해 호출하는 메소드
            System.out.println(r);

            pstmt.close();

        } catch (Exception e){}

        conn.close(); // 연결끊기

    }
}

 

MySQL 데이터베이스에 게시글을 삽입

package jdbc;

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

public class BoardInsert {
    public static void main(String[] args) {
        Connection conn = null;
        try {

            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/khw", "root", "0000");

            String sql = "INSERT INTO boards (btitle, bcontent, bwriter, bdate, bfilename,bfiledata) "
                    + "VALUES (?, ?, ?, now(), ?, ?)";

            PreparedStatement pstmt = conn.prepareStatement(sql,
                    Statement.RETURN_GENERATED_KEYS);
//자동증가된 bno값 가져옴
            pstmt.setString(1, "금요일");
            pstmt.setString(2, "금요일입니다");
            pstmt.setString(3, "juli");
            pstmt.setString(4, "뤂");
            pstmt.setBlob(5, new FileInputStream
                    ("C:\\Users\\Kang\\Pictures/고죠사토루.jfif"));

            int r=pstmt.executeUpdate();
            System.out.println(r); //갱신된 행의 개수

            if(r==1) {
                ResultSet rs=pstmt.getGeneratedKeys(); //bno값
                if(rs.next()) {
                    int bno=rs.getInt(1);
                    System.out.println("게시글 수 "+bno);
                }
                rs.close();
            }
            pstmt.close();
            conn.close();
        }catch(Exception e) {}
    }
}

 

MySQL 데이터베이스의 게시글을 업데이트

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

public class BoardUpdate {
public static void main(String[] args) throws SQLException {
Connection conn = null;
try {

Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection
("jdbc:mysql://localhost:3306/sjw", "root", "1234");

String sql = new StringBuilder()
.append("UPDATE boards SET ")
.append("btitle= ?, ")
.append("bcontent= ?, ")
.append("bfilename= ?, ")
.append("bfiledata= ? ")
.append("WHERE bno= ?")
.toString();

PreparedStatement pstmt=conn.prepareStatement(sql);
pstmt.setString(1, "hi");
pstmt.setString(2, "hello");
pstmt.setString(3, "루피.jpg");
pstmt.setBlob(4, new FileInputStream
("C://Users//itcamp//Desktop/짱구.jpg"));
pstmt.setInt(5,1);

int r=pstmt.executeUpdate();
System.out.println("수정된 행 개수" + r);
pstmt.close();


}catch(Exception e) {}
conn.close();
}

}

 

 

클래스를 사용하여 데이터베이스 작업을 수행/ 사용자 정보를 데이터베이스에 삽입하거나 가져오는 등의 작업 가능

package jdbc;

public class User {
    private String userid;
    private String username;
    private String password;
    private int age;
    private String  email;

    public String getUserid() {
        return userid;
    }

    public void setUserid(String userid) {
        this.userid = userid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
    @Override
public String toString(){
        return userid+" "+username+" "+email;
}

}
// 테이블에서 User정보를 가져온 후 콘솔에 출력하기를 원함

 

 

JDBC를 사용하여 MySQL 데이터베이스에서 사용자 정보를 선택하고 출력

package jdbc;

import java.sql.*;

public class UserSelect {
    public static void main(String[] args) throws SQLException {
        // 테이블에서 User정보를 가져온 후 콘솔에 출력하기를 원함


        Connection conn = null;

        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            //Class클래스로 JDBC드라이버를 로딩되어 지면서 DriverManager에 등록된다

            conn = DriverManager.getConnection(
                    "jdbc:mysql://localhost:3306/khw", "root", "0000");
            //  Connection 객체 생성하여 DB와 연결이 이루어지도록 함

            String sql = "select *from users where userid=?";

            PreparedStatement pstmt = conn.prepareStatement(sql);
            pstmt.setString(1, "com");

            //ResultSet : 반환값이 여러 개의 행인 경우에 쉽게 처리할 수 있게 설계된 클래스
            ResultSet rs = pstmt.executeQuery(); //정보들 가져옴
            if (rs.next()) {
                User u = new User();
                u.setUserid(rs.getString(1));
                //테이블에 저장되어 있는 userid값을 가져와서 User클래승 userid라는 필드에 세팅
                u.setUsername(rs.getString("username"));
                u.setPassword(rs.getString(3));
                u.setAge(rs.getInt(4));
                u.setEmail(rs.getString(5));

                System.out.println(u);
            }
            rs.close();
            pstmt.close();
        } catch (Exception e) {
        }
        conn.close();
    }
}

 

 

게시글 정보를 표현하는 Board 클래스를 정의

package jdbc;

import java.sql.Blob;
import java.util.Date;


public class Board {

    private int bno;
    private String btitle;
    private String bcontent;
    private String bwriter;
    private Date bdate;
    private String bfilename;
    private Blob bfiledata;

    @Override
    public String toString() {
        return btitle+" "+bwriter;
    }


    public int getBno() {
        return bno;
    }

    public void setBno(int bno) {
        this.bno = bno;
    }

    public String getBtitle() {
        return btitle;
    }

    public void setBtitle(String btitle) {
        this.btitle = btitle;
    }

    public String getBcontent() {
        return bcontent;
    }

    public void setBcontent(String bcontent) {
        this.bcontent = bcontent;
    }

    public String getBwriter() {
        return bwriter;
    }

    public void setBwriter(String bwriter) {
        this.bwriter = bwriter;
    }

    public Date getBdate() {
        return bdate;
    }

    public void setBdate(Date bdate) {
        this.bdate = bdate;
    }

    public String getBfilename() {
        return bfilename;
    }

    public void setBfilename(String bfilename) {
        this.bfilename = bfilename;
    }

    public Blob getBfiledata() {
        return bfiledata;
    }

    public void setBfiledata(Blob bfiledata) {
        this.bfiledata = bfiledata;
    }


}

 

 

JDBC를 사용하여 MySQL 데이터베이스에서 게시글 정보를 선택하고 출력

package jdbc;

import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.*;

public class BoardSelect {
    public static void main(String[] args) throws SQLException {

        Connection conn = null;
        try {

            Class.forName("com.mysql.cj.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/khw", "root", "0000");

            String sql = "select *from boards where bno=?";
            PreparedStatement pstmt = conn.prepareStatement(sql);
            pstmt.setInt(1, 2);
            //bno값이 2인 테이블 정보 다 가져옴

            ResultSet rs=pstmt.executeQuery();
            while(rs.next()) {
                Board bo=new Board();
                bo.setBno(rs.getInt(1));
                bo.setBtitle(rs.getString("btitle"));
                bo.setBcontent(rs.getString(3));
                bo.setBwriter(rs.getString(4));
                bo.setBdate(rs.getDate(5));
                bo.setBfilename(rs.getString(6));
                bo.setBfiledata(rs.getBlob(7));

                System.out.println(bo);


                Blob b=bo.getBfiledata();
                if(b!=null) { //이미지 있음
                    InputStream is= b.getBinaryStream();
                    OutputStream os=new FileOutputStream("C:\\Users\\Kang"+ bo.getBfilename());

                    is.transferTo(os);
                    os.flush();
                    os.close();
                    is.close();
                }

            }
            rs.close();
            pstmt.close();
        }catch(Exception e) {}
        conn.close();
    }
    }

 

 

 

728x90

'DBMS' 카테고리의 다른 글

[DBMS] 제약조건  (0) 2024.03.15
[DBMS] Key  (0) 2024.03.15
DBMS(계속..)  (0) 2024.03.13
728x90

구조

delimiter //
create procedure test10()
begin
	declare v_stu_no char(9);
   	declare v_sub_no char(3);
    declare v_enr_grade int;
    declare done int default 0;

declare endOfRow boolean default false; -- 행의 끝 여부

declare c1 cursor for select stu_no, sub_no, enr_grade from enrol where sub_no=104;

declare continue handler for not found set done=1;
-- 더 이상 읽을 행이 없으면 1

open c1;
li:loop
	fetch from c1 into v_stu_no, v_sub_no, v_enr_grade;
    if done then leave li;
    end if;
    select v_stu_no, v_sub_no, v_enr_grade;
    end loop;
    close c1;
end //
delimiter ;

call test10;
728x90

'DBMS > MySQL' 카테고리의 다른 글

[MySQL] IF-Then/Case-when-then/Set-While-do  (0) 2024.03.21
[MySQL] 트리거(Trigger)  (0) 2024.03.20
[MySQL] 쿼리 종합예시  (0) 2024.03.19
[MySQL] 스토어드 프로시저  (0) 2024.03.18
[MySQL] 뷰(View)  (0) 2024.03.18
728x90

트리거

  • 테이블에 DML문(Insert, Update, Delete 등) 이벤트가 발생될 때 작동
  • 직접 실행 불가 - 테이블에 이벤트 일어나야 자동 실행
  • IN, OUT 매개 변수를 사용핛 수 없음
  • MySQL은 View에 트리거 부착 불가!!!

 

예시) After 트리거

create table tmp_tbl(
userid varchar(10),
workdate date,
bigo char(1));

desc tmp_tbl;

delimiter //
create trigger tri1
after update
on student -- student테이블을 update한 이후,
for each row
begin
	insert into tmp_tbl values('aa',curdate(),'U');
end //
delimiter ;

update student set stu_weight = stu_weight-10;
delimiter //
create trigger tri5
after update
on student -- student 테이블에서 업데이트 일어난 후
for each row
begin
	if(old.stu_weight> 40.00) then
		insert into tmp_tbl values('aabb',curdate(),'A');
        end if;
end //
delimiter ;
delimiter //
create trigger tri5
after update
on student -- student 테이블에서 업데이트 일어난 후
for each row
begin
	if(old.stu_weight> 40.00) then
		insert into tmp_tbl values('aabb',curdate(),'A');
        end if;
end //
delimiter ;

update student set stu_name='김길동'
where stu_name='옥한빛';

commit;

 

 

사원테이블에 사원이 추가될 때 5000보다 급여가 많으면 emp500 테이블에 입력된 사원번호, 사원이름, 입력된 날짜를 입력하는 트리거 작성

-- 사원테이블에 사원이 추가될 때 5000보다 급여가 많으면 emp500 테이블에 입력된 사원번호, 사원이름, 입력된 날짜를 입력하는 트리거 작성
create table emp500(
empno int(4),
ename varchar(10),
workdate date);

delimiter //
create trigger tri6
after insert 
on emp
for each row
begin
   if new.sal > 5000 then
    insert into emp500 values(new.empno,new.ename,now());
    end if;
end //
delimiter ;

insert into emp 
values(1111,'gildong','student',7839,now(),5600,null,10);

insert into emp 
values(2222,'jack','student',7839,now(),4600,null,10);

select *from emp500;

 

 

After 트리거 - delete

부서테이블의 데이터 삭제 시 dept_Del 테이블에 삭제된 데이터를 저장하는 트리거를 작성

-- 부서테이블의 데이터 삭제 시 dept_Del 테이블에 삭제된 데이터를 저장하는 트리거를 작성

create table dept_del(
userid varchar(10),
workdate date,
deptno int(2),
dname varchar(14),
loc varchar(13));

delimiter //
create trigger tri7
after delete
on dept
for each row
begin
	insert into dept_del values('user',now(),old.deptno,old.dname,old.loc);
end //
delimiter ;

delete from dept where deptno=10;

select *from dept_del;

트리거 종합

-- 1. 상품 정보(product)테이블에 열 이름이 ‘비고’ 라는 열을 varchar(20)으로 삽입해라.
ALTER TABLE product ADD 비고 varchar(20) AFTER p_group;
-- 2. 1번에서 삽입한 열이 상품 정보(product)테이블에 삽입되었는지 확인해라.
select *from product;
-- 3. 상품 정보(product)테이블에 ‘비고’ 열의 구조를 char(3)으로 변경해라.
alter table product modify 비고 char(3);
-- 4. 상품코드 401에 대한 거래내역 뷰(v_trade)를 만들어라.
create view v_trade as select * from trade where p_code=401;
-- 5. 상품 정보(product)테이블에 가장 최근에 들어온 거래처 코드 정보를 검색해라(top-n질의)
select c.c_code from customer c, product p, trade t where p.p_code = t.p_code and c.c_code=t.c_code order by c.c_code desc limit 1; 
-- 6. 상품을 삽입하는 프로시저를 생성해라.
-- call p_pro(‘403’, ’7.1채널 스피커’, 180000, ‘스피커’);
delimiter //
create procedure p_pro
(in v_p_code char(3),
in v_p_name varchar(30),
in v_p_cost int,
in v_p_group varchar(30))
begin
insert into product(p_code, p_name, p_cost, p_group)
values(v_p_code, v_p_name, v_p_cost, v_p_group);
end //
delimiter ;	

call p_pro('403', '7.1채널 스피커', 180000, '스피커');

트리거종합 中 삭제

create table product_del
(u_id varchar(10),
wdate date,
p_code int(6),
p_name varchar(30),
p_cost int,
p_group varchar(30));

-- 상품 삭제 시(product테이블에서) product_del 테이블에 삽입이 이루어지는트리거를 작성해라.
-- p_code , p_name , p_cost , p_group은 기존 product테이블에 있는 값으로 삽입해라.

-- delete from product where p_code=’201’;

delimiter //
create trigger tri8
after delete
on product
for each row
begin
	insert into product_del values('usd',now(),old.p_code,old.p_name,old.p_cost,old.p_group);
    end //
    delimiter ;
    
delete from product where p_code='201';
select *from product_del;
728x90

'DBMS > MySQL' 카테고리의 다른 글

[MySQL] IF-Then/Case-when-then/Set-While-do  (0) 2024.03.21
[MySQL] 커서(Cursor)  (0) 2024.03.20
[MySQL] 쿼리 종합예시  (0) 2024.03.19
[MySQL] 스토어드 프로시저  (0) 2024.03.18
[MySQL] 뷰(View)  (0) 2024.03.18

+ Recent posts