Skip to content

Instantly share code, notes, and snippets.

@daanta-real
Last active October 7, 2021 14:28
Show Gist options
  • Save daanta-real/cbbb18ffcb4876262c7a34a285b818d4 to your computer and use it in GitHub Desktop.
Save daanta-real/cbbb18ffcb4876262c7a34a285b818d4 to your computer and use it in GitHub Desktop.
Test17 ~ Test18 테이블확장검색기
코드 단축 및 기능 확장을 위해 만들어본 라이브러리입니다.
1. $.pr / $.pn / $.pf : 코드단축용 메소드
// System.out.print → $.pr 코드단축
// System.out.println → $.pn 코드단축
// System.out.printf → $.pf 코드단축
public static void pr (Object o) { System.out.print(o); }
public static void pn () { System.out.println(); }
public static void pn (Object o) { System.out.println(o); }
public static void pf (String format, Object ... args) { System.out.printf(format, args); }
2. $.getTableStr : 2차원 문자열 배열을 넘겨주면, 그 배열을 보기 좋게 SQL 테이블 형태의 텍스트로 반환해줍니다.
// String[][] 을 Oracle SQL Command Line에서 보던 테이블 형태로 출력
public static String getTableStr (String[][] data) {
// 각 컬럼 별 최대 문자길이를 출력
int[] len = new int[data[0].length];
Arrays.fill(len, 0);
for(String[] str: data) {
for(int i = 0; i < str.length; i++ ) {
int stringLen = str[i].length();
if(stringLen > len[i]) len[i] = stringLen;
}
}
// 결과문자열 준비
StringBuilder sb = new StringBuilder();
// 컬럼명 출력
String[] lis = data[0];
for(int i = 0; i < lis.length; i++) {
int printWidth = len[i] + 2;
String val = lis[i];
String printContent = String.format("%-" + printWidth + "s", val);
sb.append(printContent);
sb.append("\t");
}
sb.append('\n');
// 컬럼명 줄 바로 밑의 =====들을 출력
for(int i = 0; i < len.length; i++) {
int l = len[i];
sb.append(new String(new char[l]).replace("\0", "=")); // '='s
sb.append(new String(new char[2]).replace("\0", " ")); // spaces
sb.append("\t");
}
sb.append('\n');
// 데이터 출력
for(int i = 1; i < data.length; i++) {
String[] row = data[i];
for(int j = 0; j < row.length; j++) {
int printWidth = len[j] + 1;
String printContent = String.format("%-" + printWidth + "s", row[j]);
sb.append(printContent);
sb.append("\t");
}
sb.append('\n');
}
// 결과회신
return sb.toString();
}
// 위치홀더(?)를 포함한 SQL 쿼리문을 작성해주는 메소드
public static String makeSearchSQL(Map<String, String> map) {
// SQL 생성
StringBuilder sb = new StringBuilder();
sb.append("SELECT * FROM product WHERE 1=1");
// 필터링 부분 추가
// 날짜계열(made, expire)들은 반드시 YY-MM-DD 형식으로 받아야 함
if(map.containsKey("expire_lwr")) sb.append(" AND expire >= TO_DATE(?, 'YY-MM-DD')");
if(map.containsKey("expire_upr")) sb.append(" AND expire <= TO_DATE(?, 'YY-MM-DD')");
if(map.containsKey("made_lwr" )) sb.append(" AND made >= TO_DATE(?, 'YY-MM-DD')");
if(map.containsKey("made_upr" )) sb.append(" AND made <= TO_DATE(?, 'YY-MM-DD')");
if(map.containsKey("name" )) sb.append(" AND INSTR(name, ?) > 0");
if(map.containsKey("no" )) sb.append(" AND no = ?");
if(map.containsKey("price_lwr" )) sb.append(" AND price >= ?");
if(map.containsKey("price_upr" )) sb.append(" AND price <= ?");
if(map.containsKey("type" )) sb.append(" AND INSTR(type, ?) > 0");
// 정렬기준이 있다면 정렬기준 추가
if(map.containsKey("orderBy")) {
String[] str = map.get("orderBy").split("-");
sb.append(" ORDER BY ");
sb.append(str[0]);
sb.append(" ");
sb.append(str[1]);
}
return sb.toString();
}
// map으로부터 넘겨받은 모든 옵션을 반영하여 검색을 실시하고, 그 결과를 List<ProductDto>로 리턴해줌
public List<ProductDto> batchSearch(Map<String, String> map) throws Exception {
// 1. 접속
Connection conn = JdbcUtils.connect(USERNAME, PASSWORD);
// 2-1. SQL 전송 객체 생성
String sql = makeSearchSQL(map);
// 2-2. SQL의 placeholder들 값 채워넣기
int count = 0;
PreparedStatement ps = conn.prepareStatement(sql);
if(map.containsKey("expire_lwr")) ps.setString(++count, map.get("expire_lwr"));
if(map.containsKey("expire_upr")) ps.setString(++count, map.get("expire_upr"));
if(map.containsKey("made_lwr" )) ps.setString(++count, map.get("made_lwr" ));
if(map.containsKey("made_upr" )) ps.setString(++count, map.get("made_upr" ));
if(map.containsKey("name" )) ps.setString(++count, map.get("name" ));
if(map.containsKey("no" )) ps.setInt (++count, Integer.parseInt(map.get("no" )));
if(map.containsKey("price_lwr" )) ps.setInt (++count, Integer.parseInt(map.get("price_lwr" )));
if(map.containsKey("price_upr" )) ps.setInt (++count, Integer.parseInt(map.get("price_upr" )));
if(map.containsKey("type" )) ps.setString(++count, map.get("type" ));
// 3. 실행하고 결과 받아옴
ResultSet rs = ps.executeQuery();
// 4. 받아온 값 list로 저장
List<ProductDto> list = new ArrayList<>();
while(rs.next()) {
ProductDto one = new ProductDto();
one.setNo (rs.getInt ("no" ));
one.setName (rs.getString("name" ));
one.setType (rs.getString("type" ));
one.setPrice (rs.getInt ("price" ));
one.setMade (rs.getString("made" ));
one.setExpire(rs.getString("expire"));
list.add(one);
}
return list;
}
// 개요 및 도움말 출력
private static void printHelp() {
$.pn("[검색시스템 안내]\n"
+ "1. 명령 목록: 아래 키워드 중 하나를 입력하여 명령을 실행하세요.\n"
+ " 옵션초기화:init / 옵션추가:add / 검색실시:run / 종료:quit\n"
+ "2. 검색옵션 목록: 위 명령어 뒤에 ,를 쓰고 아래 키워드 중 하나를 양식에 맞게 추가 입력하여 검색 옵션을 적용하세요.\n"
+ " 번호:no,숫자 / 최저가격:price_lwr,숫자 / 최고가격:price_upr,숫자 / 이름:name,문자 / 유형:type,문자\n"
+ " 제조일자이후:made_lwr,날짜 / 제조일자이전:made_upr,날짜 / 유통기한이후:expire_lwr,날짜 / 유통기한이전:expire_upr,날짜\n"
+ " 정렬기준:orderBy,항목-(ASC:오름차순/DESC:내림차순)\n"
+ " ※ 이름과 유형의 경우 유사검색 실시됩니다.\n"
+ " ※ 상기 날짜 유형 네 개는 모두 YY-MM-DD형태로 입력하시기 바랍니다.\n\n");
}
// 임시 SQL문을 가라로 만들어 출력
private static void printTemporarySql(Map<String, String> map) {
$.pn("[현재 검색옵션 및 구문 확인]");
if(map.size() == 0) $.pn("현재 설정된 검색옵션이 없습니다.");
else {
$.pn("현재 설정된 검색옵션이 있습니다!!! (" + map.size() + "개)");
$.pn("1. SQL문은 얼추 이렇게 될 것입니다. ('처리가 정확하진 않아요 흥미로만 보세요)");
String sql = ProductDao.makeSearchSQL(map); // 위치홀더가 들어있는 구문 Get
// 위치홀더 전부 실제 들어갈 문자열로 바꿈
Iterator<String> it = map.keySet().iterator();
while(it.hasNext()) sql = sql.replaceFirst("[?]", "'" + map.get(it.next()) + "'");
sql = sql.replaceAll("( )+", " "); // 두 개 이상 연속된 공백은 trim
// 최종출력
$.pn(" " + sql);
$.pn("2. 그리고 상세 옵션 내역은 아래와 같이 될 것입니다.");
map.forEach((key, val) -> $.pf(" - 항목: %s, 값: %s\n", key, val));
}
$.pn();
}
// Dao로부터 받아온 검색 결과를 정식으로 출력
private static void printResult(List<ProductDto> list) {
$.pn("[검색결과를 출력합니다.]");
String[][] data = new String[list.size() + 1][6];
data[0] = new String[] { "no", "name", "type", "price", "made", "expire" };
for(int i = 0; i < list.size(); i++) {
ProductDto dto = list.get(i);
data[i + 1] = new String[] {
String.valueOf(dto.getNo()),
dto.getName(),
dto.getType(),
String.valueOf(dto.getPrice()),
dto.getMade(),
dto.getExpire()
};
}
$.pn($.getTableStr(data));
}
// 메인
public static void main(String[] args) throws Exception {
// 변수정의
Map<String, String> map = new TreeMap<>(); // 검색옵션 모음
Scanner sc = new Scanner(System.in);
// 반복문 실행
while(true) {
// 1. 안내문 출력
printHelp();
// 2. 현재 시점에서 예상되는 SQL 구문과, 이에 적용된 검색옵션 출력
printTemporarySql(map);
// 3. 명령어 입력에 따른 커맨드 수행
$.pn("[명령을 입력하십시오]");
String[] cmd = sc.nextLine().split(",");
switch(cmd[0]) {
case "init": $.pn("[모든 검색옵션을 초기화합니다.]");
map = new TreeMap<>(); break;
case "add" : $.pf("[옵션을 추가합니다]\n - 옵션명:(%s), 옵션값:(%s)\n", cmd[1], cmd[2]);
map.put(cmd[1], cmd[2]); break;
case "run" : $.pn("[검색 실시]");
ProductDao dao = new ProductDao();
List<ProductDto> list = dao.batchSearch(map);
printResult(list); // 결과출력
map = new TreeMap<>(); break;
// 잘못 입력했거나 종료 입력 시
default: { $.pn("[종료하겠습니다.]"); sc.close(); System.exit(0); } break;
}
$.pn(); $.pn(); $.pn();
}
}
[검색시스템 안내]
1. 명령 목록: 아래 키워드 중 하나를 입력하여 명령을 실행하세요.
옵션초기화:init / 옵션추가:add / 검색실시:run / 종료:quit
2. 검색옵션 목록: 위 명령어 뒤에 ,를 쓰고 아래 키워드 중 하나를 양식에 맞게 추가 입력하여 검색 옵션을 적용하세요.
번호:no,숫자 / 최저가격:price_lwr,숫자 / 최고가격:price_upr,숫자 / 이름:name,문자 / 유형:type,문자
제조일자이후:made_lwr,날짜 / 제조일자이전:made_upr,날짜 / 유통기한이후:expire_lwr,날짜 / 유통기한이전:expire_upr,날짜
정렬기준:orderBy,항목-(ASC:오름차순/DESC:내림차순)
※ 이름과 유형의 경우 유사검색 실시됩니다.
※ 상기 날짜 유형 네 개는 모두 YY-MM-DD형태로 입력하시기 바랍니다.
[현재 검색옵션 및 구문 확인]
현재 설정된 검색옵션이 없습니다.
[명령을 입력하십시오]
add,made_lwr,20-01-01
add,price_upr,2000
add,expire_upr,20-08-15
add,orderBy,name-ASC
[옵션을 추가합니다]
- 옵션명:(made_lwr), 옵션값:(20-01-01)
- 옵션명:(price_upr), 옵션값:(2000)
- 옵션명:(expire_upr), 옵션값:(20-08-15)
- 옵션명:(orderBy), 옵션값:(name-ASC)
[현재 검색옵션 및 구문 확인]
현재 설정된 검색옵션이 있습니다!!! (4개)
1. SQL문은 얼추 이렇게 될 것입니다. (정확하진 않아요 임시로 출력하는거니 이해용으로만 보세요)
SELECT * FROM product WHERE 1=1 AND expire <= TO_DATE('20-08-15', 'YY-MM-DD') AND made >= TO_DATE('20-01-01', 'YY-MM-DD') AND price <= 'name-ASC' ORDER BY name ASC
2. 그리고 상세 옵션 내역은 아래와 같이 될 것입니다.
- 항목: expire_upr, 값: 20-08-15
- 항목: made_lwr, 값: 20-01-01
- 항목: orderBy, 값: name-ASC
- 항목: price_upr, 값: 2000
[명령을 입력하십시오]
run
[검색 실시]
[검색결과를 출력합니다.]
no name type price made expire
== ==== ==== ===== =================== ===================
7 바나나킥 과자 1500 2020-05-03 00:00:00 2020-06-03 00:00:00
5 참이슬 주류 1000 2020-01-05 00:00:00 2020-04-05 00:00:00
6 처음처럼 주류 1000 2020-03-15 00:00:00 2020-08-15 00:00:00
// 위치홀더(?)를 포함한 SQL 쿼리문을 작성해주는 메소드
public static String makeSearchSQL(Map<String, String> map) {
// SQL 생성
StringBuilder sb = new StringBuilder();
sb.append("SELECT * FROM member WHERE 1=1");
// 필터링 부분 추가
// 날짜계열(made, expire)들은 반드시 YY-MM-DD 형식으로 받아야 함
if(map.containsKey("birth_max")) sb.append(" AND member_birth <= TO_DATE(?, 'YY-MM-DD')");
if(map.containsKey("birth_min")) sb.append(" AND member_birth >= TO_DATE(?, 'YY-MM-DD')");
if(map.containsKey("email" )) sb.append(" AND INSTR(member_email, ?) > 0");
if(map.containsKey("grade" )) sb.append(" AND INSTR(member_grade, ?) > 0");
if(map.containsKey("id" )) sb.append(" AND INSTR(member_id , ?) > 0");
if(map.containsKey("join_max" )) sb.append(" AND member_join <= TO_DATE(?, 'YY-MM-DD')");
if(map.containsKey("join_min" )) sb.append(" AND member_join >= TO_DATE(?, 'YY-MM-DD')");
if(map.containsKey("nick" )) sb.append(" AND INSTR(member_nick , ?) > 0");
if(map.containsKey("phone" )) sb.append(" AND INSTR(member_phone, ?) > 0");
if(map.containsKey("point_max")) sb.append(" AND member_point <= ?");
if(map.containsKey("point_min")) sb.append(" AND member_point >= ?");
if(map.containsKey("pw" )) sb.append(" AND INSTR(member_pw , ?) > 0");
// 정렬기준이 있다면 정렬기준 추가
if(map.containsKey("orderBy")) {
String[] str = map.get("orderBy").split("-");
sb.append(" ORDER BY ");
sb.append(str[0]);
sb.append(" ");
sb.append(str[1]);
}
return sb.toString();
}
// map으로부터 넘겨받은 모든 옵션을 반영하여 검색을 실시하고, 그 결과를 List<ProductDto>로 리턴해줌
public List<MemberDto> batchSearch(Map<String, String> map) throws Exception {
// 1. 접속
Connection conn = JdbcUtils.connect(USERNAME, PASSWORD);
// 2-1. SQL 전송 객체 생성
String sql = makeSearchSQL(map);
// 2-2. SQL의 placeholder들 값 채워넣기
int count = 0;
PreparedStatement ps = conn.prepareStatement(sql);
if(map.containsKey("birth_max")) ps.setString(++count, map.get("birth_max"));
if(map.containsKey("birth_min")) ps.setString(++count, map.get("birth_min"));
if(map.containsKey("email" )) ps.setString(++count, map.get("email" ));
if(map.containsKey("grade" )) ps.setString(++count, map.get("grade" ));
if(map.containsKey("id" )) ps.setString(++count, map.get("id" ));
if(map.containsKey("join_max" )) ps.setString(++count, map.get("join_max" ));
if(map.containsKey("join_min" )) ps.setString(++count, map.get("join_min" ));
if(map.containsKey("nick" )) ps.setString(++count, map.get("nick" ));
if(map.containsKey("phone" )) ps.setString(++count, map.get("phone" ));
if(map.containsKey("point_max")) ps.setInt (++count, Integer.parseInt(map.get("point_max")));
if(map.containsKey("point_min")) ps.setInt (++count, Integer.parseInt(map.get("point_min")));
if(map.containsKey("pw" )) ps.setString(++count, map.get("pw" ));
// 3. 실행하고 결과 받아옴
ResultSet rs = ps.executeQuery();
// 4. 받아온 값 list로 저장
List<MemberDto> list = new ArrayList<>();
while(rs.next()) {
MemberDto one = new MemberDto();
one.setMemberId (rs.getString("member_id" ));
one.setMemberPw (rs.getString("member_pw" ));
one.setMemberNick (rs.getString("member_nick" ));
one.setMemberEmail(rs.getString("member_email"));
one.setMemberPhone(rs.getString("member_phone"));
one.setMemberGrade(rs.getString("member_grade"));
one.setMemberPoint(rs.getInt ("member_point"));
one.setMemberBirth(rs.getString("member_birth")); // 원본Dto가 String자료형이어서..
one.setMemberJoin (rs.getDate ("member_join" ));
list.add(one);
}
return list;
}
// 개요 및 도움말 출력
private static void printHelp() {
$.pn("[검색시스템 안내]\n"
+ "1. 명령 목록: 아래 키워드 중 하나를 입력하여 명령을 실행하세요.\n"
+ " 옵션초기화:init / 옵션추가:add / 검색실시:run / 종료:quit\n"
+ "2. 검색옵션 목록: 위 명령어 뒤에 ,를 쓰고 아래 키워드 중 하나를 양식에 맞게 추가 입력하여 검색 옵션을 적용하세요.\n"
+ " 아디:id,문자 / 비번:pw,문자 / 닉네임:nick,문자 / 이메일:email,문자 / 폰번호:phone,문자 /\n"
+ " 등급:grade,문자 / 최저포인트:point_min,숫자 / 최고포인트:point_max,숫자 /\n"
+ " 생일이후:birth_min,문자 / 생일이전:birth_max,문자 / 횐갑이후:join_min,문자 / 횐갑이전:join_max,문자\n"
+ " 정렬기준:orderBy,항목-(ASC:오름차순/DESC:내림차순)\n"
+ " ※ 문자는 유사검색 실시됩니다.\n"
+ " ※ 가입일자와 생일 두 가지는 모두 YY-MM-DD형태로 입력하시기 바랍니다.\n\n");
}
// 임시 SQL문을 가라로 만들어 출력
private static void printTemporarySql(Map<String, String> map) {
$.pn("[현재 검색옵션 및 구문 확인]");
if(map.size() == 0) $.pn("현재 설정된 검색옵션이 없습니다.");
else {
$.pn("현재 설정된 검색옵션이 있습니다!!! (" + map.size() + "개)");
$.pn("1. SQL문은 얼추 이렇게 될 것입니다. ('처리가 정확하진 않아요 흥미로만 보세요)");
String sql = MemberDao.makeSearchSQL(map); // 위치홀더가 들어있는 구문 Get
// 위치홀더 전부 실제 들어갈 문자열로 바꿈
Iterator<String> it = map.keySet().iterator();
while(it.hasNext()) sql = sql.replaceFirst("[?]", "'" + map.get(it.next()) + "'");
sql = sql.replaceAll("( )+", " "); // 두 개 이상 연속된 공백은 trim
// 최종출력
$.pn(" " + sql);
$.pn("2. 그리고 상세 옵션 내역은 아래와 같이 될 것입니다.");
map.forEach((key, val) -> $.pf(" - 항목: %s, 값: %s\n", key, val));
}
$.pn();
}
// Dao로부터 받아온 검색 결과를 정식으로 출력
private static void printResult(List<MemberDto> list) {
$.pn("[검색결과를 출력합니다.]");
String[][] data = new String[list.size() + 1][9];
data[0] = new String[] {
"member_id" ,
"member_pw" ,
"member_nick" ,
"member_email",
"member_phone",
"member_grade",
"member_point",
"member_birth",
"member_join"
};
for(int i = 0; i < list.size(); i++) {
MemberDto dto = list.get(i);
data[i + 1] = new String[] {
dto.getMemberId(),
dto.getMemberPw(),
dto.getMemberNick(),
dto.getMemberEmail(),
dto.getMemberPhone(),
dto.getMemberGrade(),
String.valueOf(dto.getMemberPoint()),
dto.getMemberBirth(),
String.valueOf(dto.getMemberJoin())
};
}
$.pn($.getTableStr(data));
}
// 메인
public static void main(String[] args) throws Exception {
// 변수정의
Map<String, String> map = new TreeMap<>(); // 검색옵션 모음
Scanner sc = new Scanner(System.in);
// 반복문 실행
while(true) {
// 1. 안내문 출력
printHelp();
// 2. 현재 시점에서 예상되는 SQL 구문과, 이에 적용된 검색옵션 출력
printTemporarySql(map);
// 3. 명령어 입력에 따른 커맨드 수행
$.pn("[명령을 입력하십시오]");
String[] cmd = sc.nextLine().split(",");
switch(cmd[0]) {
case "init": $.pn("[모든 검색옵션을 초기화합니다.]");
map = new TreeMap<>(); break;
case "add" : $.pf("[옵션을 추가합니다]\n - 옵션명:(%s), 옵션값:(%s)\n", cmd[1], cmd[2]);
map.put(cmd[1], cmd[2]); break;
case "run" : $.pn("[검색 실시]");
MemberDao dao = new MemberDao();
List<MemberDto> list = dao.batchSearch(map);
printResult(list); // 결과출력
map = new TreeMap<>(); break;
// 잘못 입력했거나 종료 입력 시
default: { $.pn("[종료하겠습니다.]"); sc.close(); System.exit(0); } break;
}
$.pn(); $.pn(); $.pn();
}
}
[검색시스템 안내]
1. 명령 목록: 아래 키워드 중 하나를 입력하여 명령을 실행하세요.
옵션초기화:init / 옵션추가:add / 검색실시:run / 종료:quit
2. 검색옵션 목록: 위 명령어 뒤에 ,를 쓰고 아래 키워드 중 하나를 양식에 맞게 추가 입력하여 검색 옵션을 적용하세요.
아디:id,문자 / 비번:pw,문자 / 닉네임:nick,문자 / 이메일:email,문자 / 폰번호:phone,문자 /
등급:grade,문자 / 최저포인트:point_min,숫자 / 최고포인트:point_max,숫자 /
생일이후:birth_min,문자 / 생일이전:birth_max,문자 / 횐갑이후:join_min,문자 / 횐갑이전:join_max,문자
정렬기준:orderBy,항목-(ASC:오름차순/DESC:내림차순)
※ 문자는 유사검색 실시됩니다.
※ 가입일자와 생일 두 가지는 모두 YY-MM-DD형태로 입력하시기 바랍니다.
[현재 검색옵션 및 구문 확인]
현재 설정된 검색옵션이 없습니다.
[명령을 입력하십시오]
add,point_min,0
add,birth_max,99-01-10
[옵션을 추가합니다] - 옵션명:(point_min), 옵션값:(0)
[옵션을 추가합니다] - 옵션명:(birth_max), 옵션값:(99-01-10)
[현재 검색옵션 및 구문 확인]
현재 설정된 검색옵션이 있습니다!!! (2개)
1. SQL문은 얼추 이렇게 될 것입니다. (정확하진 않아요 임시로 출력하는거니 이해용으로만 보세요)
SELECT * FROM member WHERE 1=1 AND member_birth <= TO_DATE('99-01-10', 'YY-MM-DD') AND member_point >= '0'
2. 그리고 상세 옵션 내역은 아래와 같이 될 것입니다.
- 항목: birth_max, 값: 99-01-10
- 항목: point_min, 값: 0
[명령을 입력하십시오]
run
[검색 실시]
[검색결과를 출력합니다.]
member_id member_pw member_nick member_email member_phone member_grade member_point member_birth member_join
========= ========= =========== ============ ============= ============ ============ =================== ===========
testuser testuser 테스트유저 null null 준회원 100 1999-01-10 00:00:00 2021-10-07
testuser2 testuser2 ㅋㅋㅋ123 null null 준회원 100 1999-01-10 00:00:00 2021-10-07
testuser3 testuser3 ㅋㅋㅋ1234 null 010-1234-1234 준회원 100 1999-01-10 00:00:00 2021-10-07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment