Java 프레임워크 만들기 - JSP

자바 데이터베이스 매니저 만들기(JDBC 드라이버) 1 - Java Database Manager(JDBC Driver, Statement), 이클립스(Eclipse)

carrotweb 2021. 5. 31. 12:04
728x90
반응형

JDBC 드라이버들을 이용하여 데이터베이스와 연결할 수 있게 데이터베이스 매니저를 만들겠습니다.

이번에는 단일 또는 복수개의 데이터베이스 프로퍼티를 통해 다양한 데이터베이스와 연결할 수 있게 구성하고 쿼리를 요청할 때마 데이터베이스에 접속하고 쿼리를 실행하고 접속을 종료하는 메서드와 쿼리만 실행하는 메서드를 만들어 트랜잭션도 처리할 수 있게 개발하겠습니다.

다음에는 컨넥션 풀(Connection Pool)을 이용하는 방식과 JNDI(Java Naming and Directory Interface) API를 이용하는 방식을 통해 서블릿(Servlet)의 데이터소스(DataSource)에 대해 알아보겠습니다.

데이터소스(DataSource)란 서버로부터 데이터베이스에 연결을 하기 위해 사용되는 이름으로 WAS(Web Application Server)에서 관리되는 데이터베이스 풀의 이름입니다.

1. "test2" 프로젝트의 "Java Resources"에 "src/main/resources"에서 "/com/home/project/test2/config"에 있는 "database-maria.properties"파일을 생성합니다.

id=databaseMaria
driverClassName=org.mariadb.jdbc.Driver
url=jdbc:mariadb://localhost:3306/test
username=root
password=password

"password"는 마리아디비(MariaDB) 설치 시 입력한 패스워드를 입력하시면 됩니다.

프로퍼티(properties) 생성 방법은 (https://carrotweb.tistory.com/45)를 참고하시기 바랍니다.

2. "web.xml"에 "context-param"으로 마리아디비 데이터베이스 정보가 저장된 데이터베이스 프로퍼티 경로를 설정합니다.

<context-param>
	<param-name>DatabaseManager</param-name>
	<param-value>/com/home/project/test2/config/database-maria.properties</param-value>
</context-param>

 

3. "test2" 프로젝트의 "Java Resources"에서 "src/main/java"의 "com.home.project.test2"를 선택한 후 오른쪽 버튼을 클릭하여 컨텍스트 메뉴 [New > Package]를 클릭하여 "Name"의 기존 패키지명에 ".database"를 추가하고 "Finish"버튼을 클릭합니다. 추가된 "com.hom.project.test2.database"에서 오른쪽 버튼을 클릭하여 컨텍스트 메뉴 [New > Class]를 클릭하고 "Constructors from superclass"를 선택하여 "TestDatabaseManager.java"를 생성합니다.

 

데이터베이스 프로퍼티(properties) 파일을 읽어 저장하기 위해 프로퍼티 필드를 추가합니다. 그리고 프로퍼티로부터 읽어 들인 정보로 데이터베이스 드라이버가 로딩되었는지 확인하기 위해 부울 값 필드를 추가합니다.

/**
 * 데이터베이스 프로퍼티
 */
private Properties databaseInfo = new Properties();

/**
 * 데이터베이스 드라이버 초기화 여부
 */
private boolean initDriver = false;

/**
 * 데이터베이스 드라이버 초기화 여부를 가져옵니다.
 * @return 데이터베이스 드라이버 초기화 여부
 */
public boolean isInitDriver() {
	return initDriver;
}

 

데이터베이스 프로퍼티(properties) 파일을 읽어 프로퍼티로 로드하게 메서드를 추가합니다.

/**
 * 데이터베이스 매니저 프로퍼티를 로드하고 검증합니다.
 * @param databaseManagerPath 데이터베이스 매니저 프로퍼티 경로
 * @return 프로퍼티 로드 여부
 */
private boolean loadProperties(String databaseManagerPath) {
	boolean result = true;

	InputStream inputStream = getClass().getResourceAsStream(databaseManagerPath);
	if (inputStream != null) {
		try {
			databaseInfo.load(inputStream);
			System.out.println("데이터베이스 프로퍼티를 읽음");

			String id = databaseInfo.getProperty("id");
			if (id == null || id.trim().isEmpty()) {
				System.out.println("데이터베이스 프로퍼티에 id가 없음");
				result = false;
			}
			String driverClassName = databaseInfo.getProperty("driverClassName");
			if (driverClassName == null || driverClassName.trim().isEmpty()) {
				System.out.println("데이터베이스 프로퍼티에 driverClassName이 없음");
				result = false;
			}
			String url = databaseInfo.getProperty("url");
			if (url == null || url.trim().isEmpty()) {
				System.out.println("데이터베이스 프로퍼티에 url이 없음");
				result = false;
			}
			String username = databaseInfo.getProperty("username");
			if (username == null || username.trim().isEmpty()) {
				System.out.println("데이터베이스 프로퍼티에 username이 없음");
				result = false;
			}
			String password = databaseInfo.getProperty("password");
			if (password == null || password.trim().isEmpty()) {
				System.out.println("데이터베이스 프로퍼티에 password가 없음");
				result = false;
			}

			if (!result) {
				System.out.println("데이터베이스 프로퍼티 정보가 정확하지 않음");
			}
		} catch (IOException e) {
			System.out.println("데이터베이스 프로퍼티를 읽지 못함");
			result = false;
			e.printStackTrace();
		} finally {
			try {
				inputStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	} else {
		System.out.println("데이터베이스 프로퍼티를 읽지 못함");
		result = false;
	}
	return result;
}

 

프로퍼티에서 드라이버(driver) 클래스 명을 가져와 JDBC 드라이버(driver) 클래스를 로드하게 메서드를 추가합니다.

/**
 * 데이터베이스 드라이버를 로드합니다.
 */
private void initDriver() {
	String driverClassName = databaseInfo.getProperty("driverClassName");
	try {
		Class<?> dbDriver = Class.forName(driverClassName);
		System.out.println("데이터베이스 드라이버(" + dbDriver.toString() + ")가 로딩됨");
		initDriver = true;
	} catch (ClassNotFoundException e) {
		System.out.println("데이터베이스 드라이버가 로딩되지 않음");
		e.printStackTrace();
	}
}

 

 

클래스 생성자를 통해 데이터베이스 프로퍼티 파일을 읽고 데이터베이스 드라이버(driver)를 로드하게 생성자를 추가합니다.

/**
 * 생성자
 * @param databaseManagerPath 데이터베이스 매니저 프로퍼티 경로
 */
public TestDatabaseManager(String databaseManagerPath) {
	if (loadProperties(databaseManagerPath)) {
		initDriver();
	}
}

 

"DriverManager"의 "getConnection"메서드를 이용하여 데이터베이스(Database)에 접속하게 메소드를 추가합니다. 쿼리(query) 실행 전에 호출됩니다.

/**
 * 데이터베이스에 연결합니다. 
 * @return 데이터베이스 연결 객체
 */
public Connection getConnection() {
	if (!initDriver) {
		System.out.println("데이터베이스 드라이버가 로딩되지 않음");
		return null;
	}

	Connection connection = null;
	String url = databaseInfo.getProperty("url");
	String username = databaseInfo.getProperty("username");
	String password = databaseInfo.getProperty("password");
	try {
		connection =  DriverManager.getConnection(url, username, password);
		System.out.println("데이터베이스에 연결됨");
	} catch (SQLException e) {
		System.out.println("데이터베이스에 연결하지 못함");
		e.printStackTrace();
	}
	return connection;
}

 

데이터베이스(Database)와 접속을 종료하게 메서드를 추가합니다. 쿼리(query) 실행 후에 호출됩니다.

/**
 * 데이터베이스에 연결을 종려합니다.
 * @param connection 데이터베이스 연결 객체
 */
public void closeConnection(Connection connection) {
	if (connection != null) {
		try {
			connection.close();
			System.out.println("데이터베이스에서 연결을 종료함");
		} catch (SQLException e) {
			System.out.println("데이터베이스에서 연결을 종료하지 못함");
			e.printStackTrace();
		}
	}
}

 

드라이버(driver) 로드와 연결, 종료에 대해서는 이전 자바 마리아디비 연결 (JDBC 드라이버)(https://carrotweb.tistory.com/69)에서 설명했습니다. 참고하시기 바랍니다.

우선 정적인 쿼리(query)문을 실행할 수 있게 "executeQuery"메서드와 "executeUpdate"메서드를 만들겠습니다. 정적인 쿼리(query)문은 "SQL Injection"으로 보안상 취약점이 있어 사용하지 않습니다. 테스트를 위해서 사용해 보겠습니다.

"executeQuery"메소드는 "SELECT"쿼리문을 실행하기 위해 "createStatement"메서드를 이용하여 쿼리 구문을 생성하고 "executeQuery"메서드를 통해 실행되게 합니다. 리턴 값은 "ResultSet"객체로 쿼리 결과가 리턴됩니다.

/**
 * 쿼리문(SELECT)을 실행한다.
 * @param query 쿼리 문자열
 * @return ResultSet 객체
 */
public ResultSet executeQuery(String query) {
	ResultSet resultSet = null;
	Connection connection = getConnection();
	if (connection != null) {
		resultSet = executeQuery(connection, query);
		closeConnection(connection);
	}
	return resultSet;
}

/**
 * 쿼리문(SELECT)을 실행한다.
 * @param connection 데이터베이스 연결 객체
 * @param query 쿼리 문자열
 * @return ResultSet 객체
 */
public ResultSet executeQuery(Connection connection, String query) {
	if (!initDriver) {
		System.out.println("데이터베이스 드라이버가 로딩되지 않음");
		return null;
	}
	if (connection == null) {
		System.out.println("데이터베이스가 연결되지 않음");
		return null;
	}
	if (query == null || query.trim().isEmpty()) {
		System.out.println("쿼리가 빈 문자열임");
		return null;
	}

	ResultSet resultSet = null;
	Statement statement = null;

	try {
		statement = connection.createStatement();
		System.out.println("Statement를 생성함");
	} catch (SQLException e) {
		System.out.println("Statement를 생성하지 못함");
		e.printStackTrace();
	}

	if (statement != null) {
		try {
			resultSet = statement.executeQuery(query);
			System.out.println("Query[" + query + "]를 실행함");
		} catch (SQLException e) {
			System.out.println("Query[" + query + "]를 실행하지 못함");
			e.printStackTrace();
		} finally {
			try {
				statement.close();
				System.out.println("Statement를 종료함");
			} catch (SQLException e) {
				System.out.println("Statement를 종료하지 못함");
				e.printStackTrace();
			}
		}
	}

	return resultSet;
}

 

"executeUpdate"메서드는 "UPDATE, INSERT, DELETE"쿼리문을 실행하기 위해 "createStatement"메서드를 이용하여 쿼리 구문을 생성하고 "executeUpdate"메서드를 통해 실행되게 합니다. 리턴값은 처리된 행 수입니다.

/**
 * 쿼리문(UPDATE, INSERT, DELETE)을 실행한다.
 * @param query 쿼리 문자열
 * @return 처리된 행 수(-1: 데이터베이스 연결 오류, 0: 처리된 행 없음, 1이상: 처리된 행 수)
 */
public int executeUpdate(String query) {
	int result = -1;
	Connection connection = getConnection();
	if (connection != null) {
		result = executeUpdate(connection, query);
		closeConnection(connection);
	}
	return result;
}

/**
 * 쿼리문(UPDATE, INSERT, DELETE)을 실행한다.
 * @param connection 데이터베이스 연결 객체
 * @param query 쿼리 문자열
 * @return 처리된 행 수(-1: 데이터베이스 연결 오류, 0: 처리된 행 없음, 1이상: 처리된 행 수)
 */
public int executeUpdate(Connection connection, String query) {
	if (!initDriver) {
		System.out.println("데이터베이스 드라이버가 로딩되지 않음");
		return -1;
	}
	if (connection == null) {
		System.out.println("데이터베이스가 연결되지 않음");
		return -1;
	}
	if (query == null || query.trim().isEmpty()) {
		System.out.println("쿼리가 빈 문자열임");
		return -1;
	}

	int result = -1;
	Statement statement = null;

	try {
		statement = connection.createStatement();
		System.out.println("Statement를 생성함");
	} catch (SQLException e) {
		System.out.println("Statement를 생성하지 못함");
		e.printStackTrace();
	}

	if (statement != null) {
		try {
			result = statement.executeUpdate(query);
			System.out.println("Query[" + query + "]를 실행함");
		} catch (SQLException e) {
			System.out.println("Query[" + query + "]를 실행하지 못함");
			e.printStackTrace();
		} finally {
			try {
				statement.close();
				System.out.println("Statement를 종료함");
			} catch (SQLException e) {
				System.out.println("Statement를 종료하지 못함");
				e.printStackTrace();
			}
		}
	}

	return result;
}

 

"ServletContextListener"에서 "id"로 여러 개의 데이터베이스들을 구분하여 관리할 수 있도록 메서드를 추가합니다.

/**
 * 데이터베이스 프로퍼티에 id를 가져옵니다.
 * @return id
 */
public String getId() {
	return databaseInfo.getProperty("id") == null ? "" : databaseInfo.getProperty("id").trim();
}

 

이어서 "ServletContextListener"에서 데이터베이스 매니저를 이용하여 데이터베이스를 관리하고 쿼리를 실행하도록 하겠습니다.

728x90
반응형