Programming/JDBC

CRUD 서비스 만들기 3.(페이징쿼리 적용부터~). 2021-09-01

최동훈1 2021. 9. 1. 17:03

우선 지난시간에 내가 짠 쿼리대로, notice 테이블을 서버로부터 가져올때, 등록시간이 최신순으로(등록순 내림차순 한다음), 10개씩 가져오는 쿼리를 적용해 보겠다. NoticeService 클래스가 너무 길어서 SELECT 기능을 담당하는 getList() 부분만 가져왔다.

public List<Notice> getList(int page) throws ClassNotFoundException, SQLException{
		String url="jdbc:oracle:thin:@localhost:1521/xepdb1";
        String sql ="select * from(select ROWNUM as num, n.* from (select * from notice order by regdate disc) n ) "
        		+ "where num between ? and ?";
		
		int start= 1+ 10*(page-1);
        //등차수열 공식을 이용해서 1부터 10개씩 끊기게 함.
		int last = 10*page;
		Class.forName("oracle.jdbc.driver.OracleDriver");
		Connection con = 
		DriverManager.getConnection(url,"newlec","vdongv1620");
	
	    PreparedStatement st=con.prepareStatement(sql);
        // PreparedStatement 로 ? 로 된 쿼리의 문자열을 채워줌.
	    ResultSet rs=st.executeQuery();
	    
	    st.setInt(1, start);
	    st.setInt(2, last);
	    
	    List<Notice> list=new ArrayList();
	    
	    
		while(rs.next()) {
			int id=rs.getInt("id");
			String title=rs.getString("title");
			String writer_id=rs.getString("writer_id");
			String content=rs.getString("content");
			String regdate=rs.getString("Regdate");
			int hit=rs.getInt("hit");
			Notice notice=new Notice(id,title,writer_id,content,regdate,hit);
			
		list.add(notice);
		
		}
		
		
		
		
		
		st.close();
		con.close();
		return list;

	}

변경된 부분 : 원래는 getList()함수의 파라미터가 없었는데, 이제는 뽑으려는 페이지 번호를 넘겨받게 한 다음, BETWEEN 쿼리에 페이지번호에 따라 10개씩 끊기도록 만들었다. 

그다음 실행해 보았다.

 

그런데 이런 오류가 발생되었다. 보니까 notice에 저장된 테이블의 수가 7개 밖에 없었다. 그래서 10개를 출력할려고 하니 오류가 생긴 것이다.

7개 밖에 존재하지 않는 테이블.

그래서 나는 11개 로 테이블을 추가하고 다시 실행하니 똑같은 에러가 나왔다. 그래서 테이블의 개수 문제가 아니라 다른곳에 문제점이 있음을 발견하고, 에러 로그를 하나하나 따라올라가니 PreparedStatement에서 에러가 발생한것을 보게 되었다.( 위 에러창 참고) 그래서  코드를 다시보니, st.setInt() 메서드를 호출하기 전에, 이미 executeQuery()로 "?" 인덱스에 채워지지 않은채로 쿼리를 실행해버려서 문제가 생긴 것이였다. 자바도 절차형 언어이기때문에 위에서부터 밑으로 써내려가는 순서가 중요하다는 사실을 간과했다.

 //원래 잘못된 쿼리
 ResultSet rs=st.executeQuery();
	    
	    st.setInt(1, start);
	    st.setInt(2, last);
	   
       //다시 바꾼 쿼리.
       //먼저 setInt 메서드를 통해 preparedStatement(sql)에
       //들어가있는 미완성 sql의 ? 에 인자를 넘겨준다.
       
        st.setInt(1, start);
	    st.setInt(2, last);
        
        ResultSet rs=st.executeQuery();

이렇게 원래대로 하니 같은 에러는 나지 않았지만 다른 에러가 났다.

위 인덱스에서 누락된 IN Out 에러를 해결하고 나온 또 다른 애러

그런데 이건 해결하기 쉬웠다. 쿼리의 문제라는것이 명확해 보였기 때문이다. 이유를 보니 페이징 쿼리에서 order by 절 부분이 문제였다. 내림차순을 정렬하는 키워드는 disc가 아닌 desc 였다.

select * from (select RowNum as num, N.* from
(select * from notice order by regdate desc) 
N) 
where num between 1 and 10;

이렇게 쿼리를 고쳤다.

제대로 수정해서 완성한 페이징 코드.

public List<Notice> getList(int page) throws ClassNotFoundException, SQLException{
		String url="jdbc:oracle:thin:@localhost:1521/xepdb1";
        String sql ="select * from(select ROWNUM as num, n.* from (select * from notice order by regdate desc) n ) "
        		+ "where num between ? and ?";
		
		int start= 1+ 10*(page-1);
		int last = 10*page;
		Class.forName("oracle.jdbc.driver.OracleDriver");
		Connection con = 
		DriverManager.getConnection(url,"newlec","vdongv1620");
	
	    PreparedStatement st=con.prepareStatement(sql);
	    st.setInt(1, start);
	    st.setInt(2, last);
	    
	    
	    ResultSet rs=st.executeQuery();
	    
	    List<Notice> list=new ArrayList();
	    
	    
		while(rs.next()) {
			int id=rs.getInt("id");
			String title=rs.getString("title");
			String writer_id=rs.getString("writer_id");
			String content=rs.getString("content");
			String regdate=rs.getString("Regdate");
			int hit=rs.getInt("hit");
			Notice notice=new Notice(id,title,writer_id,content,regdate,hit);
			
		list.add(notice);
		
		}
		
		st.close();
		con.close();
		return list;

	}

정상적으로 페이징되어 출력된 모습.

이것은 시간순 내림차순해서 10개씩 출력한것이기에, 뒤 페이지로 갈 수록, 과거의 글들이 출력된다.

그런데 페이징하는 쿼리가 너무 길다. 그래서 뷰를 이용해서 쿼리를 간단히 해 보겠다. 뷰는 테이블은 아니지만 마치 테이블처럼 사용 가능한, 제약조건이 걸린 레코드들의 집합을 저장할수있는 구조이다.

create view  Notice_view as
select * from
(select ROWNUM as num,n.* from
(select * from notice order by regdate desc) 
n );

이렇게 뷰를 생성하고, 이 뷰는 마치 테이블과 같이 사용 가능하다.

String sql ="select * from NOTICE_VIEW "
        		+ "where num between ? and ?";

정상적으로 동작한다. 뷰를 사용하면 복잡한 쿼리를 간단하게 할 수 있다.

 

다음 포스팅까지 나 스스로 낸 과제.

1. 사용자가 종료를 를 누를때 까지 1)상세검색 2) 이전 3) 다음 4) 종료. 가 되는 선택지와 게시글을 출력할수있는 UI를 반복해서 사용자에게 결과값을 보여주자.

2. 현재는 페이지수가 상수로 고정되어있어서, 페이지가 넘어갈때마다 현재 보고있는 페이지가 몇번째 페이지인지 사용자에게 보여주는 기능을 넣자. 

3. 현재 출력되는 레코드 수를 상단에 출력되도록 만들자.

 

4. 사용자의 입력값, 2: 이전, 3: 다음 에 따라 페이징하여 다음 게시글이나 이전 게시글이 UI에 나타나게끔 만들자.

 

즉, 요약하자면 게시판의 검색을 제외한 기본적인 기능을 구현하는 목표이다.

 

공부시간 2시간.

순공시간 1시간.

 

오늘은 3시 10분까지는 공부하다가 4시 30분까지 낮잠잤다. 아무래도 내가 직접 고민해서 만들어보는 시간이 없어서, 공부가 안된다고 내 뇌가 느꼈던거 같다. 그래서 다음에는 상기한 기능들을 아무 도움없이 나 스스로 직접 완성해 봐야겠다.