일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- Java
- s3
- 환경 구성
- 아이비시트
- 환경구성
- 자바8
- 자바
- athena
- Log
- db
- Git
- AWS
- jQuery
- ibsheet
- #jQuery
- 자바스크립트
- 로그 데이터
- AWS SQS
- 카이호스트만
- naver smartEditor
- aws lambda
- intellij
- JavaScript
- function
- java8
- 인텔리J
- AWS Athena
- AWS Glue
- aws S3
- Study
- Today
- Total
애매한 잡학사전
java에서 Database 접속 구현하기 with 인터페이스 본문
데이터베이스에 있는 데이터를 조회해서 AWS Athena에 저장하는 Java 배치 프로그램을 만들면서 DB manager를
구현했는데 정리해 보려고 합니다. 바로 클래스를 생성해서 구현할 수 있지만 이번에는 의존성 주입(DI : Dependency Injection) 연습을 위해 인터페이스로 생성했습니다.
제가 정리할 내용은 다음과 같습니다.
- DB manager 인터페이스 생성
- DB manager 클래스 생성 및 인터페이스의 abstract method 구현
DB manager 인터페이스 생성
의존성 주입을 위해 인터페이스를 생성합니다.
처음 생각했을 때는 필요한 method는 connection, close 이렇게 2개였지만 조금 더 생각을 하다 보니
기본적인 CRUD 기능을 별도의 클래스로 구현하는 것보다 인터페이스에 넣어서 DB manager 객체를 생성하고
호출하는 곳에서는 CRUD method를 호출, 호출한 method 내부에서 connection 생성 후 CRUD를 처리하면
될 것 같았습니다. 그래서 기본적인 SELECT, INSERT, UPDATE, DELETE method를 생성합니다.
SELECT method는 파라미터가 있는 것과 없는 것 2가지로 생성하였습니다.
/**
* DB connection
* @return Connection
*/
Connection getConnection();
/**
* close
* @param rs resultSet
*/
void close(ResultSet rs);
/**
* close
* @param stmt PreparedStatement
*/
void close(PreparedStatement stmt);
/**
* select query 실행
* @param query select query
* @return select 결과 리턴
*/
List<Map<String, Object>> select(String query);
/**
* select query 실행 with Param
* @param query select query
* @param paramVal parameter
* @return 조회 내용
*/
List<Map<String, Object>> select(String query, Map<String, String> paramVal);
/**
* insert query 실행
* @param query insert query
* @param paramVal value
* @return insert count
*/
int insert(String query, Map<String, String> paramVal);
/**
* update query 실행
* @param query update query
* @param paramVal value
* @return update count
*/
int update(String query, Map<String, String> paramVal);
/**
* delete query 실행
* @param query delete query
* @param paramVal value
* @return delete count
*/
int delete(String query, Map<String, String> paramVal);
DB manager 클래스 생성 및 인터페이스의 abstract method 구현
이제 이 인터페이스를 구현한 클래스를 생성합니다.
인터페이스에 있는 모든 method를 생성해주고 method body를 작성합니다.
먼저 default 생성자입니다.
private final String dbUser;
private final String dbPassword;
private final String databaseName;
public DatabaseManager(){
this.dbUser = StringUtils.getProperty("Globals.UserName");
this.dbPassword = StringUtils.getProperty("Globals.Password");
this.databaseName = StringUtils.getProperty("Globals.DbName");
}
호출하는 곳에서 객체 생성을 하면 property에 있는 DB명, user명, password를 가져와서 전역 변수에 담아 놓습니다.
다음은 connection method입니다.
@Override
public Connection getConnection(){
Connection conn = null;
try {
Class.forName(StringUtils.getProperty("Globals.DriverClassName"));
String dbUrl = StringUtils.getProperty("Globals.Url")+databaseName
+";encrypt=true;trustServerCertificate=true";
conn = DriverManager.getConnection(dbUrl, dbUser, dbPassword);
} catch (ClassNotFoundException classNotFoundException) {
logger.error("ClassNotFoundException : " + classNotFoundException.getMessage());
} catch (SQLException sqlException){
logger.error("SQLException : " + sqlException.getMessage());
} catch(Exception exception){
logger.error("exception : " + exception.getMessage());
}
return conn;
}
property에서 DriverClassName과 DB url 정보를 가져와서 connection을 생성하는 소스코드를 작성했습니다.
이제 close method를 구현하겠습니다.
@Override
public void close(ResultSet rs){
try{ if(rs != null) rs.close(); } catch(Exception ignored){}
}
@Override
public void close(PreparedStatement stmt){
try{ if(stmt != null) stmt.close(); } catch(Exception ignored){}
}
close method는 try-with-resource를 활용하려고 합니다.
connection은 try-with-resource를 100퍼센트 활용할 수 있는데 preparedStatement와 ResultSet은 완전히 적용시킬 수 없어서 별도의 close method를 생성하고 close 로직을 작성하였습니다.
왜 100% 활용할 수 없는지에 대해서는 아래에서 CRUD method 구현 시 알아보도록 하겠습니다.
다음은 CRUD method를 구현합니다.
/**
* DB 조회 내용 값 세팅
* @param resultList 반환 list
* @param rs result set
* @param metaData result meta data
* @throws SQLException sql exception
*/
private void getResultList(List<Map<String, Object>> resultList, ResultSet rs) throws SQLException {
Map<String, Object> row;
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
while(rs.next()){
row = new HashMap<>();
for(int i=1; i <= columnCount; i++){
String keyName = metaData.getColumnName(i);
Object rsVal = rs.getObject(i);
row.put(keyName, rsVal);
}
resultList.add(row);
}
}
select 할 때 파라미터가 없을 경우와 있을 경우의 결과 값을 List에 세팅하는 로직은 같기 때문에 별도의 method로 분리하고 resultSet에 있는 컬럼명을 key로 Map 데이터를 생성하고 최종적으로 List에 담아서 사용하기 위해 소스를 작성합니다.
/**
* select query 실행
* @param query - select query
* @return select 결과 리턴
*/
@Override
public List<Map<String, Object>> select(String query){
List<Map<String, Object>> resultList = new ArrayList<>();
try(Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query);
ResultSet rs = stmt.executeQuery()){
getResultList(resultList, rs);
} catch (SQLException sqlException) {
logger.error("select sqlException : " + sqlException.getMessage());
} catch (Exception ex){
logger.error("select Exception : " + ex.getMessage());
}
return resultList;
}
쿼리에 파라미터가 없는 select method입니다.
try-with-resource를 적용시켜 try 내부에서만 Connection, PreparedStatement, ResultSet을 사용할 수 있게 하고, 결괏값을 담을 List 객체를 생성하여 getResultList method로 resultSet과 함께 넘겨줍니다.
/**
* select query 실행 with Param
* @param query select query
* @param paramVal parameter
* @return 조회 내용
*/
@Override
public List<Map<String, Object>> select(String query, Map<String, String> paramVal){
List<Map<String, Object>> resultList = new ArrayList<>();
ResultSet rs = null;
try(Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)){
for(int cnt = 1; cnt <= paramVal.size(); cnt++){
stmt.setString(cnt, paramVal.get(String.valueOf(cnt)));
}
rs = stmt.executeQuery();
ResultSetMetaData metaData = rs.getMetaData();
getResultList(resultList, rs, metaData);
} catch (SQLException sqlException) {
logger.error("select sqlException : " + sqlException.getMessage()
+ ", paramVal=" + paramVal.toString());
} catch (Exception ex){
logger.error("select Exception : " + ex.getMessage());
} finally {
close(rs);
}
return resultList;
}
쿼리에 파라미터가 있는 select method 입니다.
Map으로 파라미터를 받아서 PreparedStatement에 값을 세팅해주는 로직을 추가하였습니다.
위에서 이야기했던 try-with-resource를 100% 적용할 수 없는 이유가 있는데, 파라미터가 없는 select method의 경우에는 100% 활용이 가능하지만 파라미터가 있는 select method의 경우에는 preparedStatment의 파라미터가 있어서 쿼리를 실행하기 전에 for문으로 파라미터를 생성해야 하기 때문에 try-with-resource를 100% 적용할 수 없어 ResultSet만 별도로 close 할 수 있는 method를 추가했습니다.
/**
* insert query 실행
* @param query - insert query
* @param paramVal - value
* @return insert count
*/
@Override
public int insert(String query, Map<String, String> paramVal){
int intInsertCnt = 0;
try(Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)){
for(int cnt = 1; cnt <= paramVal.size(); cnt++){
stmt.setString(cnt, paramVal.get(String.valueOf(cnt)));
}
intInsertCnt = stmt.executeUpdate();
} catch (SQLException sqlException) {
logger.error("insert sqlException : " + sqlException.getMessage());
} catch (Exception ex){
logger.error("insert Exception : " + ex.getMessage());
}
return intInsertCnt;
}
insert를 실행할 method 입니다.
파라미터를 받아서 preparedStatement에 값을 세팅하고 실행하는 로직을 추가하였습니다.
insert는 ResultSet이 없기 때문에 try-with-resource를 활용하여 별도의 close method 호출은 없습니다.
/**
* update query 실행
* @param query - update query
* @param paramVal - value
* @return update count
*/
@Override
public int update(String query, Map<String, String> paramVal){
int intUpdateCnt = 0;
try(Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)){
for(int cnt = 1; cnt <= paramVal.size(); cnt++){
stmt.setString(cnt, paramVal.get(String.valueOf(cnt)));
}
intUpdateCnt = stmt.executeUpdate();
} catch (SQLException sqlException) {
logger.error("update sqlException : " + sqlException.getMessage());
} catch (Exception ex){
logger.error("update Exception : " + ex.getMessage());
}
return intUpdateCnt;
}
update mehtod도 insert method와 같은 로직이지만 method name으로 호출 시에 어떤 로직인지를 구분하기 위해 별도의 method로 구현하였습니다.
/**
* delete query 실행
* @param query - delete query
* @param paramVal - value
* @return delete count
*/
@Override
public int delete(String query, Map<String, String> paramVal){
int intDeleteCnt = 0;
try(Connection conn = getConnection();
PreparedStatement stmt = conn.prepareStatement(query)){
for(int cnt = 1; cnt <= paramVal.size(); cnt++){
stmt.setString(cnt, paramVal.get(String.valueOf(cnt)));
}
intDeleteCnt = stmt.executeUpdate();
} catch (SQLException sqlException) {
logger.error("delete sqlException : " + sqlException.getMessage());
} catch (Exception ex){
logger.error("delete Exception : " + ex.getMessage());
}
return intDeleteCnt;
}
delete method도 insert, update와 같은 로직인데 구분을 위해서 별도의 method로 구현하였습니다.
이상으로 DB에 접속해서 데이터 처리를 할 수 있는 기본적인 소스코드를 구현해 보았습니다.
이 method 이외에 insert를 여러 번 하는 경우와 페이징 처리를 위해 전체 count를 조회하는 method 이렇게 2개의
method가 더 필요했었는데 일단은 기본적인 내용만 정리하려고 합니다.
궁금한 사항이 있으시면 댓글 남겨주시면 최대한 답변드리겠습니다.
감사합니다.
'DEV > JAVA' 카테고리의 다른 글
[자바8] 람다 표현식 - 2장 (0) | 2022.02.15 |
---|---|
[자바8] 람다 표현식 - 1장 (0) | 2022.02.13 |
[자바8] 인터페이스 java 8 interface - 2장 (0) | 2022.01.27 |
[자바8] 인터페이스 java 8 interface - 1장 (0) | 2022.01.24 |
AWS Personalize putUsers 처리 Importing users incrementally (0) | 2021.10.26 |