Java 프레임워크 만들기 - JSP

자바 데이터베이스 커넥션 풀 매니저 만들기 2 - Java Database Connection Pool Manager, 이클립스(Eclipse)

carrotweb 2021. 6. 15. 22:37
728x90
반응형

데이터베이스 커넥션 풀을 적용하기 전에 기존과의 성능 차이를 확인하기 위해 쿼리를 처리하는 메소드에서 데이터베이스 접속 전과 종료 후에 소요시간을 계산하여 출력되게 하겠습니다.

long startTime = System.currentTimeMillis();
{데이터베이스 쿼리문 실행 블럭}
long endTime = System.currentTimeMillis();
System.out.println((endTime - startTime) + "ms 소요됨");


4. "test2" 프로젝트의 "Java Resources"에서 "src/main/java"의 "com.home.project.test2.dao.TestLoginDaoImpl.java"에 "selectMember()"메소드 함수에서 "testDatabaseManager.getConnection()"메소드 전과 "testDatabaseManager.closeConnection()"메소드 후에 추가합니다.


5. "Servers"탭에서 "tomcat8"를 선택하고 "start"버튼(start the server)을 클릭합니다. 웹 브라우저에서 "http://localhost:8080/test2/testform.do"를 입력합니다. 아이디에는 "testid"를 패스워드에는 "testpwd"를 입력하고 "로그인"버튼을 클릭합니다.
"Console"탭을 보면 데이터베이스 쿼리 처리 시간이 출력됩니다.

[Console]

데이터베이스에 연결됨
PreparedStatement를 생성함
Query[sql : 'SELECT MBR_ID, MBR_PWD, MBR_PWD_SALT, MBR_NM FROM MBR_ACCOUNT_TB WHERE MBR_ID=?', parameters : ['testid']]를 실행함
PreparedStatement를 종료함
데이터베이스에서 연결을 종료함
283ms 소요됨


6. "test2" 프로젝트의 "Java Resources/src/main/java"에서 "com.home.project.test2.listener.TestServletContextListener"의 "contextInitialized()"메소드에 있는 "databaseMappingMap"의 인자를 "TestDatabaseManager"에서

// DBId-Database 맵
Map<String, TestDatabaseManager> databaseMappingMap = new HashMap<String, TestDatabaseManager>();

String databaseManagers = sce.getServletContext().getInitParameter("DatabaseManager");
if (databaseManagers != null && !databaseManagers.trim().isEmpty()) {
	String[] databaseManagerPaths = databaseManagers.trim().split("\n|,|;|\\s+");
	for (String databaseManagerPath : databaseManagerPaths) {
		databaseManagerPath = databaseManagerPath.trim();
		if (!databaseManagerPath.isEmpty()) {
			TestDatabaseManager testDatabaseManager = new TestDatabaseManager(databaseManagerPath);
			if (testDatabaseManager.isInitDriver()) {
				databaseMappingMap.put(testDatabaseManager.getId(), testDatabaseManager);
			}
		}
	}
}

"TestDatabasePoolManager"로 수정합니다. 그리고 "isInitDriver()"메소드를 "isInitDatabasePool()"메소드로 수정합니다.

// DBId-Database 맵
Map<String, TestDatabasePoolManager> databaseMappingMap = new HashMap<String, TestDatabasePoolManager>();

String databaseManagers = sce.getServletContext().getInitParameter("DatabaseManager");
if (databaseManagers != null && !databaseManagers.trim().isEmpty()) {
	String[] databaseManagerPaths = databaseManagers.trim().split("\n|,|;|\\s+");
	for (String databaseManagerPath : databaseManagerPaths) {
		databaseManagerPath = databaseManagerPath.trim();
		if (!databaseManagerPath.isEmpty()) {
			TestDatabasePoolManager testDatabaseManager = new TestDatabasePoolManager(databaseManagerPath);
			if (testDatabaseManager.isInitDatabasePool()) {
				databaseMappingMap.put(testDatabaseManager.getId(), testDatabaseManager);
			}
		}
	}
}


"contextInitialized()"메소드에서 DAO를 "databaseMappingMap"의 형 변환을 "TestDatabaseManager"에서

if (repositoryeInstance != null) {
	Field[] Fields = findClass.getDeclaredFields();
	for (Field field : Fields) {
		TestAnnAutowired testAnnAutowired = field.getAnnotation(TestAnnAutowired.class);
		TestAnnQualifier testAnnQualifier = field.getAnnotation(TestAnnQualifier.class);
		if (testAnnAutowired != null && testAnnQualifier != null) {
			System.out.println("[Repository Field] " + field.getType().getName() + " 타입의 " + field.getName() + " 맴버 필드을 찾음");
			TestDatabaseManager testDatabaseManager = (TestDatabaseManager) databaseMappingMap.get(testAnnQualifier.value());
			if (testDatabaseManager != null) {
				field.setAccessible(true);
				try {
					field.set(repositoryeInstance, testDatabaseManager);
					System.out.println("[Repository Field] " + field.getName() + "에 의존성 주입을 처리함");
				} catch (IllegalArgumentException | IllegalAccessException e) {
					e.printStackTrace();
					System.out.println("[Repository Field] " + field.getName() + "에 의존성 주입을 처리하지 못함");
				}
			}
		}
	}
}

"TestDatabasePoolManager"로 수정합니다.

if (repositoryeInstance != null) {
	Field[] Fields = findClass.getDeclaredFields();
	for (Field field : Fields) {
		TestAnnAutowired testAnnAutowired = field.getAnnotation(TestAnnAutowired.class);
		TestAnnQualifier testAnnQualifier = field.getAnnotation(TestAnnQualifier.class);
		if (testAnnAutowired != null && testAnnQualifier != null) {
			System.out.println("[Repository Field] " + field.getType().getName() + " 타입의 " + field.getName() + " 맴버 필드을 찾음");
			TestDatabasePoolManager testDatabaseManager = (TestDatabasePoolManager) databaseMappingMap.get(testAnnQualifier.value());
			if (testDatabaseManager != null) {
				field.setAccessible(true);
				try {
					field.set(repositoryeInstance, testDatabaseManager);
					System.out.println("[Repository Field] " + field.getName() + "에 의존성 주입을 처리함");
				} catch (IllegalArgumentException | IllegalAccessException e) {
					e.printStackTrace();
					System.out.println("[Repository Field] " + field.getName() + "에 의존성 주입을 처리하지 못함");
				}
			}
		}
	}
}


"contextInitialized()"메소드 끝에 "databaseMappingMap"를 속성에 추가합니다.

sce.getServletContext().setAttribute("DatabaseMappingMap", databaseMappingMap);


"test2" 프로젝트의 "Java Resources/src/main/java"에서 "com.home.project.test2.listener.TestServletContextListener"의 "contextDestroyed()"메소드에서 생성된 데이터베이스 커넥션 풀이 파괴되게 합니다.

public void contextDestroyed(ServletContextEvent sce)  {
	System.out.println("ServletContext 파괴 실행");
	@SuppressWarnings("unchecked")
	Map<String, TestDatabasePoolManager> databaseMappingMap = (Map<String, TestDatabasePoolManager>)sce.getServletContext().getAttribute("DatabaseMappingMap");
	Iterator<String> keyIter = databaseMappingMap.keySet().iterator();
	while (keyIter.hasNext()) {
		String dbId = (String) keyIter.next();
		TestDatabasePoolManager testDatabaseManager = databaseMappingMap.get(dbId);
		testDatabaseManager.destroyDatabasePool();
	}
}

 


7. "test2" 프로젝트의 "Java Resources/src/main/java"에서 "com.home.project.test2.dao.TestLoginDaoImpl.java"의 멤버 필드 형을 "TestDatabaseManager"에서

@TestAnnAutowired
@TestAnnQualifier("databaseMaria")
private TestDatabaseManager testDatabaseManager;

"TestDatabasePoolManager"로 수정합니다.

@TestAnnAutowired
@TestAnnQualifier("databaseMaria")
private TestDatabasePoolManager testDatabaseManager;


8. "Servers"탭에서 "tomcat8"를 선택하고 "start"버튼(start the server)을 클릭합니다.
"Console"탭을 보면 데이터베이스 커넥션 풀이 생성된 것을 확인할 수 있습니다.

[Console]

데이터베이스 프로퍼티를 읽음
데이터베이스 드라이버(class org.mariadb.jdbc.Driver)가 로딩됨
데이터베이스 커넥션 풀을 생성 시작
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[1]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[2]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[3]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[4]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[5]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[6]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[7]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[8]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[9]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[10]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[11]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[12]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[13]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[14]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[15]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[16]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[17]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[18]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[19]를 생성함
데이터베이스에 연결됨
데이터베이스와 연결된 커넥션 객체[20]를 생성함
데이터베이스 커넥션 풀을 생성함
[Repository] com.home.project.test2.dao.TestLoginDaoImpl 인스턴스를 생성함
[Repository Field] com.home.project.test2.database.TestDatabasePoolManager 타입의 testDatabaseManager 맴버 필드을 찾음
[Repository Field] testDatabaseManager에 의존성 주입을 처리함


웹 브라우저에서 "http://localhost:8080/test2/testform.do"를 입력합니다. 아이디에는 "testid"를 패스워드에는 "testpwd"를 입력하고 "로그인"버튼을 클릭합니다.
"Console"탭을 보면 데이터베이스 커넥션 풀에서 커넥션 객체를 가져오고 쿼리한 후 반환하는 것을 확인 할 수 있습니다.

[Console]

데이터베이스 커넥션 객체[com.home.project.test2.database.TestDatabaseConnection@3d297c93]를 가져옴
PreparedStatement를 생성함
Query[sql : 'SELECT MBR_ID, MBR_PWD, MBR_PWD_SALT, MBR_NM FROM MBR_ACCOUNT_TB WHERE MBR_ID=?', parameters : ['testid']]를 실행함
PreparedStatement를 종료함
데이터베이스 커넥션 객체[com.home.project.test2.database.TestDatabaseConnection@3d297c93]를 반환함
1ms 소요됨

쿼리 처리 시간이 이전과 대비해 "283ms"에서 "1ms"으로 상당히 빨라진 것을 확인할 수 있습니다.

이처럼 데이터베이스 커넥션 풀을 이용하면 수 많은 데이터베이스 연결과 종료 시간을 줄일 수 있어 시스템의 성능을 향상시킬 수 있습니다.

참고로,
"Tomcat"에서 "stop"버튼(stop the server)을 클릭하면 "contextDestroyed"에 적용된 메시지가 나타나지 않습니다. 그렇지만 내부적으로 정상적으로 동작됩니다.

이클립스에서 "contextDestroyed"에 적용된 메시지를 확인하고 싶으시면 "contextDestroyed"에서 간단하게 스페이스를 추가하여 저장하시면 reload로면서 "contextDestroyed"에서 적용된 메시지가 나타날 겁니다.
"Tomcat"의 "config"의 "server.xml"의 "Context"에 "reloadable=true"이 설정되어 있어야 합니다.

[Console]

ServletContext 파괴 실행
데이터베이스와 연결을 종료하고 커넥션 객체[1]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[2]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[3]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[4]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[5]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[6]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[7]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[8]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[9]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[10]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[11]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[12]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[13]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[14]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[15]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[16]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[17]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[18]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[19]를 삭제함
데이터베이스와 연결을 종료하고 커넥션 객체[20]를 삭제함
데이터베이스 커넥션 풀을 파괴함

 

728x90
반응형