항목, 속성, 키

Datastore의 데이터 객체를 항목이라고 합니다. 항목에는 명명된 속성이 한 개 이상 있으며 각 속성에는 값이 한 개 이상 있을 수 있습니다. 같은 종류의 항목이라도 속성이 동일할 필요는 없으며, 항목의 특정 속성 값이 모두 동일한 데이터 유형이 아니어도 됩니다. 필요한 경우 애플리케이션이 자체 데이터 모델에 이러한 제한을 설정하고 시행할 수 있습니다.

Datastore는 다양한 속성 값 데이터 유형을 지원합니다. 여기에는 다음이 포함됩니다.

  • 정수
  • 부동 소수점 수
  • 문자열
  • 날짜
  • 바이너리 데이터

전체 유형 목록은 속성 및 값 유형을 참조하세요.

Datastore의 각 항목에는 항목을 고유하게 식별하는 가 있습니다. 키는 다음 구성요소로 구성됩니다.

  • 멀티테넌시를 허용하는 항목의 네임스페이스
  • Datastore 쿼리 목적으로 항목을 분류하는 항목의 종류
  • 다음 중 하나에 해당하는 개별 항목의 식별자
    • 키 이름 문자열
    • 정수 숫자 ID
  • Datastore 계층 구조에서 항목 위치를 나타내는 선택적 상위 경로

애플리케이션은 항목의 키를 사용하여 Datastore에서·개별 항목을 가져오거나 항목 키 또는 속성 값에 따라 쿼리를 실행하여 항목을 한 개 이상 검색할 수 있습니다.

자바 App Engine SDK에는 Datastore의 기능을 직접적으로 지원하는 간단한 API(com.google.appengine.api.datastore 패키지에서 제공)가 포함되어 있습니다. 이 문서의 모든 예시는 이러한 하위 수준 API를 기준으로 합니다. 이 API를 애플리케이션에서 직접 사용하거나 고유한 데이터 관리 레이어를 빌드하기 위한 기초로 사용할 수 있습니다.

Datastore 자체는 지정된 속성에 특정 유형의 값이 있는지 여부와 같은 항목 구조에 대한 제한을 적용하지 않습니다. 이 태스크는 애플리케이션에서 수행됩니다.

종류 및 식별자

각 Datastore 항목은 쿼리 목적으로 항목을 분류하는 특정 종류에 속합니다. 예를 들어 인사 관리 애플리케이션은 회사의 각 직원을 종류가 Employee인 항목으로 나타낼 수 있습니다. 자바 Datastore API에서는 항목을 만들 때 Entity() 생성자의 인수로 항목 종류를 지정합니다. 밑줄(__) 두 개로 시작하는 종류 이름은 모두 예약되어 있어 이를 사용할 수 없습니다.

다음 예시에서는 Employee 종류의 항목을 만들고 속성 값을 채우고 Datastore에 저장합니다.

Entity employee = new Entity("Employee", "asalieri");
employee.setProperty("firstName", "Antonio");
employee.setProperty("lastName", "Salieri");
employee.setProperty("hireDate", new Date());
employee.setProperty("attendedHrTraining", true);

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();
datastore.put(employee);

종류 외에도 각 항목에는 항목이 생성될 때 할당된 식별자가 있습니다. 식별자는 항목 키의 일부이므로 항목과 영구적으로 연관되며 변경할 수 없습니다. 식별자를 할당할 수 있는 두 가지 방법은 다음과 같습니다.

  • 애플리케이션은 항목에 자체 키 이름 문자열을 지정할 수 있습니다.
  • Datastore에서 항목에 정수 숫자 ID를 자동으로 할당하도록 할 수 있습니다.

항목에 키 이름을 할당하려면 항목을 만들 때 생성자에 대한 두 번째 인수로 이름을 제공합니다.

Entity employee = new Entity("Employee", "asalieri");

Datastore가 숫자 ID를 자동으로 할당하도록 하려면 이 인수를 생략합니다.

Entity employee = new Entity("Employee");

식별자 할당

두 가지 자동 ID 정책을 사용하여 자동으로 ID를 생성하도록 Datastore를 구성할 수 있습니다.

  • default 정책은 거의 균일하게 분포한 미사용 ID의 임의 시퀀스를 생성합니다. 각 ID는 최대 16자리 숫자입니다.
  • legacy 정책은 연속되지 않는 더 작은 정수 ID 시퀀스를 만듭니다.

사용자에게 항목 ID를 표시하거나 항목 ID의 순서를 결정하려는 경우 수동으로 할당하는 것이 좋습니다.

Datastore는 거의 균일하게 분포된 임의 순서의 미사용 ID를 생성합니다. 각 ID는 최대 16자리 숫자입니다.

상위 경로

Cloud Datastore의 항목은 파일 시스템의 디렉터리 구조와 유사한 계층 구조 형식의 공간을 형성합니다. 항목을 만들 때 선택적으로 다른 항목을 상위 요소로 지정할 수 있으며, 새 항목은 상위 항목의 하위 요소가 됩니다. 파일 시스템과 달리 상위 항목이 실제로 존재할 필요는 없습니다. 상위 요소가 없는 항목은 루트 항목입니다. 항목과 상위 요소 간의 연결은 영구적이며 항목이 생성되면 변경할 수 없습니다. Cloud Datastore는 상위 요소가 동일한 2개의 항목 또는 2개의 루트 항목(상위 요소가 없는 항목)에 동일한 숫자 ID를 할당하지 않습니다.

항목의 상위 요소, 상위 요소의 상위 요소 등은 재귀적으로 해당 항목의 상위가 되고, 항목의 하위 요소, 하위 요소의 하위 요소 등은 해당 항목의 하위가 됩니다. 루트 항목과 그 하위에 있는 모든 항목은 동일한 항목 그룹에 속합니다. 루트 항목에서 시작하여 상위 요소에서 하위 요소로 진행되고 지정된 항목까지 이어지는 항목의 시퀀스를 해당 항목의 상위 경로라고 합니다. 항목을 식별하는 전체 키는 상위 경로를 지정하고 항목 자체로 종료되는 종류-식별자 쌍의 시퀀스로 구성됩니다.

[Person:GreatGrandpa, Person:Grandpa, Person:Dad, Person:Me]

루트 항목의 경우 상위 경로는 비어 있으며 키는 항목의 고유한 종류 및 식별자로만 구성됩니다.

[Person:GreatGrandpa]

이 개념은 다음 다이어그램에서 설명합니다.

항목 그룹에서 루트 항목과 하위 항목의 관계 표시

항목의 상위를 지정하려면 하위 항목 생성 시 상위 항목의 키를 Entity() 생성자에 대한 인수로 제공합니다. 상위 항목의 getKey() 메서드를 호출하여 키를 가져올 수 있습니다.

Entity employee = new Entity("Employee");
datastore.put(employee);

Entity address = new Entity("Address", employee.getKey());
datastore.put(address);

새 항목에도 키 이름이 포함된 경우 Entity() 생성자에 대한 두 번째 인수로 키 이름을 제공하고 상위 항목의 키를 세 번째 인수로 제공합니다.

Entity address = new Entity("Address", "addr1", employee.getKey());

트랜잭션 및 항목 그룹

항목을 생성, 업데이트 또는 삭제하려는 모든 시도는 트랜잭션의 관점에서 수행됩니다. 단일 트랜잭션에 포함할 수 있는 이러한 작업의 수에는 제한이 없습니다. 데이터의 일관성을 유지하기 위해 트랜잭션은 포함된 모든 작업을 Datastore에 하나의 단위로 적용합니다. 즉, 작업이 하나라도 실패하면 어떠한 작업에도 적용되지 않습니다. 또한 동일 트랜잭션 내에서 수행되는 모든 strong consistency를 가지는 읽기(상위 쿼리 또는 get)에서 데이터의 일관된 스냅샷이 관찰됩니다.

위에 언급한 대로 항목 그룹은 상위 항목을 통해 공통 루트 요소로 연결되는 항목 집합입니다. 데이터를 항목 그룹으로 구성하면 수행 가능한 트랜잭션을 제한할 수 있습니다.

  • 트랜잭션에서 액세스하는 모든 데이터는 최대 25개의 항목 그룹에만 포함될 수 있습니다.
  • 트랜잭션 내에서 쿼리를 사용하려면 올바른 데이터에 일치하는 상위 필터를 지정할 수 있는 방식으로 데이터가 항목 그룹으로 구성되어 있어야 합니다.
  • 단일 항목 그룹 내에서는 쓰기 처리량 한도가 초당 약 1개의 트랜잭션으로 제한되어 있습니다. 이 한도가 존재하는 이유는 Datastore가 높은 안정성과 내결함성을 제공하기 위해 광범위한 지리적 영역에 걸쳐 각 항목 그룹의 마스터 없는 동기 복제 작업을 수행하기 때문입니다.

많은 애플리케이션에서 관련성이 없는 데이터를 포괄적으로 확인할 때는 eventual consistency(즉, 여러 항목 그룹에 걸친 비상위 쿼리로 약간 오래된 데이터를 반환할 수 있음)를 사용하고 관련성이 높은 단일 데이터 세트를 보거나 수정할 때는 strong consistency(상위 쿼리 또는 단일 항목의 get)를 사용하는 것이 좋을 수 있습니다. 이러한 애플리케이션에서는 일반적으로 관련성이 높은 각 데이터 세트마다 별도의 항목 그룹을 사용하는 방식이 바람직합니다. 자세한 내용은 strong consistency를 위한 구조화를 참조하세요.

속성 및 값 유형

항목과 연관된 데이터 값은 하나 이상의 속성으로 구성됩니다. 각 속성에는 이름과 하나 이상의 값이 있습니다. 속성에는 둘 이상의 유형 값이 있을 수 있으며, 두 개의 항목에 동일한 속성의 서로 다른 유형 값이 있을 수 있습니다. 속성은 색인 생성되거나 색인 생성되지 않을 수 있으며, 속성 P를 기준으로 정렬 또는 필터링하는 쿼리는 P가 색인 생성되지 않은 항목을 무시합니다. 항목 하나에 포함될 수 있는 색인 생성된 속성은 최대 20,000개입니다.

지원되는 값 유형은 다음과 같습니다.

값 유형 Java 유형 정렬 순서 참고
정수 short
int
long
java.lang.Short
java.lang.Integer
java.lang.Long
숫자 긴 정수로 저장된 후 필드 유형으로 변환됨

값이 범위를 벗어나면 오버플로 발생
부동 소수점 수 float
double
java.lang.Float
java.lang.Double
숫자 64비트 배정밀도,
IEEE 754
불리언 boolean
java.lang.Boolean
false<true
텍스트 문자열(short) java.lang.String 유니코드 최대 1,500 바이트

값이 1,500바이트보다 크면 IllegalArgumentException 발생
텍스트 문자열(long) com.google.appengine.api.datastore.Text 없음 최대 1MB

색인 생성되지 않음
바이트 문자열(short) com.google.appengine.api.datastore.ShortBlob 바이트순 최대 1,500 바이트

값이 1,500바이트보다 길면 IllegalArgumentException 발생
바이트 문자열(long) com.google.appengine.api.datastore.Blob 없음 최대 1MB

색인 생성되지 않음
날짜 및 시간 java.util.Date 시간순
지리적 지점 com.google.appengine.api.datastore.GeoPt 위도순 우선 적용
후 경도순
우편 주소 com.google.appengine.api.datastore.PostalAddress 유니코드
전화번호 com.google.appengine.api.datastore.PhoneNumber 유니코드
이메일 주소 com.google.appengine.api.datastore.Email 유니코드
Google 계정 사용자 com.google.appengine.api.users.User 이메일 주소
(유니코드 순서)
채팅 메시지 핸들 com.google.appengine.api.datastore.IMHandle 유니코드
링크 com.google.appengine.api.datastore.Link 유니코드
카테고리 com.google.appengine.api.datastore.Category 유니코드
평가 com.google.appengine.api.datastore.Rating 숫자
Datastore 키 com.google.appengine.api.datastore.Key
또는 참조된 객체(하위 항목)
경로 요소 기준
(종류, 식별자,
종류, 식별자...)
최대 1,500 바이트

값이 1,500바이트보다 길면 IllegalArgumentException 발생
Blobstore 키 com.google.appengine.api.blobstore.BlobKey 바이트순
삽입 항목 com.google.appengine.api.datastore.EmbeddedEntity 없음 색인이 생성되지 않음
Null null 없음

중요: users.User는 고유 ID 및 이메일 주소를 포함하므로 속성 값으로 저장하지 않는 것이 좋습니다. 사용자가 자신의 이메일 주소를 변경하고 이전에 저장한 user.User 값과 새 user.User 값을 비교하면 서로 일치하지 않게 됩니다. 대신 User 사용자 ID 값을 사용자의 안정적인 고유 식별자로 사용하세요.

텍스트 문자열 및 인코딩되지 않은 바이너리 데이터(바이트 문자열)에 대해 Datastore는 두 가지 값 유형을 지원합니다.

  • short 문자열(최대 1500바이트)은 색인이 생성되고 쿼리 필터 조건 및 정렬 순서에 사용될 수 있습니다.
  • long 문자열(최대 1MB)은 색인이 생성되지 않으며 쿼리 필터 및 정렬 순서에 사용될 수 없습니다.
참고: 긴 바이트 문자열 유형 이름은 Datastore API에서 Blob으로 지정되지만 이 유형은 Blobstore API에 사용되는 blob과는 관련이 없습니다.

쿼리에 혼합 유형 값이 있는 필드가 있으면 Datastore는 내부 표시를 기준으로 확정된 순서를 사용합니다.

  1. Null 값
  2. 고정 소수점 수
    • 정수
    • 날짜 및 시간
    • 평가
  3. 부울 값
  4. 바이트 시퀀스
    • 바이트 문자열
    • 유니코드 문자열
    • Blobstore 키
  5. 부동 소수점 수
  6. 지리적 지점
  7. Google 계정 사용자
  8. Datastore 키

긴 텍스트 문자열, 긴 바이트 문자열, 삽입 항목은 색인이 생성되지 않기 때문에 순서가 정의되지 않습니다.

항목 작업

애플리케이션은 Datastore API를 사용하여 항목을 생성, 검색, 업데이트, 삭제할 수 있습니다. 애플리케이션이 항목의 전체 키를 알고 있거나 상위 키, 종류, 식별자에서 파생시킬 수 있는 경우 키를 사용하여 항목에서 직접 작동할 수 있습니다. 또한 애플리케이션은 Datastore 쿼리의 결과로 항목 키를 가져올 수 있습니다. 자세한 내용은 Datastore 쿼리 페이지를 참조하세요.

자바 Datastore API는 DatastoreService 인터페이스의 메서드를 사용하여 항목 작업을 수행합니다. 정적 메서드 DatastoreServiceFactory.getDatastoreService()를 호출하여 DatastoreService 객체를 가져옵니다.

DatastoreService datastore = DatastoreServiceFactory.getDatastoreService();

항목 생성

Entity() 클래스의 인스턴스를 만들어 새 항목을 만들 수 있으며 항목 종류를 Entity 생성자의 인수로 제공합니다.

필요한 경우 항목 속성에 값을 채운 후 DatastoreService.put() 메서드에 인수로 전달하여 Datastore에 저장합니다. 항목의 키 이름을 지정하려면 생성자의 두 번째 인수로 전달하면 됩니다.

Entity employee = new Entity("Employee", "asalieri");
// Set the entity properties.
// ...
datastore.put(employee);

키 이름을 제공하지 않으면 Datastore가 항목 키의 숫자 ID를 자동으로 생성합니다.

Entity employee = new Entity("Employee");
// Set the entity properties.
// ...
datastore.put(employee);

항목 검색

특정 키로 식별되는 항목을 검색하려면 Key 객체를 DatastoreService.get() 메서드에 전달합니다.

// Key employeeKey = ...;
Entity employee = datastore.get(employeeKey);

항목 업데이트

기존 항목을 업데이트하려면 항목 객체의 속성을 수정한 후 DatastoreService.put() 메서드에 전달합니다. 이렇게 하면 객체 데이터가 기존 항목을 덮어씁니다. put()을 호출할 때마다 전체 객체가 Datastore로 전송됩니다.

항목 삭제

항목 키가 지정되면 DatastoreService.delete() 메서드로 항목을 삭제할 수 있습니다.

// Key employeeKey = ...;
datastore.delete(employeeKey);

반복 속성

단일 속성 내에 여러 개의 값을 저장할 수 있습니다.

Entity employee = new Entity("Employee");
ArrayList<String> favoriteFruit = new ArrayList<String>();
favoriteFruit.add("Pear");
favoriteFruit.add("Apple");
employee.setProperty("favoriteFruit", favoriteFruit);
datastore.put(employee);

// Sometime later
employee = datastore.get(employee.getKey());
@SuppressWarnings("unchecked") // Cast can't verify generic type.
    ArrayList<String> retrievedFruits = (ArrayList<String>) employee
    .getProperty("favoriteFruit");

삽입 항목

경우에 따라서는 항목을 다른 항목의 속성으로 삽입하는 것이 편리할 수 있습니다. 이 방식은 예를 들어 항목 내에 속성 값의 계층 구조를 만들 때 유용합니다. 이를 위해 Java 클래스 EmbeddedEntity를 사용할 수 있습니다.

// Entity employee = ...;
EmbeddedEntity embeddedContactInfo = new EmbeddedEntity();

embeddedContactInfo.setProperty("homeAddress", "123 Fake St, Made, UP 45678");
embeddedContactInfo.setProperty("phoneNumber", "555-555-5555");
embeddedContactInfo.setProperty("emailAddress", "test@example.com");

employee.setProperty("contactInfo", embeddedContactInfo);

삽입된 항목이 색인에 포함된 경우 하위 속성을 쿼리할 수 있습니다. 삽입된 항목을 색인 생성에서 제외할 경우, 모든 하위 속성도 색인 생성에서 제외됩니다. 선택적으로 키를 삽입된 항목과 연결할 수 있지만, 완전한 항목과 달리 키는 필수가 아니며 키가 제공되었더라도 이를 사용하여 항목을 검색할 수 없습니다.

삽입 항목의 속성에 수동으로 값을 채우는 대신 setPropertiesFrom() 메서드를 사용하여 기존 항목에서 복사할 수 있습니다.

// Entity employee = ...;
// Entity contactInfo = ...;
EmbeddedEntity embeddedContactInfo = new EmbeddedEntity();

embeddedContactInfo.setKey(contactInfo.getKey()); // Optional, used so we can recover original.
embeddedContactInfo.setPropertiesFrom(contactInfo);

employee.setProperty("contactInfo", embeddedContactInfo);

이후에 동일한 메서드를 사용하여 삽입 항목에서 원래 항목을 복구할 수 있습니다.

Entity employee = datastore.get(employeeKey);
EmbeddedEntity embeddedContactInfo = (EmbeddedEntity) employee.getProperty("contactInfo");

Key infoKey = embeddedContactInfo.getKey();
Entity contactInfo = new Entity(infoKey);
contactInfo.setPropertiesFrom(embeddedContactInfo);

일괄 작업

DatastoreService 메서드 put(), get(), delete()(및 AsyncDatastoreService 상대 메서드)에는 반복 가능한 객체(put()Entity 클래스, get()delete()Key 클래스)를 허용하고 이 객체를 사용하여 단일 Datastore 호출에서 여러 항목에 작업을 수행하는 일괄 버전이 포함되어 있습니다.

Entity employee1 = new Entity("Employee");
Entity employee2 = new Entity("Employee");
Entity employee3 = new Entity("Employee");
// ...

List<Entity> employees = Arrays.asList(employee1, employee2, employee3);
datastore.put(employees);

이러한 일괄 작업은 모든 항목 또는 키를 항목 그룹으로 그룹화한 후 각 항목 그룹에서 요청된 작업을 병렬로 수행합니다. 일괄 호출은 단 하나의 서비스 호출에 대해서만 오버헤드를 발생시키므로 개별 항목에 대해 호출을 별도로 수행하는 것보다 빠릅니다. 항목 그룹 여러 개가 관련된 경우 모든 그룹의 작업이 서버 측에서 동시에 수행됩니다.

키 생성

애플리케이션은 KeyFactory 클래스를 사용하여 항목의 종류 및 식별자와 같은 알려진 구성요소의 항목에 대한 Key 객체를 만들 수 있습니다. 항목에 상위 요소가 없으면 종류 및 식별자(키 이름 문자열 또는 숫자 ID)를 정적 메서드 KeyFactory.createKey()에 전달하여 키를 만듭니다. 다음 예시에서는 키 이름 "GreatGrandpa" 또는 숫자 ID 74219를 사용하여 Person 종류 항목의 키를 만듭니다.

Key k1 = KeyFactory.createKey("Person", "GreatGrandpa");
Key k2 = KeyFactory.createKey("Person", 74219);

키에 경로 구성요소가 포함된 경우 도우미 클래스 KeyFactory.Builder를 사용하여 경로를 빌드할 수 있습니다. 이 클래스의 addChild 메서드는 경로에 단일 항목을 추가하고 빌더 자체를 반환하므로, 루트 항목을 시작으로 여러 개의 호출을 서로 연결하여 한 번에 한 항목씩 경로를 빌드할 수 있습니다. 전체 경로를 빌드한 후에는 getKey를 호출하여 결과 키를 검색합니다.

Key k =
    new KeyFactory.Builder("Person", "GreatGrandpa")
        .addChild("Person", "Grandpa")
        .addChild("Person", "Dad")
        .addChild("Person", "Me")
        .getKey();

또한 KeyFactory 클래스에는 키와 해당 문자열 표현 사이의 전환을 위한 정적 메서드 keyToStringstringToKey가 포함됩니다.

String personKeyStr = KeyFactory.keyToString(k);

// Some time later (for example, after using personKeyStr in a link).
Key personKey = KeyFactory.stringToKey(personKeyStr);
Entity person = datastore.get(personKey);

키의 문자열 표현은 '웹에 적합한' 형식입니다. 여기에는 HTML 또는 URL에서 특수하게 고려되는 문자가 포함되어 있지 않습니다.

빈 목록 사용

지금까지 Datastore에는 빈 목록을 나타내는 속성 표현이 없었습니다. 자바 SDK에서는 빈 컬렉션을 null 값으로 저장하여 이 문제를 해결했습니다. 따라서 null 값과 빈 목록을 구분할 수 있는 방법이 없습니다. 하위 호환성 유지를 위해 다음과 같은 기본 동작이 사용됩니다.

  • null 속성이 Datastore에 null로 기록됩니다.
  • 빈 컬렉션이 Datastore에 null로 기록됩니다.
  • null은 Datastore에서 null로 읽습니다.
  • 빈 컬렉션은 null로 읽습니다.

하지만 기본 동작을 변경할 경우, 자바 SDK가 빈 목록 저장을 지원합니다. 애플리케이션의 기본 동작을 변경할 때의 영향을 고려한 후 빈 목록 지원을 설정하는 것이 좋습니다.

빈 목록을 사용할 수 있도록 기본 동작을 변경하려면 앱을 초기화하는 동안 다음과 같이 DATASTORE_EMPTY_LIST_SUPPORT 속성을 설정합니다.

System.setProperty(DatastoreServiceConfig.DATASTORE_EMPTY_LIST_SUPPORT, Boolean.TRUE.toString());

위와 같이 이 속성을 true로 설정하면 다음과 같은 결과가 나타납니다.

  • null 속성이 Datastore에 null로 기록됩니다.
  • 빈 컬렉션은 Datastore에 빈 목록으로 기록됩니다.
  • null은 Datastore에서 null로 읽습니다.
  • Datastore에서 읽을 때 빈 목록이 빈 컬렉션으로 반환됩니다.