Couchbase Connect 2015에서는 N1QL을 사용하여 샘플 Couchbase 버킷의 데이터를 쿼리하는 애플리케이션 예제를 시연했습니다.
컨퍼런스를 놓치셨더라도 문제 없습니다. 이 애플리케이션을 재현하는 방법을 살펴보고 Couchbase 4.0의 몇 가지 주요 사항을 확인해 보겠습니다.
전제 조건
- Apache Maven 3
- Java 개발 키트(JDK) 1.7
- 카우치베이스 서버 4.0
- IntelliJ IDEA 14.1+, Eclipse 또는 NetBeans. 이 예제에서는 IntelliJ IDEA가 사용됩니다.
새 프로젝트 만들기
IntelliJ IDEA를 열고 새 Java 프로젝트를 생성하도록 선택하고, 요청이 있는 경우 JDK 1.7을 사용하도록 합니다. 이 가이드의 목적에 따라 프로젝트를 호출해 보겠습니다. try-cb-java.
이제 마우스 오른쪽 버튼을 클릭합니다. try-cb-java 을 선택한 다음, 프로젝트 트리에서 프레임워크 지원 추가 을 클릭하고 Maven. 이렇게 하면 pom.xml 파일을 프로젝트에 추가합니다.
Maven 설정
내부 pom.xml 파일에 프로젝트에 더 매력적인 그룹 이름을 지정하는 것부터 시작하세요:
|
1 2 3 |
<groupId>com.카우치베이스.예제</groupId> |
그런 다음 나머지 종속성을 파일에 추가합니다. 여기에는 Spring Boot, Couchbase 클라이언트 및 Spring 보안 프레임워크가 포함됩니다.
|
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 |
<부모> <groupId>org.스프링 프레임워크.boot</groupId> <artifactId>봄-boot-스타터-부모</artifactId> <버전>1.2.3.릴리스</버전> </부모> <종속성> <종속성> <groupId>org.스프링 프레임워크.boot</groupId> <artifactId>봄-boot-스타터-웹</artifactId> </종속성> <종속성> <groupId>org.스프링 프레임워크</groupId> <artifactId>봄-tx</artifactId> </종속성> <종속성> <groupId>org.스프링 프레임워크.보안</groupId> <artifactId>봄-보안-핵심</artifactId> </종속성> <종속성> <groupId>com.카우치베이스.클라이언트</groupId> <artifactId>자바-클라이언트</artifactId> <버전>2.2.0-dp</버전> </종속성> </종속성> <리포지토리> <저장소> <id>카우치베이스</id> <이름>카우치베이스 repo</이름> <URL>http://files.couchbase.com/maven2 <스냅샷><활성화>false</활성화></스냅샷> </저장소> </리포지토리> <빌드> <플러그인> <플러그인> <groupId>org.스프링 프레임워크.boot</groupId> <artifactId>봄-boot-maven-플러그인</artifactId> </플러그인> </플러그인> </빌드> |
실행 프로필 만들기
현재로서는 현재 구성된 프로필이 없기 때문에 애플리케이션을 실행하려고 하면 오류가 발생하거나 아무 일도 일어나지 않습니다.
도구 모음에서 다음을 선택합니다. 실행 -> 구성 편집 을 클릭하고 새 Maven 구성을 추가하도록 선택합니다. 이름을 원하는 대로 지정할 수 있지만 명령줄 필드에 다음을 입력하는 것이 중요합니다:
|
1 2 3 |
봄-boot:실행 |
이 글에서는 구성의 이름을 다음과 같이 지정하겠습니다. 스프링 부팅.
이제 IntelliJ IDEA를 개발할 준비가 되었습니다.
카우치베이스 버킷에서 인덱스 만들기
이 튜토리얼에서는 N1QL 쿼리를 사용하므로 먼저 Couchbase Server 버킷에 인덱스를 추가해야 합니다. 이 작업은 코드를 통해 쉽게 수행할 수 있지만, 이 예제에서는 바로 가기를 사용하여 Mac OS 및 Windows에 Couchbase 4+를 설치하면 자동으로 설치되는 Couchbase 쿼리(CBQ) 클라이언트를 통해 추가하겠습니다.
Mac OS의 경우 다음에서 CBQ를 실행합니다. /애플리케이션/카우치베이스 서버 앱/콘텐츠/리소스/카우치베이스 코어/빈/cbq 를 클릭하고 다음을 실행합니다:
|
1 2 3 |
만들기 기본 INDEX def_primary 켜기 `여행-샘플` 사용 gsi; |
Windows의 경우 다음 주소에서 CBQ를 실행합니다. C:/프로그램 파일/카우치베이스/서버/bin/cbq.exe 를 클릭하고 Mac OS에서와 동일한 N1QL 명령을 실행합니다.
메인 애플리케이션 클래스 만들기
이 프로젝트의 주요 수업은 다음과 같습니다. Application.java 을 마우스 오른쪽 버튼으로 클릭하여 만들 수 있습니다. trycb 프로젝트 트리에서 패키지를 선택하고 신규 -> 자바 클래스.
클래스를 가장 기본적인 실행 가능한 상태로 만들려면 다음을 추가하세요:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
패키지 trycb; 가져오기 org.스프링 프레임워크.boot.스프링 애플리케이션; 가져오기 org.스프링 프레임워크.boot.자동 구성.스프링 부팅 애플리케이션; 가져오기 org.스프링 프레임워크.웹.바인드.주석.요청 매핑; 가져오기 org.스프링 프레임워크.웹.바인드.주석.RestController; @스프링 부팅 애플리케이션 @RestController @요청 매핑("/api") public 클래스 애플리케이션 { public 정적 void 메인(문자열[] args) { 스프링 애플리케이션.실행(애플리케이션.클래스, args); } } |
다음을 선택하여 프로젝트가 오류 없이 실행되는지 확인합니다. 실행 -> '스프링 부팅' 실행 를 클릭합니다.
CORS(교차 출처 리소스 공유) 처리하기
대부분의 테스트가 로컬에서 이루어지므로 CORS가 활성화되어 있는지 확인해야 하며, 그렇지 않으면 웹 브라우저가 자바스크립트로 API 엔드포인트를 호출할 때 불만을 제기할 수 있습니다.
Application 클래스가 Filter 클래스를 구현하는지 확인하고 다음 코드를 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@오버라이드 public void doFilter(서블릿 요청 req, 서블릿 응답 res, 필터체인 체인) 던지기 IOException, 서블릿 예외 { Http서블릿응답 응답 = (Http서블릿응답) res; 응답.setHeader("액세스 제어-허용-오리진", "*"); 응답.setHeader("액세스 제어-허용-헤더", "원본, X-요청-함께, 콘텐츠 유형, 수락"); 체인.doFilter(req, res); } @오버라이드 public void init(FilterConfig filterConfig) 던지기 서블릿 예외 {} @오버라이드 public void 파괴() {} |
카우치베이스 클러스터 및 버킷 옵션 구성하기
현재로서는 기본적으로 Couchbase가 개입하지 않는 Spring Boot 애플리케이션이 있습니다. Maven을 통해 Java 클라이언트를 포함했으므로 이제 사용을 시작할 때입니다.
애플리케이션 클래스에 다음을 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
@가치("${호스트명}") 비공개 문자열 호스트 이름; @가치("${버킷}") 비공개 문자열 버킷; @가치("${비밀번호}") 비공개 문자열 비밀번호; public @Bean 클러스터 클러스터() { 반환 카우치베이스클러스터.create(호스트 이름); } public @Bean 버킷 버킷() { 반환 클러스터().오픈버킷(버킷, 비밀번호); } |
저희는 아직 호스트 이름, 버킷및 비밀번호 변수를 사용할 수 있지만, Couchbase 클러스터와 특정 버킷에 연결하는 데 사용됩니다.
리소스 변수 추가
우리가 사용하고 있는 것을 보셨죠? 호스트 이름, 버킷및 비밀번호이제 설정할 차례입니다.
IntelliJ IDEA 프로젝트 트리에서 다음을 마우스 오른쪽 버튼으로 클릭합니다. src/main/resources 을 클릭하고 새로 만들기 -> 파일. 새 파일 이름을 지정합니다. application.properties 를 클릭하고 다음 줄을 추가합니다:
|
1 2 3 |
호스트 이름=127.0.0.1 버킷=여행-샘플 비밀번호= |
스프링 부트가 이 문제를 해결합니다. application.properties 파일로 이동합니다. 애플리케이션 관련 속성에 대한 자세한 정보는 스프링 공식 문서.
RESTful 엔드포인트 만들기
이 애플리케이션은 API 기반이 될 것이므로 특정 엔드포인트를 만들어야 합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
@요청 매핑(값="/에어포트/찾기", 메서드=요청 메서드.GET) public 목록<지도<문자열, 개체>> 공항(@요청 매개변수 문자열 검색) { } @요청 매핑(값="/flightPath/findAll", 메서드=요청 메서드.GET) public 목록<지도<문자열, 개체>> 모두(@요청 매개변수 문자열 에서, @요청 매개변수 문자열 에, @요청 매개변수 문자열 leave) 던지기 예외 { } @요청 매핑(값="/user/login", 메서드=요청 메서드.GET) public 개체 로그인(@요청 매개변수 문자열 사용자, @요청 매개변수 문자열 비밀번호) { } @요청 매핑(값="/user/login", 메서드=요청 메서드.POST) public 개체 createLogin(@RequestBody 문자열 json) { } @요청 매핑(값="/user/flights", 메서드=요청 메서드.POST) public 개체 책(@RequestBody 문자열 json) { } @요청 매핑(값="/user/flights", 메서드=요청 메서드.GET) public 개체 예약됨(@요청 매개변수 문자열 사용자 이름) { } |
기본적으로 사용자 등록 및 로그인, 항공편 예약 및 검색, 항공편 정보 검색을 위한 엔드포인트가 제공됩니다.
이러한 엔드포인트 뒤에 있는 로직은 청결성을 위한 다른 클래스에 나타납니다.
데이터베이스 클래스 만들기
방금 Spring Boot 애플리케이션의 구동 엔드포인트를 설정했지만 이제 데이터베이스와 상호 작용하는 로직을 살펴볼 차례입니다.
이 프로젝트의 데이터베이스 클래스는 다음과 같습니다. Database.java 을 마우스 오른쪽 버튼으로 클릭하여 만들 수 있습니다. trycb 패키지를 선택하고 IntelliJ IDEA의 프로젝트 트리에서 신규 -> 자바 클래스.
다음 내용을 추가하여 클래스의 골격을 완성하세요:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
패키지 trycb; public 클래스 데이터베이스 { 비공개 데이터베이스() { } public 정적 목록<지도<문자열, 개체>> 모든 공항 찾기(final 버킷 버킷, final 문자열 매개변수) { } public 정적 목록<지도<문자열, 개체>> 모든 비행 경로 찾기(final 버킷 버킷, 문자열 에서, 문자열 에, 캘린더 leave) { } public 정적 응답 엔티티<문자열> 로그인(final 버킷 버킷, final 문자열 사용자 이름, final 문자열 비밀번호) { } public 정적 응답 엔티티<문자열> createLogin(final 버킷 버킷, final 문자열 사용자 이름, final 문자열 비밀번호) { } 비공개 정적 목록<지도<문자열, 개체>> 추출 결과 또는 던지기(QueryResult 결과) { } } |
이제부터 사용자가 상호 작용할 가능성이 높은 순서대로 각 메서드를 완성해 보겠습니다.
새 사용자 만들기
사용자가 POST를 발행할 때 /api/user/login로 설정하면 다음 데이터베이스 함수를 호출해야 합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
public 정적 응답 엔티티<문자열> createLogin(final 버킷 버킷, final 문자열 사용자 이름, final 문자열 비밀번호) { JsonObject 데이터 = JsonObject.create() .put("type", "user") .put("name", 사용자 이름) .put("비밀번호", BCrypt.hashpw(비밀번호, BCrypt.gensalt())); JsonDocument doc = JsonDocument.create("user::" + 사용자 이름, 데이터); 시도 { 버킷.삽입(doc); JsonObject 응답데이터 = JsonObject.create() .put("성공", true) .put("데이터", 데이터); 반환 new 응답 엔티티<문자열>(응답데이터.toString(), HttpStatus.확인); } catch (예외 e) { JsonObject 응답데이터 = JsonObject.빈() .put("성공", false) .put("실패", "계정을 만드는 동안 오류가 발생했습니다.") .put("예외", e.getMessage()); 반환 new 응답 엔티티<문자열>(응답데이터.toString(), HttpStatus.확인); } } |
요청에 포함된 사용자 이름과 비밀번호는 JSON 객체에 추가되고 비밀번호는 Spring BCrypt 라이브러리를 사용하여 암호화됩니다. 사용자 데이터를 추적하기 위해 새 사용자는 다음과 같은 제목의 문서에 저장됩니다. user::{사용자명_여기}. 사용 bucket.insert(doc)를 호출하면 데이터를 버킷에 삽입하려고 시도합니다. 예외가 발생하지 않으면 삽입이 성공하고 응답이 반환됩니다. 예외가 발생하면 삽입에 실패하고 오류가 반환됩니다.
기존 사용자로 로그인하기
사용자가 동일한 주소로 GET을 발행할 때 /api/user/login 엔드포인트를 호출하려면 다음 데이터베이스 함수를 호출해야 합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
public 정적 응답 엔티티<문자열> 로그인(final 버킷 버킷, final 문자열 사용자 이름, final 문자열 비밀번호) { JsonDocument doc = 버킷.get("user::" + 사용자 이름); JsonObject 응답 콘텐츠; 만약(BCrypt.checkpw(비밀번호, doc.콘텐츠().getString("비밀번호"))) { 응답 콘텐츠 = JsonObject.create().put("성공", true).put("데이터", doc.콘텐츠()); } else { 응답 콘텐츠 = JsonObject.빈().put("성공", false).put("실패", "잘못된 사용자 이름 또는 비밀번호"); } 반환 new 응답 엔티티<문자열>(응답 콘텐츠.toString(), HttpStatus.확인); } |
사용 bucket.get("user::" + 사용자명) 에 제공된 사용자 이름을 입력하면 Java 애플리케이션이 버킷에 문서가 있는 경우 해당 문서를 가져옵니다. Spring BCrypt 라이브러리에는 제공된 비밀번호가 저장된 암호화된 비밀번호와 일치하는지 여부를 확인할 수 있는 훌륭한 함수가 있습니다. 일치하면 성공 객체를 반환하고, 그렇지 않으면 로그인 실패 객체를 반환합니다.
N1QL 결과 추출 및 Java에서 읽을 수 있게 만들기
N1QL은 QueryResult 객체를 반환하는 것은 요청하는 프런트엔드에 데이터를 반환하는 경우 바람직하지 않을 수 있습니다. 우리가 정말로 하고 싶은 것은 이 데이터를 목록 객체입니다.
|
1 2 3 4 5 6 7 8 9 10 11 12 |
비공개 정적 목록<지도<문자열, 개체>> 추출 결과 또는 던지기(QueryResult 결과) { 만약 (!결과.최종 성공()) { throw new 데이터 검색 실패 예외("쿼리 오류: " + 결과.오류()); } 목록<지도<문자열, 개체>> 콘텐츠 = new ArrayList<지도<문자열, 개체>>(); 에 대한 (QueryRow 행 : 결과) { 콘텐츠.추가(행.값().toMap()); } 반환 콘텐츠; } |
이 함수는 N1QL 데이터가 반환될 때마다 호출됩니다.
모든 공항 찾기
이제 공항 검색과 관련하여 N1QL의 마법 같은 기능에 대해 알아보겠습니다.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public 정적 목록<지도<문자열, 개체>> 모든 공항 찾기(final 버킷 버킷, final 문자열 매개변수) { 성명서 쿼리; AsPath 접두사 = 선택("공항명").에서(i(버킷.이름())); 만약 (매개변수.길이() == 3) { 쿼리 = 접두사.어디(x("faa").eq(s(매개변수.대문자()))); } else 만약 (매개변수.길이() == 4 && (매개변수.같음(매개변수.대문자()) || 매개변수.같음(매개변수.toLowerCase()))) { 쿼리 = 접두사.어디(x("icao").eq(s(매개변수.대문자()))); } else { 쿼리 = 접두사.어디(i("공항명").같은(s(매개변수 + "%"))); } QueryResult 결과 = 버킷.쿼리(쿼리.simple(쿼리)); 반환 추출 결과 또는 던지기(결과); } |
위의 코드에서 IntelliJ IDEA와 함께 Fluent API를 사용하여 N1QL 쿼리를 생성하는 것을 볼 수 있습니다. 기본적으로 원시 SQL을 보면 다음과 같이 보일 것입니다:
|
1 2 3 |
선택 공항명 FROM `여행-샘플` 어디 faa = {{매개변수}} |
위 예에서 {{PARAMS}}는 LAX 또는 이와 유사한 공항을 나타냅니다. 물론 매개변수 길이가 3인 경우입니다.
모든 항공편 노선 찾기
마지막으로 비행 경로를 찾는 방법만 남았습니다:
|
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 |
public 정적 목록<지도<문자열, 개체>> 모든 비행 경로 찾기(final 버킷 버킷, 문자열 에서, 문자열 에, 캘린더 leave) { 성명서 쿼리 = 선택(x("faa").as("fromAirport")) .에서(i(버킷.이름())) .어디(x("공항명").eq(s(에서))) .union() .선택(x("faa").as("toAirport")) .에서(i(버킷.이름())) .어디(x("공항명").eq(s(에))); QueryResult 결과 = 버킷.쿼리(쿼리.simple(쿼리)); 만약 (!결과.최종 성공()) { throw new 데이터 검색 실패 예외("쿼리 오류: " + 결과.오류()); } 문자열 출발공항 = null; 문자열 toAirport = null; 에 대한 (QueryRow 행 : 결과) { 만약 (행.값().containsKey("fromAirport")) { 출발공항 = 행.값().getString("fromAirport"); } 만약 (행.값().containsKey("toAirport")) { toAirport = 행.값().getString("toAirport"); } } 성명서 joinQuery = 선택("a.name", "s.flight", "s.utc", "r.sourceairport", "r.destinationairport", "r.equipment") .에서(i(버킷.이름()).as("r")) .unnest("r.schedule AS s") .join(i(버킷.이름()).as("a") + " ON KEYS r.airlineid") .어디(x("r.sourceairport").eq(s(출발공항)).그리고(x("r.destinationairport").eq(s(toAirport))).그리고(x("s.day").eq(leave.get(캘린더.DAY_OF_MONTH)))) .주문 기준(정렬.asc("a.name")); QueryResult 다른 결과 = 버킷.쿼리(joinQuery); 반환 추출 결과 또는 던지기(다른 결과); } |
이 방법에서는 두 가지 N1QL 쿼리를 수행합니다. 첫 번째는 다음과 같이 쉽게 번역할 수 있습니다:
|
1 2 3 |
선택 faa AS 출발공항 FROM `여행-샘플` 어디 공항명 = {{매개변수.FROM}} UNION 선택 faa AS toAirport FROM `여행-샘플` 어디 공항명 = {{매개변수.TO}} |
물론 {{PARAMS}}는 엔드포인트에 전달된 모든 것을 의미합니다. 이 문에서, 우리는 모든 에서 공항과 모든 에 공항.
두 결과 집합을 모두 가져온 후, 두 결과 집합을 반복하여 에 그리고 에서 공항이 존재하지 않으면 기본값을 NULL로 설정하여 다음 쿼리가 성공하지 못하도록 합니다.
두 번째 쿼리는 다음과 같은 원시 쿼리로 변환할 수 있습니다:
|
1 2 3 |
선택 a.이름, s.비행, s.UTC, r.소스공항, r.목적지공항, r.장비 FROM `여행-샘플` AS r UNNEST r.일정 AS s JOIN `여행-샘플` AS a 켜기 키 r.airlineid 어디 r.소스공항 = {{TO}} AND r.목적지공항 = {{TO}} AND s.일 = 3 주문 BY a.이름 ASC |
JSON 문서에서 중첩을 해제하고 이제 평평한 키에 결합하여 항공편에 대한 일정 정보를 가져오고 있습니다.
애플리케이션 및 데이터베이스 클래스 마무리
이제 엔드포인트와 데이터베이스 메서드가 있지만 서로 연결되어 있지 않습니다. 이제 Application.java 클래스를 열고 이전에 만든 함수에 몇 가지 코드를 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
@요청 매핑(값="/user/login", 메서드= 요청 메서드.GET) public 개체 로그인(@요청 매개변수 문자열 사용자, @요청 매개변수 문자열 비밀번호) { 반환 데이터베이스.로그인(버킷(), 사용자, 비밀번호); } @요청 매핑(값="/user/login", 메서드=요청 메서드.POST) public 개체 createLogin(@RequestBody 문자열 json) { JsonObject jsonData = JsonObject.fromJson(json); 반환 데이터베이스.createLogin(버킷(), jsonData.getString("user"), jsonData.getString("비밀번호")); } |
두 개의 정적 데이터베이스 메서드는 사용자 계정과 관련된 각 엔드포인트에서 호출됩니다. 이전에 만든 다른 엔드포인트에 대해서도 동일한 프로세스를 수행할 수 있습니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
@요청 매핑("/에어포트/찾기") public 목록<지도<문자열, 개체>> 공항(@요청 매개변수 문자열 검색) { 반환 데이터베이스.모든 공항 찾기(버킷(), 검색); } @요청 매핑("/flightPath/findAll") public 목록<지도<문자열, 개체>> 모두(@요청 매개변수 문자열 에서, @요청 매개변수 문자열 에, @요청 매개변수 문자열 leave) 던지기 예외 { 캘린더 캘린더 = 캘린더.getInstance(로캘.US); 캘린더.setTime(날짜 형식.getDateInstance(날짜 형식.쇼트, 로캘.US).parse(leave)); 반환 데이터베이스.모든 비행 경로 찾기(버킷(), 에서, 에, 캘린더); } |
샘플 엔드포인트 테스트
애플리케이션의 엔드포인트를 테스트하는 방법에는 몇 가지가 있습니다. 이 예에서는 cURL을 사용하지만 Google Chrome용 Postman 또는 이와 유사한 것을 사용할 수도 있습니다.
cURL을 설치한 상태에서 터미널 또는 명령 프롬프트를 열고 다음을 입력합니다:
|
1 2 3 |
curl -X GET 'http://localhost:8080/api/airport/findAll?search=LAX' |
위의 cURL 명령은 api/에어포트/findAll 엔드포인트의 매개변수를 전달하고 검색=LAX. 성공하면 다음과 같은 응답을 받아야 합니다:
|
1 |
[{"공항명":"Los Angeles Intl"}] |
다른 모든 엔드포인트에 대해서도 동일한 종류의 테스트를 수행할 수 있습니다.
결론
방금 카우치베이스 서버와 Java용 스프링 부트를 사용하는 샘플 여행 애플리케이션을 설정하는 방법을 살펴보았습니다. 프런트엔드를 설정하지는 않았지만 AngularJS, jQuery 또는 ReactJS와 같은 언어를 사용하여 프런트엔드를 추가하는 것은 매우 가능합니다.
이 전체 프로젝트와 AngularJS 프런트엔드는 다음에서 얻을 수 있습니다. 카우치베이스 랩 깃허브 채널.