/*
* TableDemo.java requires no other files.
*/
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
/**
* TableDemo is just like SimpleTableDemo, except that it
* uses a custom TableModel.
* 다음과 같은 클래스, 메소드를 사용하여 컬럼의 폭을 조정하거나, 선택영역에 대한 정보를
* 즉시 알 수 있으며, Table과 Model간 데이터 동기화 하고, 셀에 CheckBox가 나타나게 할 수 있으며,
* 테이블 셀에 ComboBox를 사용하는 예를 보이고, 셀을 더블클릭하는 경우 나타나는 CellEditor를
* 지정하는 방법을 제시한다.
* JTableHeader, AbstractTableModel, ListSelectionModel, ListSelectionListener, CellEditor,
* TableColumn, getValueAt(), setValueAt(), fireTableCellUpdated(), getColumnClass()
* isCellEditable(), setPreferredWidth()
*/
public class JTable_Oracle extends JPanel {
private boolean DEBUG = true;
JTable table;
MyTableModel model;
JTableHeader header;
public JTable_Oracle() {
super(new GridLayout(1,0));
model = new MyTableModel();
table = new JTable(model);
// 이부분은 테이블 헤더를 사용하는 방법을 제시할 뿐이며 문맥상 의미는 없다
// 테이블헤더를 이용하지 않는다면 이부분을 제거해도 됨
header = table.getTableHeader();
header.addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e) {
System.out.println("Mouse Clicked");
model.setValueAt("Hello", 0, 1);
}
});// 테이블 헤더 사용예 끝
table.setPreferredScrollableViewportSize(new Dimension(500, 70));
table.setRowSelectionAllowed( true );
table.setColumnSelectionAllowed( true );
table.setSelectionForeground( Color.white );
table.setSelectionBackground( Color.red );
// 2번째 컬럼을 위한 CellEditor를 설정한다
// 즉, 2번째 컬럼을 더블 클릭하면 해당 셀값을 편집할 수 있는 JTextField가 나타나게 한다
// CellEditor가 필요없다면 이 부분을 제거해도 됨
TableColumn lastNameColumn = table.getColumnModel().getColumn(1);
JTextField tf = new JTextField();
tf.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
Object o = e.getSource();
String value = null;
if(o instanceof JTextField) value=((JTextField)o).getText();
int row = table.getSelectedRow();
int col = table.getSelectedColumn();
System.out.println("Last Name("+row+", "+col+") changed-->"+value);
//model에게 테이블 셀의 데이터가 변경되었음 알려서 model의 setValueAt(row, col)메소드가
//실행되도록 하여 실제 모델의 데이터가 변경될 수 있게 한다.
model.fireTableCellUpdated(row, col);
}
});
lastNameColumn.setCellEditor(new DefaultCellEditor(tf));
//두번째 컬럼을 위한 CellEditor 설정 끝
//ListSelectionModel에 ListSelectionListener를 설정하여 셀 선택을 감지한다.
//셀 선택 즉시 해당 셀의 정보를 구할 필요가 없다면 이부분을 제거해도 됨
ListSelectionModel sm = table.getSelectionModel();
sm.addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent e) {
if(e.getValueIsAdjusting()) return;
ListSelectionModel lsm = (ListSelectionModel)e.getSource();
if(lsm.isSelectionEmpty()){
}else{
int minRow = lsm.getMinSelectionIndex();
int maxRow = lsm.getMaxSelectionIndex();
int minCol = table.getSelectedColumn();
int maxCol = minCol + table.getSelectedColumnCount();
System.out.println("선택범위:"+minRow+","+minCol+" ~ "+maxRow+","+(maxCol-1));
}
}
});//ListSelectionListener설정 끝
// 테이블 컬럼의 폭을 변경하는 예
// 테이블 컬럼의 폭을 변경할 필요가 없다면 이부분을 제거해도 됨
TableColumn column = null;
for(int i=0;i<table.getColumnCount();i++) {
column = table.getColumnModel().getColumn(i);
if(i==2) column.setPreferredWidth(100); // default width is 75.
else column.setPreferredWidth(50);
}// 테이블 컬럼의 폭을 변경하는 예 끝
// 테이블 셀에 ComboBox를 설정하는 방법
// ComboBox를 셀에 설정할 필요가 없다면 이 부분을 제거해도 됨
column = table.getColumnModel().getColumn(2);
JComboBox cb = new JComboBox();
cb.addItem("Snowboarding");
cb.addItem("Rowing");
cb.addItem("Chasing toddlers");
cb.addItem("Speed reading");
cb.addItem("Teaching high school");
cb.addItem("None");
column.setCellEditor(new DefaultCellEditor(cb));
// 테이블 셀에 ComboBox를 설정하는 방법 끝
// 테이블 행의 높이 설정법
table.setRowHeight(30);
// 테이블 행의 높이 설정끝
//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(table);
//Add the scroll pane to this panel.
add(scrollPane);
} // end of constructor
// JTable의 모델을 내부 클래스로 선언함
class MyTableModel extends AbstractTableModel {
private String[] columnNames;
private Object[][] data;
MyTableModel() {
// 오라클 데이터베이스에 연결하여 emp테이블의 모든 데이터를 가져 온 후에
// columnNames, data 배열에 저장한다.
java.sql.Connection con = null;
java.sql.Statement stmt = null;
java.sql.ResultSet rs = null;
String dbURL = "jdbc:oracle:thin:@localhost:1521:ORCL";
try {
Class.forName("oracle.jdbc.driver.OracleDriver");
con = java.sql.DriverManager.getConnection(dbURL, "scott", "tiger");
stmt = con.createStatement();
rs = stmt.executeQuery("select * from emp");
java.sql.ResultSetMetaData md = rs.getMetaData();
int columnCount = md.getColumnCount();
columnNames = new String[columnCount];
for(int i=0;i<columnCount;i++)
columnNames[i] = md.getColumnName(i+1);
java.util.Vector v = new java.util.Vector();
Object[] obj = null;
while(rs.next()) {
obj = new Object[columnCount];
obj[0] = new Integer(rs.getInt("EMPNO"));
obj[1] = rs.getString("ENAME");
obj[2] = rs.getString("JOB");
obj[3] = new Integer(rs.getInt("MGR"));
obj[4] = rs.getDate("HIREDATE");
obj[5] = new Integer(rs.getInt("SAL"));
obj[6] = new Integer(rs.getInt("COMM"));
obj[7] = new Integer(rs.getInt("DEPTNO"));
v.add(obj);
}
int rowCount = v.size();
data = new Object[rowCount][columnCount];
for(int i=0;i<rowCount;i++)
data[i] = (Object[]) (v.get(i));
rs.close();
stmt.close();
con.close();
}catch(Exception e){
try{
if(rs!=null) rs.close();
if(stmt!=null) stmt.close();
if(con!=null) con.close();
}catch(Exception ee) {ee.printStackTrace();}
}
}// end of constructor
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return data.length;
}
public String getColumnName(int col) {
return columnNames[col];
}
public Object getValueAt(int row, int col) {
return data[row][col];
}
/*
* JTable uses this method to determine the default renderer/
* editor for each cell. If we didn't implement this method,
* then the last column would contain text ("true"/"false"),
* rather than a check box.
*/
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
/*
* Don't need to implement this method unless your table's
* editable.
*/
public boolean isCellEditable(int row, int col) {
//Note that the data/cell address is constant,
//no matter where the cell appears onscreen.
if (col < 1) {
return false;
} else {
return true;
}
}
/*
* Don't need to implement this method unless your table's
* data can change.
* 테이블의 특정 셀을 마우스로 더블 클릭하여 데이터를 수정한 후에,
* model.fireTableCellUpdated(row, col) 메소드를 호출하면 모델의 데이터가 갱신된다.
* 즉, Table --> Model 갱신
* 반대로, 모델의 setValueAt(Object, row, col) 메소드를 직접 호출하여 모델의 데이터를
* 수정한 후에 이 메소드 안에서 fireTableCellUpdated(row, col)메소드를 호출하면
* 테이블의 해당 셀이 갱신된다. 즉, Model --> Table 갱신
*/
public void setValueAt(Object value, int row, int col) {
if (DEBUG) {
System.out.println("Setting value at " + row + "," + col
+ " to " + value
+ " (an instance of "
+ value.getClass() + ")");
}
data[row][col] = value;
// view(JTable)에 알려서 수정된 모델의 데이터를 테이블에 반영한다.
fireTableCellUpdated(row, col);
if (DEBUG) {
System.out.println("New value of data:");
printDebugData();
}
}
private void printDebugData() {
int numRows = getRowCount();
int numCols = getColumnCount();
for (int i=0; i < numRows; i++) {
System.out.print(" row " + i + ":");
for (int j=0; j < numCols; j++) {
System.out.print(" " + data[i][j]);
}
System.out.println();
}
System.out.println("--------------------------");
}
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("TableDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
JTable_Oracle newContentPane = new JTable_Oracle();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}