N1QL의 GA 릴리스 이후 SQL 데이터베이스에서 Couchbase로 콘텐츠를 옮기는 방법에 대한 질문을 많이 받습니다. 그렇게 하는 방법에는 여러 가지가 있습니다. 오늘은 가장 간단한 방법을 선택했습니다. 각 테이블의 각 행을 JsonDocument로 변환하여 Couchbase에 저장하겠습니다. 저는 Postgres와 그 샘플 데이터 세트 MySQL에서 영감을 얻은 사킬라 샘플. Java를 사용하겠지만 여기에 제시된 가이드라인은 다른 언어에도 적용할 수 있습니다.
실행 중인 SQL 데이터베이스에 연결하기
저는 Java를 사용하고 있으므로 Spring Boot와 데이터베이스 연결을 처리하는 JDBC 패키지를 구현할 것입니다. 제가 해야 할 일은 올바른 종속성과 속성을 설정하여 JdbcTemplate. 이 개체를 사용하면 SQL 쿼리를 쉽게 실행할 수 있습니다.
종속성
모든 것을 깔끔하게 자동으로 구성하려면 다음과 같은 종속성이 필요합니다:
1 2 3 4 5 6 7 |
종속성 { 컴파일 "org.springframework.boot:spring-boot-starter", "org.springframework.boot:spring-boot-starter-data-jpa", "org.postgresql:postgresql:9.4-1206-jdbc4" } |
저는 Postgres로 테스트하고 있지만 Spring JDBC에서 지원하는 다른 드라이버를 추가할 수 있습니다. 스프링 부팅 스타터 데이터-jpa를 사용하면 미리 구성된 JdbcTemplate을 주입할 수 있습니다.
구성
Spring 프레임워크가 데이터베이스를 찾도록 하려면 구성 파일에 다음 속성을 추가하세요(예: src/main/resources/application.properties).
1 2 3 4 5 6 7 8 9 10 |
봄.jpa.데이터베이스=POSTGRESQL 봄.데이터 소스.플랫폼=postgres 봄.jpa.show-sql=true 봄.jpa.최대 절전 모드.ddl-자동=create-drop 봄.데이터베이스.드라이버 클래스 이름=org.포스트그레스큐엘.드라이버 봄.데이터 소스.URL=jdbc:포스트그레스큐엘://192.168.99.100:5432/dvdrental 봄.데이터 소스.사용자 이름=postgres 봄.데이터 소스.비밀번호=비밀번호 |
물론 사용 중인 데이터베이스에 따라 이를 미세 조정해야 합니다. 여기서는 기본 포트 5432로 192.168.99.100에서 실행되는 Postgres를 사용하고 있습니다. 사용하려는 데이터베이스의 이름은 dvdrental입니다.
코드
모든 것이 올바르게 구성되었다면 JdbcTemplate을 삽입하고 SQL DB 쿼리를 시작할 수 있어야 합니다.
1 2 3 4 5 6 7 8 9 10 |
@자동 유선 JdbcTemplate jdbcTemplate; @오버라이드 public void doStuff() 던지기 예외 { 문자열 sql = "SELECT id FROM table"; Long id = jdbcTemplate.쿼리포객체(sql, Long.클래스); } |
Couchbase에 연결
제 목표는 SQL 데이터베이스에서 Couchbase로 콘텐츠를 이동하는 것이므로 Couchbase 연결도 필요합니다.
종속성
Java 프로젝트에서 Couchbase를 사용하려면 다음 종속성을 추가해야 합니다:
1 2 3 4 5 |
종속성 { 컴파일 "com.couchbase.client:java-client:2.2.3" } |
이렇게 하면 카우치베이스에 액세스할 수 있습니다. Java SDK.
구성
기본 Couchbase 구성에는 기본적으로 서버 IP 주소, 버킷 이름, 버킷 비밀번호의 세 가지 속성이 필요합니다. Spring Boot 방식으로 이 작업을 수행하면 다음과 같습니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
@구성 public 클래스 데이터베이스 { @가치("${호스트명}") 비공개 문자열 호스트 이름; @가치("${버킷}") 비공개 문자열 버킷; @가치("${비밀번호}") 비공개 문자열 비밀번호; public @Bean 클러스터 클러스터() { 반환 카우치베이스클러스터.create(호스트 이름); } public @Bean 버킷 버킷() { 반환 클러스터().오픈버킷(버킷, 비밀번호); } } |
속성 호스트 이름, 버킷 및 비밀번호는 애플리케이션 속성 파일에 직접 추가할 수 있습니다.
1 2 3 4 5 6 7 8 |
# 호스트 이름, 쉼표로 구분된 카우치베이스 노드 IP 또는 호스트 이름 목록 호스트 이름: localhost,127.0.0.1 # 버킷 이름 버킷: 기본값 # 버킷 비밀번호 비밀번호: |
코드
카우치베이스에서 데이터베이스와 동등한 수준의 세분화 수준은 문서를 저장하는 버킷이 될 것입니다. 이전 구성을 사용하면 간단히 버킷을 주입하고 놀기 시작할 수 있습니다.
1 2 3 4 5 6 7 8 9 |
@자동 유선 버킷 버킷; @오버라이드 public void doStuff() 던지기 예외 { JsonDocument doc = 버킷.get("key"); } |
테이블
이 시점에서 SQL 데이터베이스와 Couchbase에 연결이 완료되었습니다. 이제 데이터를 이동하기 시작할 수 있습니다. 데이터를 이동하는 가장 쉬운 방법은 각 테이블의 각 행을 하나의 문서로 간주하는 것입니다.
SQL 스키마 가져오기
JdbcTemplate을 사용하여 데이터베이스의 스키마를 자동으로 가져오는 것부터 시작해 보겠습니다. 여기서 흥미로운 객체는 데이터베이스 메타데이터를 사용하면 데이터베이스의 전체 구조를 파악할 수 있습니다. 이 API는 사용하기 가장 쉬운 것은 아니지만 적어도 문서화되어 있습니다.
데이터베이스 메타데이터 쿼리 결과를 테이블 및 열 목록에 매핑하겠습니다. 이를 위해 다음과 같은 Java 클래스를 만들었습니다:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
public 클래스 표 { 비공개 문자열 이름; 비공개 목록<칼럼> 열 = new ArrayList<칼럼>(); 비공개 문자열 primaryKey; public 표(문자열 테이블 이름) { 이.이름 = 테이블 이름; } public void 설정 기본 키(문자열 primaryKey) { 이.primaryKey = primaryKey; } public void 추가 열(문자열 이름, int 유형) { 열.추가(new 칼럼(이름, 유형)); } public 문자열 getName() { 반환 이름; } public 목록<칼럼> getColumns() { 반환 열; } public 문자열 getPrimaryKey() { 반환 primaryKey; } public JsonObject toJsonObject() { JsonObject 객체 = JsonObject.create(); JsonArray jsonColumns = JsonArray.create(); 에 대한 (칼럼 col : 열) { jsonColumns.추가(col.toJsonObject()); } 객체.put("tableName", 이름); 객체.put("primaryKey", primaryKey); 객체.put("columns", jsonColumns); 반환 객체; } } public 클래스 칼럼 { 비공개 문자열 이름; 비공개 int 유형; public 칼럼(문자열 이름, int 유형) { 이.이름 = 이름; 이.유형 = 유형; } public 문자열 getName() { 반환 이름; } public int getType() { 반환 유형; } public JsonObject toJsonObject() { JsonObject 객체 = JsonObject.create(); 객체.put("name", 이름); 객체.put("type", 유형); 반환 객체; } } |
작성하기 가장 흥미로운 코드는 아니지만, 마지막에는 SQL 데이터베이스 테이블의 JSON 표현을 얻을 수 있습니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
public void 데이터베이스 스키마 가져오기() 던지기 예외 { // 테이블 스키마를 검색할 데이터베이스 Medatadata 객체 가져오기 데이터베이스 메타데이터 데이터베이스 메타데이터 = jdbcTemplate.데이터 소스 가져오기().getConnection().메타데이터 가져오기(); 목록<문자열> 테이블 이름 = new ArrayList<문자열>(); // 테이블 이름 가져오기 결과 설정 결과 = 데이터베이스 메타데이터.getTables(카탈로그, 스키마 패턴, 테이블 이름 패턴, 유형); 동안 (결과.다음()) { 문자열 테이블 이름 = 결과.getString(3); 문자열 테이블 유형 = 결과.getString(4); // (뷰, 카운터 등이 아닌) 테이블만 가져오는지 확인합니다. 만약 (!테이블 이름.isEmpty() && "TABLE".같음(테이블 유형)) { 테이블 이름.추가(테이블 이름); 로그.debug("테이블을 가져옵니다." + 테이블 이름); } } // 테이블 스키마를 테이블 개체에 매핑 지도<문자열, 표> 테이블 = new 해시맵<문자열, 표>(); JsonObject 테이블 스키마 = JsonObject.create(); 에 대한 (문자열 테이블 이름 : 테이블 이름) { 결과 = 데이터베이스 메타데이터.getColumns(카탈로그, 스키마 패턴, 테이블 이름, 열 이름 패턴); 표 테이블 = new 표(테이블 이름); 동안 (결과.다음()) { 문자열 columnName = 결과.getString(4); // JDBCType 열거형에 매핑 int columnType = 결과.getInt(5); 테이블.추가 열(columnName, columnType); } 결과 = 데이터베이스 메타데이터.getPrimaryKeys(카탈로그, 스키마 패턴, 테이블 이름); 동안 (결과.다음()) { 문자열 columnName = 결과.getString(4); 테이블.설정 기본 키(columnName); } 테이블.put(테이블 이름, 테이블); 테이블 스키마.put(테이블 이름, 테이블.toJsonObject()); } JsonDocument 스키마독 = JsonDocument.create(tablesSchemaId, 테이블 스키마); JsonDocument doc = 버킷.업서트(스키마독); } |
콘텐츠
재미있는 부분은 여기입니다. 여기서부터 테이블 행을 JsonDocument에 매핑하기 시작합니다. 이전 섹션에서는 모든 테이블의 이름을 검색할 수 있는 상태가 되었습니다. 하나의 테이블 이름에서 테이블의 모든 행을 반환하는 SQL 쿼리를 만들 수 있습니다.
Spring에는 RowMapper를 정의할 수 있는 메커니즘이 있습니다. 쿼리에서 반환되는 각 행에 대해 원하는 객체를 반환할 수 있습니다. 저는 카우치베이스를 사용하고 있기 때문에, 저는 JsonDocument.
다음은 구현 예시입니다. 이 RowMapper에는 이전에 정의된 Table 객체가 필요하므로 mapRow 메서드를 구현해야 합니다. 여기서 해야 할 일이 몇 가지 있습니다.
첫 번째 작업은 고유 키를 만드는 것입니다. 행은 테이블별로 범위가 지정되므로 일부 ID는 다른 테이블의 행에 대해 정확히 동일할 수 있습니다. 하지만 문서는 버킷별로 범위가 지정되므로 행 ID와 테이블 이름을 반영하는 고유한 문서 키를 만들어야 합니다. 문서의 출처를 추적하기 위해 테이블 이름에 _tableName 필드도 추가하겠습니다.
그 다음 흥미로운 단계는 유형 매핑입니다. JSON은 SQL 데이터베이스보다 더 적은 수의 유형을 지원하므로 여기서 몇 가지 변환 작업을 수행해야 합니다. 이것이 바로 getJsonTypedValue 메서드가 하는 일입니다. 이 메서드는 대부분의 JDBC 유형을 기본 JSON 유형(문자열, 숫자, 부울, 배열, 객체, null)으로 변환할 수 있는지 확인합니다. 마지막에는 Couchbase에 저장할 수 있는 JsonDocument가 생성됩니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
public 클래스 JSONRowMapper 구현 행 매퍼<문서> { 표 테이블; public JSONRowMapper(표 테이블) { 이.테이블 = 테이블; } public JsonDocument mapRow(결과 설정 rs, int rowNum) 던지기 SQLException { 문자열 id = 테이블.getName() + "::" + rs.getString(테이블.getPrimaryKey()); JsonObject 객체 = JsonObject.create(); 객체.put("_tableName", 테이블.getName()); 에 대한 (칼럼 col : 테이블.getColumns()) { 개체 값 = getJsonTypedValue(col.유형, rs.getObject(col.이름), col.이름); 객체.put(col.이름, 값); } 반환 JsonDocument.create(id, 객체); } public 개체 getJsonTypedValue(int 유형, 개체 값, 문자열 이름) 던지기 SQLException { 만약 (값 == null) { 반환 null; } JDBCType 현재 = JDBCType.valueOf(유형); 스위치 (현재) { case 타임스탬프: 타임스탬프 타임스탬프 = (타임스탬프) 값; 반환 타임스탬프.getTime(); case 타임스탬프_위드_시간대: 타임스탬프 ts = (타임스탬프) 값; JsonObject tsWithTz = JsonObject.create(); tsWithTz.put("timestamp", ts.getTime()); tsWithTz.put("시간대", ts.getTimeZoneOffset()); 반환 tsWithTz; case 날짜: 날짜 sqlDate = (날짜) 값; 반환 sqlDate.getTime(); case 십진수: case 숫자: BigDecimal bigDecimal = (BigDecimal) 값; 반환 bigDecimal.doubleValue(); case 배열: 배열 배열 = (배열) 값; 개체[] 객체 = (개체[]) 배열.getArray(); 반환 JsonArray.에서(객체); case 바이너리: case BLOB: case 롱바이어리: 반환 Base64.getEncoder().encodeToString((바이트[]) 값); case 기타: case JAVA_OBJECT: // 데이터베이스별, 기본값은 문자열 값 반환 값.toString(); 기본값: 반환 값; } } } |
RowMapper를 사용하면 작업이 정말 쉬워집니다. 테이블의 이름을 반복하고 쿼리를 실행한 다음 결과를 Couchbase에 저장할 수 있습니다. 이 작업을 동기식으로 수행하면 다음과 같이 보일 것입니다:
1 2 3 4 5 6 7 8 9 10 11 12 |
에 대한 (문자열 테이블 이름 : 테이블 이름) { 문자열 sql = "에서 * 선택" + 테이블 이름 + ";"; 목록<JsonDocument> rs = jdbcTemplate.쿼리(sql, new JSONRowMapper(테이블.get(테이블 이름))); 만약 (!rs.isEmpty()) { 에 대한 (JsonDocument doc : rs) { 버킷.업서트(doc); } } } 버킷.업서트(스키마독); |
하지만 저는 비동기 버전을 선호합니다:
1 2 3 4 5 6 7 8 |
관찰 가능.에서(테이블 이름).플랫맵(s -> { 문자열 sql = 문자열.형식("%s에서 * 선택;", s); 반환 관찰 가능.에서(jdbcTemplate.쿼리(sql, new JSONRowMapper(테이블.get(s)))); }) // 가져올 테이블이 포함된 jsonDocument로 시작합니다. .startWith(스키마독).플랫맵(doc -> 비동기 버킷.업서트(doc)); |
여기서는 Rx의 잠재력을 최대한 활용하지 않고 있습니다. 이 함수 는 Couchbase에 문서를 작성하고 시간 초과 및 오류 관리를 처리합니다.
편의를 위해 구현된 모든 단계를 패키지로 묶어 이전에 보여드렸습니다. 단일 프로젝트. 속성 파일이 올바르게 구성되었는지 확인하고 임포터를 실행하기만 하면 됩니다:
1 2 3 |
$ ./bin/카우치베이스-자바-수입자 내 구성.속성 |
다음 내용을 살펴보십시오. README 파일에서 자세한 내용을 확인하세요.
결론
오늘은 SQL 콘텐츠를 Couchbase로 이동하는 방법을 배웠지만 아직 해야 할 일이 남아있습니다. 다음 시간에는 SQL 비즈니스 로직을 애플리케이션 계층으로 이동하는 방법을 알려드리겠습니다.