깃허브 주소 :
https://github.com/joeteo/bookManagerMFCandMysql.git
자료 추가 및 선택 행 삭제 기능 구현
먼저 이전에 쓴 글을 참조해서 데이터 베이스를 생성하고 MySQL 라이브러리를 포함 시킨다.
2022.07.13 - [프로그래밍/MFC (C++)] - MFC ] Visual Studio에서 MySQL 라이브러리 추가하기
2022.07.13 - [데이터베이스] - MySQL ] HeidiSQL 기본 사용법
CMysqlController .h 와 .cpp 파일을 프로젝트 파일 내 복사해 포함시킨다.
CMysqlController.h 에 define 되어 있는 부분은 본인 설정에 맞게 수정한다.
CMysqlController.cpp 의 SelectQuery 함수
bool CMysqlController::SelectQuery(char* sql, vector<Datarow*>& row)
{
MYSQL* connection = NULL, conn;
MYSQL_RES* sql_result;
MYSQL_ROW sql_row;
int query_stat;
mysql_init(&conn);
connection = mysql_real_connect(&conn, DB_HOST, DB_USER, DB_PASS,
DB_NAME, 3306, (char*) NULL, 0);
mysql_query(connection, "set session character_set_connection=euckr;");
mysql_query(connection, "set session character_set_results=euckr;");
mysql_query(connection, "set session character_set_client=euckr;");
if(connect == NULL)
{
fprintf(stderr, "Mysql connection error : %s", mysql_error(&conn));
return false;
}
query_stat = mysql_query(connection, sql);
if(query_stat != 0)
{
fprintf(stderr, "Mysql query error : %s", mysql_error(&conn));
return false;
}
sql_result = mysql_store_result(connection);
while((sql_row = mysql_fetch_row(sql_result)) != NULL)
{
row.push_back(new Datarow(sql_row[0], (LPSTR)(sql_row[1]),
(LPSTR)(sql_row[2]), (LPSTR)sql_row[3], (LPSTR)sql_row[4]));
//CString tmp;
//tmp.Format(_T("%s, %s, %s, %s, %s\n"), sql_row[0], (LPSTR)(sql_row[1]),
// (LPSTR)(sql_row[2]), (LPSTR)sql_row[3], (LPSTR)sql_row[4]);
//result += tmp;
}
mysql_free_result(sql_result);
mysql_close(connection);
return true;
}
기존에 CString 으로 받던 부분을 vector<Datarow*>& row 형태로 바꾸었다.
Datarow 클래스는 한 행을 담을 용도로 만들어 둔 CString 묶음의 클래스이고 외부에서 Datarow 포인터 타입 벡터를 넘기면 참조자 형태로 받는다.
함수를 살펴보면 mysql_store_result 함수로 sql_result 에 결과를 저장하고 다시 sql_result를 mysql_fetch_row 함수로 sql_row 에 담고 mysql_free_result로 sql_result를 해제해준다.
그래서 저 while 문안에 파싱한 데이터를 담는 부분을 넣어주면 되는데 기존의 코드를 수정해서 벡터에 동적할당 해서 담는 것으로 했다. Datarow는생성자에서 5개의 매개변수를 받아 멤버 이니셜라이저로 초기화한다.
bookManagerDlg.cpp 의 Select Query로 데이터 갱신해서 리스트 컨트롤에 새로 그리는 함수
void CbookManagerDlg::RenewListControl()
{
CMysqlController conn;
vector<Datarow*> row;
m_list.DeleteAllItems();
if (conn.SelectQuery("select * from TB_Book", row) == true)
{
}
for (size_t i = 0; i < row.size(); i++)
{
m_list.InsertItem(i, row.at(i)->getID());
m_list.SetItem(i, 1, LVIF_TEXT, row.at(i)->getBookname(), NULL, NULL, NULL, NULL);
m_list.SetItem(i, 2, LVIF_TEXT, row.at(i)->getAuthor(), NULL, NULL, NULL, NULL);
m_list.SetItem(i, 3, LVIF_TEXT, row.at(i)->getPrice(), NULL, NULL, NULL, NULL);
m_list.SetItem(i, 4, LVIF_TEXT, row.at(i)->getOther(), NULL, NULL, NULL, NULL);
}
for (size_t i = 0; i < row.size(); i++)
{
delete row.at(i);
}
}
먼저 리스트 컨트롤 .DeleteAllItems(); 으로 초기화하고 SelectQuery 함수 호출해서 전체 검색하며 벡터를 두 번째 인수로 넘기는데 받을 때 참조자로 받아서 Call-by-reference 가 된다.
벡터 사이즈만큼 for문을 돌며 리스트 컨트롤을 그리고, 벡터는 SelectQuery 함수 안에서 동적할당해준게 있으니 전부 해제해준다.
참고로 이렇게 컨트롤을 통해 값 변경을 한 것은 UpdateData(false)를 안해줘도 된다.
bookManagerDlg.cpp 의 도서 추가 버튼 클릭 이벤트 함수
void CbookManagerDlg::OnBnClickedButtonadd()
{
CAddDlg child;
child.SetParentPtr(this);
child.DoModal();
}
자식 다이얼로그를 생성하고 포인터를 넘긴 후 창을 그린다.
자식 다이얼로그 CAddDlg.cpp에서 추가버튼이 클릭되었을 때 이벤트 처리 함수
void CAddDlg::OnBnClickedButton2()
{
UpdateData(true);
CMysqlController conn;
CString temp;
temp = _T("insert into TB_Book(bookname, author, price, other) VALUES('");
temp += m_addName;
temp += _T("', '");
temp += m_addAuthor;
temp += _T("', ");
temp += m_addPrice;
if(m_addOther == _T(""))
{
temp += _T(", NULL");
}else
{
temp += _T(", '");
temp += m_addOther;
temp += _T("'");
}
temp += _T(")");
if (conn.InsertQuery(LPSTR(LPCTSTR(temp))) == true)
{
AfxMessageBox(_T("성공"));
}
else
{
AfxMessageBox(_T(" 실패"));
}
dlgPtr->RenewListControl();
EndDialog(IDCANCEL);
}
에디트 컨트롤의 값을 읽어 InsertQuery 함수로 쿼리문을 보내고 부모 포인터를 사용해 창을 갱신한 후 EndDialog() 로 창을 종료한다.
bookManagerDlg.cpp 의 도서 삭제 버튼 클릭 이벤트 함수
void CbookManagerDlg::OnBnClickedButtondelete()
{
UpdateData(true);
int clickindex = m_list.GetSelectionMark();
CMysqlController conn;
CString temp;
temp = _T("delete from TB_Book where id=");
CString idNum = m_list.GetItemText(clickindex, 0);
temp += idNum;
if (conn.InsertQuery(LPSTR(LPCTSTR(temp))) == true)
{
AfxMessageBox(_T("성공"));
RenewListControl();
}
else
{
AfxMessageBox(_T(" 실패"));
}
UpdateData(false);
}
리스트 컨트롤의 .GetSelectionMark(); 멤버함수로 현재 클릭된 인덱스 값을 확인한다. .GetItemText(clickindex, 0); 으로 첫번째 열인 id 값을 반환받는다. 쿼리문을 보내 삭제한다. (함수 이름은 InsertQuery지만 반환이 없는 쿼리문은 다 이 함수를 쓰면 된다) 성공시 리스트 컨트롤을 다시 갱신한다.
나중에 바코드 인식이랑 프린터를 추가해서 프로젝트를 확장해 보고싶어서 일단 제목에 (1)이라고 표시했다.
'프로그래밍 > MFC (C++)' 카테고리의 다른 글
MFC ] stringstream 사용하여 문자열 파싱하기 (0) | 2022.07.14 |
---|---|
MFC ] char* -> CString , CString -> char* 변환하기 (0) | 2022.07.14 |
MFC ] 시리얼 통신으로 LED 제어하기 (2) (0) | 2022.07.13 |
MFC ] DoModal() 창 닫기, 강제 종료 (0) | 2022.07.13 |
MFC ] 리스트 컨트롤에서 클릭된 아이템 값 가져오기 (0) | 2022.07.13 |