JDO(자바 데이터 객체)는 자바에서 데이터베이스에 액세스하기 위한 표준 인터페이스로, 자바 클래스와 데이터베이스 테이블 간에 매핑을 제공합니다. JDO를 Datastore와 함께 사용하기 위한 오픈소스 플러그인이 있으며, 이 페이지에서는 해당 플러그인을 시작하는 방법에 대한 정보를 제공합니다.
경고: 대부분의 개발자는 낮은 수준의 Datastore API 또는 Datastore 전용으로 개발된 오픈소스 API 중 하나(예: Objectify)를 사용하는 것이 더 좋습니다. JDO는 기존 관계형 데이터베이스에 사용하도록 설계되었기 때문에 항목 그룹이나 상위 쿼리 등 관계형 데이터베이스와 차별화되는 Datastore의 몇 가지 측면을 명시적으로 나타낼 수 있는 방법이 없습니다. 이로 인해 이해하고 해결하기 어려운 미묘한 문제가 발생할 수 있습니다.
App Engine 자바 SDK에는 App Engine용 DataNucleus 플러그인의 버전 2.x가 포함됩니다. 이 플러그인은 JDO 3.0을 통해 App Engine Datastore를 사용할 수 있도록 해주는 DataNucleus Access Platform의 버전 3.0에 상응합니다.
JDO에 대한 자세한 내용은 Access Platform 3.0 문서를 참조하세요. 특히 JDO 매핑과 JDO API를 참조하세요.
경고: App Engine용 DataNucleus 플러그인의 버전 2.x는 DataNucleus v3.x를 사용합니다. 이 새로운 플러그인은 이전(1.x) 플러그인과 완전히 호환되지는 않습니다. 새 버전으로 업그레이드하는 경우 애플리케이션을 업데이트하고 테스트해야 합니다.
JDO 2.x 및 3.0을 지원하는 빌드 도구
Maven을 사용하면 App Engine용 DataNucleus 플러그인 2.x 또는 3.0 버전을 사용할 수 있습니다.
- Maven 사용자:
pom.xml
파일에서 다음 구성으로 클래스를 향상시킵니다.<plugin> <groupId>org.datanucleus</groupId> <artifactId>maven-datanucleus-plugin</artifactId> <version>3.2.0-m1</version> <configuration> <api>JDO</api> <props>${basedir}/datanucleus.properties</props> <verbose>true</verbose> <enhancerName>ASM</enhancerName> </configuration> <executions> <execution> <phase>process-classes</phase> <goals> <goal>enhance</goal> </goals> </execution> </executions> <dependencies> <dependency> <groupId>org.datanucleus</groupId> <artifactId>datanucleus-core</artifactId> <version>3.1.3</version> </dependency> </dependencies> </plugin>
DataNucleus 플러그인 버전 2.x로 이전
이 섹션에서는 DataNucleus Access Platform 3.0 및 JDO 3.0에 상응하는 App Engine용 DataNucleus 플러그인 버전 2.x를 사용하도록 앱을 업그레이드하는 방법을 안내합니다. 이 플러그인은 1.x 플러그인과 완전히 호환되지는 않으며 변경될 수 있습니다. 업그레이드하는 경우 애플리케이션 코드를 업데이트하고 테스트해야 합니다.
새로운 기본 동작
App Engine DataNucleus 플러그인의 버전 2.x에서는 몇 가지 기본값이 이전 버전과 다릅니다.
- 트랜잭션이 아닌
PersistenceManager.makePersistent()
,PersistenceManager.deletePersistent()
호출은 이제 원자적으로 실행됩니다. 이전에는 이러한 함수는 다음 트랜잭션이나PersistenceManager.close()
시 실행되었습니다. - 이제 더 이상 중복 PersistenceManagerFactory(PMF) 할당에 대한 예외가 없습니다. 대신 지속성 속성
datanucleus.singletonPMFForName
을 true로 설정하면 해당 이름에 현재 할당된 싱글톤 PMF가 반환됩니다. - 이제 미소유 관계가 지원됩니다. 미소유 관계를 참조하세요.
구성 파일 변경사항
애플리케이션을 App Engine DataNucleus 플러그인 버전 2.x로 업그레이드하려면 build.xml
과 jdoconfig.xml
에서 구성 설정을 변경해야 합니다.
경고! 구성을 업데이트한 후에는 이전 버전과 호환되는지를 확인하기 위해 애플리케이션 코드를 테스트해야 합니다. 새 애플리케이션을 설정하고 있으며 최신 버전의 플러그인을 사용하려면 JDO 3.0 설정을 진행하세요.
PersistenceManagerFactoryClass
속성이 변경되었습니다.jdoconfig.xml
에서 이 줄을 변경합니다.
<property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.store.appengine.jdo.DatastoreJDOPersistenceManagerFactory"/>
to:
<property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/>
build.xml
내
DataNucleus 2.x에 맞게 copyjars
타겟을 변경해야 합니다.
copyjars
타겟이 변경되었습니다. 다음 섹션을 업데이트하세요.
to:<target name="copyjars" description="Copies the App Engine JARs to the WAR."> <mkdir dir="war/WEB-INF/lib" /> <copy todir="war/WEB-INF/lib" flatten="true"> <fileset dir="${sdk.dir}/lib/user"> <include name="**/*.jar" /> </fileset> </copy> </target>
<target name="copyjars" description="Copies the App Engine JARs to the WAR."> <mkdir dir="war/WEB-INF/lib" /> <copy todir="war/WEB-INF/lib" flatten="true"> <fileset dir="${sdk.dir}/lib/user"> <include name="**/appengine-api-1.0-sdk*.jar" /> </fileset> <fileset dir="${sdk.dir}/lib/opt/user"> <include name="appengine-api-labs/v1/*.jar" /> <include name="jsr107/v1/*.jar" /> <include name="datanucleus/v2/*.jar" /> </fileset> </copy> </target>
datanucleusenhance
타겟이 변경되었습니다. 다음 섹션을 업데이트하세요.
to:<target name="datanucleusenhance" depends="compile" description="Performs enhancement on compiled data classes."> <enhance_war war="war" /> </target>
<target name="datanucleusenhance" depends="compile" description="Performs enhancement on compiled data classes."> <enhance_war war="war"> <args> <arg value="-enhancerVersion"/> <arg value="v2"/> </args> </enhance_war> </target>
JDO 3.0 설정
JDO를 사용하여 Datastore에 액세스하려면 App Engine 앱에 다음이 필요합니다.
- JDO 및 DataNucleus 플러그인의 JAR은 앱의
war/WEB-INF/lib/
디렉터리에 있어야 합니다. jdoconfig.xml
이라는 구성 파일은 JDO에 App Engine Datastore를 사용하라고 지시하는 구성과 함께 앱의war/WEB-INF/classes/META-INF/
디렉터리에 있어야 합니다.- 프로젝트의 빌드 프로세스가 컴파일된 데이터 클래스에서 컴파일 후 '보정' 단계를 수행하여 컴파일된 데이터를 JDO 구현에 연결해야 합니다.
JAR 복사
JDO 및 Datastore JAR은 App Engine 자바 SDK에 포함되어 있습니다. appengine-java-sdk/lib/opt/user/datanucleus/v2/
디렉터리에서 찾을 수 있습니다.
JAR을 애플리케이션의 war/WEB-INF/lib/
디렉터리에 복사합니다.
appengine-api.jar
도 war/WEB-INF/lib/
디렉터리에 있는지 확인합니다. 프로젝트를 만들 때 이미 복사했을 수도 있습니다. App Engine DataNucleus 플러그인은 이 JAR을 사용하여 Datastore에 액세스합니다.
jdoconfig.xml 파일 만들기
JDO 인터페이스에는 애플리케이션의 war/WEB-INF/classes/META-INF/
디렉터리에 jdoconfig.xml
이라는 구성 파일이 필요합니다. 이 위치에서 이 파일을 직접 만들거나 빌드 프로세스를 통해 이 파일을 소스 디렉터리에서 복사할 수 있습니다.
다음과 같은 내용으로 파일을 만듭니다.
<?xml version="1.0" encoding="utf-8"?> <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> <persistence-manager-factory name="transactions-optional"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> <property name="datanucleus.appengine.singletonPMFForName" value="true"/> </persistence-manager-factory> </jdoconfig>
Datastore 읽기 정책 및 호출 기한 설정
Datastore 쿼리 페이지에 설명된 대로 읽기 정책(strong consistency와 eventual consistency)과 호출 기한을 설정하여 Datastore의 동작을 맞춤설정할 수 있습니다. JDO에서는 jdoconfig.xml
파일의 <persistence-manager-factory>
요소에 원하는 값을 지정하여 이 작업을 수행합니다. 특정 PersistenceManager
인스턴스를 통해 실행되는 모든 호출은 PersistenceManagerFactory
를 통해 관리자가 생성될 때 효과적인 구성 값을 사용합니다. 단일 Query
객체에 이 설정을 재정의할 수도 있습니다.
PersistenceManagerFactory
에 읽기 정책을 설정하려면 datanucleus.appengine.datastoreReadConsistency
이라는 속성을 포함합니다.
가능한 값은 EVENTUAL
및 STRONG
입니다. 지정하지 않으면 기본값은 STRONG
입니다. 하지만 이 설정은 특정 항목 그룹 내 상위 쿼리에만 적용됩니다.
상위 쿼리가 아닌 쿼리는 적용되는 읽기 정책에 관계없이 항상 최종 일관성을 띱니다.)
<property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" />
읽기 및 쓰기에 대해 별도의 데이터 저장소 호출 기한을 설정할 수 있습니다. 읽기의 경우 JDO 표준 속성 javax.jdo.option.DatastoreReadTimeoutMillis
를 사용합니다. 쓰기의 경우 javax.jdo.option.DatastoreWriteTimeoutMillis
을 사용합니다. 값은 밀리초 단위의 시간입니다.
<property name="javax.jdo.option.DatastoreReadTimeoutMillis" value="5000" /> <property name="javax.jdo.option.DatastoreWriteTimeoutMillis" value="10000" />
XG(교차 그룹) 트랜잭션을 사용하려면 다음 속성을 추가합니다.
<property name="datanucleus.appengine.datastoreEnableXGTransactions" value="true" />
같은 jdoconfig.xml
파일에서 서로 다른 name
속성을 사용하는 <persistence-manager-factory>
요소 여러 개가 동일한 앱에서 서로 다른 구성으로 PersistenceManager
인스턴스를 사용하도록 할 수 있습니다. 예를 들어 다음 jdoconfig.xml
파일은 구성 세트 두 개를 설정합니다. 하나는 "transactions-optional"
이고, 다른 하나는 "eventual-reads-short-deadlines"
입니다.
<?xml version="1.0" encoding="utf-8"?> <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> <persistence-manager-factory name="transactions-optional"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> </persistence-manager-factory> <persistence-manager-factory name="eventual-reads-short-deadlines"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="true"/> <property name="datanucleus.appengine.datastoreReadConsistency" value="EVENTUAL" /> <property name="javax.jdo.option.DatastoreReadTimeoutMillis" value="5000" /> <property name="javax.jdo.option.DatastoreWriteTimeoutMillis" value="10000" /> <property name="datanucleus.singletonPMFForName" value="true" /> </persistence-manager-factory> </jdoconfig>
이름이 지정된 구성 세트로 PersistenceManager
를 만드는 방법에 대한 내용은 아래의 PersistenceManager 인스턴스 가져오기를 참조하세요.
데이터 클래스 향상
JDO는 빌드 프로세스에서 컴파일 후 '보정' 단계를 사용하여 데이터 클래스를 JDO 구현에 연결합니다.
명령줄에서 다음 명령어를 사용하여 컴파일된 클래스에서 향상 단계를 수행할 수 있습니다.
java -cp classpath com.google.appengine.tools.enhancer.Enhance class-files
classpath에는 appengine-java-sdk/lib/
디렉터리에 있는 JAR appengine-tools-api.jar
및 모든 데이터 클래스가 포함되어야 합니다.
DataNucleus 바이트코드 보정기에 대한 자세한 내용은 DataNucleus 문서를 참조하세요.
PersistenceManager 인스턴스 가져오기
앱은 PersistenceManager 클래스의 인스턴스를 사용하여 JDO와 상호작용합니다. PersistenceManagerFactory 클래스의 인스턴스에서 메서드를 인스턴스화하고 호출하여 이 인스턴스를 가져옵니다. 팩토리는 JDO 구성을 사용하여 PersistenceManager 인스턴스를 만듭니다.
PersistenceManagerFactory 인스턴스는 초기화되는 데 시간이 다소 걸리므로 앱은 단일 인스턴스를 재사용해야 합니다. PersistenceManagerFactory 인스턴스를 관리하는 쉬운 방법은 다음과 같이 정적 인스턴스가 있는 싱글톤 래퍼 클래스를 만드는 것입니다.
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; } }
팁: "transactions-optional"
은 jdoconfig.xml
파일에 있는 구성 세트의 이름을 나타냅니다. 앱에서 여러 구성 세트를 사용하는 경우에는 원하는 JDOHelper.getPersistenceManagerFactory()
를 호출하도록 이 코드를 확장해야 합니다. 코드는 각 PersistenceManagerFactory
의 싱글톤 인스턴스를 캐시해야 합니다.
앱은 팩토리 인스턴스를 사용하여 Datastore에 액세스하는 요청마다 하나의 PersistenceManager 인스턴스를 만듭니다.
import javax.jdo.JDOHelper; import javax.jdo.PersistenceManager; import javax.jdo.PersistenceManagerFactory; import PMF; // ... PersistenceManager pm = PMF.get().getPersistenceManager();
PersistenceManager를 사용하여 데이터 객체를 저장, 업데이트, 삭제하고 Datastore 쿼리를 수행합니다.
PersistenceManager 인스턴스를 마쳤으면 close()
메서드를 호출해야 합니다. close()
메서드를 호출한 후에 PersistenceManager 인스턴스를 사용하면 오류가 발생합니다.
try { // ... do stuff with pm ... } finally { pm.close(); }
JDO 3.0의 지원되지 않는 기능
JDO 인터페이스의 다음 기능은 App Engine 구현에서 지원되지 않습니다.
- 다대다 소유 관계
- 'join' 쿼리. 상위 종류를 대상으로 쿼리를 실행할 때 필터에 하위 항목의 필드를 사용할 수 없습니다. 쿼리에서 키를 사용하여 상위 요소의 관계 필드를 직접 테스트할 수는 있습니다.
- JDOQL 그룹화 및 기타 집계 쿼리
- 다형성 쿼리. 클래스 쿼리를 실행하여 서브클래스의 인스턴스를 가져올 수 없습니다. 각 클래스는 Datastore에서 별도의 항목 종류로 표시됩니다.
트랜잭션 중지 및 기존 JDO 앱 포팅
사용이 권장되는 JDO 구성은 datanucleus.appengine.autoCreateDatastoreTxns
라는 속성을 true
로 설정합니다.
이는 Datastore 트랜잭션을 애플리케이션 코드에서 관리되는 JDO 트랜잭션에 연결할 것을 JDO 구현에 지시하는 App Engine 고유 속성입니다. 처음부터 새로 앱을 빌드하는 경우 이 속성을 사용하는 것이 좋습니다. 하지만 기존의 JDO 기반 애플리케이션을 App Engine에서 실행하려는 경우 이 속성 값을 false
로 설정하는 대체 지속성 구성을 사용하려 할 수 있습니다.
<?xml version="1.0" encoding="utf-8"?> <jdoconfig xmlns="http://java.sun.com/xml/ns/jdo/jdoconfig" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://java.sun.com/xml/ns/jdo/jdoconfig"> <persistence-manager-factory name="transactions-optional"> <property name="javax.jdo.PersistenceManagerFactoryClass" value="org.datanucleus.api.jdo.JDOPersistenceManagerFactory"/> <property name="javax.jdo.option.ConnectionURL" value="appengine"/> <property name="javax.jdo.option.NontransactionalRead" value="true"/> <property name="javax.jdo.option.NontransactionalWrite" value="true"/> <property name="javax.jdo.option.RetainValues" value="true"/> <property name="datanucleus.appengine.autoCreateDatastoreTxns" value="false"/> </persistence-manager-factory> </jdoconfig>
이것이 유용할 수 있는 이유를 이해하려면 트랜잭션 내에서 같은 항목 그룹에 속하는 객체만 사용할 수 있다는 점을 기억해야 합니다. 기존 데이터베이스를 사용하여 빌드한 애플리케이션은 일반적으로 글로벌 트랜잭션을 사용할 수 있다고 가정하기 때문에 트랜잭션 내의 모든 레코드 집합을 업데이트할 수 있습니다. App Engine Datastore는 글로벌 트랜잭션을 지원하지 않기 때문에 코드가 글로벌 트랜잭션을 사용할 수 있다고 가정할 경우 App Engine에서 예외가 발생합니다. 매우 커질 수 있는 코드베이스를 살펴보고 모든 트랜잭션 관리 코드를 삭제하는 대신 간단히 Datastore 트랜잭션을 사용 중지할 수 있습니다. 이렇게 해도 다중 레코드 수정의 원자성에 대한 코드의 가정을 처리하는 데 아무런 도움도 되지 않지만 앱이 제대로 작동되므로 트랜잭션 코드를 한 번에 리팩토링하는 대신 필요에 따라 증분 방식으로 리팩토링할 수 있게 됩니다.