ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 6. 자바와 SQL의 힘을 합쳐 필드 생성, 레코드 한 개 조회하기
    PROGRAMMING/SQL 2022. 4. 24. 02:32

    하루입니다.

     

    오늘은 전자상점에 서비스를 제공하려 합니다. 전자제품과 관련된 값으로는 고유한 번호, 이름, 제조사, 가격, 할인판매가, 재고, 판매여부, 제조일이 있습니다. 

     

     제가 원하는 건 상점에서 특정 번호를 누르면 제품 추가 / 상세 정보 확인 / 전체 조회 / 변경 / 삭제의 기능을 수행할 수 있는 거예요. 자바와 데이터베이스를 연결해서 자바에 값이 출력되기만 하는 게 아닌 실제로 DB에 값이 들어가게 하려 합니다. 

     

    표현계층에서는 고객이 맞는 번호를 누른다면 원하는 작업을 할 수 있게 하고, 영속화 계층에서는 DB와 상호작용하도록 작업하고, 서비스계층에서는 고객이 프로그램을 실행할 때의 업무로직을 짤 거예요. 먼저 VO(value object)를 작성하도록 합시다. 이제부터 vo를 만들 때 주의할 점은 테이블과 닮게 만들어야 한다는 점입니다.

     

     

     

     

     

    NO, RPRICE, DISCOUNTPRICE, STOCK은 NUMBER / NAME, COMPANY, STATUS는 VARCHAR / CREATEDDATE는 DATE 형식이네요. 필드에 입력할 때는 int, String, Date로 입력하겠습니다. 

     

    public class Product
    
        private int no;
    	private String name;
    	private String company;
    	private int price;
    	private int discountPrice;
    	private int stock;
    	private String status;
    	private Date createdDate;
        
        기본생성자 생성
        
        getter setter 메소드 생성
        
        toString 재정의

     

     

     

     

    다음으론 repo와 app을 생성하고 메뉴의 틀을 잡도록 합시다.

    public class ProductInsertApp
    
    while (true) {
    		try {
    		System.out.println("------------------------------------");
    		System.out.println("1.등록 2.전체조회 3.상세정보 4.수정 5.삭제");
    		System.out.println("------------------------------------");
    		
    	System.out.println("메뉴를 선택해주세요.");
    	int menuNo = reader.readInt();
    	System.out.println();
    		
    	if (menuNo == 1) {
    		System.out.println("<< 새 상품 등록 >>");
    		System.out.println("상품등록 정보를 입력하세요.");
    			
    		System.out.println("상품이름 입력");
    		String name = reader.readString();
    		System.out.println("제조회사 입력");
    		String company = reader.readString();
    		System.out.println("상품가격 입력");
    		int price = reader.readInt();
    		System.out.println("입고수량 입력");
    		int stock = reader.readInt();
                
            System.out.println("[처리완료] 신규 상품이 등록되었습니다.");
    		}
    		
    		} catch (Exception e) {
    			e.printStackTrace();
    			System.out.println("에러가 발생했습니다.");
    		}
    	}

     

     

     

     

    이 다음엔 뭐가 필요할까요? 신규 상품을 등록할 수 있는 업무로직이 필요하겠죠. ProductRepository에서 업무로직을 만들겠습니다. 

     

    public class ProductRepository
    
    public void insertProduct(Product product) throws ClassNotFoundException, SQLException {
    		
    		String sql = "insert into sample_products "
    				+ "(product_no, product_name, product_company, product_price, product_stock) "
    				+ "values "
    				+ "(product_seq.nextval, ?, ?, ?, ?)";
    		
    		Class.forName("oracle.jdbc.OracleDriver");
    		Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "xx", "xxxxxxxx");
    		PreparedStatement pstmt = connection.prepareStatement(sql);
    		pstmt.setString(1, product.getName());
    		pstmt.setString(2, product.getCompany());
    		pstmt.setInt(3, product.getPrice());
    		pstmt.setInt(4, product.getStock());
    		pstmt.executeUpdate();
    		
    		pstmt.close();
    		connection.close();
    		
    	}

    헉 그런데요. 컬럼은 많은데 왜 저 네 가지만 데이터를 입력하게 하나요? 

     

    그 이유는 1. not null인 값만 입력할 건데 그럼 no name company price stock 총 다섯 개임.

                  2. 그 중에서도 no는 시퀀스를 통해 숫자가 자동으로 1씩 올라가도록 설정해 둠.

                  3. 자동설정 / null 허용을 제외하면 name company price stock 총 네 개.

                  4. (product_seq.nextval, ?, ?, ?, ?) 이 부분에서 no에 시퀀스를 대입한 것을 알 수 있다.

     

    헉 그렇군요!

     

     

     

    다음은 제품 상세정보 확인과 전체 상품 조회를 구현하려 합니다. 의외로 ... 이 부분이 꽤나 중요합니다. 더 정확하게 말하자면 데이터 조회하기에서 [조회결과가 없거나 한 행만 조회되는 경우] 와 [조회결과가 없거나 여러 행이 조회되는 경우]로 나눌 수 있겠네요! 그리고 ResultSet이라는 것의 작동 원리도 알아야 합니다!

     

    데이터 조회하기 (SELECT)

    1. 조회결과가 없거나 한 행만 조회되는 경우

    • PRIMARY KEY로 설정된 컬럼이 조회조건으로 이용되는 경우
    • 컬럼에 PK가 있다면 무조건 값이 있어야 하며 같은 값이 없어야 한다.
    • 이메일로 조회, 주민번호로 조회, 사원번호로 조회 등 ...

     

    • UNIQUE로 설정된 컬럼이 조회조건으로 이용되는 경우
    • 컬럼에 UNIQUE가 있다면 같은 값이 없어야하나 null은 있을 수도 있습니다.
    • 새로운 사원이 와서 사원번호를 발행해 줘야 하는 상황 (사원번호 없지만 중복은 안 됨)

     

    // 상품테이블에서 상품번호(Primary key)로 조회하기
    public Product getProduct(int productNo) { ... }]
    
    // 사원테이블에서 사원아이디(Primary key)로 조회하기
    public Employee getEmployee(int employeeId) { ... }
    
    // 직종테이블에서 직종아이디(Primary key)로 조회하기
    public Job getJob(int jobId) { ... }
    
    // 사원테이블에서 이메일(Unique)로 조회하기
    public Employee getEmployeeByEmail(String email) { ... }
    • 이 값을 출력하기 위해서는?
    • RS 객체에 조회결과가 저장되는데, 하나도 없거나 한 개만 존재하므로 반복작업을 수행할 필요가 없다. 
    • 이게 무슨 뜻이냐! 반복할 필요 없이 존재여부만 확인하면 되는 것. 
    • if(rs.next)

     

     

     

     

    2.조회결과가 없거나 여러 행이 조회되는 경우

    • PRIMARY KEY, UNIQUE로 설정되어 있지 않는 컬럼이 조회조건으로 이용되는 경우
    • 이름, 수량, 급여, 입사일, 소속 부서 등 ... 
    // 상품테이블에서 상품이름으로 조회하기
    public List<Product> getProductByName(String name) { ... }
    
    // 사원테이블에서 급여로 조회하기
    public List<Employee> getEmployeeBySalary(double Salary) { ... }
    
    // 사원테이블에서 소속부서 아이디로 조회하기
    public List<Employee> getEmployeeByDepartmentId(int departmentId) { ... }
    • RS 객체에 조회결과가 저장되는데, 하나도 없거나 여러 개가 존재하므로 반복작업을 수행해야 한다.
    • 맨 마지막 행까지 반복해서 데이터를 꺼내야 한다.
    • 이럴 때 WHILE문이 사용된다!
      • while(rs.next) 를 사용한다.
      • rs.next()는 커서가 위치한 곳에 데이터행이 존재하면 true를 반환한다.
      • 따라서 rs.next()의 실행결과가 true라면 계속 RS에서 데이터를 추출할 수 있다.
      • 이 값은 controller에서 다시 향상된-for문을 사용하여 꺼낸다!

     

     

     

    * 추가사항

    DEPARTMENTS 테이블에서 DEPARTMENT_ID는 PRIMARY_KEY라고 하자. EMPLOYEES 테이블에서 DEPARTMENT_ID는 PRIMARY KEY도 UNIQUE도 아니라고 하자. DEPARTMENTS 테이블에서 DEPARTMENT_ID를 조회하면 0건 아님 1건이, EMPLOYEES에서 DEPARTMENT_ID를 조회하면 0건 아님 여러 건이 나온다. 1건이 나올 수도 있으나 앞으로 여러 건이 될 수 있다. 

     

     

    ResultSet(이 뭔가요)

    사실 ... 이 그림 하나로 모두 설명됩니다 ... 

     

     PreparedStatement 인터페이스의 메소드에서 SELECT는 ResultSet executeQuery()를 사용하던 거 기억 나시나요? 이 때 ResultSet(이하 RS)에는 SELECT문의 실행결과가 들어가 있습니다. 하단의 RS 객체를 보면 데이터들이 들어 있는 걸 볼 수 있습니다. RS에는 커서가 내장되어 있는데요, 처음부터 행을 가리키는 것이 아닌 데이터의 하나 위에 위치하고 있습니다. 이 커서를 한 행 내리는 메소드는 boolean next()입니다. 만약 내렸는데 값이 있다면 true! 없다면 false! 를 반환합니다. 왠지 if for while이 생각나죠 ^^ ... ? 

     커서가 있는 행에서 데이터를 가져오기 위해서는 int no = rs.getint(product_no); 와 같은 형태로 적으면 됩니다! 

     

     

     

     

    그럼 이제 코딩을 해 볼까요!

     

    상품번호(한 개)를 전달받아서 일치하는 상품정보를 조회해서 반환하겠습니다! 

     

    먼저 생각을 ... 해 봅니다 ... 번호를 입력했을 때 정보가 나오게 하는 메소드니까 productNo가 매개변수(대충 이 값이 필요하다는 뜻임)일테고 ... sample_product 테이블에서 전체 데이터를 where product_no에 따라 꺼내야 할 것. select니까 RS를 사용해서 상품 번호가 nnnn인 것을 찾으면 되겠구나 ... 나머지 과정은 위와 같습니다. 코딩! 시작!

     

    - 상품번호 한 개 전달받기 위해 DAO에 코딩한 내용 - 

    	public Product getProductByNo (int productNo) throws ClassNotFoundException, SQLException {
    		Product product = null;
    		
    		String sql = "select product_no, product_name, product_company, product_price, product_discount_price, product_stock, product_status, product_created_date "
    				+ "from sample_product "
    				+ "where product_no = ? ";
                    
            Class.forName("오라클 오라클 드라이버 연결");
    		Connection connection = DriverManager.getConnection("포트 아이디 비밀번호");
    		
    		PreparedStatement pstmt = connection.prepareStatement(sql);
    		pstmt.setInt(1, productNo);
    		ResultSet rs = pstmt.executeQuery();
    		
    		if (rs.next()) {
    			product = new Product();
    		//	int no = rs.getInt("product_no");
    		//	product.setNo(rs.getInt("product_no"));
                    //  아래와 같은 말이다. 간략화(?)한 것.
    			product.setNo(rs.getInt("product_no"));
    			product.setName(rs.getString("producy_name"));
    			product.setCompany(rs.getString("product_company"));
    			product.setPrice(rs.getInt("product_price"));
    			product.setDiscountPrice(rs.getInt("product_discount_price"));
    			product.setDiscountPrice(rs.getInt("product_discount_price"));
    			product.setStock(rs.getInt("product_stock"));
    			product.setCreatedDate(rs.getDate("createdDate"));
    		}
            
    		rs.close();
    		pstmt.close();
    		connection.close();
    		
    		return product;
    	}
    • 코드 설명 : getProductByNo라는 메소드를 repo에 만들었다. 이 메소드는 productNo를 매개변수로 받는다. 왜냐? 우리는 상품번호를 받아서 그걸 찾을 거거든. 이 기능을 구현할 메소드가 No 메소드니까 당연히 상품번호를 받아야 함. 쿼리문을 작성한다. 우리가 얻고자 하는 건 번호라서 번호에 ? 표시를 한다. 띄어쓰기와 오타 검수할 것. DB에 연결한다.
    • pstmt.setInt(1, productNo);의 뜻은 저기 ?가 1번째 값(순서)이고 그 자리에 productNo를 넣는다는 뜻이다. 그럼 전달받은 productNo가 sql where문으로 들어가서 같은 번호를 찾겠지. 내가 헷갈렸어서 볼드 표시.
    • Rs를 사용한다. 위에서 말했듯 하나하나 내장 커서를 내리며 사용자가 입력한 productNo와 일치하는 product_no가 있나 찾는 것. 없다면 null값을 반환하겠고. 있다면 밑의 get set을 실행할 것. 사실 아직도 get set은 잘 모르겠다. 대강 이해한 걸 적자면 이 코드에서는 db에 있는 값들을 rs.get을 이용해 얻고 그 값들을 set을 사용해서 변수(?) int no 여기 이런 값들에 담는다는 것. 아무튼 잘 찾는다면 db의 값을 자바 println에서도 볼 수 있는 것이다. 
    • 마무리는 항상 close()로. 입출력 다 있는 메소드니까 return도 잊으면 안 된다. 

     

     

     

    이제 이걸 출력해야겠죠. 출력은 App에서 해 보겠습니다. 앞서 말했듯 if문을 사용해 내가 찾는 번호가 db에 있다면 상품 정보와 "[처리 완료] 상품정보를 확인하세요." 가 출력되고, 없다면 "[처리 완료] 상품번호와 일치하는 상품정보가 존재하지 않습니다." 라고 뜨게 할 것입니다.

     

    		} else if (menuNo == 3) {
    			System.out.println("<< 상품 상세정보 조회 >>");
    			System.out.println("상품번호로 상품상세정보를 조회해 보세요.");
    			
    			System.out.println("상품번호 입력");
    			int productNo = reader.readInt();
    			
    			Product product = productRepository.getProductByNo(productNo);
    			if (product == null) {
    				System.out.println("[처리 완료] 상품번호와 일치하는 상품정보가 존재하지 않습니다.");
    			} else {
    				System.out.println("------------------------------------");
    				System.out.println("상품번호: " + product.getNo());
    				상품번호, 상품이름, 제조회사, 정상가격, 할인가격, 판매상태, 재고수, 등록일자 동일
    				System.out.println("------------------------------------");
    				System.out.println("");
    			}
    				System.out.println("[처리 완료] 상품정보를 확인하세요.");
    		}

     

     

     


     

    급 마무리

     

    감사합니다. 안녕히 계세요.

     

     

     

Designed by Tistory.