파서(Parser)는 일련의 단어나 문장의 문법을 이해하여 구문 트리(syntax tree) 형태로 표현해 주는 프로그램입니다. 파서는 프로그래밍 언어 컴파일러, 자연어 처리(NLP), SQL 처리 등 다양한 분야에서 폭넓게 활용되고 있습니다. 이번 튜토리얼에서는 오픈소스 라이브러리인 JSqlParser를 사용해 SQL 문을 파싱하는 방법을 알아보겠습니다.
JSqlParser란?
JSqlParser는 GitHub에서 오픈소스로 공개된 SQL 문 파서입니다. SQL 문을 파싱하여 Java 클래스의 계층 구조로 변환해 줍니다. 이 글에서는 JSqlParser로 CREATE TABLE 문을 파싱하고, 여기에 기본 키(PRIMARY KEY)를 추가하는 Java 프로그램을 단계별로 작성해 보겠습니다.
대상 CREATE TABLE 문
CREATE TABLE College_db.Students (
Student_id int,
Name varchar(255),
Course varchar(255),
Join_date DATE
);
위 CREATE TABLE 문에서 Student_id 컬럼을 기본 키로 지정하는 것이 목표입니다.
JSqlParser로 CREATE TABLE 문을 파싱하는 Java 프로그램
1단계: Maven 프로젝트에 JSqlParser 의존성 추가
Maven 프로젝트를 생성한 후 pom.xml 파일에 아래 의존성을 추가합니다.
<dependency>
<groupId>com.github.jsqlparser</groupId>
<artifactId>jsqlparser</artifactId>
<version>4.2</version>
</dependency>
2단계: 입력 CREATE TABLE 문을 변수에 할당
아래는 기본 키가 없는 원본 CREATE TABLE 문입니다. 개행 문자(\n)는 필수가 아니며, 전체 SQL을 한 줄로 작성해도 무방합니다. 여기서는 가독성을 위해 여러 줄로 나누어 작성했습니다.
String createTableSql = "CREATE TABLE College_db.Students (\n" +
" Student_id int,\n" +
" Name varchar(255),\n" +
" Course varchar(255),\n" +
" Join_date DATE\n" +
");";
3단계: JSqlParser로 SQL 파싱
CCJSqlParserUtil 유틸리티 클래스의 parse() 메서드에 CREATE TABLE SQL을 인자로 전달하여 호출합니다.
Statement createTable = CCJSqlParserUtil.parse(createTableSql);
parse() 메서드 실행이 완료되면 Statement 객체인 createTable에는 아래 그림과 같은 Java 클래스 계층 구조가 담기게 됩니다.
JSqlParser의 파싱 결과 — Java 클래스 계층 구조
4단계: 파서 출력 결과 살펴보기
parse() 메서드가 성공적으로 실행되면 CREATE TABLE 문에 대한 모든 정보를 얻을 수 있습니다. 데이터베이스·테이블 이름과 각 컬럼의 정의를 조회해 보고, 데이터베이스와 테이블 이름도 변경해 보겠습니다.
System.out.println("Table Name from query: " + ((CreateTable) createTable).getTable().getName());
System.out.println("Database Name from query: " + ((CreateTable) createTable).getTable().getSchemaName());
System.out.println("\nColumns in the given insert query");
System.out.println("---------------------------------\n");
for(ColumnDefinition col: ((CreateTable) createTable).getColumnDefinitions())
{
System.out.println(col.getColumnName() + " - " + col.getColDataType().toString());
}
//데이터베이스 및 테이블 이름 변경
((CreateTable) createTable).getTable().setSchemaName("College_db_bk");
((CreateTable) createTable).getTable().setName("Students_bkup");
5단계: 기본 키 제약 조건을 인덱스로 생성
JSqlParser에서는 CHECK 제약 조건, EXCLUDE 제약 조건, 외래 키(Foreign Key), Named Constraint 등 다양한 인덱스를 설정할 수 있습니다. 기본 키를 추가하려면 아래와 같이 NamedConstraint 객체를 생성해야 합니다.
//기본 키 제약 조건 생성
NamedConstraint namedConstraint = new NamedConstraint();
namedConstraint.setType("PRIMARY KEY");
//기본 키에 포함할 컬럼 이름 추가
List<Index.ColumnParams> columns = new ArrayList<>();
Index.ColumnParams columnParams = new Index.ColumnParams("Student_id");
columns.add(columnParams);
//기본 키 제약 조건에 컬럼 설정
namedConstraint.setColumns(columns);
이제 Student_id를 기본 키로 하는 NamedConstraint를 인덱스 리스트에 담습니다.
//인덱스 리스트에 기본 키 제약 조건 설정
List<Index> indexList = new ArrayList<>();
indexList.add(namedConstraint);
6단계: CREATE TABLE 문에 인덱스 설정
아래와 같이 인덱스 리스트를 CREATE TABLE 문에 설정하면, 최종 생성되는 SQL에 기본 키가 함께 포함됩니다.
((CreateTable) createTable).setIndexes(indexList);
기본 키를 추가하는 전체 Java 프로그램
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.statement.create.table.ColumnDefinition;
import net.sf.jsqlparser.statement.create.table.CreateTable;
import net.sf.jsqlparser.statement.create.table.Index;
import net.sf.jsqlparser.statement.create.table.NamedConstraint;
import java.util.ArrayList;
import java.util.List;
public class AddPrimaryKey {
public static void main(String[] args) {
System.out.println("Adding Primary key in the CREATE TABLE statement");
System.out.println("-------------------------------------\n");
//입력용 CREATE TABLE SQL 할당
String createTableSql = "CREATE TABLE College_db.Students (\n" +
" Student_id int,\n" +
" Name varchar(255),\n" +
" Course varchar(255),\n" +
" Join_date DATE\n" +
");";
try {
//JSqlParser로 CREATE TABLE 문 파싱
Statement createTable = CCJSqlParserUtil.parse(createTableSql);
//테이블 및 데이터베이스 이름 조회
System.out.println("Table Name from query: " + ((CreateTable) createTable).getTable().getName());
System.out.println("Database Name from query: " + ((CreateTable) createTable).getTable().getSchemaName());
System.out.println("\nColumns in the given insert query");
System.out.println("---------------------------------\n");
for (ColumnDefinition col : ((CreateTable) createTable).getColumnDefinitions()) {
System.out.println(col.getColumnName() + " - " + col.getColDataType().toString());
}
//데이터베이스 및 테이블 이름 변경
((CreateTable) createTable).getTable().setSchemaName("College_db_bk");
((CreateTable) createTable).getTable().setName("Students_bkup");
//기본 키 제약 조건 생성
NamedConstraint namedConstraint = new NamedConstraint();
namedConstraint.setType("PRIMARY KEY");
//기본 키에 포함할 컬럼 이름 추가
List<Index.ColumnParams> columns = new ArrayList<>();
Index.ColumnParams columnParams = new Index.ColumnParams("Student_id");
columns.add(columnParams);
//기본 키 제약 조건에 컬럼 설정
namedConstraint.setColumns(columns);
//인덱스 리스트에 기본 키 제약 조건 추가
List<Index> indexList = new ArrayList<>();
indexList.add(namedConstraint);
//CREATE TABLE 문에 인덱스 설정
((CreateTable) createTable).setIndexes(indexList);
System.out.println("New CREATE TABLE statement with Primary Key");
System.out.println("---------------------------------");
System.out.println(createTable + ";");
} catch (JSQLParserException e) {
throw new RuntimeException(e);
}
}
}
실행 결과
아래 실행 결과에서 볼 수 있듯이, Student_id가 기본 키로 포함된 새로운 CREATE TABLE 문이 생성되었으며, 데이터베이스와 테이블 이름도 College_db_bk.Students_bkup으로 변경된 것을 확인할 수 있습니다.
Adding Primary key in the CREATE TABLE statement
-------------------------------------
Table Name from query: Students
Database Name from query: College_db
Columns in the given insert query
---------------------------------
Student_id - int
Name - varchar (255)
Course - varchar (255)
Join_date - DATE
New CREATE TABLE statement with Primary Key
---------------------------------
CREATE TABLE College_db_bk.Students_bkup (Student_id int,
Name varchar (255),
Course varchar (255),
Join_date DATE,
PRIMARY KEY (Student_id));
함께 읽으면 좋은 글
- Apache Hive 파서 예제