JTable : 회원가입폼 (2)

heeezni·2025년 5월 28일
post-thumbnail

회원가입폼(INSERT & SELECT * 까지)

프로세스 끊기

	//윈도우 창을 닫는 순간 호출되는 메서드.
	//연결되어있던 자원을 해제하는 용도로 적합
	//여기서  데이터베이스 끊기
	@Override
	public void windowClosing(WindowEvent e) {
		System.out.println("windowClosing()");
		//데이터 베이스 접속 끊기
		try {
			con.close();
		} catch (SQLException e1) {
			e1.printStackTrace();
		}
		//프로세스 종료 (실행중인 프로그램 = 프로세스)
		System.exit(0);
	}

PK로 정보 1건만 SELECT

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에 보여주지 않고, 내부에서만 보관해서 조작 시 필요한 값으로 활용


Table의 정보 수정 (UPDATE)

선택한 회원의 정보를 수정

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()메서드에서 호출하자 */
			}
        }

Table의 정보 삭제 (DELETE)

선택한 회원의 정보를 수정

여기서 나오는 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()메서드에서 호출하자 */
			}
        }

DML (insert, update, delete) 전용 pstmt.executeUpdate()의 반환값

pstmt = con.prepareStatement(sql);
int result = pstmt.executeUpdate();  // insert, update, delete 전용

반환값: ⭐ 영향을 받은 레코드 수 (int) ⭐

반환값 (result)의미
> 0정상 수행됨. 몇 건이 실제로 insert/update/delete 되었는지 나타냄
0SQL 실행은 됐지만, 영향을 받은 레코드가 없음 (즉, 실패로 간주)
예외 발생문법 오류, 연결 오류 등으로 인해 실행 자체가 안 됨 → SQLException 발생

🔍 DML vs SELECT 구분

유형메서드설명
INSERT, UPDATE, DELETEexecuteUpdate()영향받은 행 수(int) 반환
SELECTexecuteQuery()ResultSet 반환

TableModel

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)


Call by reference (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());

TableModelListener

  • 사용목적 : 편집이 일어났는지 감지 (log 용)
        // 모델 자신을 리스너로 등록
        this.addTableModelListener(this);
    }

    // TableModelListener 인터페이스 구현시 필수 재정의
    @Override
    public void tableChanged(TableModelEvent e) {
    /* 테이블의 셀 값이 **변경된 후**, 
    모델에 의해 이벤트가 발생하면 이 메서드가 호출됨 */
        System.out.println("편집했어?");
    }
}
  • 참고로
    현재 예제에서는 사용자가 값을 바꾸면 setValueAt()에서 모든 걸 처리하므로,
    TableModelListenertableChanged()는 사실상 “옵션 기능”
profile
아이들의 가능성을 믿었던 마음 그대로, 이제는 나의 가능성을 믿고 나아가는 중입니다.🌱

0개의 댓글