본문 바로가기

programming/Gukbi

국비 교육 53일차 - Trigger

728x90
반응형

오전에는 프로젝트를 좀 배우고 오후에는 급하게 트리거를 배웠다. PL/SQL의 연장선 느낌. 

 

-- 트리거 (자동 이벤트 처리 = 미리 설정된 조건이 맞는 경우에 자동 실행)
/*
    트리거는 
    입고 => INSERT => 재고 
        새로운 상품
    입고 => UPDATE => 재고
        수량, 금액 
    입고 => DELETE => 재고
        취소 
        
    출고 => INSERT, UPDATE, DELETE => 재고 
    
    형식) 
        = 생성
            CREATE OR REPLACE TRIGGER trigger_name
            BEFORE|AFTER [INSERT|UPDATE|DELETE] ON table_name
            DECLARE 
                변수 선언 => 변수가 없으면 생략
            BEGIN
                트리거 구현
            END;
            /
        = 삭제
            DROP TRIGGER tri_name
        = 수정 
            ALTER TRIGGER tri_name => 수정
*/

 트리거는 SQL문장에서 어떤 변화가 생겼을때 자동으로 변화를 시켜주는 메소드 같은거라고 이해를 했다. 그렇기 때문에 재고 프로그램이나 댓글 카운트와 같은 기능에서 많이 쓰인다. 

 

CREATE TABLE product(
    no NUMBER,
    name VARCHAR2(100),
    price NUMBER
);
CREATE TABLE stock_in (
    no NUMBER,
    stock NUMBER,
    price NUMBER
);

CREATE TABLE stock_out(
    no NUMBER,
    stock NUMBER,
    price NUMBER
);
CREATE TABLE stock(
    no NUMBER,
    stock NUMBER,
    price NUMBER,
    total NUMBER
);

 트리거를 사용해보기 위해 테이블을 차례대로 만들어줬다. 상품, 입고, 출고, 재고량 순서대로 이다. 

 

 

입고가 되었을때 작동하는 트리거

-- 입고 = 재고
CREATE OR REPLACE TRIGGER stock_in_Trigger
AFTER INSERT ON stock_in 
FOR EACH ROW
DECLARE 
    v_cnt NUMBER;
BEGIN
    SELECT COUNT(*) INTO v_cnt
    FROM stock
    WHERE no=:NEW.no;
    
    IF (v_cnt=0) THEN -- 새로운 상품이 입고 
     INSERT INTO stock VALUES(:NEW.no, :NEW.stock, :NEW.price, :NEW.stock*:NEW.price);
    ELSE -- 기존에 있는 상품
        UPDATE stock SET
        stock=stock+:NEW.stock,
        total=total+(:NEW.stock*:NEW.price)
        WHERE no=:NEW.no;
    END IF;
END;
/

 기존에 있는 재고를 세어주는 v_cnt 변수를 하나 선언해주고, count(*)를 해준 값을 넣어준다. 만약 품명이 없는 상품이 입고 되면 새로운 row를 추가해준다. 기존에 있는 상품이라면 재고를 +1 해주고 total 가격에도 stock*price만큼 해준 값을 UPDATE 한다. 

 

밑의 코드는 출고 됐을때의 트리거이다. 

CREATE OR REPLACE TRIGGER stock_out_Trigger
AFTER INSERT ON stock_out
FOR EACH ROW 
DECLARE 
    v_cnt NUMBER;
BEGIN
    SELECT stock INTO v_cnt
    FROM stock
    WHERE no=:NEW.no;
    
    IF(:NEW.stock=v_cnt) THEN
     DELETE FROM stock 
     WHERE no=:NEW.no;
    ELSE
     UPDATE stock SET 
     stock=stock-:NEW.stock,
     total=total-(:NEW.stock*:NEW.price)
     WHERE no=:NEW.no;
    END IF;
END;
/

출고되는 수량과 재고의 수량이 다르면 재고만큼 빼주고, 같으면 아예 삭제해버리는 메소드이다. 

 

 

 

그리고 프로젝트를 위해서.. 데이터를 수집하는 코드를 다시 짜서 수집을 해줬다. 

jsoup을 이용해서 데이터를 다 담아서 오라클에 담아줬다. 

package com.sist.dao;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

// 다음에서 데이터 읽어 오기 => MovieDAO => 데이터 저장 
public class MovieManager {
   public void movieData()
   {
	   MovieDAO dao=new MovieDAO();
	   try
	   {
		   String url="https://movie.daum.net/premovie/kakaopage?flag=Y";
		   int cno=11;
		   Document doc=Jsoup.connect(url).get();
		   /*
		    *  <div class="thumb_cont">
                                <strong class="tit_item">
                                    <a href="/moviedb/main?movieId=137317" 
                                    class="link_txt" data-tiara-layer="moviename">미나리</a>
		    */
		   Elements link=doc.select("div.thumb_cont strong.tit_item a.link_txt");
		   /*
		    *  <span class="txt_info">
							        개봉<span class="txt_num">21.03.03</span>
						        </span>
		    */
		   //Elements regdate=doc.select("span.txt_info span.txt_num");
		   /*
		    *  <span class="txt_append">
                                    <span class="info_txt">평점<span class="txt_grade">7.2</span></span>
                                    <span class="info_txt">예매율<span class="txt_num">22.1%</span></span>
                                </span>
		    */
		   /*
		    *  <div class="poster_movie">
                                        <img src="https://img1.daumcdn.net/thumb/C408x596/?fname=https%3A%2F%2Ft1.daumcdn.net%2Fmovie%2F335359e6a5a36f0d334bc20ff2f2c0488b30c67d" class="img_thumb" alt="미나리">
                                    <span class="rank_num">1</span>
                                        <span class="txt_tag">
                                            <span class="ico_movie ico_see see12">12세이상관람가</span>
                                        </span>
                                </div>
		    */
		   //Elements grade=doc.select("span.txt_tag span.ico_movie");
		   //Elements score=doc.select("span.txt_append span.txt_grade");
		   //
		   //Elements poster=doc.select("div.poster_movie img.img_thumb");
		   /*
		    *  <div class="poster_info">
                       <a href="/moviedb/main?movieId=137317" class="link_story" data-tiara-layer="poster">
                        2021년 전 세계가 기다린어느 한국 가족의 원더풀한 이야기"미나리는 어디서든 잘 자라"낯선 미국, 아칸소로 떠나온 한국 가족.가족들에게 뭔가 해내는 걸 보여주고 싶은 아빠 '제이콥'(스티븐 연)은자신만의 농장을 가꾸기 시작하고 엄마 '모니카'(한예리)도 다시 일자리를 찾는다.아직 어린 아이들을 위해‘모니카’의 엄마 ‘순자’(윤여정)가 함께 살기로 하고가방 가득 고춧가루, 멸치, 한약 그리고 미나리씨를 담은 할머니가 도착한다.의젓한 큰딸 '앤'(노엘 케이트 조)과 장난꾸러기 막내아들 '데이빗'(앨런 김)은여느 그랜마같지 않은 할머니가 영- 못마땅한데…함께 있다면, 새로 시작할 수 있다는 희망으로하루하루 뿌리 내리며 살아가는어느 가족의 아주 특별한 여정이 시작된다!
                 </a>
		    */
		   //Elements story=doc.select("div.poster_info a.link_story");
		   /*
		    *   MNO       NOT NULL NUMBER  (O)       
				CNO                NUMBER  (O)    
				POSTER    NOT NULL VARCHAR2(260) (O)  
				TITLE     NOT NULL VARCHAR2(200) (O)
				DIRECTOR  NOT NULL VARCHAR2(100)  
				ACTOR              VARCHAR2(1000) 
				REGDATE   NOT NULL VARCHAR2(200)  (O)
				GENRE     NOT NULL VARCHAR2(100)  
				NATION    NOT NULL VARCHAR2(50)   
				GRADE     NOT NULL VARCHAR2(50)   (O)
				TIME      NOT NULL VARCHAR2(50)   
				SCORE              NUMBER(2,1)    (O)
				SHOWUSER           VARCHAR2(30)   
				BOXOFFICE          VARCHAR2(10)   
				STORY              CLOB           (O)
				KEY                VARCHAR2(30)
		    */
		   for(int i=0;i<link.size();i++)
		   {
			   try
			   {
				   // HTML => 태그와 태그사이 <a>(값)</a> => text()
				   // <a 속성="값"> => attr(속성) ==> img , a 
				   MovieVO vo=new MovieVO();
				   
					/*
					 * if(link.get(i).text().equals("타락천사")) continue;
					 * System.out.println("제목:"+link.get(i).text());
					 * System.out.println("링크:"+link.get(i).attr("href"));
					 * System.out.println("개봉일:"+regdate.get(i).text());
					 * System.out.println("등급:"+grade.get(i).text());
					 * System.out.println("평점:"+score.get(i).text());
					 * System.out.println("포스터:"+poster.get(i).attr("src"));
					 * System.out.println("줄거리:"+story.get(i).text()); 
					 * vo.setCno(cno);
					 * vo.setTitle(link.get(i).text()); 
					 * vo.setRegdate(regdate.get(i).text());
					 * vo.setGrade(grade.get(i).text());
					 * vo.setScore(Double.parseDouble(score.get(i).text()));
					 * vo.setPoster(poster.get(i).attr("src")); 
					 * vo.setStory(story.get(i).text());
					 */
				   // https://movie.daum.net/moviedb/main?movieId=137317
				   Document doc2=Jsoup.connect("https://movie.daum.net"
				            +link.get(i).attr("href")).get();
				   /*
				    *   <div class="inner_tit">
                    <span class="txt_tit">소울</span>
                </div>
				    */
				   Element title=doc2.selectFirst("div.inner_tit span.txt_tit");
				   System.out.println("제목:"+title.text());
				   vo.setCno(cno);
				   vo.setTitle(link.get(i).text());
				   /*
				    *  div class="info_desc" data-tiara-layer="desc">
                            <div class="desc_cont">
				    */
				   Element story=doc2.selectFirst("div.info_desc div.desc_cont");
				   String s=story.text();
				   //s=s.substring(0,s.indexOf("["));
				   vo.setStory(story.text());
				   System.out.println("줄거리:"+s);
				   
				   /*
				    * 
				    *   info_poster">
                    <a href="#photoId=1396501" class="thumb_img" data-tiara-layer="poster" data-tiara-copy="메인_포스터">
            <span class="bg_img
				    */
				   Element poster=doc2.selectFirst("div.info_poster span.bg_img");
				   String p=poster.attr("style");
				   p=p.substring(p.indexOf("(")+1,p.lastIndexOf(")"));
				   System.out.println(p);
				   vo.setPoster(p);
				   /*
				    *   개봉	2021.01.20
						장르	애니메이션/판타지
						국가	미국
						등급	전체관람가
						러닝타임	107분
						평점	8.8
						누적관객	1,977,906명
						박스오피스	7위
				    */
				   Elements info1=doc2.select("div.inner_cont dl.list_cont dt");
				   Elements info2=doc2.select("div.inner_cont dl.list_cont dd");
				   for(int j=0;j<info1.size();j++)
				   {
					   try 
					   {
						   String str=info1.get(j).text();
						   if(str.equals("개봉"))
						   {
							   System.out.println("개봉:"+info2.get(j).text());
							   vo.setRegdate(info2.get(j).text());
						   }
						   else if(str.equals("등급"))
						   {
							   System.out.println("등급:"+info2.get(j).text());
							   vo.setGrade(info2.get(j).text());
						   }
						   else if(str.equals("평점"))
						   {
							   System.out.println("평점:"+info2.get(j).text());
							   vo.setScore(Double.parseDouble(info2.get(j).text()));
						   }
						   else if(str.equals("장르"))
						   {
							   System.out.println("장르:"+info2.get(j).text());
							   vo.setGenre(info2.get(j).text());
						   }
						   else if(str.equals("국가"))
						   {
							   System.out.println("국가:"+info2.get(j).text());
							   vo.setNation(info2.get(j).text());
						   }
						   else if(str.equals("러닝타임"))
						   {
							   System.out.println("러닝타임:"+info2.get(j).text());
							   vo.setTime(info2.get(j).text());
						   }
						   else if(str.equals("누적관객"))
						   {
							   System.out.println("누적관객:"+info2.get(j).text());
							   vo.setShowUser(info2.get(j).text());
						   }
						   else if(str.equals("박스오피스"))
						   {
							   System.out.println("박스오피스:"+info2.get(j).text());
							   vo.setBoxoffice(info2.get(j).text());
						   }
					   }catch(Exception ex) {}
						   
				   }
				   //System.out.println("동영상:"+youtubeGetKey(vo.getTitle()));
				   vo.setKey(youtubeGetKey(vo.getTitle()));
				   dao.movieInsert(vo);
				   
				   Thread.sleep(100);// 읽는 속도와 오라클에 저장하는 속도 차이 
				   // P / S
				   System.out.println("========================================================");
		   
			   }catch(Exception ex) {ex.printStackTrace();}
		   }
	   }catch(Exception ex){ex.printStackTrace();}
   }
   // https://www.youtube.com/results?search_query=
   public String youtubeGetKey(String title)
   {
	   String key="";
	   try
	   {
		   String url="https://www.youtube.com/results?search_query="+title;
		   Document doc=Jsoup.connect(url).get();
		   // /watch?v=bdcIC8d4nW0
		   Pattern p=Pattern.compile("/watch\\?v=[^가-힣]+");
		   /*
		    *  /watch?v=bdcIC8d4nW0","webPageType":"WEB_PAGE_TYPE_WATCH","rootVe":3832}
		    */
		   Matcher m=p.matcher(doc.toString());
		   while(m.find())
		   {
			   String str=m.group();// 찾은 문자열을 읽어 온다 
			   str=str.substring(str.indexOf("=")+1,str.indexOf("\""));
			   key=str;
			   break;
		   }
	   }catch(Exception ex){ex.printStackTrace();}
	   return key;
   }
   public static void main(String[] args) {
	    MovieManager m=new MovieManager();
	    m.movieData();
   }
}

특히 유튜브 소스 긁어오는게 좀 어려웠다. Matcher와 Pattern이라는걸 처음써봐서 좀 헷갈렸는데 이게 ai할때 패턴을 찾아오는 방식이라고 했다. 그렇게 어려운건 아니지만 여기서 여러가지를 응용해서 데이터를 쌓아온다고 상상해보니 되게 흥미롭게 느껴졌다. 나중에 더 깊게 배워볼 수 있는 기회가 있었으면 좋겠다. 

 

여튼 데이터 수집을 다 끝내고 나서 영화를 띄워줄 수 있게 homepage를 만들어 봤다. 

 

package com.sist.movie;

import java.io.*;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@WebServlet("/MovieHomeServlet")
public class MovieHomeServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out=response.getWriter();
		out.println("<div class=container>");
		out.println("<div class=row>");
		out.println("<embed src=\"http://youtube.com/embed/bdcIC8d4nW0\\u0026t=4s\" width=960px height=500>");
		out.println("</div>");
		out.println("</div>");
	}

}

 이건 그냥 홈페이지에 동영상을 띄우는 소스인데, 이걸 이제 mainpage에서도 띄워줄수 있어야 한다. 

 

package com.sist.movie;

import java.io.*;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/MovieMainServlet")
public class MoiveMainServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// html 전송
		response.setContentType("text/html;charset=UTF-8");
		PrintWriter out=response.getWriter();
		String mode=request.getParameter("mode");
		String view="";
		if(mode==null)
			mode="0";
		int index=Integer.parseInt(mode);
		switch(index)
		{
		case 0: view="MovieHomeServlet"; break;
		case 1: view="MovieReserveServlet"; break;
		case 2: view="MovieBoxofficeServlet"; break;
		}
		
		out.println("<html>");
		out.println("<head>");
		out.println("<link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/css/bootstrap.min.css\">");
		out.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js\"></script>");
		out.println("<script src=\"https://maxcdn.bootstrapcdn.com/bootstrap/3.4.1/js/bootstrap.min.js\"></script>");
		out.println("<style>");
		out.println(".row{");
		out.println("width:960px;margin:0px auto}");
		out.println("</style>");
		out.println("</head>");
		out.println("<body>");
		out.println("<nav class=\"navbar navbar-inverse\">\r\n"
				+ "  <div class=\"container-fluid\">\r\n"
				+ "    <div class=\"navbar-header\">\r\n"
				+ "      <a class=\"navbar-brand\" href=\"MovieMainServlet\">SIST Movie</a>\r\n"
				+ "    </div>\r\n"
				+ "    <ul class=\"nav navbar-nav\">\r\n"
				+ "      <li class=\"active\"><a href=\"#\">Home</a></li>\r\n"
				+ "      <li class=\"dropdown\">\r\n"
				+ "        <a class=\"dropdown-toggle\" data-toggle=\"dropdown\" href=\"#\">랭킹\r\n"
				+ "        <span class=\"caret\"></span></a>\r\n"
				+ "        <ul class=\"dropdown-menu\">\r\n"
				+ "          <li><a href=\"MovieMainServlet?mode=1\">예매순위</a></li>\r\n"
				+ "          <li><a href=\"MovieMainServlet?mode=2\">박스오피스</a></li>\r\n"
				+ "          <li><a href=\"MovieMainServlet?mode=3\">OTT</a></li>\r\n"
				+ "        </ul>\r\n"
				+ "      </li>\r\n"
				+ "      <li class=\"dropdown\">\r\n"
				+ "        <a class=\"dropdown-toggle\" data-toggle=\"dropdown\" href=\"#\">상영/예정작\r\n"
				+ "        <span class=\"caret\"></span></a>\r\n"
				+ "        <ul class=\"dropdown-menu\">\r\n"
				+ "          <li><a href=\"MovieMainServlet?mode=4\">넷플릭스</a></li>\r\n"
				+ "          <li><a href=\"MovieMainServlet?mode=5\">왓차</a></li>\r\n"
				+ "          <li><a href=\"MovieMainServlet?mode=6\">카카오페이지</a></li>\r\n"
				+ "        </ul>\r\n"
				+ "      </li>\r\n"
				+ "      <li><a href=\"#\">영화뉴스</a></li>\r\n"
				+ "    </ul>\r\n"
				+ "  </div>\r\n"
				+ "</nav>");
		out.println("<div style=\"height:50px\"></div>");
		RequestDispatcher rd=request.getRequestDispatcher(view);
		rd.include(request, response);
		out.println("</body>");
		out.println("</html>");
	}

}

 

 

화면 표시를 어떻게 해줄것인지 정해줄건지를 잘 생각해봐야 한다. 메뉴바를 만들었기 때문에 이걸 고정하고 창을 바꿔가면서 띄워 줄 수 있는 방법을 생각해 봐야 한다. 

 

저번에는 page로 화면을 나눠줬다면, 이번에는 mode로 메뉴바를 바꾸면 화면이 전환되게끔 코드를 짜주었다. 

일단 mainpage에는 mode에 입력된 값이 없기 때문에 null이면 0을 입력해준다. 

 

response.setContentType("text/html;charset=UTF-8");
		PrintWriter out=response.getWriter();
		String mode=request.getParameter("mode");
		String view="";
		if(mode==null)
			mode="0";
		int index=Integer.parseInt(mode);
		switch(index)
		{
		case 0: view="MovieHomeServlet"; break;
		case 1: view="MovieReserveServlet"; break;
		case 2: view="MovieBoxofficeServlet"; break;
		}

 

그리고 1, 2인 경우를 switch case로 나눠서 view라는 변수에 담아준다. 

 

RequestDispatcher rd=request.getRequestDispatcher(view);
		rd.include(request, response);

 

그리고 나서 include라는 메소드를 써서 변수에 담아준 페이지들을 띄우면 일단 기본 틀은 완성이다. 

프로젝트 할때도 이런식으로 프로그램을 짜야 하기때문에 앞으로 잘 알아두어야 할 코드같다. 

 

이제 다음주에는 이걸 완성하게 될것 같다

728x90
반응형