이번엔 시작하기(上)에 이어, 데이터스토어에 대해 정리해 보려고 합니다.
앱 엔진 자바 룩은 썬 마이크로시스템즈의 최고 오픈소스 임원으로부터 한 마디 핀잔을 들은 것 처럼, 자바 6를 지원하기는 하지만 전체 집합을 모두 지원하지는 않습니다.(꼭 핀잔을 들을 일이었는지는 모르겠지만.. 입장 차이겠죠?^^;)
예를 들어 java.io.FileInputStream을 지원하긴 하는데, java.io.FileOutputStream을 지원하진 않습니다. 얼핏 드는 생각으로는 구글 서버에 외부로부터의 파일이 유입되거나 악의적인 코드가 작성되면 골치아프다는 측면에서 빼버린 것 같다는 생각도 듭니다만, 이런저런 이유들이 있겠죠.
그래서 앱 엔진이 제공하는 인프라 자체에 데이터를 누적하는 방법이 상당히 제약적인데, 결국 앱 엔진이 제공하는 데이터스토어(Datastore)에 의지할 수 밖에 없습니다.
몇 가지 슬픈 점을 정리해보면, 우리가 매우 익숙한 create table 같은 DDL로 엔티티를 정의하고 insert into 문장이나 update set 문장으로 데이터를 추가/수정할 수 없습니다.
하지만 기쁜 점이 여전히 남아있기에.. 시도하지 않을 수 없습니다. 무료잖아요. 꽤 많은 저장공간과 꽤 많은 트래픽이 무료.. 무료...!! -ㅅ-;;
도무지 희망찬 일인지 잘 모르겠지만 아무튼 앱 엔진은 데이터스토어의 저수준 API만 제공하는 것이 아니라, JDO(Java Data Objects)와JPA(Java Persistence API) 인터페이스를 함께 제공합니다.
내공 깊은 시스템 프로그래머 같은 분이 아니라면 왠만하면 저수준 말고 제공해주는 도구를 쓰게 되는 게 자연스러운 심리인 것 같습니다.
처음엔 익숙치 않은 JDO 쓰래서 살짝 심술이 났지만, 제공되는 샘플 보니까 OOP 개념을 알고 있으면 쉽게 적용할 수 있는 방식인 것 같아서 마음이 편해졌습니다.
JDO의 세계에서는 create table 같은 액션을 클래스 작성으로 대신할 수 있습니다.
Employee.java
import java.util.Date;
import javax.jdo.annotations.IdGeneratorStrategy;
import javax.jdo.annotations.IdentityType;
import javax.jdo.annotations.PersistenceCapable;
import javax.jdo.annotations.Persistent;
import javax.jdo.annotations.PrimaryKey;
@PersistenceCapable(identityType = IdentityType.APPLICATION)
public class Employee {
@PrimaryKey
@Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
@Persistent
private String firstName;
@Persistent
private String lastName;
@Persistent
private Date hireDate;
public Employee(String firstName, String lastName, Date hireDate) {
this.firstName = firstName;
this.lastName = lastName;
this.hireDate = hireDate;
}
// 아래는 필드 접근을 위한 메소드들. JDO는 이 메소드들을 사용하지 않지만, 우리의 애플리케이션에서 필요해요.
public Long getId() {
return id;
}
public String getFirstName() {
return firstName;
}
// ... other accessors...
}
좋은 샘플이죠.~
@PersistenceCapable은 작성 중인 설계가 데이터가 되었을 때 지속범위 정도가 될까요?
IdentityType.APPLICATION은 이 데이터가 구글 앱 엔진 범위 내에서 유효하다는 것을 의미하고 IdentityType.DATASTORE가 구글 앱 엔진 대시보드에서 조회도 가능하고 외부에서 접근할 수 있게(멋대로 추측 중) 하는 범위를 뜻하는 것 같습니다. 저는 .APPLICATION으로만 테스트했고, DATASTORE로 하고 싶은 간절한 소망은 아직 지원하지 않는다고 해서 잠시 마음 한 구석에 접어 두었습니다.
한 번 훑으면서 이해하셨겠지만 @Persistent는 테이블 개념에서 컬럼을 나타내고, @PrimaryKey로 주 키 설정,IdGeneratorStrategy.IDENTITY로 아이덴티티를 설정해줄 수 있는 것을 볼 수 있습니다.
이제 엔티티 선언을 살펴봤으니 자료를 조회하고 쓰는 쪽을 봐야겠죠?
우리는 PersistenceManagerFactory 객체에서 얻을 수 있는 PersistenceManager라는 녀석으로 데이터스토어와 친하게 지낼 수 있습니다. 명세를 자세히 보시면 나오지만 PersistenceManagerFactory는 메모리에 활성화하는데 비용도 크고 애플리케이션 주기 동안 딱 한 번 만들어낼 수 있어서, 이 녀석을 static final로 넣어두고 실제 일을 도와줄 PersistenceManager만 땡겨 씁니다.
PMF.java
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
public final class PMF {
private static final PersistenceManagerFactory pmfInstance =
JDOHelper.getPersistenceManagerFactory("transactions-optional");
private PMF() {}
public static PersistenceManagerFactory get() {
return pmfInstance;
}
}
자, 말씀드린 내용에 대한 샘플입니다. 필요한 static 메소드와 필드가 정의되어 있습니다. 그리고 아래처럼 활용이 가능합니다.
import java.util.Date;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import Employee;
import PMF;
// ...
Employee employee = new Employee("Alfred", "Smith", new Date());
PersistenceManager pm = PMF.get().getPersistenceManager();
try {
pm.makePersistent(employee);
} finally {
pm.close();
}
new Employee(...)와 같은 방법으로 데이터를 설정해주고 PersistenceManager의 makePersistent 메소드에 데이터를 넘겨서 insert할 수 있습니다. 만일 PrimaryKey가 같은 데이터가 이미 존재한다면 insert 대신 update를 합니다. 간단하죠?
그리고 JDO는 SQL 사촌 같은 이름을 가진 JDOQL을 제공합니다.
import java.util.List;
import Employee;
// ...
String query = "select from " + Employee.class.getName() + " where lastName == 'Smith'";
List<Employee> employees = (List<Employee>) pm.newQuery(query).execute();
어떤가요? 쉽죠? 보통 select 문과 좀 달라보이는 건, object를 얻어내기에 필드를 임의로 지정하지 못합니다. 그냥 다 가져와서 쓰면 됩니다. 비교문이 사실.. SQL 표준에 비하면 많이 빈약한데, 그래도 lastName like '%멍미%' 까지는 아니더라도 lastName like '멍미1%' 정도는 lastName >= '멍미1' and lastName < '멍미2' 정도로 표현해볼 수 있습니다.
이제 기본적인 사항들에 대해 어느 정도 살펴본 듯 한데, 지금 시점에서의 몇 가지 명세를 더 살펴봤습니다.
우리가 클래스로 정의했던 데이터 객체는 우리에게 친숙한 엔티티(entity) 정도로 볼 수 있고, 엔티티는 하나 이상의 프로퍼티를 가질 수 있습니다. 정수, 실수, 문자열, 날짜, 바이너리 등 여러가지가 될 수 있죠.
그리고 각각의 엔티티는 유일하게 구분할 수 있는 키를 가집니다. 위 샘플에서 본 것 처럼 1)데이터스토어가 제공해주는 숫자생성키를 사용하거나 2)애플리케이션에서 직접 문자열로 키를 지정해줄 수 있습니다.
JDOQL을 이용해서 특정 프로퍼티를 기준으로 정렬해서 데이터를 불러올 수 있고, 애플리케이션을 deploy하기 전에 텍스트 설정 파일을 통해 직접 인덱스를 설정해줄 수 있습니다.
트랜잭션과 관련해서 데이터스토어는 optimistic concurrency를 적용해서, 특정 애플리케이션 인스턴스가 엔티티를 수정하려는 시도를 하고 있는 동안은 다른 모든 인스턴스의 수정 시도가 실패하거나 재시도를 할 수 있습니다.
또한 어느 정도 기능과 사용량에 제약이 있으니 애플리케이션 개발에 나서기 전에, 요구사항을 충족할 수 있는지 살펴보는 것이 안전하겠습니다.
기능 부문
- 위에 언급한 것과 같이 지금은 IdentityType.APPLICATION만 지원하며 IdentityType.DATASTORE는 지원 예정,
- 조인이나 그룹핑 등 일부 구문,
- 그 외 아래 연결 주소 참조
사용량 부문
- 한 엔티티(row / record)의 최대 크기: 1MB,
- 한 엔티티를 위한 인덱스 값은 최대 1000개,
- 배치로 한 번에 넣고 지울 수 있는 엔티티 수는 최대 500개,
- 불러올 수 있는 건 2배인 1000개.
댓글 없음:
댓글 쓰기