JTable에서 사용하는 Model객체
● JTable
- JTable은 데이터베이스의 검색결과를 GUI에 보여주기 위해 사용되는 컴포넌트이다. 가로와 세로로 구성된 테이블을 을 사용해서 행과 열에 데이터를 위치시킨다.
- JTable을 사용하는 방법은 다음과 같다.
① 테이블에 출력될 데이터를 2차원 배열에 저장한다.
Object[][] records = { { ... }, { ... }, { ... } };
② 제목으로 사용할 문제열을 1차원 배열에 저장한다.
String[] titles = { “제목1”, “제목2”, ... , “제목n” };
③ 위의 ①과 ②를 사용해서 JTable을 생성한다.
JTable table = new JTable(records, titles);
④ 위의 ③에서 생성한 테이블을 스크롤이 있는 패널에 붙인다. 테이블에 출력되는 데이터의 갯수가 많으면 한 화면 을 넘기게되는데, 이런 경우에는 스크롤을 사용해서 다음 화면의 데이터를 볼 수 있도록 해야 한다. 그래서, 일반적으 로 테이블을 스크롤이 있는 패널(JScrollPane)에 붙인다.
JScrollPane scrollpanel = new JScrollPane();
⑤ 위의 ④에서 생성된 스크롤이 있는 패널을 윈도우 프레임에 붙인다.
add(scrollPanel);
- 테이블 사용 예제
public class TableExam extends JFrame { Object[][] data = {
{"홍길동", "hong@mail.com", "1221", 15}, {"박길동", "park@mail.com", "1245", 30}, {"최길동", "choi@mail.com", "2332", 20}
};//테이블에 출력할 데이터
String[] columnName = {"이름", "이메일 주소", "암호", "나이"};//열의 제목으로 사용될 문자열 JTable table;
public TableExam() {
table = new JTable(data, columnName);
add(new JScrollPane(table));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(500, 200);
setVisible(true);
}
public static void main(String[] args) { new TableExam();
} }
● JTable과 MVC(Model-View-Controller) 구조
- 모델-뷰-컨트롤러 구조는 데이터의 저장과 접근에 대한 제공은 모델이 담당하고, 화면 표시는 뷰, 이벤트의 처리는 컨트롤러가 하도록 각 역할을 구분한 구조이다. 즉, 역할의 분담을 통하여 상호간의 영향을 최소화 하고 각 요소의 독립성을 보장하여 독자적인 역할에 충실할 수 있는 형태이다.
- 이 구조를 사용하면 한 개의 데이터 모델을 사용해서 다양한 형태의 뷰를 사용해서 보여줄 수 있다.
- JTable을 이용한 데이터 출력작업을 MVC 구조로 분리하면, JTable은 뷰 역할만을 수행하고 테이블에 출력되는 데이터는 모델 역할만 수행한다. 따라서, JTable은 데이터에 관여할 필요가 없어지고, 모델 역할을 하는 데이터도 JTable에 관여하지 않는다.
- JTable은 단지 데이터를 보여주는 역할을 한다.
- JTable은 내부적으로 DefaultTableModel 객체를 사용해서 테이블에 출력할 모델로 사용한다.
- 모델 객체를 사용해서 테이블을 작성하는 절차는 다음과 같다.
① 모델 클래스 설계
public class 클래스이름 extends AbstractTableModel { }
② 모델 객체 생성
클래스이름 model = new 클래스이름();
③ 모델을 사용해서 JTable 객체 생성
JTable table = new JTable(model);
④ 윈도우 프레임에 테이블을 붙인다.
add(new JScrollPane(table));
- JTable의 개념도(MVC)
- 테이블은 데이터의 행의 갯수와 열의 갯수, 열의 제목 등을 읽어서 데이터를 테이블에 출력한다.
- JTable이 기본적으로 가지고 있는 모델이 있는데, 이 모델은 TableModel 이다.
- 모델 객체는 테이블에서 사용할 수 있도록 “행의 갯수”, “열의 갯수”, “열의 제목” 등을 알려주는 메서드를 가지고 있다. 각각 getRowCount(), getColumnCount(), getColumnName(열의번호) 메서드이다.
- JTable의 기본으로 가지고 있는 TableModel을 사용한 예
class JTableTest1 {
public static void main(String[] args) {
JFrame frame = new JFrame("참가자 명단 프로그램");
frame.setPreferredSize(new Dimension(300, 150));
frame.setLocation(500, 400);
Container contentPane = frame.getContentPane();
String colNames[] = { "이름", "나이", "성별" };
Object data[][] = { { "김철수", 24, '남' }, { "이순희", 21, '여' }, { "박지영", 26, '여' } };
JTable table = new JTable(data, colNames);
JScrollPane scrollPane = new JScrollPane(table);
contentPane.add(scrollPane, BorderLayout.CENTER);
JButton button = new JButton("출력");
button.addActionListener(new PrintActionListener(table));
contentPane.add(button, BorderLayout.SOUTH);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
} }
class PrintActionListener implements ActionListener { JTable table;
PrintActionListener(JTable table) { this.table = table;
}
public void actionPerformed(ActionEvent e) { TableModel model = table.getModel();
int rowNum = model.getRowCount(); ← 데이터의 행의 갯수를 얻어온다.
int colNum = model.getColumnCount(); ← 데이터의 열의 갯수를 얻어온다.
for (int col = 0; col < colNum; col++) {
String colName = model.getColumnName(col); ← 지정된 열의 제목을 얻어온다.
System.out.print(colName + "\t");
}
System.out.println();
for (int row = 0; row < rowNum; row++) { for (int col = 0; col < colNum; col++) {
Object obj = model.getValueAt(row, col);
System.out.print(obj + "\t");
}
System.out.println();
}
System.out.println("---");
} }
● 데이터베이스의 조회결과를 JTable에 출력
- 데이터베이스의 조회 결과를 JTable에 출력하기 위해서는 우선 Model에 조회결과 데이터를 저장해야 한다. 그런데, JTable이 기본적으로 가지고 있는 TableModel에는 데이터베이스의 조회결과를 저장하는 일을 하는 메서드가
존재하지 않는다.(다음 그림의 ? 부분)
- 따라서, 조회 결과를 모델에 저장하는 메서드가 있는 모델이 필요하다.
- TableModel에는 조회 결과를 저장하는 메서드가 존재하지 않으므로, 새로운 모델을 만든 후 데이터 베이스 조회결과를 모델에 저장하는 새로운 메서드를 정의해야 한다.
- 새로운 모델 객체를 만드는 방법은 다음과 같다.
① AbstractTableModel 클래스를 상속해서 새로운 이름의 모델 클래스를 만든다.
class NewModel extends AbstractTableModel {
}
② 모델 객체가 기본적으로 가지고 있는 다음의 메서드들을 오버라이딩한다.(data는 데이터베이스 조회결과가 저장될 2차원 배열의 이름)
public int getColumnCount() {//출력할 데이터의 열의 갯수를 알려주는 메소드 return data[0].length;//4개 열로 구성되어 있으므로 4를 리턴한다.
}
public int getrowCount() {//출력할 데이터의 행의 갯수를 알려주는 메소드 return data.length;//3개의 행으로 구성되어 있으므로 3을 리턴한다.
}
메소드 이름 설 명
next() 현재 행에서 한 행 다음으로 이동
previous() 현재 행에서 한 행 앞으로 이동
first() 현재 행에서 첫번째 행의 위치로 이동
last() 현재 행에서 마지막 행의 위치로 이동
public int getrowCount() {//출력할 데이터의 행의 갯수를 알려주는 메소드 return data.length;//3개의 행으로 구성되어 있으므로 3을 리턴한다.
}
//테이블에서 해당 열,행을 선택했을 때 그곳에 저장된 데이터를 알려주는 메소드 public Object getValueAt(int rowIndex, int columnIndex) {
return data[rowIndex][columnIndex];//해당 위치의 데이터를 리턴한다.
}
public String getColumnName(int column) {//지정된 열(column)의 제목을 알려주는 메서드 return columnName[column];
}
③ 데이터베이스의 검색 결과의 건 수를 이용해서 모델에 저장될 데이터의 행의 수를 설정하는 메서드를 작성한다.
void 메서드이름(ResultSet resultSet) { try {
resultSet.last(); ← 검색된 결과의 맨 마지막 행으로 이동한다.
rows = resultSet.getRow(); ← 마지막 행의 번호를 읽어온다.
} catch(Exception e) { e.printStackTrace();
}
※ 위 ③의 메서드에서 ResultSet의 last() 메서드를 사용하기 위해서는 ResultSet이 다음과 같이 생성되어야 한다.
- 데이터베이스로 부터의 검색 결과는 ResultSet 객체에서 관리한다.
String str = "select * from employees";
ResultSet rs = stmt.executeQuery(str);
- ResultSet에서 다음과 같은 메소드를 사용해서 검색 결과를 처리한다.
검색 결과
ResultSet
rs.first() rs.next()
rs.previous()
rs.last()
- 하지만, rs에서 사용할 수 있는 메서드는 next() 메서드만 가능하다. 다른 세 개의 메서드를 모두 사용하기 위해서는 다음과 같이 ResultSet을 생성해야 한다.
PreparedStatement pstmtScroll =
con.prepareStatement(str, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet rsScroll = pstmtScroll.executeQuery(sql);
④ 데이터베이스의 검색 결과를 모델에 저장하는 메서드를 작성한다.
void 메서드이름(ResultSet resultSet) { ResultSetMetaData rsmd;
try {
rsmd = resultSet.getMetaData();//검색된 결과에서 메타데이터를 읽어온다.
cols = rsmd.getColumnCount();//메타데이터에서 검색된 데이터의 열의 갯수를 읽는다.
columnName = new String[cols];//열의 갯수만큼 열 제목을 저장할 문자열 배열 생성 for(int i = 0; i < cols; i++) {//열의 갯수만큼 열의 제목을 읽어서 배열에 저장
columnName[i] = rsmd.getColumnName(i+1);
}
data = new Object[rows][cols];
//검색된 데이터의 행과 열값을 이용해서 데이터를 저장할 2차원 배열을 생성한다.
int r = 0;//시작 행의 번호를 설정 while(resultSet.next()) {
for(int c = 0; c < cols; c++) {
data[r][c] = resultSet.getObject(c + 1);
}//검색된 한 행의 데이터를 읽어온 후 data배열에 저장한다.
r++;//행의 번호를 증가시킨다.
}
resultSet.close();
}catch(Exception e) {
e.printStackTrace();
} }
⑤ Swing의 GUI 화면에서 JTable 컴포넌트와 모델 객체를 생성하고, 데이터베이스의 검색결과를 가지고 있는 ResultSet 객체를 두 가지의 버전(next() 메서드만 사용할 수 있는 버전, next(), first(), last(), previous() 메서드를 사용할 수 있는 버전)으로 생성한다.
⑥ 위의 ⑤번 과정에서 생성한 두 개의 ResultSet 객체 중 next() 메서드만 사용할 수 있는 ResultSet 객체를 ③번 과정에서 작성한 메서드에 전달하고, next(), first(), last(), previous() 메서드를 사용할 수 있는 ResultSet 객체를
④번 과정에서 작성한 메서드에 전달한다.