본문 바로가기
Dev/JAVA

쇼핑몰 Session | 주문 | 상세주문 insert

by 컴포넌트설계자 2026. 3. 11.

쇼핑몰 테이블 관계도 


다이어그램 


goods 


DDL +DML

drop table customer;
-- 고객테이블
create table customer(
  user_id varchar(30) primary key, -- 사용자ID
  user_pwd varchar(30) not null,-- 비밀번호
  user_name varchar(30),-- 이름
  reg_date date-- 가입일
);


insert into  customer values('jang','1111','유재석',now());
insert into  customer values('lee','1111','이효리',now());
insert into  customer values('kim','1111','송중기',now());
insert into  customer values('king','1111','송혜교',now());

-- 상품테이블
create table goods(
  goods_id varchar(20) primary key, -- 상품번호
  goods_name varchar(50) not null, -- 상품이름
  goods_price int ,-- 가격
  stock int, -- 재고량
  regdate datetime default now() -- 등록일
);


insert into goods values('A01','새우깡',1500,10,now());
insert into goods values('A02','허니버터칩',2500,10,now());
insert into goods values('A03','콘칩',2000,10,now());
insert into goods values('A04','빼빼로',1500,10,now());
insert into goods values('A05','감자깡',1500,10,now());

delete from goods;

-- 주문테이블
drop table orders;

create table orders(
  order_id int primary key auto_increment, -- 주문코드
  order_date datetime not null, -- 주문일자
  user_id varchar(30) not null references customer(user_id) , -- 주문한사람
  address varchar(100) not null, -- 배송지
  total_amount int not null -- 총구매금액
);


-- 주문상세
create table order_line(
  order_line_id int primary key auto_increment, -- 주문상세코드
  order_id int not null , -- 주문코드(주문아이디)
  goods_id varchar(20) not null , -- 상품아이디
  unit_price int not null, -- 단가
  qty int not null, -- 주문수량
  amount int not null, -- 단가*주문수량 한 금액
  foreign key(order_id) references orders(order_id) , 
  foreign key(goods_id) references goods(goods_id)
);

-- ---------------------------------------------------------------------
select * from orders;
-- 주문하기
 -- 1) jang 아이디가 A01 상품 2개, AO2 상품 1개 구입한다.
  INSERT INTO ORDERS(ORDER_DATE,USER_ID, ADDRESS, TOTAL_AMOUNT)
  VALUES(now(),'jang','경기도 판교', 5500);
  
  -- 방금 삽입한 order_id 값 가져오기
SET @order_id = LAST_INSERT_ID();
  
insert into order_line(order_id, goods_id,unit_price, qty, amount)
values(@order_id , 'A01', 1500,2 , 3000 );
  
insert into order_line(order_id, goods_id,unit_price, qty, amount)
values(@order_id , 'A02', 2500,1 , 2500 );
  
  
  -- 주문수량만큼 재고량 감소시킨다.
  update goods set stock=stock-2 where goods_id='A01';
  update goods set stock=stock-1 where goods_id='A02';
 
 commit;
 
 select * from orders;
 select * from order_line;
 
 drop table order_line;
 drop table orders;
 
 -- 2) KIM 아이디가 A01 상품 3개 구입한다.
   INSERT INTO ORDERS( ORDER_DATE,USER_ID, ADDRESS, TOTAL_AMOUNT)
  VALUES(now(),'kim','서울시 강남구', 4500);
  
    -- 방금 삽입한 order_id 값 가져오기
SET @order_id = LAST_INSERT_ID();

  insert into order_line(order_id, goods_id,unit_price, qty, amount)
  values(@order_id, 'A01', 1500,3 , 4500 );
  
  update goods set stock=stock-3 where goods_id='A01';
  

 
 -- 3) JANG 아이디가 A03 상품 2개와 A04 상품 1개 구입한다.
  INSERT INTO ORDERS( ORDER_DATE,USER_ID, ADDRESS, TOTAL_AMOUNT)
  VALUES(now(),'jang','경기도 분당구', 5500);
  
    -- 방금 삽입한 order_id 값 가져오기
SET @order_id = LAST_INSERT_ID();

  insert into order_line(order_id, goods_id,unit_price, qty, amount)
  values(@order_id , 'A03', 2000,2 , 4000 );
  
   insert into order_line(order_id, goods_id,unit_price, qty, amount)
  values( @order_id , 'A04', 1500,1 , 1500 );
  
  update goods set stock=stock-2 where goods_id='A03';
  update goods set stock=stock-1 where goods_id='A04';
  
  -- -----------------------------------------------------
  select * from customer;
  select * from goods;
  select * from orders;
  select * from order_line;
  
  
   drop table order_line;
  drop table orders;
  drop table goods;
  drop table customer;

본 시스템은 JDBC를 이용하여 쇼핑몰의 주문 프로세스를 구현하였습니다. 데이터베이스의 참조 무결성을 유지하기 위해 '주문 마스터(ORDERS)'와 '주문 상세(ORDER_LINE)'를 1:N 관계로 설계하였으며, 자바의 트랜잭션(Transaction) 제어를 통해 데이터의 일관성을 보장합니다.


2. 주요 메서드별 코드 흐름 분석

[A] orderInsert (주문 마스터 등록 및 제어)

이 메서드는 전체 주문 공정의 컨트롤 타워 역할을 하며, 성공과 실패(Commit/Rollback)를 최종 결정합니다.

  1. 트랜잭션 시작: con.setAutoCommit(false);
    • JDBC의 기본값인 자동 커밋을 해제하여, 모든 작업(주문+상세+재고)이 성공했을 때만 한꺼번에 반영되도록 설정합니다.
  2. 유효성 검사 (Total Amount & Stock):
    • this.getTotalAmount(order)를 호출하여 상품 가격 합산 및 재고 부족 여부를 미리 체크합니다. 재고가 없으면 여기서 예외를 던져 프로세스를 중단합니다.
  3. 주문 번호 획득 (Key Retrieval): RETURN_GENERATED_KEYS
    • DB가 자동으로 생성한 order_id를 자바 쪽으로 다시 가져옵니다. 이 번호가 있어야 다음 단계인 상세 내역 저장 시 부모 주문을 식별할 수 있습니다.
  4. 결과 검증:
    • 상세 등록 결과(int[] re)를 루프로 확인하여 하나라도 성공(1)이 아니면 즉시 rollback()을 호출합니다.

[B] orderLineInsert (주문 상세 일괄 등록)

사용자가 구매한 여러 개의 품목을 효율적으로 처리하는 성능 최적화 구간입니다.

  1. 연결(Connection) 공유:
    • orderInsert에서 생성한 con 객체를 그대로 전달받아 동일한 트랜잭션 범위 내에서 작업합니다.
  2. 배치 처리 (Batch Processing):
    • ps.addBatch(): 루프를 돌며 INSERT 쿼리를 즉시 실행하지 않고 메모리 큐에 쌓습니다.
    • ps.executeBatch(): 쌓인 쿼리들을 단 한 번의 통신으로 DB에 전송합니다. 상품이 많을수록 네트워크 비용을 획기적으로 줄여줍니다.
  3. 파라미터 초기화: ps.clearParameters()를 통해 다음 상품 정보를 넣기 전 기존 세팅을 깨끗이 비웁니다.

3. 프로그램 실행 시퀀스 (Step-by-Step)

  1. 검증 단계: getTotalAmount() 호출 → DB 상품 정보 조회 및 재고/금액 확인.
  2. 마스터 등록: ORDERS 테이블에 Insert 실행.
  3. ID 획득: getGeneratedKeys()로 방금 생성된 orderId 추출.
  4. 상세 등록: orderId를 지참하여 orderLineInsert() 호출.
  5. 일괄 전송: 상세 품목들을 addBatch 후 executeBatch로 DB에 한꺼번에 반영.
  6. 최종 확정: 모든 배열 결과값이 1이면 con.commit(), 실패 시 con.rollback().
  7. 자원 해제: finally 블록에서 close()를 통해 커넥션 반납.

4. 핵심 기술 포인트 요약

  • Atomic(원자성): 주문은 성공하거나 아예 실패해야 합니다. setAutoCommit(false)가 이를 보장합니다.
  • 성능(Performance): 다중 품목 저장 시 Batch 처리를 사용하여 DB 부하를 최소화했습니다.
  • 유연성(Flexibility): Orders 객체 내부에 List<OrderLine>을 포함하는 계층적 모델을 사용하여 복잡한 주문 정보도 객체 하나로 관리할 수 있습니다.




singleton 

중복이 안되고 순서가 없다.

sessionset은 session들을 관리 - 관리자, 사용자 관리 로그인된 모든사용자 관리 정보 
sessionset에 session들이 모여져 있음

처음 sessionset으로 로그인 하는 과정 흐름도

 이과정 중요

다시 print로 와서 userid를 가지고와서 정보 출력

 

singleton으로 만든이유는 언제든지 가지고 와야하기 때문 

https://hellojason.tistory.com/42


 

싱글톤 패턴 이란? (Singleton Pattern)

객체지향의 디자인 패턴 중에서 아주 많이 들어봄 직하고, 비교적 간단한 패턴인 Singletone에 대해서 알아보겠습니다.Single 이라는 말 그대로, 하나의 인스턴스를 가지는 패턴을 의미합니다. 디자

hellojason.tistory.com

관리자는 멀티로그인 기능 이용 

 

id에 해당하는걸로 세션을 만들고 remove로 제거 하고 
set에서 지워버린다.

오토커밋 해제 하고 여러sql과 dao가 다 마치면 그때 commit으로 커넥션을 유지해준다. 

자동으로 만들어진 키를 리턴할 수 있도록 만들어주는 옵션이 있다. preparedstatement 
autoincrement 키 리턴
전체금액메서드를 따로 빼준다. 

상품 총구매금액 구하는 메서드
orderLineList 

 

 total += line.getQty() * goods.getGoodsPrice(); //주문수량 * 가격이 총구매금액 계산하는 메서드

주문번호를 getInt(1)에 넣어준다. 

주문상세에 계속 for문으로 insert를 날린다. 

 

1. 주문 전체 흐름도 (Sequence)

주문이 일어날 때 코드 내부에서 발생하는 순서입니다.

  1. 시작: con.setAutoCommit(false)로 수동 트랜잭션 모드 진입.
  2. 검증: getTotalAmount()를 호출해 재고가 충분한지 확인하고 총액 계산.
  3. 1차 저장: ORDERS 테이블에 Insert 후, 생성된 order_id를 리턴받음.
  4. 2차 저장: 리턴받은 order_id를 들고 orderLineInsert() 호출 (상세 내역들 Batch 저장).
  5. 재고 수정: decrementStock() 호출하여 실시간 재고 차감.
  6. 마무리: 모든 과정 성공 시 con.commit(), 하나라도 실패 시 con.rollback().

2. 핵심 메서드 상세 설명

① orderInsert (주문 마스터 등록 및 트랜잭션 제어)

이 메서드는 전체 공정의 **'컨트롤 타워'**입니다.

  • RETURN_GENERATED_KEYS: 주문번호는 DB가 자동으로 만드는데, 그 번호를 알아야 다음 단계인 상세 내역(order_line)을 저장할 수 있습니다. 이 옵션이 그 번호를 Java로 다시 가져오는 열쇠입니다.
  • 트랜잭션 제어: commit과 rollback을 직접 관리합니다. 상세 내역 저장 중 에러가 나면 이미 들어간 주문 마스터 정보도 취소시켜서 데이터가 꼬이는 것을 방지합니다.

② orderLineInsert (주문 상세 일괄 등록)

사용자가 여러 상품을 샀을 때 이를 효율적으로 처리하는 곳입니다.

  • Batch 처리 (addBatch, executeBatch): 상품이 3개라면 Insert 문을 3번 따로 날리지 않고, 바구니(Batch)에 담아두었다가 한 번에 DB로 보냅니다. 성능 최적화의 핵심입니다.
  • 결과 검증: int[] re 배열을 반환받아 모든 행이 정상적으로(1) 들어갔는지 체크합니다.

③ getTotalAmount (검증 및 계산)

단순히 돈만 더하는 게 아니라 비즈니스 로직을 수행합니다.

  • goods.getStock() < line.getQty(): 돈 계산을 하기 전에 재고가 있는지 먼저 확인합니다. 재고가 없으면 바로 Exception을 던져서 주문 전체를 중단시킵니다.

④ selectOrdersByUserId (주문 내역 조회)

사용자가 마이페이지에서 "내 주문 목록"을 볼 때 사용됩니다.

  • 계층 구조 복구: DB에서는 orders와 order_line이 떨어져 있지만, Java 객체에서는 Orders 객체 안에 List<OrderLine>을 넣어줌으로써 하나의 완성된 주문 정보로 조립합니다.

JDBC에서의 배치처리 ps.addBatch() -> 똑같은 insert문장을 축약해준다. 모아져있는 애들을 한번에 전송한다. 
한꺼번에 받아져 온다. 
result = ps.executeBatch();
결과가 많아서 int []로 반환한다. 

배치(Batch)란?

여러 개의 SQL 문을 한 번에 모아서(DBMS에게) 실행하는 방식

- 일반적인 JDBC 실행 방식은 SQL 한 개당 DB와 통신 → 네트워크 비용이 크고 성능 저하 발생

- Batch를 사용하면 다수의 SQL을 모아 한 번에 전송 → 성능 향상

 

JDBC에서는 addBatch()를 통해 여러 개의 SQL 문장을 하나의 묶음(batch) 으로 모아두고,

executeBatch()를 호출하면 그 묶음 전체를 한꺼번에 실행한다.

 

왜 배치를 쓰는가?

1)성능 향상

매번 DB에 연결해서 처리하는 것보다, 한 번에 여러 건을 처리하면 훨씬 빠르고 효율적이다.

 

2)트랜잭션 관리 용이

전체 배치를 하나의 트랜잭션으로 묶어서 성공/실패를 함께 관리할 수 있다.

 

3)코드 간결화

반복문 안에서 매번 executeUpdate() 대신 addBatch()로 모아두고 한 번만 실행한다.



 

int i  :  re
if ( i ! = 1) {
con.rollback();

}

결과가 하나라도 1이 아니면 실패니까 롤백해라