JDO에서 데이터 만들기, 가져오기, 삭제하기

JDO 데이터 객체를 Datastore에 저장하는 것은 PersistenceManager 인스턴스의 makePersistent() 메서드를 호출하기만 하면 되는 간단한 일입니다. App Engine JDO 구현은 객체의 기본 키 필드를 사용하여 어떤 데이터 저장소에 어떤 데이터 객체에 해당하는지를 추적하며, 새 객체의 키를 자동으로 생성할 수 있습니다. 키를 사용하여 항목을 빠르게 검색하고, 알려진 값(예: 계정 ID)으로부터 키를 생성할 수 있습니다.

영구 객체 만들기

Datastore에 간단한 데이터 객체를 저장하려면 PersistenceManager의 makePersistent() 메서드를 호출하여 인스턴스에 전달합니다.

PersistenceManager pm = PMF.get().getPersistenceManager();

Employee e = new Employee("Alfred", "Smith", new Date());

try {
    pm.makePersistent(e);
} finally {
    pm.close();
}

makePersistent() 호출은 동기식이며, 객체가 저장되고 색인이 업데이트될 때까지 반환되지 않습니다.

JDO에서 여러 객체를 저장하려면 makePersistentAll(...) 메서드를 객체 컬렉션과 함께 호출합니다. 이 메서드는 일련의 개별 makePersistent(...) 호출보다 훨씬 효율적인 단일 저수준 일괄 저장 작업을 사용합니다.

참고: 데이터 객체의 영구 필드가 다른 영구 데이터 객체를 참조하고 해당 객체가 저장된 적이 없거나 로드 이후 변경된 적이 없는 경우에는 참조된 객체도 데이터 저장소에 저장됩니다. 관계를 참조하세요.

모든 항목에는 App Engine에서 고유한 키가 있습니다. 전체 키는 애플리케이션 ID, 종류, 항목 ID 등 여러 가지 정보 조각을 포함하고 있습니다. (키는 항목 그룹에 대한 정보도 포함합니다. 자세한 내용은 트랜잭션을 참조하세요.)

객체의 키는 인스턴스의 필드에 저장됩니다. @PrimaryKey 주석을 사용하여 기본 키 필드를 식별합니다.

앱은 객체가 만들어질 때 키의 ID 부분을 문자열로 제공하거나, 데이터 저장소가 숫자 ID를 자동으로 허용하도록 할 수 있습니다. 전체 키는 데이터 저장소의 모든 항목에서 고유해야 합니다. 다시 말해, 객체의 ID는 같은 종류 그리고 같은 상위 항목 그룹(있는 경우) 아래의 모든 객체 중에서 고유해야 합니다. 필드 및 주석의 유형을 사용하여 원하는 키 동작을 선택합니다.

클래스가 관계에서 '하위' 클래스로 사용되는 경우, 키 필드는 상위 항목 그룹을 나타낼 수 있는 유형이어야 합니다. Key 인스턴스이거나 문자열로 인코딩된 키 값이어야 합니다. 항목 그룹에 대한 자세한 내용은 트랜잭션을, 관계에 대한 자세한 내용은 관계를 참조하세요.

팁: 앱에서 새 객체를 만들고 같은 종류(그리고 같은 상위 항목 그룹)의 다른 객체와 동일한 문자열 ID를 부여하는 경우, 새 객체를 저장하면 데이터 저장소에 있는 다른 객체를 덮어씁니다. 새 객체를 만들기 전에 문자열 ID가 이미 사용 중인지를 확인하려면 트랜잭션을 사용하여 해당 ID의 항목을 가져오려고 시도해보면 됩니다. 그리고 존재하지 않는 경우 새 ID를 만들면 됩니다. 트랜잭션을 참조하세요.

네 가지 유형의 기본 키 필드가 있습니다.

Long

Datastore에서 자동으로 생성되는 항목 ID인 긴 정수(java.lang.Long)입니다. ID가 데이터 저장소에 의해 자동으로 생성되어야 하는, 상위 항목 그룹이 없는 객체에 사용합니다. 인스턴스의 long 키 필드는 인스턴스가 저장될 때 채워집니다.

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Long id;
인코딩되지 않은 문자열

객체가 생성될 때 애플리케이션에서 제공하는 항목 ID('키 이름')인 문자열(java.lang.String)입니다. ID가 애플리케이션에 의해 제공되어야 하는 상위 항목 그룹이 없는 객체에 사용합니다. 애플리케이션은 저장하기 전에 이 필드를 원하는 ID로 설정합니다.

import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    private String name;

Key 인스턴스(com.google.appengine.api.datastore.Key)입니다. 키 값에는 상위 항목 그룹(있을 경우)의 키 그리고 앱에서 할당한 문자열 ID나 시스템에서 생성된 숫자 ID가 포함됩니다. 앱에서 할당한 문자열 ID가 있는 객체를 만들려면 ID로 키 값을 만들고 필드를 값으로 설정합니다. 시스템에서 할당한 숫자 ID가 있는 객체를 만들려면 키 필드를 null로 둡니다. (상위 항목 그룹을 사용하는 방법에 대한 자세한 내용은 트랜잭션을 참조하세요.)

import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
import com.google.appengine.api.datastore.Key;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;

    public void setKey(Key key) {
        this.key = key;
    }

앱은 KeyFactory 클래스를 사용하여 Key 인스턴스를 만들 수 있습니다.

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;

// ...
        Key key = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");
        Employee e = new Employee();
        e.setKey(key);
        pm.makePersistent(e);
인코딩된 문자열로의 키

Key와 유사하지만 값이 키의 인코딩된 문자열 형식입니다. 인코딩된 문자열 키를 사용하면 포팅할 수 있는 방식으로 애플리케이션을 작성하면서도 App Engine 데이터 저장소 항목 그룹을 활용할 수 있습니다.

import javax.jdo.annotations.Extension;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;

// ...
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String encodedKey;

앱은 저장하기 전에 키와 이름을 사용하여 이 값을 채우거나 null로 남겨 놓을 수 있습니다. 인코딩된 키 필드가 null인 경우에는 객체가 저장될 때 시스템에서 생성되는 키로 필드가 채워집니다.

KeyFactory 메서드 keyToString()stringToKey()를 사용하여 Key 인스턴스와 인코딩된 문자열 표현 간에 서로 변환할 수 있습니다.

인코딩된 키 문자열을 사용할 때는 추가 필드를 사용하여 객체의 문자열 또는 숫자 ID에 대한 액세스 권한을 제공할 수 있습니다.

    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    @Extension(vendorName="datanucleus", key="gae.encoded-pk", value="true")
    private String encodedKey;

    @Persistent
    @Extension(vendorName="datanucleus", key="gae.pk-name", value="true")
    private String keyName;

    // OR:

    @Persistent
    @Extension(vendorName="datanucleus", key="gae.pk-id", value="true")
    private Long keyId;

"gae.pk-name" 필드는 객체를 저장하기 전에 키 이름으로 설정될 수 있습니다. 객체가 저장될 때, 인코딩된 키 필드는 키 이름을 포함하는 전체 키로 채워집니다. 유형은 String이어야 합니다.

"gae.pk-id" 필드는 객체가 저장될 때 채워지며 수정될 수 없습니다. 해당 유형은 Long이어야 합니다.

생성된 키(valueStrategy = IdGeneratorStrategy.IDENTITY를 사용하는 키 필드)가 있는 새 객체가 만들어질 때 해당 키 값은 null로 시작합니다. 키 필드는 객체가 데이터 저장소에 기록될 때 채워집니다. 트랜잭션을 사용하는 경우에는 트랜잭션이 커밋될 때 객체가 기록됩니다. 그렇지 않으면 객체가 생성 중인 경우 makePersistent() 메서드가 호출될 때 또는 객체가 업데이트 중인 경우 PersistenceManager 인스턴스의 close() 메서드가 호출될 때 기록됩니다.

키를 만드는 방법에 대한 자세한 내용은 항목, 속성, 키를 참조하세요.

키를 기준으로 객체 가져오기

해당 키를 기준으로 객체를 검색하려면 PersistenceManager의 getObjectById() 메서드를 사용합니다. 이 메소드는 객체의 클래스와 키를 사용합니다.

        Key k = KeyFactory.createKey(Employee.class.getSimpleName(), "Alfred.Smith@example.com");
        Employee e = pm.getObjectById(Employee.class, k);

인코딩되지 않은 문자열 ID(String) 또는 숫자 ID(Long)인 키 필드를 클래스가 사용하는 경우, getObjectByID()는 간단한 값을 키 매개변수로 사용할 수 있습니다.

        Employee e = pm.getObjectById(Employee.class, "Alfred.Smith@example.com");

키 인수는 지원되는 키 필드 유형(문자열 ID, 숫자 ID, 키 값, 인코딩된 키 문자열) 중 하나이거나 클래스에 있는 키 필드와 다른 유형일 수 있습니다. App Engine은 클래스 이름 및 제공된 값에서 전체 키를 추출할 수 있어야 합니다. 문자열 ID 및 숫자 ID는 배타적이므로 숫자 ID를 사용하는 호출은 절대로 문자열 ID가 있는 항목을 반환하지 않습니다. 키 값 또는 인코딩된 키 문자열이 사용되는 경우, 키는 클래스에 의해 종류가 표시되는 항목을 참조해야 합니다.

객체 업데이트

JDO로 객체를 업데이트하는 한 가지 방법은 객체를 가져온 후에 객체를 반환한 PersistenceManager가 아직 열려 있는 동안 수정하는 것입니다. PersistenceManager가 닫히면 변경사항이 영구적인 것이 됩니다. 예:

public void updateEmployeeTitle(User user, String newTitle) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    try {
        Employee e = pm.getObjectById(Employee.class, user.getEmail());
        if (titleChangeIsAuthorized(e, newTitle) {
            e.setTitle(newTitle);
        } else {
            throw new UnauthorizedTitleChangeException(e, newTitle);
        }
    } finally {
        pm.close();
    }
}

Employee 인스턴스는 PersistenceManager에서 반환된 것이기 때문에 PersistenceManager는 Employee 영구 필드의 모든 수정사항을 알고 있으며 PersistenceManager가 닫힐 때 Datastore에서 해당 수정사항을 자동으로 업데이트합니다. 수정사항을 알고 있는 이유는 Employee 인스턴스가 PersistenceManager에 '연결'되어 있기 때문입니다.

PersistenceManager가 닫힌 후에는 클래스를 '분리 가능'한 것으로 선언함으로써 객체를 수정할 수 있습니다. 이렇게 하려면 detachable 속성을 @PersistenceCapable 주석에 추가하세요.

import javax.jdo.annotations.PersistenceCapable;

@PersistenceCapable(detachable="true")
public class Employee {
    // ...
}

이제 로드된 PersistenceManager가 닫힌 후에 Employee 객체의 필드를 읽고 쓸 수 있습니다. 다음 예에서는 분리된 객체를 어떻게 유용하게 사용할 수 있는지 확인할 수 있습니다.

public Employee getEmployee(User user) {
    PersistenceManager pm = PMF.get().getPersistenceManager();
    Employee employee, detached = null;
    try {
        employee = pm.getObjectById(Employee.class,
            "Alfred.Smith@example.com");

        // If you're using transactions, you can call
        // pm.setDetachAllOnCommit(true) before committing to automatically
        // detach all objects without calls to detachCopy or detachCopyAll.
        detached = pm.detachCopy(employee);
    } finally {
        pm.close();
    }
    return detached;
}

public void updateEmployeeTitle(Employee e, String newTitle) {
    if (titleChangeIsAuthorized(e, newTitle) {
        e.setTitle(newTitle);
        PersistenceManager pm = PMF.get().getPersistenceManager();
        try {
            pm.makePersistent(e);
        } finally {
            pm.close();
        }
    } else {
        throw new UnauthorizedTitleChangeException(e, newTitle);
    }
}

분리된 객체는 데이터 전송 객체를 만들기 위한 훌륭한 대안입니다. 분리된 객체를 작업하는 방법에 대한 자세한 내용은 DataNucleus 문서를 참조하세요.

객체 삭제

Datastore에서 객체를 삭제하려면 PersistenceManager의 deletePersistent() 메서드를 객체와 함께 호출합니다.

pm.deletePersistent(e);

JDO에서 여러 객체를 삭제하려면 deletePersistentAll(...) 메서드를 객체 컬렉션과 함께 호출합니다. 이 메서드는 일련의 개별 deletePersistent(...) 호출보다 훨씬 효율적인 단일 저수준 일괄 저장 작업을 사용합니다.

객체에 영구적인 하위 객체를 포함하는 필드가 있는 경우, 하위 항목도 삭제됩니다. 자세한 내용은 관계를 참조하세요.

쿼리와 일치하는 모든 객체를 삭제하려면 JDOQL의 '쿼리별 삭제' 기능을 사용하면 됩니다. 자세한 내용은 쿼리별로 항목 삭제를 참고하세요.