dependency는 다음을 참조한다.
dependencies {
...
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-bigquery'
...
}
만약 버전을 참고하지 못해 에러가 발생한다면 최신버전인 1.2.5.RELEASE 를 입력하면 된다(2020.09.01 기준)
dependencies {
...
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-bigquery', version: '1.2.5.RELEASE'
...
}
application.properties에 다음을 설정한다.
spring.cloud.gcp.bigquery.datasetName=[데이터셋 이름]
spring.cloud.gcp.credentials.location=[경로]
인증에 대한 내용은 아래 포스팅을 참조하길 바란다
https://lemontia.tistory.com/961
그럼 이제 테스트를 통해 데이터셋 목록 조회, 그리고 데이터를 가져오는 작업을 추가하겠다.
# 데이터셋 목록 조회
@SpringBootTest
class BigQueryTest {
@Autowired
BigQuery bigQuery;
@Test
@DisplayName("데이터셋 조회")
void bigquerySearchTest() throws Exception {
// given
for (Dataset dataset: bigQuery.listDatasets().iterateAll()) {
DatasetId datasetId = dataset.getDatasetId();
System.out.println("datasetId = " + datasetId);
}
}
}
인터넷에 보면 BigQuery인스턴스 생성을 다음처럼 가져오는 경우가 있는데
BigQuery service = BigQueryOptions.getDefaultInstance().getService();
이렇게 될 경우 oauth2 에서 에러가 난다. 내가 받은 인증파일을 통해 로드한 것이 아니기 때문. 그러니 일단은 GcpBigQueryAutoConfiguration 로 구현된 BigQuery 서비스(@Autowired) 를 이용하자
실행결과 다음과같이 로그가 찍힌다.
datasetId = GenericData{classInfo=[datasetId, projectId], {datasetId=[dataset id], projectId=[project id]}}
datasetId = GenericData{classInfo=[datasetId, projectId], {datasetId=[dataset id], projectId=[project id]}}
파라미터도 datesetid와 projectId만 있으니 참조하자.
그럼 이번에는 쿼리를 이용해 조회하는 것을 만들어보자
데이터가 아래와같이 들어있는 테이블이다.
아래는 조회하기 위한 코드.
...
@Test
@DisplayName("데이터 조회")
void bigQueryDataSearchTest() {
String sql = "select * from `temp.temp_table` ";
QueryJobConfiguration queryConfig = QueryJobConfiguration.newBuilder(sql)
.build();
JobId jobId = JobId.of(UUID.randomUUID().toString());
Job queryJob = bigQuery.create(JobInfo.newBuilder(queryConfig).setJobId(jobId).build());
try {
queryJob = queryJob.waitFor();
} catch (InterruptedException e) {
e.printStackTrace();
}
if(queryJob == null) {
throw new RuntimeException("쿼리조회 실패");
}
System.out.println("queryJob.getStatus() = " + queryJob.getStatus());
System.out.println("queryJob.getStatus().getError() = " + queryJob.getStatus().getError());
System.out.println("==========================================");
TableResult queryResults = null;
try {
queryResults = queryJob.getQueryResults();
// 전체 row 수
long totalRows = queryResults.getTotalRows();
System.out.println("totalRows = " + totalRows);
// 개별 항목
for (FieldValueList fieldValues : queryResults.iterateAll()) {
System.out.println("fieldValues = " + fieldValues);
String name = fieldValues.get("NAME").getStringValue();
System.out.println("name = " + name);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
...
결과를 보면 다음같이 console 에 찍힌다
queryJob.getStatus() = JobStatus{state=DONE, error=null, executionErrors=null}
queryJob.getStatus().getError() = null
==========================================
totalRows = 3
fieldValues = [FieldValue{attribute=PRIMITIVE, value=강혜연}, FieldValue{attribute=PRIMITIVE, value=28}, FieldValue{attribute=PRIMITIVE, value=서울}]
name = 강혜연
fieldValues = [FieldValue{attribute=PRIMITIVE, value=김수현}, FieldValue{attribute=PRIMITIVE, value=25}, FieldValue{attribute=PRIMITIVE, value=경기도}]
name = 김수현
fieldValues = [FieldValue{attribute=PRIMITIVE, value=아이유}, FieldValue{attribute=PRIMITIVE, value=20}, FieldValue{attribute=PRIMITIVE, value=서울}]
name = 아이유
queryJob을 통해서 통신하여 데이터를 호출하고, 실패할경우InterruptedException이 일어난다.
예를들어 테이블명을 잘못 입력하면 다음과 같은 에러가 발생한다.
GET https://www.googleapis.com/bigquery/v2/projects/[API URL]
{
"code" : 404,
"errors" : [ {
"domain" : "global",
"message" : "Not found: Table [프로젝트 ID]:temp.temp_table1 was not found in location asia-northeast3",
"reason" : "notFound"
} ],
"message" : "Not found: Table [프로젝트 ID]:temp.temp_table1 was not found in location asia-northeast3",
"status" : "NOT_FOUND"
}
코드를 읽어보면 location asia-northeast3(서울지역이다) 에서 temp.temp_table1 이 없다는 뜻이다. 본래 데이터가 있는 테이블은 temp.temp_table 이므로 에러가 발생해야 하는게 맞다.
이 에러는 InterruptedException 에서 캐치된 내용이다.
queryJob.getQueryResults() 를 통해 데이터를 추출한다. 이때 총 갯수를 totalRows를 통해 알 수 있다. 샘플에선 갯수가 3개였으니 3이라 표시되어 있다.
그리고 다음구문은 각 컬럼의 값을 가져오는 것이다.
String name = fieldValues.get("NAME").getStringValue();
이때 대소문자를 구문하니 주의하자. 만약 대소문자가 틀리면 다음과 같은 에러를 리턴한다.
java.lang.IllegalArgumentException: Field with name 'name' was not found
at com.google.cloud.bigquery.FieldList.getIndex(FieldList.java:78)
at com.google.cloud.bigquery.FieldValueList.get(FieldValueList.java:72)
헌데 fieldValues를 보면 필드명이 따로 명시되어 있지 않은데 어떻게 필드명을 매칭시켜서 가져올 수 있을까?
답은 스키마에 있다.
TableResult 객체에는 다음과 같이 스키마 정보를 갖고 있다.
그래서 여기서 get 을 통해 이름이든 인덱스든 같은 정보를 리턴하는 것이다.
즉 데이터를 추출하는 방법을 여기서는 get("이름") 을 사용했지만 get("인덱스 번호") 를 사용해도 된다.
String name = fieldValues.get("NAME").getStringValue();
String name = fieldValues.get(0).getStringValue();
둘은 같은 값을 리턴한다.
# 검색조건 추가하기
이번엔 쿼리에 검색조건을 추가해 받는걸로 해보자.
테스트는 위와 동일하게 하되 query부분만 수정한다.
...
String sql = "select * from `temp.temp_table` where name=@name";
QueryJobConfiguration queryConfig = QueryJobConfiguration.newBuilder(sql)
.addNamedParameter("name", QueryParameterValue.string("아이유"))
.build();
...
SQL문을 쓸때는 @를 붙여 파라미터 이름을 짓고(@name 등) 이것을 파싱하는 것은 addNameParameter 에서 하면 된다. key, Value 형태로 넣어야 하며, Value는 QueryParameterValue 클래스를 이용한다.
한번 실행해보면 다음과 같이 결과가 나온다.
jobId = JobId{project=null, job=3be21668-381e-44bc-987b-d31fed4a3ebf, location=null} queryJob.getStatus() = JobStatus{state=DONE, error=null, executionErrors=null} queryJob.getStatus().getError() = null ========================================== totalRows = 1 fieldValues = [FieldValue{attribute=PRIMITIVE, value=아이유}, FieldValue{attribute=PRIMITIVE, value=20}, FieldValue{attribute=PRIMITIVE, value=서울}] name = 아이유 |
공식문서를 이용하면 다른방법으로도 파싱할 수 있으며 in 절을 하는 방법도 나와있다.
https://cloud.google.com/bigquery/docs/parameterized-queries?hl=ko#java
끝.
'공부 > 프로그래밍' 카테고리의 다른 글
[airflow] Mysql의 캐릭터셋(charset)이 euc-kr 일 때(혹은 로드시 can't decode byte 0xb5에러가 날때) (0) | 2020.09.07 |
---|---|
[python3] 기존에 되던것이 No module named 뜰 때 (0) | 2020.09.03 |
[airflow] mac에서 DAG실행 시 오류날 때 (0) | 2020.09.01 |
[airflow] Mysql 데이터를 GCS(Google Cloud Storage)로 저장하기(mysql_to_gcs, 한글깨짐, 날짜포멧 수정) (0) | 2020.08.27 |
[aws,ses] SES(Simple Email Service)서비스로 메일전송하기(springboot) (4) | 2020.08.27 |
댓글