[자바 JDBC 게시판] 4일차

김정현·2022년 8월 11일

JDBC게시판

목록 보기
3/9

1. DBUtil을 이용한 소스코드 정리

1) article write시

​ SecSql의 객체 .append()를 이용해 쿼리문을 작성하고

​ DBUtil.insert메소드를 통해 DB에 추가

		SecSql sql = new SecSql();
			sql.append("INSERT INTO article");
			sql.append(" SET regDate = NOW()");
			sql.append(", updateDate = NOW()");
			sql.append(", title = ?", title);
			sql.append(", `body` = ?", body);		
			
			DBUtil.insert(conn, sql);

2) article modify시

​ write와 마찬가지로 sql객체로 쿼리문을 담고

​ DBUtil.update메소드를 통해 수정

		SecSql sql = new SecSql();
		sql.append("UPDATE article");
		sql.append(" SET updateDate = NOW()");
		sql.append(", updateDate = NOW()");
		sql.append(", title = ?", title);
		sql.append(", `body` = ?", body);
		sql.append(" WHERE id = ?", id);

		DBUtil.update(conn, sql);

3) article list시

​ 쿼리문을 담는 방식은 동일하고

​ DBUtil.selectRows(conn, sql)메소드는 반환값이 List<Map<String, Object>>이다.

​ Map 리스트이므로 반복문을 통하여 Map객체를 하나씩 꺼내 articles에 추가하였다.

​ 이때 Article객체 생성시 articleMap(Map형태)를 매개변수로 받을수 있게 생성자도 추가

	List<Article> articles = new ArrayList<>();
		SecSql sql = new SecSql();
		sql.append("SELECT *");
		sql.append(" FROM article");
		sql.append(" ORDER BY id DESC");

		List<Map<String, Object>> articlesListMap
		= DBUtil.selectRows(conn, sql);
		
		for(Map<String, Object> articleMap : articlesListMap) {
			articles.add(new Article(articleMap));
		}

​ Article생성자 추가 코드

public Article(Map<String, Object> articleMap) {
		this.id = (int)articleMap.get("id");
		this.title = (String)articleMap.get("title");
		this.body = (String)articleMap.get("body");
	}

2. 게시물 삭제 기능 구현

	else if (cmd.startsWith("article delete ")) {
		int id = Integer.parseInt(cmd.split(" ")[2]);	
		
        SecSql sql = new SecSql();
		sql.append("SELECT COUNT(*) FROM article");			
		sql.append(" WHERE id = ?", id);
		
		int articlesCount = DBUtil.selectRowIntValue(conn, sql);

		if (articlesCount == 0) {
			System.out.printf("%d번 게시글은 존재하지 않습니다.\n", id);
			return 0;
		}
		
		System.out.printf("== %d번 게시물 삭제 ==\n", id);
		
		sql = new SecSql();
		sql.append("DELETE FROM article");			
		sql.append(" WHERE id = ?", id);

		DBUtil.delete(conn, sql);

		System.out.printf("%d번 게시물이 삭제 되었습니다\n", id);
  • id에 해당하는 게시물이 있는지 찾아보기위해 COUNT(*) 를 이용해 쿼리문을 작성 하고
public static int selectRowIntValue(Connection dbConn, SecSql sql) {
	Map<String, Object> row = selectRow(dbConn, sql);
	for (String key : row.keySet()) {
		return (int) row.get(key);
	}

	return -1;
}
  • int값을 반환하는 selectRowIntValue(conn, sql)을 보면 첫번째 쿼리문(sql)을통해 selectRow(dbConn, sql)메서드를 이용해 나온 로우의 값을 MAP<String, Object> row 에 연결하는것을 볼수있다. 이때 row의 keySet() (key들을 모아놓은 SET)을 이용해 COUNT() Key의 value를 가져온다. 만약 SELECT COUNT()한 값이 0이아니라면 존재한다는것으로 볼수있다.
  • selectRowIntValue(conn, sql)메소드는 집계함수의 값이 int형인것만 사용가능하지 않을듯 싶다...

3. 게시물 상세보기 기능 구현

	else if (cmd.startsWith("article detail ")) {
		int id = Integer.parseInt(cmd.split(" ")[2]);
		System.out.printf("== %d번 게시물 상세보기 ==\n", id);
		
		SecSql sql = new SecSql();
		sql.append("SELECT *");
		sql.append("FROM article");
		sql.append("WHERE id = ?", id);

		Map<String, Object> articleMap = DBUtil.selectRow(conn, sql);
		
		if (articleMap.isEmpty()) {
			System.out.printf("%d번 게시글은 존재하지 않습니다.\n", id);
			return 0;
		}
					
		Article article = new Article(articleMap);
		System.out.printf("번호 : %d\n", article.id);
		System.out.printf("작성날짜 : %s\n", article.regDate);
		System.out.printf("수정날짜 : %s\n", article.updateDate);
		System.out.printf("제목 : %s\n", article.title);
		System.out.printf("내용 : %s\n", article.body);		

	}
  • delete와 비슷한 방식으로 id로 해당하는 로우를 DBUtil.selectRow()메소드를 통해 찾고

    articleMap에 key와 value의 값으로 담는다. 이때 Map타입의 articleMap을 전에 만들었던 Article()생성자의 인자로 넣으면 Article 객체를 생성할수 있다. article의 필드를 꺼내어 상세보기로 만듬.

  • 주의할점
    Article클래스의 필드 regDate와 updateDate는 String타입이다.

    하지만 DB에서 article테이블의 regDate와 updateDate 타입은 DATETIME이다.

    String타입으로 DATETIME타입을 담을수 없으므로 LocalDateTime타입으로 변경해야한다.


DBUtil 정리(확실하지 않음)

insert()

public static int insert(Connection dbConn, SecSql sql) {
            int id = -1;
            PreparedStatement stmt = null;
            ResultSet rs = null;
            try {
                stmt = sql.getPreparedStatement(dbConn);
                stmt.executeUpdate();
                rs = stmt.getGeneratedKeys();

                if (rs.next()) {
                    id = rs.getInt(1);
                }
            return id;
        }
  • rs = stmt.getGeneratedKeys(); : 생성된 키를 result값으로 받아서 rs.next()로 값을 불러온다.

selectRows()

public static List<Map<String, Object>> selectRows(Connection dbConn, SecSql sql) throws SQLErrorException {
			List<Map<String, Object>> rows = new ArrayList<>();
			PreparedStatement stmt = null;
			ResultSet rs = null;
    
            try {
                stmt = sql.getPreparedStatement(dbConn);
                rs = stmt.executeQuery();
                ResultSetMetaData metaData = rs.getMetaData(); 
                int columnSize = metaData.getColumnCount();

                    while (rs.next()) {
                        Map<String, Object> row = new HashMap<>();

                        for (int columnIndex = 0; columnIndex < columnSize; columnIndex++) {
                            String columnName = metaData.getColumnName(columnIndex + 1);
                            Object value = rs.getObject(columnName);

                            if (value instanceof Long) {
                                int numValue = (int) (long) value;
                                row.put(columnName, numValue);
                            } else if (value instanceof Timestamp) {
                                String dateValue = value.toString();
                                dateValue = dateValue.substring(0, dateValue.length() - 2);
                                row.put(columnName, dateValue);
                            } else {
                                row.put(columnName, value);
                            }
                        }

                        rows.add(row);
                    }

                return rows;
           }
  • ResultSetMetaData metaData = rs.getMetaData(); : 메타데이터는 저장된 데이터가아니라 , 해당 데이터에 관한 정보를 갖는 데이터를 의미

  • Result 클래스의 rs.getMetaData() : 메소드를 이용하여 ResultSetMetaData 클래스 객체를 만들어 사용

  • metaData.getColumnName(index) : index는 1부터 시작한다. ResultSetMetaData의 컬럼명을 가져온다. 반환값은 String

  • ResultSetMetaData.getColumnCount() : 컬럼의 수를 가져옴

  • rs.getObject(columnName); : 컬럼명을이용해 result객체에서 컬럼에대한 값을 Object타입으로 가져옴

  • 반복문을 통해 컬럼과 컬럼에 해당하는 값을 row(Map)에 Key와 Value로 put저장

  • 이때 value의 값을 형변환해서 row에 저장 후 rows(List) List에 연결


ResultSet 자료 꺼내올때 궁금한점

  • 컬럼이 id가 int인데 String으로 꺼내오면 오류가 나려나..
    -> 결과 ResultSet에 있는 데이터들은 getXXX()의 XXX타입으로 가져오는거 같음...
  • id를 boolean값으로 가져오는데 오류 대신 true로 나옴..
    허나 regDate(DATETIME)을 int로 가져오는건 에러
    String은 가능

Mysql의 자료형 타입에 따라 호출

  • 예를 들어 id컬럼이 타입이 int이면 getInt()메서드를 사용하고
    title 컬럼의 타입이 char이면 getString()메서드를 사용해야한다.

ResultSet, Cursor

  • ResultSet은 excuteQuery()메소드 실행한다음 반환되는 레코드셋을 저장한다
    레코드셋은 가상의 데이터베이스 테이블 형태이다.
  • cursor로 데이터를 가져오는데 ResultSet객체가 가져올수 있는 행의 위치를 지정해준다
  • 처음 커서의 위치는 결과물(필드)에 위치하지 않기 때문에 cursor를 이동해야한다
  • 커서를 이동하는 역할은 Result next()메서드가 수행한다
  • next()메서드의 리턴 타입은 boolean이다 다음행의 결과물이 있으면 true리턴

selectRow()

public static Map<String, Object> selectRow(Connection dbConn, SecSql sql) {
            List<Map<String, Object>> rows = selectRows(dbConn, sql);	
            if (rows.size() == 0) {
                    return new HashMap<>();
                }
            return rows.get(0);
            }
  • 로우 한개를 찾는 것
  • 알맞은 쿼리문을 selectRows()메소드로 실행시키면 rows에는 로우한개가 저장되있을것
  • 이때 size() 가 0이면 해당하는 로우를 찾을수 없다는것 new HashMap<>() 반환
  • size()가 0이 아니라면 rows의 첫번째 인덱스에 해당하는 Map 반환 ( rows.get(0) )

selectRowIntValue()

public static int selectRowIntValue(Connection dbConn, SecSql sql) {
			Map<String, Object> row = selectRow(dbConn, sql);
                for (String key : row.keySet()) {
					return (int) row.get(key);
			}
			return -1;
		}	
  • 추측???????????????????? 집계함수를 이용한 쿼리문을 작성했을때 해당하는 집계함수 칼럼의 데이터를 Int형으로 가져오는 메소드 아닌가 싶다.
  • row.keySet() : row에 key들을 모아놓은 Set

selectRowStringValue()

public static String selectRowStringValue(Connection dbConn, SecSql sql) {
			Map<String, Object> row = selectRow(dbConn, sql);
			for (String key : row.keySet()) {
				return (String) row.get(key);
			}
			return "";
		}
  • 추측???????????????????? selectRowIntValue()와 마찬가지로 집계함수 칼럼의 데이터를 String형으로 가져오는게 아닌가싶다.

selectRowStringValue()

public static boolean selectRowBooleanValue(Connection dbConn, SecSql sql) {
            Map<String, Object> row = selectRow(dbConn, sql);
            for (String key : row.keySet()) {
                return ((int) row.get(key)) == 1;
            }
            return false;
        }
  • 추측???????????????????? selectRowIntValue()와 마찬가지로 집계함수 칼럼의 데이터를 Boolean형으로 가져오는게 아닌가싶다.

0개의 댓글