JAVA-DBConnection(JDBC)

  • JAVA에서 db에 접근할 수 있도록 해주는 API를 JDBC라고 한다.

  • Java DataBase Connectivity

    자바에서 데이터베이스에 접속할 수 있도록 해주는 자바 API
    자바 프로그램이 데이터베이스와 연결되어 데이터를 주고 받을 수 있게 해주는 프로그래밍 인터페이스다.

  • JDBC의 역할

    통역자 역할 : 응용프로그램과 DBMS간의 통신을 중간에 번역해주는 역할

  • java.sql Package 필요하다.

    • java.sql.Driver
      DB와 연결하는 Driver class를 만들 때 반드시 implements해야 하는
      interface로 JDBC 드라이버의 중심이 되는 interface다.
    • java.sql.Connection
      특정 데이터베이스와 연결정보를 가지는 interface입니다.
      DriverManager로 부터 Connection 객체를 가져옵니다.
    • java.sql.ResultSet
      SELECT구문 실행 결과를 조회할 수 있는 방법을 정의한 interface
    • java.sql.PreparedStatement
      Statement의 하위 interface입니다.
      SQL문을 미리 컴파일 하여 실행 속도를 높입니다.
    • java.sql.CallableStatement
      PreparedStatement의 하위 interface입니다.
      DBMS의 Stored procedure을 호출합니다.

          
  1. Java에서 SQL문을 문자열 타입으로 작성
  2. 설치된 Driver를 이용해서 Connection 객체 생성
  3. Connection 객체를 통해 PreparedStatement 객체 생성(SQL문을 넘겨주며)
  4. 만들어진 PreparedStatement 객체를 통해 SQL문 수행
  5. SELECT문이라면 검색 결과가 있으므로 ResultSet 객체로 받기
  6. 적절한 횟수의 rs.next()를 통해 받아오고자 하는 데이터가 위치한 행 선택
  7. rs.get~~~()로 각 컬럼의 데이터 받아오기

JDBC API

  • Java DataBase Connectivity의 약자로 JDBC는 자바 언어로 데이터베이스 프로그래밍을 하기 위한 라이브러리이다.
  • JDBC는 DBMS에 종속되지 않는 관련 API를 제공한다. JDBC API는 JDK에서 제공하며 JDBC 프로그래밍을 위해서는 JDBC드라이버가 필요하다! JDBC 드라이버는 각 DBMS 회사에서 제공하는 라이브러리 압축파일이다.
    우리는 MySQL 데이터베이스관리시스템을 사용하지만 오라클을 사용한다면 오라클용 JDBC 드라이버가 필요하다.

*라이브러리 위치	
	● C:\Program Files (x86)\MySQL\Connector J 8.0 
    

   
    

세팅

먼저 자바와 mysql은 서로 다른 프로그램이다.
Java에서 sql에 접근하고 싶으면 java build path를 만들어야 한다.
외부의 jar파일을 이용하여 java의 api에 연결하는 것이다.
보통의 jar파일은 다음과 같은 경로에 있다.

C:\Program Files (x86)\MySQL\Connector J 8.0
mysql-connector-java-8.0.27.jar파일을 import
해당 파일을 참조하고 싶으면 프로젝트 > 우측마우스 클릭 > properties > java build path > add external JARs > jar 파일선택, apply and close

위와같은 절차를 마치면 우리의 프로젝트 안에 referenced libraries가 생성되고 그안에 우리가 넣은 jar파일이 존재한다.

세팅코드 작성

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

public class JDBC_Test {

	public static void main(String[] args) {
		//------------------------- 접속하기 위해 작성 -----------------------
		String sql = "insert into test (strdata, timedata) values('세번째 문자열', now())";
		Connection conn;
		
		// 어떤 클래스를 사용하겠다고 불러오는 것
		try {
       	// java 쪽으로 설계도 클래스를 사용하고자 불러오는 것
			Class.forName("com.mysql.cj.jdbc.Driver"); 
			System.out.println("드라이버 로딩 성공!");
			
			// 다리를 짓고자 하는 목적지
			// jdbc:mysql:// 이 뒤에는 데이터베이스가 존재하는 컴퓨터의 아이피주소를 입력
			// 같은 컴퓨터에 데이터베이스가 존재한다면 locahost:3306를 작성 외부면 외부의 아이피 주소입력
			// MySQL에 있는 web0315에 지어야 한다.
			String url = "jdbc:mysql://localhost:3306/web0315";
			// 로그인 하듯이 아디 비번 작성
			String user = "root";
			String password = "1234";
			// -------------------------------------------------------------
			
			// ----------------------- 다리를 짓기 시작 -------------------------
			// 만들어진 다리를 돌려주는 코드
			conn = DriverManager.getConnection(url, user, password);
			
			// 만들어논 다리를 이용(만들어 놓은 다리 위를 왔다갔다 할 택배차 만들기)
			// prepareStatement 메소드에 우리가 시행하고자 할 sql문을 넘겨주면서 뽑아준다.
			PreparedStatement ps = conn.prepareStatement(sql);
			
			// 택배차에게 명령문 전달 시키고 DBMS에 명령 수행하도록 시키기
			// executeUpdate는 수행된 행의 갯수를 돌려줌(바뀐 행의 갯수)
			int result = ps.executeUpdate();
			// result가 1 → 정상수행
			// result가 1이 아니면 문제가 있다.
			System.out.println(result);
			
		} catch (ClassNotFoundException e) {
			System.out.println("드라이버 로딩 실패!");
		} catch(SQLException sqle) {
			System.out.println("DB 연결 실패!" + sqle);
		}
		
	}

}
▲ 위와 같이 작성하면 메소드마다 작업을 해줘야 하는 단점이 있다.
  그렇기 때문에 아래와 같이 작업을 해준다.

DBconnection을 할때마다 연결하는 코드를 작성하고 연결시킨다음 쿼리문을 통해 데이터를 crud하는 작업은 매번하기에는 번거롭고 우리의 프로젝트가 만약 데이터베이스가 필요한 프로젝트라면 한번만 연결하는 프로젝트일 가능성은 매우 낮다.
(여기까지가 위에서 작업한 것을 이야기 한것임)
따라서 객체로써 클래스로 만들어 활용하면 필요할 때마다 객체화하여 가져오는 것이 더욱 효율적일 것이다. 이전에 만든적이 없으면 한번 만들어 넣어놓고 그 다음에 사용할 때는 return conn을 했으니 conn에 담겨져있는것을 return해서 가지고 온다.
(이것이 만들어 놓고 사용하는 것이 '싱글톤 패턴'이다.

그렇기 때문에, 클래스로 만들어서 다른 클래스나 패키지에서 참조될 수 있도록 하자
필요와 사용성에 따라 클래스의 타입을 결정한다.
여기서는 먼저 public으로 class를 생성할 것이다.
(DBconnection 클래스 생성 - 처음에만 만들어 주고 그 다음엔 만들어진 것 사용)
위의 과정을 DBconnection에서 해준다.

public class DBConnection{
	
}

다음 자바의 sql내장 타입인 Connection을 이용하여 변수를 만든다.
다음 객체를 다른 클래스에서 이용하지 못하게 private으로 만들고 메소드를 별도로 만들어 메소드를 이용하여 접근할 수 있도록 할 것이다.

다른 패키지와 다른 클래스에서 이용할 수 있도록 public과 전역에서 사용하는 static으로 만들자

public class DBConnetion{
	//싱글톤 패턴 : 단 하나의 객체만 만들어서 사용하도록 설계된 패턴
	private static Connection conn;
    public static Connection getConnection(){
    	
    }
}

다음은 Class.forName()을 호출하여 Driver가 자기자신을 초기화 하여 DriverManager에 등록하게 한다. 즉, 개발자가 관리하지 않는 static객체들이 알아서 DriverManager에 등록되게 하는 것이다.
어떤 인자로도 전달하지 않고 그냥 호출만 하지만 여기서는 해당되는 jdbc의 Driver를 문자열로 작성해준다.
또한 작성된 드라이브는 객체를 참조하지 못했을 때 오류를 리턴해야 함으로 try-catch문으로 만들어야 한다. 자동완성 시키면 편하다.
cntl + shift + z

public class DBConnection{
//싱글톤 패턴 : 단 하나의 객체만 만들어서 사용하도록 설계된 패턴
	private static Connection conn;
    public static Connection getConnection(){
    	try{
        	//Java쪽으로 설계도 클래스를 사용하고자 불러오는것
        	Class.forName("com.mysql.cj.jdbc.Driver");
            System.out.println("드라이버 로딩성공!");
        }catch(ClassNotFoundException e){
        	System.out.println("실패");
        }
    }
}

이제 드라이브가 로딩성공하였다면 이제는 우리의 MySQL과 SQL의 아이디 비밀번호를 통해 DBMS에 접근하는 코드를 작성한다.
driverManager의 내장 함수인 getConnection을 이용하여 DBMS의 경로, 아이디, 비밀번호를 넘기면 데이터베이스에 접근 할 수 있다.
또한 연결이 실패 될 수 있음으로 try-catch문으로 만드는데 catch문은 연결이되니 연결시켜서 SQLException 에러를 리턴한다.
그리고 getConnection함수를 이전에 만든 Connection 객체에 넣을 것인다.
이 객체가 null인지의 여부를 판단하여 연결문을 실행 할 지 아니면 그냥 데이터베이스 연결을 담은 conn을 리턴할지 결정한다.

import java.sql.Connection;

public class DBConnection {
// DB관련 jar 파일을 build하여 import 시킨 다음
//	DB와 연결해야 한다. 
//	connection 관련 객체를 담는 static 변수를 private로 담는다. 
	private static Connection conn;
//	해당 변수를 실행 시 dbconnection이 되도록 한다.
	public static Connection getConnection() {
		if(conn == null) {
//			conn 객체에 접근하지 않는 경우 아래의 코드를 실행한다.
			try {
//				데이터베이스에 접근하는 드라이브를 실행
				Class.forName("com.mysql.cj.jdbc.Driver");
				System.out.println("드라이버 로딩성공!");
				
                //다리를 짓고자 하는 목적지
				String url = "jdbc:mysql://localhost:3306/web0315";
				String user = "아이디";
				String password = "비밀번호";
// 드라이브메니저의 내장메소드를 이용해 우리가 사용할 dbms정보를 넘겨주고 있다.
				conn = DriverManager.getConnection(url, user, password);
			} catch (ClassNotFoundException e) {
				System.out.println("드라이버 로딩 실패 : "+cnfe);
			}catch(SQLException sqle) {
				System.out.println("DB 연결 실패 : "+sqle);
			}
		}
		return conn;
	}
}

여기까지 왔다면 이제 데이터베이스를 사용할 준비가 끝난것이다.
사용하는 방법은 다음과 같다.
sql문은 담는 변수, Connection객체 초기화, PreparedStatement 초기화
준비.

사용할 sql문 작성, conn에 우리가 만든 DB클래스의 메소드 담기,
PreparedStatement에 Connection 객체명인 conn에 prepareStatement메소드 사용하여 sql문 담기
여기까지 하면 준비가 완료된 것이다.
이제 어떻게 사용하는가에 따라 달라진다.
우리는 select를 사용하기에 executeQuery메소드를 이용하고 만약 그외의 sql문을 사용한다면 executeUpdate메소드를 사용하는 것이다.
이 메소드를 사용했다면 resultSet에 변수로 담아주어 가져온 정보를 출력해주면 끝이다.

● ⓐ에서 만들었던 클래스를 DBConnection을 거쳐서 받아오기 형식으로
  수정
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class JDBCTest {
	public static void main(String[] args) {
		String sql = "insert into test (strdata, timedata) values('세번째 문자열',now())";
		Connection conn;
		PreparedStatement ps;
		try {
			// DBConnection에 있는 것을 받아온것
			// 그렇기 때문에 여기서 작성할 필요가 없다.
//			conn = DBConnection.getConnection();
//			
//			//만들어놓은 다리위를 왔다갔다 할 택배차 만들기
//			ps = conn.prepareStatement(sql);
//			
//			//택배차에게 명령문 전달 시키고 DBMS에 명령 수행하도록 시키기
//			int result = ps.executeUpdate();
//			
//			System.out.println(result);
			
			// where 조건문이 없기 때문에 여러개다.
			sql = "select * from test";
			// 지금같은 경우는 같은 메소드기 때문에 다시 사용할 필요는 없지만
			// 다른 메소드라면 새롭게 사용한다. 하지만 같은 객체를 사용한다.
			conn = DBConnection.getConnection();
			
			ps = conn.prepareStatement(sql);
			// executeUpdate()	: 시스템에 변화가 생기는 쿼리문 수행(insert, update, delete)-변화된 행의 개수 리턴
			// executeQuery()	: 변화 없는 쿼리문 수행(select)-검색된 결과 리턴(ResultSet) 
			//가지고오는 결과가 ResultSet rs인데 테이블 자체를 받아온다.
            // 테이블은 데이터가 아니기 때문에 rs.next를 사용해서 데이터를
            // 가져오는 형식
			ResultSet rs = ps.executeQuery();
			
			// rs행을 전부다 다 돌았을 때 끝나야 한다.
			// rs.next() : 검색된 결과에서 행을 하나 이동시키며 이동된곳에 행이 존재한다면 true, 아니라면 false
			// 이동하다가 다음행이 없다면 false라서 탈출
			// rs.next() 가 없으면 데이터를 가지고 올 수 없다.
			// 처음 가리키고 있는 것은 데이터가 아니라 맨위를 가리키고 있기 때문이다.
			while(rs.next()) {
				// 컬럼명을 작성해주면 가리키고 있는 행의 해당 컬럼값을 가지고 온다.
                // 가지고 있는 행의 intdata열, 2번째 열, timedata열을 가져옴
				int intdata = rs.getInt("intdata");
				// 컬럼인덱스로도 데이터를 가지고 올 수 있다.
				String strdata = rs.getString(2);
				String timedata = rs.getString("timedata");
				
				System.out.println("행의 데이터 : "+intdata+" / "+strdata+" / "+timedata);
			}
			
		} catch(SQLException sqle) {
			System.out.println("sql문 수행 실패!"+sqle);
		} 
	}
	
	
}

추가적으로 쿼리문에 변수를 이용한다면 ?를 작성한다.
?에 문자열 연결로 변수명을 작성하던가 혹은 곧바로 sql문안에 ""가 들어간 변수가 필요하다면 setString을 이용한다. excute를 사용하는 것처럼 prepareStatement메소드가 있는 ps객체명에 붙여서 사용한다.

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Scanner;

public class JDBCTest2 {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		System.out.print("추가할 문자열 : ");
		String strdata = sc.nextLine();//오늘 수업 끝
		
		//insert into test (strdata,timedata) values(오늘 수업 끝,now());
		String sql = "insert into test (strdata,timedata) values(?,now())";
		
		Connection conn = DBConnection.getConnection();
		try {
			PreparedStatement ps = conn.prepareStatement(sql);
			
			//첫번재 물음표에 strdata에 있는 값을 문자열로 세팅(양쪽에 ' '를 붙여준다)
			ps.setString(1, strdata);
			
			if(ps.executeUpdate() == 1) {
				System.out.println("쿼리 수행 성공!");
			}
		} catch (SQLException sqle) {
			System.out.println("쿼리 수행 실패 : "+sqle);
		}
	}
}      

JDBC를 이용한 데이터베이스 연동과정

① JDBC 드라이버 로드

		
Class.forName("com.mysql.Jdbc.Driver"); 
→ Class.forName() 메서드를 호출하여, mysql에서 제공하는 Driver 클래스를 
  JVM method area에 로딩시킨다. 

② 데이터베이스 연결

String jdbc_url = "jdbc:mysql://localhost:3306/datebase?serverTimezone=UTC";
Connection con = DriverManager.getConnection(URL, "user", "password");

→ 두 번째 줄의 의미는 localhost:3306 (로컬 환경에서 MySQL의 포트번호가 3306이기 때문이다) 그리고 database는 스키마 이름을 지정하면 된다. 이제 Connection 객체를 만들어 사용하게 되는데 방법은 DriverManager 클래스의 static 메서드인 getConnection() 메서드를 호출해서, mysql에 연결하기 위한 커넥션 정보(url, user, password)를 입력한다. getConnection() 메서드 수행 결과로 Connection 객체를 반환하는데, 이 객체를 통해 쿼리를 날리는 statement를 작성할 수 있다. SELECT 쿼리에서는 createStatement(), INSERT에서는 prepareStatement()를 호출한다.

※ 스키마란 - 데이터베이스의 구조와 제약조건에 관해 전반적인 명세를 기술한 것

③ SQL을 위한 객체생성

	Statement stmt = con.createStatement();

④ SQL 문장 실행

	String sql = "select * from student";
	ResultSet result = stmt.executeQuery(sql); 

→ select 문장은 테이블 형태의 결과를 반환한다. 그러므로 select 문장을 실행하기 위해 Statement의 메소드 executeQuery()를 사용한다.
메소드 executeQuery()는 질의 결과로 테이블 형태의 결과를 반환하는데, 이 반환형이 인터페이스 ResultSet이다. 객체 Statement의 메소드 executeUpdate()는 create 또는 drop, insert, delete, update와 같이 테이블의 내용을 변경하는 문장에 사용한다.

⑤ 질의결과 처리

	while(result .next()){       
    String name = result .getString(1);
    String owner = result .getString(2);
    String date = result .getString(3);
}

→ ResultSet 인터페이스에는 질의 결과의 현재 행(row)을 가리키는 커서(cursor)라는 개념이 있으며, 이 커서를 다음 행으로 이동시키는 메소드가 next()이다.

▲ 예를들어 위와 같이 결과가 나온 테이블의 구조가 있다고 가정하면 처음에는 첫번 째 행을 가리킬거고 그 행이 존재한다면 true, 존재하지 않는다면 false를 리턴한다.

  • ResultSet의 메소드 getString()과 getInt()

    • ResultSet의 커서가 있는 행에서 컬럼 자료를 참조하기 위해 ResultSet이
      제공하는 메소드 getString()을 이용한다.
    • getString()의 인자는 컬럼 이름을 문자열로 쓰거나 또는 컬럼 번호를 이용할 수 있다. 컬럼 값의 자료유형에 따라 메소드 getString()뿐만 아니라 getInt(), getDouble(), getDate() 등 다양한 컬럼 반환 메소드를 제공한다.

    SELECT 쿼리 예제

	import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
 
public class Example1 {
 
   public static void main(String[] args) {
      // TODO Auto-generated method stub
      
      String jdbc_driver = "com.mysql.cj.jdbc.Driver";
      String jdbc_url = "jdbc:mysql://localhost:3306/board?serverTimezone=UTC";
      try {
          
         Class.forName(jdbc_driver).newInstance();
         Connection con = DriverManager.getConnection(jdbc_url, "root", "root");
         Statement st = con.createStatement();
         
         String sql = "SELECT * FROM member";
         ResultSet rs = st.executeQuery(sql);
 
         while(rs.next()){       
             String name = rs.getString(1);
             String owner = rs.getString(2);
             String date = rs.getString(3);
 
             System.out.println(name + " " + owner + " " + date);
         }
 
      
         rs.close();
         st.close();
         con.close();    
         
      } catch (Exception e) {
         e.printStackTrace();
      } 
   }
}
 ● INSERT 쿼리 예제
	import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
 
public class InsertTest {
    public static void main(String[] args) {
        // pet 테이블에는 이름/소유자/종/성별/출생일 칼럼이 있습니다.
        insert("봄이", "victolee", "페르시안", "m", "2010-08-21", null);
    }
 
    public static void insert(String name, String owner, String species,
                              String gender, String birth, String death){
        Connection conn = null;
        PreparedStatement pstmt = null;
 
        try{
            // 1. 드라이버 로딩
            Class.forName("com.mysql.jdbc.Driver");
 
            // 2. 연결하기
            String url = "jdbc:mysql://localhost/dev";
            conn = DriverManager.getConnection(url, "dev", "dev");
 
 
            // 3. SQL 쿼리 준비
            // 추가하려는 데이터의 값은 전달된 인자를 통해 동적으로 할당되는 값이다.
            // 즉 어떤 값이 전달될지 모르므로 Select 할 때와 달리
            // stmt = conn.createStatement(); 를 작성하지 않고
            // pstmt = conn.prepareStatement(sql); 로 작성하여 데이터를 추가할 것임을 알립니다.
            // 물론 sql 쿼리 내에서 + 연산자로 한 줄로 작성할 수 있지만 가독성이 너무 떨어지게 되므로
            // 이 방법을 권합니다.
            String sql = "INSERT INTO pet VALUES (?,?,?,?,?,?)";
            pstmt = conn.prepareStatement(sql);
 
 
            // 4. 데이터 binding
            pstmt.setString(1, name);
            pstmt.setString(2, owner);
            pstmt.setString(3, species);
            pstmt.setString(4, gender);
            pstmt.setString(5, birth);
            pstmt.setString(6, death);
 
 
            // 5. 쿼리 실행 및 결과 처리
            // SELECT와 달리 INSERT는 반환되는 데이터들이 없으므로
            // ResultSet 객체가 필요 없고, 바로 pstmt.executeUpdate()메서드를 호출하면 됩니다.
            // INSERT, UPDATE, DELETE 쿼리는 이와 같이 메서드를 호출하며
            // SELECT에서는 stmt.executeQuery(sql); 메서드를 사용했었습니다.
            // @return     int - 몇 개의 row가 영향을 미쳤는지를 반환
            int count = pstmt.executeUpdate();
            if( count == 0 ){
                System.out.println("데이터 입력 실패");
            }
            else{
                System.out.println("데이터 입력 성공");
            }
        }
 
        catch( ClassNotFoundException e){
            System.out.println("드라이버 로딩 실패");
        }
 
        catch( SQLException e){
            System.out.println("에러 " + e);
        }
 
        finally{
            try{
                if( conn != null && !conn.isClosed()){
                    conn.close();
                }
                if( pstmt != null && !pstmt.isClosed()){
                    pstmt.close();
                }
            }
            catch( SQLException e){
                e.printStackTrace();
            }
        }
    }
}
  • Insert 쿼리를 수행할 때도 먼저 Connection 객체를 얻어야 한다.
  • INSERT는 일반적으로 동적으로 값이 할당되므로createStatement()를 호출하지 않고, 쿼리를 준비하는 statement라는 의미로 prepareStatement() 메서드를 호출한다. 그러면 PreparedStatement 를 반환하는데, pstmt.setString() 메서드를 통해 동적으로 값을 할당할 수 있다. 값이 할당되면 pstmt.executeUpdate() 메서드를 실행하여 INSERT 쿼리를 실행 할 수 있다. 반환 값은 영향을 미친 row의 개수이다.

마무리

  • 쿼리를 수행할 때 동적으로 할당해야 하는 값이 있으면 PreparedStatement 객체를 사용하고, 동적으로 할당할 필요가 없으면 Statement 객체를 사용한다.
  • 쿼리의 결과가 있으면 executeQuery() 메서드를 호출하여 ResultSet 객체에 담고, 쿼리의 결과가 없으면 executeUpdate() 메서드를 호출하여 int형 변수에 결과 값을 할당한다.

MVC에 JDBC 적용

테이블 생성

● 먼저, MySQL에서 테이블을 만들어줘야 한다.
	create table user(
	useridx int primary key auto_increment,
	userid varchar(300) unique,
    # userpw는 중복이 가능하므로 unique를 넣으면 안된다.
    userpw varchar(300) not null,
    username varchar(300) not null,
    userage int,
    userphone varchar(300),
    useraddr varchar(1000)
);

create table product (
	prodnum int primary key auto_increment,
	prodname varchar(1000),
	prodprice int,
	prodamount int,
	prodinfo varchar(4000),
    # default 0 : 아무 값을 넣지 않으면 0이됨
	likecnt int default 0
);

Java 작성

build path 해주기

1. view - Index 클래스 생성

● 가장 먼저 나오는 페이지
	package view;

import java.util.Scanner;

//index : 시작하는 페이지
public class Index {
	public static void main(String[] args) {
		System.out.println("22.03 개강반 최종 프로젝트 / UMS 프로그램 입니다.");
		
		Scanner sc = new Scanner(System.in);
		while(true) {
			System.out.println("1. 회원가입\n2. 로그인\n3. 나가기");
			int choice = sc.nextInt();
			
			//Controller
			if(choice == 3) {
				System.out.println("안녕히가세요");
				break;
			}
			switch(choice) {
			case 1:
				// 회원가입
                // JoinView에서 생성자를 생성하고 코드를 작성한것을 메소드처럼 불러옴
                // 많은 데이터들의 입출력이 일어나기 때문에 코드가 길어진다.
				// 따라서 새로운 View를 띄워준다.(흐름을 이동시킨다)
				new JoinView();
				break;
			case 2:
				//로그인
				new LoginView();
				break;
			default:
				System.out.println("다시 입력하세요");
			}
		}
	}
}

2. view - JoinView 클래스 생성

● Index에 있는 회원가입을 나타내는 코드
● new JoinView();를 불러오기 위한 생성자 생성
   
	view / JoinView
package view;

import java.util.Scanner;

import dao.UserDAO;
import dto.UserDTO;
// 회원가입을 위해 생성
public class JoinView {
	// 생성자도 메소드랑 같은 역할을 한다.
	// 호출하면 내부로 와서 내부에 있는 내용 실행이 된다.
	// JoinView 생성자에 코드를 작성하고 Index에서 메소드처럼 불러오기
	public JoinView() {
		Scanner sc = new Scanner(System.in);
        //새로 가입할 "회원의 정보들" 을 데이터베이스에 저장해야 하므로
		//기능을 구현해 놓은 DAO가 필요하고, 회원 관련된 데이터이므로
		//UserDAO를 사용해야 한다.
		UserDAO udao = new UserDAO();
		
		System.out.print("아이디 : ");
		String userid = sc.next();
        // userid를 생성할때 중복검사 할 도구가 필요하다. 
        // 데이터와 통신할 클래스를 별도로 만드는데 DAO로 관리하면 된다.
        // DAO에는 데이터베이스와 직접 통신할 DBConnection Class와 직접 소통하는 클래스로 나눌 것이다. 
        // UserDAO에서 만든 메소드를 이용해 사용자 중복 검사를 실시하자
		if(udao.checkDup(userid)) {
			System.out.print("비밀번호 : ");
			String userpw = sc.next();
			System.out.print("이름 : ");
			String username = sc.next();
			System.out.print("나이 : ");
			int userage = sc.nextInt();
			System.out.print("핸드폰 번호 : ");
			String userphone = sc.next();
			System.out.print("주소 : ");
            // 새롭게 스캐너 객체를 만든이유는 nextLine()을 제대로 사용하기 위해서
			sc = new Scanner(System.in);
			String useraddr = sc.nextLine();
			
            //여기까지 왔다면 회원 가입에 필요한 모든 정보를 입력받았다는
			//뜻이므로, UserTable에 이 모든 데이터들을 저장해 주어야 한다.
			//저장하는 기능적인 코드들은 View에서 구현하는 것이 아니고
			//Userdao에 구현해 놓고 메소드를 호출하는 식으로 사용한다.
			//호출시 넘겨주어야 할 데이터들이 총 6개나 되므로 UserDTO 타입의
			//객체 하나로 포장해서 그 객체를 매개변수에 넘겨준다.
			UserDTO user = new UserDTO(userid, userpw, username, userage, userphone, useraddr);
            // UserDAO에서는 여기서 입력한 것들을 보내주기 위해서 포장한 DTO 객체를 매개변수로 넘겨준다.
            // user을 UserDAO에 넘겨준다.
			if(udao.join(user)) {
				System.out.println("회원가입 성공!");
				System.out.println(username+"님 가입을 환영합니다!");
			}
			else {
				System.out.println("회원가입 실패 / 다시 시도해 주세요.");
			}
		}
		else {
			System.out.println("중복된 아이디가 있습니다. 다시 시도해 주세요.");
		}
	}
}

3. DBConnection

● 모든 클래스에 만들 필요 없이 여기에 한번에 만들고 불러오는 형식으로 사용
	package dao;

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

public class DBConnection {
	private static Connection conn;
	
	public static Connection getConnection() {
		// 싱글톤 패턴 : 단 하나의 객체만 만들어서 사용하도록 설계된 패턴
		if(conn == null) {
			try {
				Class.forName("com.mysql.cj.jdbc.Driver");
				System.out.println("드라이버 로딩 성공!");
				
				// 다리를 짓고자 하는 목적지
				String url = "jdbc:mysql://localhost:3306/web0315";
				String user = "root";
				String password = "1234";
				
				conn = DriverManager.getConnection(url, user, password);
				
				
				
			} catch (ClassNotFoundException cnfe) {
				System.out.println("드라이버 로딩 실패 : " + cnfe);
			} catch(SQLException sqle) {
				System.out.println("DB 연결 실패 : " + sqle);
			}
		}
		return conn;
	
	}
}

4. dao - UserDAO 클래스 생성

● JoinView를 구성하기 위한 UserDAO

▼ view - JoinView 클래스에서 아래서 나와 있는 if(udao.checkDup(userid)) { 중복검사를 돌려주는 코드

	if(udao.checkDup(userid)) {
			System.out.print("비밀번호 : ");
			String userpw = sc.next();
			System.out.print("이름 : ");
			String username = sc.next();
			System.out.print("나이 : ");
			int userage = sc.nextInt();
			System.out.print("핸드폰 번호 : ");
			String userphone = sc.next();
			System.out.print("주소 : ");
			sc = new Scanner(System.in);
			String useraddr = sc.nextLine();
● UserDAO 내부 → 중복검사(checkDup)
	// 중복 검사는 'read' 그래서 SQL문에서는 'select'를 사용한다.
	// 우리가 받아올 userid를 맨 끝에 세팅을 할 거니까 '?'로 써준다.
	// 있으면 중복, 아니면 중복 아님
	public boolean checkDup(String userid) {
		String sql = "select * from user where userid=?";
		try {
			// 만들어논 다리를 이용(만들어 놓은 다리 위를 왔다갔다 할 택배차 만들기)
			// prepareStatement 메소드에 우리가 시행하고자 할 sql문을 넘겨주면서 뽑아준다.
			ps = conn.prepareStatement(sql);
           ps.setString(1, userid);
           
			// executeUpdate()	: 
           // 시스템에 변화가 생기는 쿼리문 수행(insert, update, delete)-변화된 행의 개수 리턴
			// executeQuery()	: 변화 없는 쿼리문 수행(select)-검색된 결과 리턴(ResultSet)
			// 여기서는 select를 사용하니까 executeQuery()를 사용
			// 가지고오는 결과가 ResultSet rs인데 테이블 자체를 받아온다.
           // 테이블은 데이터가 아니기 때문에 rs.next를 사용해서 데이터를
           // 가져오는 형식
			rs = ps.executeQuery();
			
			// 그냥 return하면 중복되는게 있으면 true니까 !rs.next();을 해야 한다.
			// 그래야 중복된게 없어야 true가 나오니까
			return !rs.next();
		} catch (SQLException sqle) {
			System.out.println("쿼리 수행 실패! : " + sqle);
		}
		return false;
	}
● UserDAO 내부 → 회원가입(join)
	public boolean join(UserDTO user) {
		// values(?,?,?,?,?,?)에서 ?는  (userid,userpw,username,userage,userphone,useraddr)
		// 값들이다.
		String sql = "insert into user (userid,userpw,username,userage,userphone,useraddr) "
				+ "values(?,?,?,?,?,?)";
		
		try {
       // JoinView에서 매개변수로 받을 값들을 사용
			ps = conn.prepareStatement(sql);
			ps.setString(1, user.userid);
			ps.setString(2, user.userpw);
			ps.setString(3, user.username);
			ps.setInt(4, user.userage);
			ps.setString(5, user.userphone);
			ps.setString(6, user.useraddr);
			
			
			// 여기서는 insert니까 executeUpdate()
			// executeUpdate()는 ResultSet rs 사용 안해도 된다.
			// 1개가 있으면 true
			return ps.executeUpdate() == 1;
			
		} catch (SQLException sqle) {
			System.out.println("쿼리 수행 실패! : " + sqle);
		}
		return false;
	}

5. dao - Session 클래스 생성

	package dao;

	import java.util.HashMap;

public class Session {
	//Session.datas.put("login_id","apple");
	private static HashMap<String, String> datas = new HashMap<String, String>();

	
	//Session.put("login_id","apple");
	public static void put(String key, String value) {
		datas.put(key, value);
	}
	public static String get(String key) {
		return datas.get(key);
	}
}

6. view - LoginView 클래스 생성

●  Index 클래스에 있는 new LoginView(); 작성
	package view;

import java.util.Scanner;

import dao.UserDAO;

public class LoginView {
	public LoginView() {
		Scanner sc = new Scanner(System.in);
		UserDAO udao = new UserDAO();
		
		System.out.print("아이디 : ");
		String userid = sc.next();
		System.out.print("비밀번호 : ");
		String userpw = sc.next();
		
		if(udao.login(userid,userpw)) {
			System.out.println(userid+"님 어서오세요~");
			
			//메인창 띄우기
			new MainView();
		}
		else {
			System.out.println("로그인 실패 / 다시 시도해 주세요.");
		}
	}
}

7. UserDAO 내부에 있는 login 메소드 작성

● LoginView에 있는 if(udao.login(userid,userpw)) {
  완성하기 위해서
		//							apple		 abcd1234
	public boolean login(String userid, String userpw) {
		String sql = "select * from user where userid =? and userpw=?";
		
		try {
			ps =conn.prepareStatement(sql);
			ps.setString(1, userid);
			ps.setString(2, userpw);
			
			rs = ps.executeQuery();

			if(rs.next()) {
				Session.put("login_id", userid);
				// 검색은 pk(primary key)가 가장 빠름
				// 그래서 pk인 useridx를 Session에 세팅하고 싶으면 아래와 같은 방법사용
				// 정수니까 +""해서 문자열로 바꾸기
				Session.put("login_idx", rs.getInt("useridx") + "");
                return true;
			}
		} catch (SQLException sqle) {
			System.out.println("쿼리 수행 실패! : " + sqle);
		}
		return false;
	}

8. view - MainView 클래스 생성

	package view;

import java.util.Scanner;

import dao.ProductDAO;
import dao.ProductDAO;
import dao.Session;

public class MainView {
	public MainView() {
		Scanner sc = new Scanner(System.in);
		ProductDAO pdao = new ProductDAO();
		
		while(true) {
			//우리가 만든 프로젝트는 무조건 main()부터 시작하는 프로그램이다.
			//즉 진입점이 한개이기 때문에 아래와 같은 코드를 생략 가능하다.
			//*웹 상이나 다른 프로그램에서는 진입점이 여러개일 수 있으므로
			//아래처럼 로그인 되어있는지를 먼저 검사해주는것이 필요하다.
			if(Session.get("login_id") == null) {
				System.out.println("로그인 후 이용하세요");
				break;
			}
			System.out.println("☆★☆★☆★☆★"+Session.get("login_id")+"님 어서오세요~☆★☆★☆★☆★\n"
					+ "1. 상품추가\n2. 상품수정\n3. 상품삭제\n"
					+ "4. 내 상품 보기\n5. 상품 검색\n6. 내 정보 수정\n7. 로그아웃");
			int choice = sc.nextInt();
			
			if(choice == 7) {
				//로그아웃
				System.out.println(Session.get("login_id")+"님 안녕히가세요~");
				//로그인한 정보를 담아주는 Session에서 login_id라는 KEY 와 쌍을 이루고 있는 값은
				//로그아웃을 했다면 없애주어야 한다. 따라서 null로 초기화 해주어야 한다.
				Session.put("login_id", null);
				break;
			}
			switch(choice) {
			case 1:
				//상품추가
				new AddProductView();
				break;
			case 2:
				//상품수정
				new ModifyProductView();
				break;
			case 3:
				//상품삭제
				//내 상품 목록 띄워주고 번호 입력받아서 삭제시키기
				System.out.println(pdao.getList());
				System.out.print("삭제할 상품 번호 : ");
				int prodnum = sc.nextInt();
				if(pdao.removeProduct(prodnum)) {
					System.out.println(prodnum+"번 상품 삭제 성공!");
				}
				else {
					System.out.println("알 수 없는 오류 / 다음에 다시 시도해 주세요.");
				}
				break;
			case 4:
				//내 상품 보기
				System.out.println("===========내가 올린 상품 목록===========");
				System.out.println(pdao.getList());
				System.out.println("===================================");
				break;
			
			case 5:
				//내 정보 수정
				new MyInfoView();
				break;
			}
			
		}
	}
}

9. AddProductView 클래스 생성

● MainView에 있는 new AddProductView();을 만들기 위해 생성
● 상품 추가
	package view;

import java.util.Scanner;

import dao.ProductDAO;
import dao.Session;
import dto.ProductDTO;

public class AddProductView {
	public AddProductView() {
		//상품에 관련된 정보에 접근할 객체인 pdao 생성(상품 관련된 기능들이 선언되어 있다.)
		ProductDAO pdao = new ProductDAO();
		Scanner sc = new Scanner(System.in);
		
		System.out.print("상품 이름 : ");
		String prodname = sc.nextLine();
		System.out.print("상품 가격 : ");
		int prodprice = sc.nextInt();
		System.out.print("상품 재고 : ");
		int prodamount = sc.nextInt();
		System.out.print("상품 소개 : ");
		sc = new Scanner(System.in);
		String prodinfo = sc.nextLine();
		
		//지금 추가중인 상품의 상품번호는 현재 "추가되어 있는 마지막 상품의 번호"+1로 설정해 주어야 한다.
		//pdao에 getLastNum()을 만들어서 리턴받고 +1 해준 것을 상품 번호로 사용한다.
		ProductDTO product = new ProductDTO(pdao.getLastNum()+1, prodname, prodprice,
				prodamount, prodinfo, Session.get("login_id"));//올린 사람은 현재
		//								로그인 되어 있는 유저이므로 세션에서 아이디를 받아온 후 사용
		
		//포장이 끝났다면 pdao의 메소드에 객체 넘겨주기
		if(pdao.addProduct(product)) {
			System.out.println(prodname+" 상품 추가 완료!");
		}
		else {
			System.out.println("알 수 없는 오류 / 다음에 다시 시도해 주세요.");
		}
	}
}

10. view - ModifyProductView 클래스 생성

● MainView 클래스에 있는 new ModifyProductView(); 만들기 위해서 생성
● 상품 수정
	package view;

import java.util.Scanner;

import dao.ProductDAO;

public class ModifyProductView {
	public ModifyProductView() {
		ProductDAO pdao = new ProductDAO();
		Scanner sc = new Scanner(System.in);
		//내가 올린 상품 목록 출력
		System.out.println(pdao.getList());
		System.out.print("수정할 상품 번호 : ");
		int prodnum = sc.nextInt();
		System.out.println("1. 가격수정\n2. 재고수정\n3. 설명수정");
		int choice = sc.nextInt();
		System.out.print("새로운 정보 : ");
		sc = new Scanner(System.in);
		String newData = sc.nextLine();
		
		//세가지 입력받은 데이터를 DAO에 넘겨주기(행, 열, 새로운 데이터)
		if(pdao.modifyProduct(prodnum,choice,newData)) {
			System.out.println(prodnum+"번 상품이 정상적으로 수정되었습니다.");
		}
		else {
			System.out.println("알 수 없는 오류 / 다음에 다시 시도해 주세요.");
		}
	}
}

11. dao - ProductDAO 클래스 생성

● ModifyProductView 클래스에 있는 modifyProduct 메소드를 완성하기 위해 생성
	public boolean modifyProduct(int prodnum, int choice, String newData) {
		String[] cols = {"prodprice", "modstock", "moddetail"};
		String sql = "update product set prodnum=?, " + cols[choice -1] + "=? where userid =?";
		
		
		try {
			ps = conn.prepareStatement(sql);
			ps.setInt(1, prodnum);
			ps.setString(2, newData);
			ps.setString(3, Session.get("login_id"));
			
			return ps.executeUpdate() == 1;
			
		} catch (SQLException sqle) {
			System.out.println("쿼리 수행 실패! : " + sqle);
		}
		return false;
	}

12. view - MyInfoView 클래스 생성

● MainView에 있는 new MyInfoView()를 완성하기 위해 만듬
	package view;

import java.util.Scanner;

import dao.ProductDAO;
import dao.UserDAO;

public class MyInfoView {
	public MyInfoView() {
		Scanner sc = new Scanner(System.in);
		UserDAO udao = new UserDAO();
		ProductDAO pdao = new ProductDAO();
		
		System.out.println(udao.myInfo());
		System.out.println("1. 비밀번호 수정\n2. 핸드폰 번호 수정\n3. 주소 수정\n4. 수정 취소\n5. 회원탈퇴");
		int choice = sc.nextInt();
		if(choice == 4) {
			System.out.println("메인으로 돌아갑니다.");
		} else if(choice == 5) {
			System.out.print("비밀번호 재입력 : ");
			String userpw = sc.next();
			//비밀번호 검사를 위해 udao의 checkPw() 호출
			if(udao.checkPw(userpw)) {
				//회원탈퇴 진행
				//탈퇴 진행 시 현재 탈퇴를 하려는 회원이 올린 모든 상품들도 삭제 혹은 사용불가 처리를 해주어야 한다.
				//후에 삽입될 데이터와 충돌이 발생할 수 있기 때문이다. 상품을 관리할 pdao를 통해 removeAll()을
				//먼저 진행해준다. *실제 DB 환경에서도 상품 삭제가 먼저 되어야지만 회원을 탈퇴 시킬 수 있다.
				pdao.removeAll();
				//상품까지 깨끗하게 처리되었으므로 실제 회원 데이터 삭제 진행
				if(udao.leaveId()) {
					//아련하게 인사
					System.out.println("그동안 이용해 주셔서 감사합니다...☆ 기다릴게요....");
				}
			}
			//비밀번호 검사 실패
			else {
				System.out.println("비밀번호 오류");
			}
		}
		//무언가 회원 정보를 수정하려고 선택했을 때
		else {
			System.out.print("새로운 정보 : ");
			sc = new Scanner(System.in);
			String newData = sc.nextLine();
			
			
			boolean check = false;
			// choice는 "1. 비밀번호 수정 2. 핸드폰 번호 수정 3. 주소 수정
			check = udao.modifyUser(choice, newData);
			
			if(check) {
				System.out.println("정보 수정 완료!");
			}
			else {
				System.out.println("정보 수정 실패");
			}
		}
	}
}

13. dao - UserDAO

● MyInfoView 클래스의 System.out.println(udao.myInfo());를
  사용하기 위해 만들어 준다.
● MyInfoView 클래스의 udao.modifyUser(choice, newData);를
  구현하기 위해 만들어 준다.
	public String myInfo() {
		String sql = "select * form user where userid =?";
		
		try {
			ps = conn.prepareStatement(sql);
			ps.setString(1, Session.get("login_id"));
			
			rs = ps.executeQuery();
			
			while(rs.next()) {
				String id = rs.getString("userid");
				String pw = rs.getString("userpw");
				String name = rs.getString("username");
				int age = rs.getInt("userage");
				String phonenum = rs.getString("userphone");
				String addr = rs.getString("useraddr");
				
				return "아이디 : " + id +"\n비번 : " + pw + "\n이름 : " + name + "\n나이 : " + age 
						+ "\n핸드폰 번호 : " + phonenum + "\n주소 : " + addr;
						
			}
			
		} catch (SQLException sqle) {
			System.out.println("쿼리 수행 실패! : " + sqle);
		}
		return null;
	}
	public boolean modifyUser(int col, String newData) {
		// col : 1. 비밀번호 수정 / 2.핸드폰 번호 수정 / 3. 주소수정
		// ▼ SQL문이 일정하지가 않음(상황에따라 컬럼명이 바뀜)
		// 1번 선택 : "update user set userpw= 'abcd1234' where userid ='apple'
		// 2번 선택 : "update user set userphone= '01012341234' where userid='apple'
		String[] cols ={"userpw","userphone","useraddr"};
		// cols[col -1] 이렇게 하면 매개변수로 오는 번호에 -1을 해준다. 
		// 예를들어 비밀번호 수정하려고 1번을 넣어주면 1 - 1 = 0이 되서 cols에 있는 배열 0번자리가
		// 와서 'userpw'를 사용한다.
		String sql = "update user set " + cols[col -1] + "=? where userid=?";
		
		try {
			ps = conn.prepareStatement(sql);
			ps.setString(1, newData);
			ps.setString(2, Session.get("login_id"));
			
			// 한개가 잘 수정이 됐다면 true
			return ps.executeUpdate() == 1;
			
		} catch (SQLException sqle) {
			System.out.println("쿼리 수행 실패! : " + sqle);
		}
		
		return false;
	}
profile
발전하기 위한 공부

0개의 댓글