[Java] JDBC 를 활용한 DB 관련 메타 데이터 조회법

식빵·2024년 9월 26일
0

Java Lab

목록 보기
28/29
post-custom-banner

🍞 필요성

서로 다른 DBMS 마다 원하는 메타정보를 조회해야 될 때가 있습니다.
그리고 당연히 서로 다른 DBMS 는 이에 대한 조회 방식이 모두 다릅니다.

이런 경우에는 JDBC 표준을 구현한 각 DBMS 별 JDBC 를 사용해서
메타정보를 조회하면 좋습니다. 지금부터 방법을 좀 알아봅시다.

더 알아내는 게 있으면 추가로 작성될 예정입니다.



🍞 테스트 환경 (참고)

  • Spring boot 3.3.x 사용
  • dependency
    • spring-boot-stater-data-jdbc
    • Lombok
  • jdk : temurin-21 사용




지금부터 볼 예시 코드에서 사용되는 모든 dataSource 를 만든 방법은 아래와 같습니다.

// import org.springframework.jdbc.datasource.DriverManagerDataSource;

DriverManagerDataSource dataSource
	= new DriverManagerDataSource(
    	"jdbc:postgresql://localhost:5432/postgres",
        "postgres",
        "postgres");



🍞 컬럼 메타정보 조회


여기에 작성된 것에 만족하지 못하시겠으면
https://docs.oracle.com/javase/8/docs/api/java/sql/DatabaseMetaData.html
를 한번 참조하시기 바랍니다.


1. Table Primkary Key

참고 링크 : javadoc - DatabaseMetaData.getPrimaryKeys

try (
        Connection conn = dataSource.getConnection();
    ) {
    DatabaseMetaData metaData  = conn.getMetaData();
    try (ResultSet rs = metaData.getPrimaryKeys("", "coding_toast", "somesome")) {
        ResultSetMetaData resultSetMetaData = rs.getMetaData();
        int columnCount = resultSetMetaData.getColumnCount();
        
        // primary key 가 없을 수도 있으니까 if 문으로 꼭 확인!
        if(rs.next()) {
        	System.out.println("======================");
            for (int i = 1; i <= columnCount; i++) {
                System.out.printf("%s : %s\n", 
                				  resultSetMetaData.getColumnName(i), rs.getObject(i));
            }
        }
    }
}

출력 예시

======================
table_cat : null
table_schem : coding_toast
table_name : somesome
column_name : id
key_seq : 1
pk_name : somesome_pkpk
======================
table_cat : null
table_schem : coding_toast
table_name : somesome
column_name : name
key_seq : 2
pk_name : somesome_pkpk
  • 위 출력을 통해서 같은 pk_name 으로 2개의 값이 조회되고 있습니다.
    이는 pk 에 2개의 컬럼이 사용됐다는 의미입니다.
  • 만약에 하나의 컬럼만 사용해서 pk 를 생성하면 1개만 조회됩니다.



2. Table Index

참고 링크 : javadoc - DatabaseMetaData.getIndexInfo

try (
        Connection conn = dataSource.getConnection();
    ) {
    DatabaseMetaData metaData  = conn.getMetaData();
    try (ResultSet rs = metaData.getIndexInfo(
    	"", 
        "coding_toast",  // 스키마 명
        "somesome", 	 // 테이블 명
        true, 			 // 인덱스 유니크 여부, false 를 하면 모든 인덱스 조회
        false)) 	     // 정확히 뭔진 모르겠습다. true 든 false 든 결과가 같네용...
    {
        ResultSetMetaData resultSetMetaData = rs.getMetaData();
        int columnCount = resultSetMetaData.getColumnCount();
        while (rs.next()) {
            System.out.println("======================");
            for (int i = 1; i <= columnCount; i++) {
                System.out.printf("%s : %s\n", 
                resultSetMetaData.getColumnName(i), rs.getObject(i));
            }
        }
    }
}

테이블 형태

-- auto-generated definition
create table coding_toast.somesome
(
    id      bigint         not null,
    name    varchar(255)   not null,
    number1 numeric(10, 2) not null
        constraint somesome_number_unique
            unique,
    number2 integer,
    constraint somesome_pkpk
        primary key (id, name)
);
  • 유니크 성을 나타내는 키 그룹
    • somesome_pkpk => (id, name)
    • somesome_number_unique => number1

출력방식

(출력이 너무 길어서 출력 결과를 조금 편집했습니다!)

  • 장황하지만 INDEX_NAME 으로 그룹핑을 할 수 있다는 것을 알 수 있습니다.



3. 컬럼 정보

방법(1)

참고 링크: ResultSetMetaData javadocs

try (
    Connection conn = dataSource.getConnection();
    PreparedStatement preparedStatement = 
    	conn.prepareStatement("select * from coding_toast.sample_copy limit 0");
    ResultSet resultSet = preparedStatement.executeQuery()) {
    ResultSetMetaData metaData = resultSet.getMetaData();
    int columnCount = metaData.getColumnCount();
    for (int i = 1; i <= columnCount; i++) {
        System.out.println("======================");
        System.out.println("ColumnName : " + metaData.getColumnName(i));
        System.out.println("ColumnTypeName : " + metaData.getColumnTypeName(i));
        System.out.println("ColumnClassName : " + metaData.getColumnClassName(i));
        System.out.println("Nullable : " + metaData.isNullable(i));
        System.out.println("Precision : " + metaData.getPrecision(i));
        System.out.println("Scale : " + metaData.getScale(i));
    }
}
// 출력 예시
/*
======================
ColumnName : tracking_id
ColumnTypeName : serial
ColumnClassName : java.lang.Integer
Nullable : 0 ==> Not Null 이라는 의미입니다.
Precision : 10
Scale : 0
======================
ColumnName : id
ColumnTypeName : varchar
ColumnClassName : java.lang.String
Nullable : 1  ==> Null 이 가능하다는 의미입니다.
Precision : 2147483647
Scale : 0
=====================
... 생략 ...
*/
  • 아쉬운 건 이 방식으로는 default value 를 알 수가 없습니다.
    default value 도 알고 싶다면 바로 다음목차의 방법도 혼용해서 사용하시면 됩니다.



방법(2)

참고 링크 : javadoc - DatabaseMetaData.getIndexInfo

try (Connection conn = dataSource.getConnection();
     ResultSet rs = conn.getMetaData().getColumns(
             null, //db명
             "sample_schema", // 스키마 명
             "sample_table",  // 테이블 명
             "%"			  // 검색하고자 하는 컬럼 (% = 모두 조회)
     )) {
    ResultSetMetaData metaData = rs.getMetaData();
    int columnCount = metaData.getColumnCount();
    while (rs.next()) {
        System.out.println("============================");
        for (int i = 1; i <= columnCount; i++) {
            System.out.printf("""
                    %s : %s
                    """, metaData.getColumnName(i), rs.getObject(i));
        }
    }
}
// 출력 예시 - 출력된 결과에 대한 것들이 궁금하면 
// 위에 작성된 링크 : [javadoc - DatabaseMetaData.getIndexInfo] 를 확인해주세요.
/*
============================
... (너무 많아서 부분 생략) ...
COLUMN_NAME : code
DATA_TYPE : 12
TYPE_NAME : varchar
COLUMN_SIZE : 254
DECIMAL_DIGITS : 0
NULLABLE : 1
REMARKS : null
COLUMN_DEF : null
CHAR_OCTET_LENGTH : 254
ORDINAL_POSITION : 2
IS_NULLABLE : YES
... (너무 많아서 부분 생략) ...
============================
... 생략...
*/

핵심 key

  • COLUMN_NAME : 컬럼명
  • DATA_TYPE : JDBC 내부에서 사용되는 타입의 코드값
  • TYPE_NAME : 타입 명칭 (영문)
  • DECIMAL_DIGITS : numeric 의 경우 SCALE
  • COLUMN_DEF : default 값
  • IS_NULLABLE : 널 여부
  • REMARKS : 컬럼 주석




참고링크:

profile
백엔드를 계속 배우고 있는 개발자입니다 😊
post-custom-banner

0개의 댓글