2020년 11월 13일

업데이트:


IO(입출력)

Stream(스트림) 클래스

입출력 장치에서 데이터를 읽고 쓰기 위해서 자바에서 제공하는 클래스이다. 모든 스트림은 단방향이며 각각의 장치마다 연결할 수 있는 스트림이 존재한다. 하나의 스트림으로 입출력을 동시에 수행할 수 없으므로 동시에 수행하려면 2개의 스트림이 필요하다.

//IORun.java
 
package com.kh.io.run;

import com.kh.io.view.IOView;

public class IORun {
	public static void main(String[] args) {
		new IOView().displayMain();
        //displayMain() 호출
	}
}


//IOView.java

package com.kh.io.view;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.InputMismatchException;
import java.util.Scanner;

import com.kh.io.model.service.ByteService;
import com.kh.io.model.service.CharService;

public class IOView {
    private Scanner sc = new Scanner(System.in);
    private ByteService bService = new ByteService();
    private CharService cService = new CharService();

    public void displayMain() {
        int sel = 0;

        do {
            try {
				System.out.println("==== 입출력 메뉴 ====");
				System.out.println("1. BYTE 기반 파일 작성(출력)");
				System.out.println("2. BYTE 기반 파일 열기(입력)");
				System.out.println("3. 문자 기반 파일 열기(출력)");
				System.out.println("4. 문자 기반 파일 열기(입력)");
				System.out.println("0. 종료");
				System.out.print("메뉴 선택>>> ");

				sel = sc.nextInt();
				sc.nextLine(); // 이후 동작에서 문자를 입력받을 수 있기 때문에 미리 개행문자 제거

				switch(sel) {
				case 1 : byteFileSave(); break;
				case 2 : byteFileOpen(); break;
				case 3 : charFileSave(); break;
				case 4 : charFileOpen(); break;
				case 0 : System.out.println("프로그램 종료");  break;
				default : System.out.println("잘못 입력함.");
				}
			}catch(InputMismatchException e){
				System.out.println("정수만 입력해주세요.");
				sel = -1; // sel이 0으로 유지돼서 종료되는걸 방지
				sc.nextLine(); //입력 버퍼에 남아있는 잘못 입력한 문자열을 제거
			}catch(Exception e) {
				e.printStackTrace();
			}
        } while (sel != 0);
    }

}

public void byteFileSave() throws FileNotFoundException {
    System.out.println("---- 바이트 기반 파일 입력 ----");
    System.out.println("새로 생성할 파일명 : ");
    String fileName = sc.nextLine();

    StringBuffer content = new StringBuffer();

    // 입력되는 내용 한 줄 임시 저장할 변수
    String input = null;

    System.out.println("----- 파일 내용 출력(exit 입력 시 종료) -----");

    while(true) {
      input = sc.nextLine();

      // 입력받은 문자열이 "exit"인 경우 반복문 종료
      if(input.equals("exit")) break;

      //StringBuffer에 입력받은 내용 + 개행문자를 추가
      conent.append(input + "\n");
    }

    int result = bService.byteFileSave(fileName, content.toString());
    if (result == 1){
      System.out.println(fileName + ".txt 파일 저장 성공");
    } else {
      System.out.println(fileName + ".txt 파일 저장 실패");
    }
}


// ByteService.java

package com.kh.io.model.service;

import java.io.File;
import java.io.FileNotFoundException;

import com.kh.io.model.dao.ByteDAO;

public class ByteService {
	private ByteDAO byteDAO = new ByteDAO();


public int byteFileSave(String fileName, String content) throws FileNotFoundException {
		File folder  = new File("byte");

    // byte 폴더가 존재하지 않을 경우 byte 폴더 생성
    if(!folder.exists()) {
      folder.mkdir();
    }

    int result = byteDAO.byteFileSave(fileName, content, "byte");

    return result; //byteDAO.byteFileSave()의 수행 결과를 그대로 View로 반환한다.
  }

public String byteFileOpen(String path) throws FileNotFoundException {
    // Service는 DAO로 전달할 데이터 또는 반환받은 데이터를 가공하는 역할(비즈니스 로직)

    String content = byteDAO.byteFileOpen(path);
    return content;
  }
}
// CharService.java
package com.kh.io.model.service;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import com.kh.io.model.dao.CharDAO;

public class CharService {
	  private CharDAO charDAO = new CharDAO();

    public int charFileSave(String fileName, String content)  throws IOException {
        File folder = new File("char");

        if(!folder.exists()) {
          folder.mkdir();
        }

        int result = charDAO.charFileSave(fileName, content, "char");

        return result;
}

public String charFileOpen(String path) throws FileNotFoundException {
    return charDAO.charFileOpen(path);
}


// ByteDAO.java

package com.kh.io.model.dao;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class ByteDAO {
	  // DAO(Data Access Object)
    // - 데이터 저장소에 접속하여 실제 데이터를 전송하거나 결과 값을 전달받는 역할을 하는 객체

    // 바이트 기반 스트림 : 1byte 단위로 데이터를 입력 또는 출력하는 스트림
    public int byteFileSave(String fileName, String content, String path) throws FileNotFoundException{
      int result = 0;

      // 프로그램 -> 파일로 출력
      // FileOutputStream 사용

      //byte 폴더 안에 입력받은 파일명.txt 파일을 출력할 스트림 객체 생성
      FileOutputStream fOut = new FileOutputStream(path + "/" + fileName + ".txt");

      // FileOutputStream 객체 생성 성공 시 
      // 매개변수로 작성된 파일이 없다면 -> 바로 생성
      // 있다면 -> 덮어쓰기 또는 이어쓰기
      // --> 매개변수에 'true' 작성 시 이어쓰기, false 또는 생략 시 덮어쓰기

      try {
        for(int i = 0; i < content.length(); i++) {
          //content에 작성된 문자의 개수만큼 반복하며
          //한 글자씩 스트림을 통해 파일로 출력한다.

          fOut.write(content.charAt(i));
        }

        result = 1; // 1 반환 시 출력 성공
      }catch(IOException e) {
        e.printStackTrace();
      } finally {
        // 스트림은 사용한 경우 반드시 반환해야 한다.
        try {
          if(fOut != null)  fOut.close();
        }catch(IOException e){
          e.printStackTrace();
        }
      }
      return result;
    }

    public String byteFileOpen(String path) throws FileNotFoundException {
		
		StringBuffer sb = null; // 읽어온 파일 내용을 저장할 변수

    FileInputStream fis = new FileInputStream(path);
    //FileInputStream 객체 생성 시
    //매개변수에 작성된 path에 있는 파일과 연결한다.
    //만약 해당 파일이 없다면 FileNotFoundException 발생

    //바이트 기반 스트림의 read() 메소드는
    //파일의 내용을 순차적으로 1바이트씩 읽어온다.
    //더 이상 읽어올 내용이 없다면 -1을 반환한다.

    try {
      sb = new StringBuffer();

      int value = 0; // read()를 통해 읽어올 값을 임시 저장할 변수
      // int형 변수를 사용하는 이유? 한 글자씩(char)로 읽어오기 때문에 int형으로도 가능

      while( (value = fis.read()) != -1) {
        //while문을 이용해서 read() 메소드가 -1이 나올 때까지 반복함

        //읽어온 값 value를 char 형태로 형변환하여 sb에 누적
        sb.append((char)value);
      }
    }catch(IOException e) {
      e.printStackTrace();
    }finally {
      try {
        if(fis != null) fis.close();
      }catch(IOException e) {
        e.printStackTrace();
      }
    }

    if(sb != null) return sb.toString();
    else return null;
    }
}
// CharDAO.java

package com.kh.io.model.dao;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class CharDAO {
	
	  // 문자 기반 스트림 : 2byte 문자 단위(유니코드)로 데이터를 입력 또는 출력하는 스트림
    // 프로그램 -> 파일로 content 출력

    public int charFileSave(String fileName, String content, String path) throws IOException {
        int result = 0;
        FileWriter fw = new FileWriter(path + "/" + fileName + ".txt");

        try {
            fw.write(content);
            // 문자 기반 스트림은 문자만 가지고 있는 데이터를 송수신하는 용도
            // 문자 기반 스트림의 write() 메소드는 모든 문자를 순서대로 내보내도록 내부적으로
            // 구현되어있다.

            result = 1;
        } catch(IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(fw != null) fw.close();
            } catch(IOException e){
                e.printStackTrace();
            }
        }

        return result;
    }

    // 파일 -> 프로그램으로 입력
    public String charFileOpen(String path) throws FileNotFoundException {
		    StringBuffer sb = null;
        FileReader fr = new FileReader(path);

        try {
            sb = new StringBuffer();

            //FileReader의 read() 메소드는 한 글자씩만 읽어온다.
            //더 이상 읽을 내용이 없다면 -1을 반환한다.

            int value = 0;
            while( (value = fr.read()) != -1) {
                sb.append((char)value);
            }
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(fr != null) fr.close();
            }catch(Exception e) {
                e.printStackTrace();
            }
        }

        if(sb != null) return sb.toString();
        else return null;
    }
}



JDBC

JAVA에서 데이터베이스에 접속할 수 있게 해주는 자바 API이다.

JDBC 설정

  • Character Set 설정
    • Window - Preferences - General - Workspace - Text file encoding 내 Other 체크 후 UTF-8 설정 - Apply
    • General - Editors - Text Editors - Spelling - Encoding Default UTF-8 체크
  • OJDBC 설정
    • C:\oraclexe\app\oracle\product\11.2.0\server\jdbc\lib 경로로 이동
    • ojdbc6.jar 파일을 원하는 폴더로 복사
    • 이클립스 프로젝트 우클릭 - Properites 클릭
    • Java Build Path - Add External JARs 클릭
    • ojdbc6.jar가 있는 경로로 이동 후 ojdbc6.java 선택
  • 오라클 SCOTT 계정 설정
    • C:\oraclexe\app\oracle\product\11.2.0\server\rdbms\admin 경로로 이동
    • scott.sql 파일을 SQL Developer로 드래그
    • 24 ~ 27번 줄까지 대문자 SCOTT와 TIGER를 소문자로 변경
    • F5 누른 후 계정 접속



JDBC 사용 객체

  • DriverManager
    데이터 원본에 JDBC 드라이버를 통하여 커넥션을 만드는 역할이다. Class.forName() 메소드를 통해 생성되며 예외처리가 필수이다. 직접 객체 생성이 불가능하고 getConnection() 메소드를 사용하여 객체를 생성할 수 있다.

  • Connection
    특정 데이터 원본과 연결된 커넥션을 나타내며 Statement 객체를 생성할 때도 Connection 객체를 사용하여 createStatement() 메소드를 호출하여 생성한다. SQL 문장을 실행시키기 전에 우선 Connection 객체가 존재해야한다.

  • Statement
    Connection 객체에 의해 프로그램에 리턴되는 객체에 의해 구현되는 일종의 메소드 집합을 정의한다. Connection 클래스의 createStatement() 메소드를 호출하여 얻어지며 생성된 Statement 객체로 질의 문장을 String 객체에 담아 인자로 전달하여 executeQuery() 메소드를 호출하여 SQL 질의를 수행한다.

  • PreparedStatement
    Connection 객체의 preparedStatement() 메소드를 사용하여 객체를 생성한다. SQL 문장이 미리 컴파일되고 실행 시간동안 인수 값을 위한 공간을 확보한다는 점에서 Statement와 다르다.

  • ResultSet
    SELECT문을 사용한 질의 성공 시 Result Set이 반환된다. SQL 질의에 의해 생성된 테이블을 담고 있으며 커서(cursor)로 특정 행에 대한 참조를 조작한다.

JDBC 실습

// Emp.java

package com.kh.scott.model.vo;
import java.sql.Date;
/* VO(Value Object) === DTO(Data Transfer Object)
 * --> DB 테이블의 한 행의 정보가 기록되는 저장용 객체
 *
 *  1. 반드시 캡슐화 적용
 *  2. getter/setter 반드시 작성
 *  3. 기본 생성자 필수, 매개변수 있는 생성자 선택
 *  4. VO에 로직이 포함된 메소드는 지양
 *  */


public class Emp {
	private int empNo; // 사번
    private String eName; // 이름
	private String job; // 직책
	private int mgr; // 직속 상사

	// java.sql.Date
	private Date hireDate; // 입사일
	private int sal; // 급여
	private int comm; // 커미션
	private int deptNo; // 부서번호

    // 기본 생성자
	public Emp() { }

	public Emp(int empNo, String eName, String job, int mgr, Date hireDate, int sal, int comm, int deptNo) {
		super();
		this.empNo = empNo;
		this.eName = eName;
		this.job = job;
		this.mgr = mgr;
		this.hireDate = hireDate;
		this.sal = sal;
		this.comm = comm;
		this.deptNo = deptNo;
	}

	public int getEmpNo() {
		return empNo;
	}

	public void setEmpNo(int empNo) {
		this.empNo = empNo;
	}

	public String geteName() {
		return eName;
	}

	public void seteName(String eName) {
		this.eName = eName;
	}

	public String getJob() {
		return job;
	}

	public void setJob(String job) {
		this.job = job;
	}

	public int getMgr() {
		return mgr;
	}

	public void setMgr(int mgr) {
		this.mgr = mgr;
	}

	public Date getHireDate() {
		return hireDate;
	}

	public void setHireDate(Date hireDate) {
		this.hireDate = hireDate;
	}

	public int getSal() {
		return sal;
	}

	public void setSal(int sal) {
		this.sal = sal;
	}

	public int getComm() {
		return comm;
	}

	public void setComm(int comm) {
		this.comm = comm;
	}

	public int getDeptNo() {
		return deptNo;
	}

	public void setDeptNo(int deptNo) {
		this.deptNo = deptNo;
	}

	@Override
	public String toString() {
		return "Emp [empNo=" + empNo + ", eName=" + eName + ", job=" + job + ", mgr=" + mgr + ", hireDate=" + hireDate
				+ ", sal=" + sal + ", comm=" + comm + ", deptNo=" + deptNo + "]";
	}
}
// EemRun.java

package com.kh.scott.run;
import com.kh.scott.view.EmpView;

public class EmpRun {
    public static void main(String[] args){
        new EmpView().displayMain();
    }
}
// EmpView.java

package com.kh.scott.view;

import java.util.InputMismatchException;
import java.util.List;
import java.util.Scanner;
import com.kh.scott.model.service.EmpService;
import com.kh.scott.model.vo.Emp;

// View
// - 사용자의 요청을 입력받고, 응답을 출력하는 부분(화면)

public class EmpView {
	private Scanner sc = new Scanner(System.in);
    private EmpService empService = new EmpService();

    // 메인 메뉴 역할
    public void displayMain() {
        int sel = 0;
        do {
            try { //메뉴 입력 예외 처리 부분
                System.out.println();
				System.out.println("====================================");
				System.out.println("[Main Menu]");
				System.out.println("1. 전체 사원 정보 조회");
				System.out.println("2. 사번으로 사원 정보 조회");
				System.out.println("3. 새로운 사원 정보 추가");
				System.out.println("4. 사번으로 사원 정보 수정");
				System.out.println("5. 사번으로 사원 정보 삭제");
				System.out.println("0. 프로그램 종료");
				System.out.println("====================================");

				System.out.print("메뉴 선택 : ");
				sel = sc.nextInt();
                System.out.println();

                switch(sel) {
				case 1 : selectAll(); break; // 1_1. 같은 클래스에 있는 selectAll()을 호출
				case 2 : break;
				case 3 : break;
				case 4 : break;
				case 5 : break;
				case 0 : System.out.println("프로그램 종료"); break;
				default : System.out.println("잘못 입력하셨습니다.");
				}
            } catch(InputMismatchException e) {
                System.out.println("숫자만 입력해주세요.");
                sel = -1;
                sc.nextLine();
            }
        } while(sel != 0);
    }

    // 1_2. 전체 사원 정보를 출력하는 View
    private void selectAll() {
        System.out.println("[전체 사원 정보 조회]");

        //1_3. EmpService.selectAll()을 호출하여
        // 전체 사원 정보가 담겨있는 List 반환 받음
        List<Emp> empList = empService.selectAll();

        if(empList == null) {
            System.out.println("전체 사원 정보 조회 과정에서 오류가 발생했습니다.");
        } if(empList.isEmpty()) {
            // DB연결, 조회는 성공했지만 조회 결과가 없는 경우
            System.out.println("조회 결과가 없습니다.");
        } else {
            // 조회 성공 시
            for(Emp emp : empList) {
                System.out.println(emp);
            }
        }
    }
}
// EmpService.java

package com.kh.scott.model.service;
import java.util.List;
import com.kh.scott.model.dao.EmpDAO;
import com.kh.scott.model.vo.Emp;

// Service (비즈니스 로직 처리)
// 1) 전달받은 데이터 또는 DAO의 수행 결과 데이터를 필요한 형태로 가공 처리


public class EmpService {
	private EmpDAO empDAO = new EmpDAO();

    public List<Emp> selectAll() {
        //1_4. 전체 사원 정보 조회는 별도의 데이터 가공 처리가 필요 없음
        // --> 바로 EmpDAO.selectAll()을 호출하여 결과를 반환 받음

        List<Emp> empList = empDAO.selectAll();

        //1_10. DAO 반환 데이터의 가공처리가 필요 없으므로 그대로 다시 반환
        return empList;
    }
}
// EmpDAO.java

package com.kh.scott.model.dao;

import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.kh.scott.model.vo.Emp;

public class EmpDAO {
	// DB에 있는 EMP 테이블의 모든 사원 정보를 조회 DAO
	public List<Emp> selectAll() {

        // JDBC 객체 선언(java.sql 패키지에 존재하는 객체)
        Connection conn = null;
        // DB와의 연결 정보를 담은 객체
        // --> 프로그램과 DB 사이를 연결해주는 일종의 길이다.

        Statement stmt = null;
        // Connection 객체를 통해 DB에 SQL문을 전달하고 실행하여
        // 결과를 반환받는 역할

        ResultSet rset = null;
        // DB에서 SELECT 질의 성공 시 반환되는 객체
        // - SELECT문의 결과로 생성된 테이블을 담고 있으며
        // '커서(Cursor)'를 이용해 한 행씩 참조할 수 있음

        List<Emp> empList = null;
        // DB에서 조회한 결과를 저장할 List

        try {
            //1_5. JDBC 드라이버 클래스를 메모리에 로드한다.
            Class.forName("oracle.jdbc.driver.OracleDriver");
            // 프로그램과 DB 사이의 연결 역할을 해주는 JDBC 드라이버 로드
            // 오라클 DB와 연결하기 위한 JDBC 드라이버
            // 해당 드라이버를 호출함으로써 메모리(RAM)에 로드된다.


            //1_6. DB와의 연결 작업 준비
            /*
            연결 정보를 담을 Connection을 얻어온다.
            ---> Connection을 얻어오기 위해서는 Driver Manager가 필요
            */

            conn = DriverManager.getConnection("jdbc:oracle:thin:@127.0.0.1:1521:xe", "scott", "tiger");

            // DB와의 연결과 관련된 요소들은 SQLException을 발생시킬 가능성이 있으므로 예외처리 필수
            /*
            DB 접속 성공 시 conn을 출력하면 객체의 주소가 출력된다.
            -> 실패 시 null 출력

            System.out.println(conn);
            --> oracle.jdbc.driver.T4CConnection@6438a396

            jdbc:oracle:thin -> JDBC 드라이버의 타입이 thin 타입을 의미
            @127.0.0.1 -> 자신의 컴퓨터 로컬 IP(== localhost 대체 가능)

            : 1521 --> 오라클 listener 포트 번호
            (포트 : 응용 프로그램이 외부와 통신하기 위한 통로)

            :xe -> 오라클 DB 이름
            */


            //1_7. DB 접속 성공 시 DB에다가 전달할 SQL 구문 준비
            // 이를 전달하여 결과를 얻어올 객체 Statement 객체 생성
            String query = "SELECT * FROM EMP";

            stmt = conn.createStatement();
            rest = stmt.executeQuery(query);
            // ****** stmt.executeQuery(query)
            // --> DB로 SELECT문을 전달하고 결과로 Result Set반환받음

            //1_8. ResultSet rset에 있는 SELECT 처리 결과를
            //JAVA 단에서 쉽게 사용할 수 있도록 List 객체에 저장

            //DB와의 통신 성공 시 List 객체 생성.
            // --> 오류가 났을 경우에 메모리 손실을 방지하기 위해
            empList = new ArrayList<Emp>();


            while(rset.next()) {
                // ResultSet.next() : 반환받은 조회 결과를 커서를 이용해 순차적으로 한 행씩 접근
                // 접근한 행이 존재하면 true, 없으면 false

                // ResultSet에서 현재 접근한 행의 데이터를 꺼내는 방법
                // get[타입] ("컬럼명")
                int empNo = rset.getInt("EMPNO"); // 7369
				String eName = rset.getString("ENAME"); // SMITH
				String job = rset.getString("JOB");
				int mgr = rset.getInt("MGR");
				Date hireDate = rset.getDate("HIREDATE");
				int sal = rset.getInt("SAL");
				int comm = rset.getInt("COMM");
				int deptNo = rset.getInt("DEPTNO");

                // 얻어온 행의 정보를 통해 Emp 객체를 만들어
                // empList에 추가하기

                empList.add(new Emp(mpNo, eName, job, mgr, hireDate, sal, comm, deptNo));

            }
        }catch(ClassNotFoundException e) {
            e.printStackTrace();
        }catch(SQLException e) {
            e.printStackTrace();
        }finally {
            //1_9. JDBC 관련 객체 연결 끊기(자원 반환)
            //연결을 끊을 경우 생성의 역순으로 끊어 나가기

            try {
                if(rset != null) rset.close();
                if(stmt != null) stmt.close();
                if(conn != null) conn.close();
            }
        }
    }
}

태그: ,

카테고리:

업데이트:

댓글남기기