
//윈도우 창을 닫는 순간 호출되는 메서드.
//연결되어있던 자원을 해제하는 용도로 적합
//여기서 데이터베이스 끊기
@Override
public void windowClosing(WindowEvent e) {
System.out.println("windowClosing()");
//데이터 베이스 접속 끊기
try {
con.close();
} catch (SQLException e1) {
e1.printStackTrace();
}
//프로세스 종료 (실행중인 프로그램 = 프로세스)
System.exit(0);
}
JTable에서 특정 행을 클릭했을 때,
그에 해당하는 회원의 정보를 상세히 조회해서 화면에 표시
public void select(int member4_id) {
String sql="SELECT * FROM member4 WHERE member4_id=" + member4_id;
// 클릭하는 사람이 pk 결정 → 매개변수
System.out.println(sql); // 디버깅용 (검증)
//쿼리문이 검증되었으므로 JDBC통해 네트워크로 전송하기
PreparedStatement pstmt=null;
ResultSet rs=null;
try {
pstmt=con.prepareStatement(sql);// 쿼리문 객체 생성
rs=pstmt.executeQuery(); // 레코드 결과 반환(1건)
//화면에 출력
if(rs.next()) {
/* 실행부가 꼭대기에 가있으니까 일단 한 줄 전진
+레코드가 있다면(회원이 있을때만) 아래의 코드 수행 */
la_value.setText(rs.getString("id"));
txt_name.setText(rs.getString("name"));
txt_tel.setText(rs.getString("tel"));
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(rs!=null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(pstmt!=null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
MouseListener의 메서드 재정의
@Override
public void mouseReleased(MouseEvent e) {
//테이블을 마우스로 클릭시, JTable의 메서드 중 유저가 선택한 row, col정보를 반환하는 메서드
int row=table.getSelectedRow(); // 클릭한 행 (층)
int col=table.getSelectedColumn(); // 클릭한 열 (호수)
// 방법 1️⃣ 모델에서 직접 배열을 꺼내 접근
String[][] rows=((MyModel)model).rows;
System.out.println("선택한 레코드의 PK: "+rows[row][3]);
// "4"로 나오니까 ""를 날려야함 (Wrapper클래스 사용)
//사용자 상세정보 보기
member4_id=Integer.parseInt(rows[row][3]); // 멤버변수에 보관 (int member4_id;로)
select(member4_id);
}
방법 2️⃣ JTable의 getValueAt메서드 사용 (반환형 Object)
String value=(String)table.getValueAt(row, col);
// 이 경우는 String으로 다운캐스팅
System.out.println(value);
✅ Model도 수정해야함
MyModel클래스의 멤버변수에서
회원정보를 이차원배열로 나타냈었음
String[][] rows = new String[0][4]; // ID, Name, Tel, member4_id
// 데이터 수용 능력은 PK포함해서 컬럼수 4로 늘리기
String[] columns = { "ID", "Name", "Tel", "PK" }; // 보기에는 PK숨겨도 됨
⭐ PK는 UI에 보여주지 않고, 내부에서만 보관해서 조작 시 필요한 값으로 활용
선택한 회원의 정보를 수정
member[]MouseListener 메서드 中 mouseReleased 재정의하는 곳에서
현재 유저가 선택한 한 회원의 모든 정보를 배열에 보관해놓기
➡ String[] member; 멤버변수로 빼놓음
@Override
public void mouseReleased(MouseEvent e) {
int row=table.getSelectedRow(); // 클릭한 행 (층)
String[][] rows=((MyModel)model).rows;
member=rows[row]; //멤버변수로 보관
}
edit()메서드 정의 public void edit(String[] member) { //한 사람에 대한 정보를 담는 배열을 매개변수로 선언
//이 메서드를 호출하는 자는 한 사람에 대한 정보를 '배열'로 담아서 전달
String sql="UPDATE member4 SET id='"+member[0]+"', name='"+member[1]+"', tel='"+member[2]+"'";
sql+=" WHERE member4_id="+member[3];
System.out.println(sql); // 디버깅용 (검증)
PreparedStatement pstmt=null;
try {
pstmt=con.prepareStatement(sql);
int result = pstmt.executeUpdate(); //update DML 수행
if(result>0) {
JOptionPane.showMessageDialog(this, "수정성공");
selectAll(); // MyModel의 이차원배열 갱신 및 테이블 updateUI()포함
} else {
JOptionPane.showMessageDialog(this, "변경사항이 반영되지 않았습니다.");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(pstmt!=null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
ActionListener 메서드 재정의하는 곳에서 '수정'버튼에 조건주기
@Override
public void actionPerformed(ActionEvent e) {
Object obj=e.getSource();
if(obj==bt_edit) { // 수정버튼 눌렀을 때
int result=JOptionPane.showConfirmDialog(this, "수정하시겠어요?");
//alert창의 부모 컴포넌트는 this윈도우창
if(result==JOptionPane.OK_OPTION) {
// ✅ edit()메서드 호출 전에 배열에 우리가 입력한 데이터를 반영하여 조작하기
member[1]=txt_name.getText();
member[2]=txt_tel.getText();
edit(member);//조작한 일차원 배열 member 넘기기
/* selectAll();
여기서 호출하는 건 좋은 방향.
일단 성공했을 때 나올 수 있게 지금은 edit()메서드에서 호출하자 */
}
}
선택한 회원의 정보를 수정
여기서 나오는 member는 멤버변수에 보관된 일차원배열
String[] member (한 회원의 모든 정보)
delete()메서드 정의 public void delete(String[] member) {
String sql="DELETE FROM member4 WHERE member4_id="+member[3];
System.out.println(sql); // 디버깅용 (검증)
PreparedStatement pstmt=null;
try {
pstmt=con.prepareStatement(sql);
int result=pstmt.executeUpdate(); // delete DML 수행
if(result>0) {
JOptionPane.showMessageDialog(this, "삭제 성공");
selectAll(); // MyModel의 이차원배열 갱신 및 테이블 updateUI()포함
} else {
JOptionPane.showMessageDialog(this, "삭제되지 않았습니다.");
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(pstmt!=null) {
try {
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
ActionListener 메서드 재정의하는 곳에서 '삭제'버튼에 조건주기
@Override
public void actionPerformed(ActionEvent e) {
Object obj=e.getSource();
if(obj==bt_del) { // 삭제버튼 눌렀을 때
int result=JOptionPane.showConfirmDialog(this, "삭제하시겠어요?");
//alert창의 부모 컴포넌트는 this윈도우창
if(result==JOptionPane.OK_OPTION) {
delete(member);
/* selectAll();
여기서 호출하는 건 좋은 방향.
일단 성공했을 때 나올 수 있게 지금은 delete()메서드에서 호출하자 */
}
}
pstmt.executeUpdate()의 반환값pstmt = con.prepareStatement(sql);
int result = pstmt.executeUpdate(); // insert, update, delete 전용
반환값 (result) | 의미 |
|---|---|
> 0 | 정상 수행됨. 몇 건이 실제로 insert/update/delete 되었는지 나타냄 |
0 | SQL 실행은 됐지만, 영향을 받은 레코드가 없음 (즉, 실패로 간주) |
예외 발생 | 문법 오류, 연결 오류 등으로 인해 실행 자체가 안 됨 → SQLException 발생 |
| 유형 | 메서드 | 설명 |
|---|---|---|
INSERT, UPDATE, DELETE | executeUpdate() | 영향받은 행 수(int) 반환 |
SELECT | executeQuery() | ResultSet 반환 |
TableModel인 MyModel을 살펴보자
public class MyModel extends AbstractTableModel implements TableModelListener{...}
isCellEditable()조건제한//AbstractTableModel의 메서드 재정의 (선택사항)
@Override
//지정한 셀을 편집할 수 있게함
public boolean isCellEditable(int row, int col) {
System.out.println(row+"행 , "+col+"열은 수정 가능합니다.");
return (col==0)? false : true;
// 아이디만 수정 불가하게 (0열만 수정불가)
// return true;면 전부 수정 가능
}
0열(ID)만 수정 못하게 제한 → 일반적으로 식별자 필드 수정 방지
getValueAt(), setValueAt()✅ getValueAt() : getRowCount() × getColumnCount() 수만큼 getValueAt()을 반복 호출하면서 표를 이루는 각 셀 (행,열) 좌표마다 return 값이 들어감
//AbstractTableModel의 메서드 재정의 (필수)
@Override
public Object getValueAt(int row, int col) {
//System.out.println("row="+row+", column="+col);
return rows[row][col]; // 셀에 들어가는 값
}
✅ setValueAt() : 값 변경 가능한 setter
유저가 테이블 셀에서 아무리 데이터를 편집한다 하더라도
현재 모델이 보유한 이차원 배열을 수정하지 않는 한,
값 수정 반영이 되지 않음
셀에서 원하는 데이터 value를 입력하고 엔터를 치는 순간,
해당 셀의 row, col, value값이 전달됨
@Override // 필수아님
public void setValueAt(Object value, int row, int col) { // (무엇을, 몇 층, 몇 호수에)
System.out.println("당신은 "+row+", "+col+"의 데이터를 "+value+"(으)로 변경하고 싶나요?");
// 내부 배열 데이터 변경
rows[row][col]=(String)value;
// 프레임이 보유한 edit()를 호출하려면? (Call by reference 사용)
memberRegist.edit(rows[row]); // DB에도 반영
}
유저가 셀을 클릭하고 값을 수정함
↓
엔터 입력
↓
JTable이 자동으로
setValueAt(value, row, col)호출
↓
내부 배열 변경 (배열rows[row][col]값 수정)
↓
edit(member[])→ DB UPDATE
↓
fireTableCellUpdated() ← (AbstractTableModel이 자동 호출)
↓
tableChanged(TableModelEvent e)
edit())보유한
edit()를 호출하려면?
생성자 주입을 이용하자
public class MyModel extends AbstractTableModel implements TableModelListener {
MemberRegist memberRegist;
// 제어가 필요해서 주소값 보유 위한 has a 관계 선언
public MyModel(MemberRegist memberRegist) {
this.memberRegist = memberRegist;
✅ JTable정의할 때 MemberRegist의 인스턴스 주소 받기
table=new JTable(model=new MyModel(this))
➡ 그래야 MemberRegist클래스에 있는 edit()사용하여 DB반영 가능
memberRegist.edit(rows[row])
<참고>
TableModel model = new MyModel(); //자동 업캐스팅 JTable table = new JTable(model); //위의 두 줄을 한 줄로 줄인 버전 ⬇ table=new JTable(model=new MyModel());
// 모델 자신을 리스너로 등록
this.addTableModelListener(this);
}
// TableModelListener 인터페이스 구현시 필수 재정의
@Override
public void tableChanged(TableModelEvent e) {
/* 테이블의 셀 값이 **변경된 후**,
모델에 의해 이벤트가 발생하면 이 메서드가 호출됨 */
System.out.println("편집했어?");
}
}
setValueAt()에서 모든 걸 처리하므로,TableModelListener의 tableChanged()는 사실상 “옵션 기능”