본문 바로가기
프로그래밍/MFC (C++)

MFC ] MySQL 을 활용한 도서 관리 프로그램 (1)

by eteo 2022. 7. 14.

깃허브 주소 :

https://github.com/joeteo/bookManagerMFCandMysql.git

 

GitHub - joeteo/bookManagerMFCandMysql

Contribute to joeteo/bookManagerMFCandMysql development by creating an account on GitHub.

github.com

 

 

 

 

자료 추가 및 선택 행 삭제 기능 구현

 

 

먼저 이전에 쓴 글을 참조해서 데이터 베이스를 생성하고 MySQL 라이브러리를 포함 시킨다.

2022.07.13 - [프로그래밍/MFC (C++)] - MFC ] Visual Studio에서 MySQL 라이브러리 추가하기

2022.07.13 - [데이터베이스] - MySQL ] HeidiSQL 기본 사용법

 

 

 

 

CMysqlController .h 와 .cpp 파일을 프로젝트 파일 내 복사해 포함시킨다.

 

CMysqlController.cpp
0.00MB
CMysqlController.h
0.00MB

 

 

 

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)이라고 표시했다.