| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |
- springmvc
- gradle
- 의존주입
- frontController
- jointpoint
- springjdbc
- 비즈니스레이어
- spring
- @RequestParam
- produces
- application.properties
- @RequestMapping
- SpringBoot
- 스프링
- 어노테이션
- @ResponseBody
- AOP
- @Valid
- 유효성검사
- 생성자주입
- MVC
- after-throwing
- 바인딩변수
- @
- Java
- 서비스레이어
- Model
- c:if
- PointCut
- .xml
- Today
- Total
메모장
JDBC - MVC패턴으로 만들기(웹크롤링) 본문
영화 제목과 개봉일을 웹 크롤링 하여 데이터베이스를 확보하고
JDBC작업을 하여 그것을 MVC 패턴에 맞게 나눠보자 !
일단 Model부분에 중요한 내용이 많으므로 controller 와 view의 코드를 먼저 살펴보자
Controller
package controller;
import java.util.ArrayList;
import model.Crawling;
import model.MovieDAO;
import model.MovieVO;
import view.View;
public class Controller {
private MovieDAO mDAO;
private View view;
public Controller() {
mDAO=new MovieDAO();
view=new View();
}
public void startApp() {
view.printMenu();
ArrayList<MovieVO> mdatas=Crawling.sample(); //크롤링한 샘플을 배열리스트에 저장한다.
// Q. 배열리스트를 받아오는데, insert()를 그럼 1번하나요? 라고 생각할수 있음
// A. NO! insert()를 배열리스트.size()만큼 for문(반복) 돌려야 한다.
for(int i=0;i<mdatas.size();i++) { // 데이터 사이즈만큼
MovieVO mdata=mdatas.get(i); // 정보들을 담는다.
boolean flag=mDAO.insert(mdata); //정보 추가 // boolean 타입이라 flag
if(!flag) { // false일시
view.printFalse();
return;
}
}
view.printTrue(); // 안거치고 내려오면 성공
// selectAll 전체출력
MovieVO mVO=new MovieVO(0,"",null); // 빈칸으로 넘기기 ""
ArrayList<MovieVO> mdatas2=mDAO.selectAll(mVO); // %% 일시 전체 출력이므로
// 검색과 전체 출력을 합쳐볼수 있다.
view.printMovieList(mdatas2);// 목록을 출력해준다.
// selectOne 하나 출력
int num1=view.getNum(); // 숫자를 사용자에게 받아서 뷰가 넘겨준다.
MovieVO mVO1=new MovieVO(num1,null,null); // 모델에게 보낼때 PK를 넘겨줌
MovieVO mdata1=mDAO.selectOne(mVO1); // 사용자에게 받은 번호 자리 영화를 불러와
view.printMovie(mdata1); // 영화를 출력해준다.
// delete 삭제하기
int num2=view.getNum(); // 사용자에게 번호를 받아
MovieVO mVO2=new MovieVO(num2,null,null); // pk로 넘겨줘
boolean flag2=mDAO.delete(mVO2); // 삭제는 boolean 타입이라 flag 써줘
if(flag2) { // true
view.printTrue();
}
else { // false
view.printFalse();
}
// update 영화 번호 받고 이름 받아서 그 해당하는 번호의 영화 이름 바꿔줘
int num3=view.getNum(); // 번호를 받아
String name3=view.getName(); // 이름도 입력 받아
MovieVO mVO3=new MovieVO(num3,name3,null); // 넘겨줘 모델로
boolean flag3=mDAO.update(mVO3); //선택한 영화 번호의 이름을 뭘로 바꾸고 싶어 ? 바꿔줘
if(flag3) { // true
view.printTrue();
}
else { // false
view.printFalse();
}
// selectAll 이름 검색
String name4=view.getName(); // 이름을 입력받아줘
MovieVO mVO4=new MovieVO(0,name4,null); // 이름 부분에 받아서 모델로 넘겨줄게
ArrayList<MovieVO> mdatas4=mDAO.selectAll(mVO4); // 검색에 해당하는 단어 목록 출력받았어
view.printMovieList(mdatas4); // 목록 출력 해줘
}
}
컨트롤러 부분에서는 정확히 입력메뉴를 구분하여 만들지 않았고 구현만 될 수 있도록 만들어 놓았다 .
View
package view;
import java.util.ArrayList;
import java.util.Scanner;
import model.MovieVO;
public class View {
private static Scanner sc=new Scanner(System.in);
public String getName() {
System.out.println("영화의 이름을 입력하세요.");
System.out.print("입력) ");
String name=sc.nextLine();
return name;
}
public int getNum() {
System.out.println("영화의 번호를 입력하세요.");
System.out.print("입력) ");
int num=sc.nextInt();
sc.nextLine();
return num;
}
public void printMovie(MovieVO mVO) {
System.out.println("=== 영 화 정 보 ===");
if(mVO==null) {
System.out.println("없음!");
return;
}
System.out.println(mVO);
}
public void printMenu() {
System.out.println("영화 프로그램입니다.");
System.out.println("샘플 데이터를 크롤링합니다...");
}
public void printTrue() {
System.out.println("작업 성공!");
}
public void printFalse() {
System.out.println("작업 실패.....");
}
public void printMovieList(ArrayList<MovieVO> mdatas) {
System.out.println("=== 영 화 목 록 ===");
if(mdatas.size()==0) {
System.out.println("없음!");
return;
}
for(MovieVO mdata:mdatas) {
System.out.println(mdata);
}
}
}
View 부분도 최소 필요한 부분만 만들어 놓았다 .
오늘의 중요한 부분 Model 파트
먼저 , Crawling
package model;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
//멤버변수 없이 메서드만 존재하는 클래스
//존재이유 : 메서드 실행이 목적이다. 이런 클래스들은 보편적으로 static 이 붙는다.
public class Crawling {
public static ArrayList<MovieVO> sample() {
final String url = "http://www.cgv.co.kr/movies/?lt=1&ft=0";
Connection conn = Jsoup.connect(url);
Document doc = null;
try {
doc = conn.get();
} catch (IOException e) {
e.printStackTrace();
}
Elements elems = doc.select("strong.title");
Elements elems2 = doc.select("span.txt-info");
Iterator<Element> itr = elems.iterator();
Iterator<Element> itr2 = elems2.iterator();
//////////////////////////////
ArrayList<MovieVO> mdatas=new ArrayList<MovieVO>();
//////////////////////////////
while(itr.hasNext()) {
String str = itr.next().toString();
String str2 = itr2.next().toString();
int index = str.indexOf(">");
str = str.substring(index+1);
index = str.indexOf("<");
str = str.substring(0, index);
int index2 = str2.indexOf("<strong>");
str2 = str2.substring(index2+9);
index2 = str2.indexOf("<span>");
str2 = str2.substring(0, index2);
mdatas.add(new MovieVO(0,str,str2));
}
return mdatas;
}
}
MovieVO
package model;
public class MovieVO {
private int num;
private String name;
private String odate;
public MovieVO(int num,String name,String odate) {
this.num=num;
this.name=name;
this.odate=odate;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getOdate() {
return odate;
}
public void setOdate(String odate) {
this.odate = odate;
}
@Override
public String toString() {
return this.num+". "+this.name+" ["+this.odate+"]";
}
}
별도의 class를 하나 더 만들어서 캡슐화 해주기
JDBCUtil
오늘의 중요한 부분
package model;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
// 반복해서 사용하는
//JDBC와 관련된 1번 드라이버 load부분 ,2번 connection 확보 부분 => connect()
//4.DB연결 해제 부분 => disconnect() < 이 세가지 코드를 "모듈화" >
//-> 별도의 클래스로 캡슐화하는 것이 더 유리함!
//-> 공통의 로직을 모듈화한 클래스
// == Util 클래스
// 장점
// : 유지보수 용이
// : 코드 재사용성이 증가
// : 중복코드 최소화
// : 오류의 파급효과 줄어듦
// : 개발 시간,비용 단축
// : 영업이익증가
public class JDBCUtil {
// 상단에서 상수화 시켜주기
static final String driverName_MySQL="com.mysql.cj.jdbc.Driver";
static final String driverName_Oracle="oracle.jdbc.driver.OracleDriver";
static final String url_MySQL="jdbc:mysql://localhost/hong";
static final String url_Oracle="jdbc:oracle:thin:@localhost:1521:xe";
static final String userName="root";
static final String passwd="1234";
public static Connection connect() {
Connection conn=null;
try {
Class.forName(driverName_MySQL);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
try {
conn=DriverManager.getConnection(url_MySQL, userName, passwd);
} catch (SQLException e) {
e.printStackTrace();
}
return conn;
}
public static void disconnect(Statement stmt,Connection conn) {
try { // UPDATE,DELETE,INSERT
stmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void disconnect(ResultSet rs,Statement stmt,Connection conn) {
try { // SELECTALL, SELECTONE // 메서드를 오버로딩 해준다
rs.close(); // SELECT 같은 쿼리류 에서는 받아올 것이 있어서 ResultSet을 써줘야 한다.
stmt.close(); // jsoup의 element 같은 역할을 하는 친구
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
MovieDAO
package model;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
// JDBC와 관련된 1,2 => connect()
// 4. 코드를 "모듈화" => disconnect()
// -> 별도의 클래스로 캡슐화 하는것이 더 유리함 !
// 공통의 로직을 모듈화 한 클래스를 부르는말 == Util 클래스
public class MovieDAO {
private Connection conn; // 자주쓰이는 것을 상단에 선언
private Statement stmt;
private ResultSet rs;
// Q. 크롤링 할 때 배열리스트를 받아오는데,insert()를 그럼 1번하나요?
// A. NO! insert()를 배열리스트.size()만큼 for문(반복) 돌린다.
public boolean insert(MovieVO mVO) {
conn=JDBCUtil.connect(); // conn 연결을 확보해줘 1,2번
final String sql_INSERT="INSERT INTO MOVIE (NAME,ODATE) VALUES('"+mVO.getName()+"','"+mVO.getOdate()+"');";
try { //추가해줘 테이블movie에 제목과 개봉일 제목 개봉일
stmt=conn.createStatement();
int result=stmt.executeUpdate(sql_INSERT);
// Query -> SELECT
// Update -> INSERT,UPDATE,DELETE
if(result<=0) { // 결과 값이 음수 일때
// 적용된 row가 없음! //변한게 없다.
return false;
}
} catch (SQLException e1) {
e1.printStackTrace();
return false;
} finally { // finally 마지막에 한번은 실행되야 할때 사용 - 마지막 DB와 연결해제 시 close로 잘 닫아줘야한다.
JDBCUtil.disconnect(stmt, conn);
}
return true;
}
public boolean update(MovieVO mVO) {
conn=JDBCUtil.connect(); // 모듈화로 JDBCUtil 활용하기
String sql_UPDATE="UPDATE MOVIE SET NAME='"+mVO.getName()+"' WHERE NUM="+mVO.getNum()+";";
수정해줘 테이블 이름을 이 이름으로 어디있는? 번호가 이 번호인 이름을
try {
stmt=conn.createStatement(); // 만들고 // statement 가 read,write를 제공해주는 것
int result=stmt.executeUpdate(sql_UPDATE);
// Query -> SELECT
// Update -> INSERT,UPDATE,DELETE
if(result<=0) {
// 적용된 row가 없음!
return false;
}
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
return false;
}
finally {
JDBCUtil.disconnect(stmt, conn);
}
return true;
}
public boolean delete(MovieVO mVO) {
conn=JDBCUtil.connect();
String sql_DELETE="DELETE FROM MOVIE WHERE NUM="+mVO.getNum()+";";
삭제해줘 테이블 어디를? 번호가 이 번호인 부분을
try {
stmt=conn.createStatement(); // 만들고 // statement 가 read,write를 제공해주는 것
int result=stmt.executeUpdate(sql_DELETE);
// Query -> SELECT
// Update -> INSERT,UPDATE,DELETE
if(result<=0) {
// 적용된 row가 없음!
return false;
}
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
finally {
JDBCUtil.disconnect(stmt, conn);
}
return true;
}
public ArrayList<MovieVO> selectAll(MovieVO mVO) {
conn=JDBCUtil.connect();
여기서 잠깐 이 부분은 밑에서 짚고 넘어가자 !
final String sql_SELECTALL="SELECT NUM,NAME,ODATE FROM MOVIE WHERE NAME LIKE '%"+mVO.getName()+"%';";
ArrayList<MovieVO> mdatas=new ArrayList<MovieVO>();
try {
stmt=conn.createStatement();
rs=stmt.executeQuery(sql_SELECTALL);
// Query -> SELECT
// Update -> INSERT,UPDATE,DELETE
while(rs.next()) {
int num=rs.getInt("NUM");
String name=rs.getString("NAME");
String odate=rs.getString("ODATE");
MovieVO mdata=new MovieVO(num,name,odate);
// 이렇게도 작성가능 MovieVO mdata = new MovieVO(rs.getInt("NUM"),rs.getString("NAME"),rs.getString("ODATE"));
mdatas.add(mdata);
}
} catch (SQLException e1) {
e1.printStackTrace();
return null;
} finally {
JDBCUtil.disconnect(rs, stmt, conn); //오버로딩한 메서드 사용
}
return mdatas;
}
public MovieVO selectOne(MovieVO mVO) {
conn=JDBCUtil.connect();
final String sql_SELECTONE="SELECT NUM,NAME,ODATE FROM MOVIE WHERE NUM="+mVO.getNum()+";";
try {
stmt=conn.createStatement();
rs=stmt.executeQuery(sql_SELECTONE);
// Query -> SELECT
// Update -> INSERT,UPDATE,DELETE
if(rs.next()) {
int num=rs.getInt("NUM");
String name=rs.getString("NAME");
String odate=rs.getString("ODATE");
MovieVO mdata=new MovieVO(num,name,odate);
// 이렇게도 작성가능 MovieVO mdata = new MovieVO(rs.getInt("NUM"),rs.getString("NAME"),rs.getString("ODATE"));
return mdata;
}
} catch (SQLException e1) {
e1.printStackTrace();
return null;
} finally {
JDBCUtil.disconnect(rs, stmt, conn);
}
return null;
}
}
SELECTALL 부분에서
final String sql_SELECTALL="SELECT NUM,NAME,ODATE FROM MOVIE WHERE NAME LIKE
찾아줘 번호 , 이름, 개봉일 테이블에서 어디? 이름에
'%"+mVO.getName()+"%';";
이 이름이 들어가면
---------------------------------------------------------------------------------------------------------------------
이 부분에서 우리는 목록 전체 검색이 있고, 이름검색이 따로 있어서 둘을 나눠서 코드를 작성하였는데
그 둘을 하나로 합쳐 짧은 코드를 만들수 있다 !
-----> 가능한 이유 : SQL 에서 WHERE 조건절 뒤에 컬럼 LIKE 단어를 찾는 '%%'; 이것이 들어가는데
%와 % 사이에 아무것도 안들어간 상태로 출력시 똑같이 전체 목록이 출력된다 !!
코드가 공통적인 부분 으로
final String sql_SELECTALL="SELECT NUM,NAME,ODATE FROM MOVIE WHERE NAME LIKE'%"+mVO.getName()+"%';";
들어갈 getName이 있다면 그 단어를 검색 해주고 , 전체 목록을 출력하고 싶다면 "" 큰 따옴표를 이용하여 빈값을 ModelDAO 로 전달 시켜준다.
-----> 이로써, 전체목록출력과 이름검색을 하나의 코드로 실행할 수 있다 !!
'DB > 개념정리' 카테고리의 다른 글
| JDBC - 임시 멤버 변수 구현 (0) | 2024.04.10 |
|---|---|
| JDBC PrepareStatement (0) | 2024.04.10 |
| MySQL 날짜 , 날짜함수 (0) | 2024.04.10 |
| JDBC (0) | 2024.04.10 |
| DB ,DBMS (0) | 2024.04.10 |