現在、JPA-2.0 ベースのアプリケーションを Hibernate から Google App Engine に移行しています。そこで実行したいからです。
SELECT クエリから結果を取得できないため、行き詰まっています。エンティティを永続化して ID で検索することはできますが、既存のすべてのエンティティを SELECT クエリで取得しようとすると、結果は空になります。
この問題を 1 つの単純なテスト ケースに切り分けました。
persistence.xml
<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">
<persistence-unit name="google.transactions-optional">
<provider>org.datanucleus.api.jpa.PersistenceProviderImpl</provider>
<properties>
<property name="datanucleus.NontransactionalRead" value="true"/>
<property name="datanucleus.NontransactionalWrite" value="true"/>
<property name="datanucleus.ConnectionURL" value="appengine"/>
<property name="datanucleus.singletonEMFForName" value="true"/>^
</properties>
</persistence-unit>
</persistence>
JPAエンティティークラス
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
class TestEntity
{
@Id
String id;
String name;
TestEntity(String id, String name)
{
this.id = id;
this.name = name;
}
// For deseralization
@SuppressWarnings("unused")
private TestEntity()
{
}
}
JUnit テストの失敗
import org.junit.*;
import static org.junit.Assert.*;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.TypedQuery;
import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
public class SimpleJpaTest
{
private static final String PERSISTENCE_UNIT = "google.transactions-optional";
private static final EntityManagerFactory EMF = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT);
private final LocalServiceTestHelper _helper;
public SimpleJpaTest()
{
_helper = new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig().setDefaultHighRepJobPolicyUnappliedJobPercentage(100));
}
@Before
public void setUp()
{
_helper.setUp();
}
@After
public void tearDown()
{
_helper.tearDown();
}
@Test
public void testPersistAndGet()
{
String id = "test";
String name = "Test";
// Persist entity
EntityManager entityManager1 = EMF.createEntityManager();
entityManager1.getTransaction().begin();
TestEntity entity1 = new TestEntity(id, name);
entityManager1.persist(entity1);
entityManager1.getTransaction().commit();
entityManager1.close();
// Find specific entity
EntityManager entityManager2 = EMF.createEntityManager();
entityManager2.getTransaction().begin();
TestEntity result2 = entityManager2.find(TestEntity.class, id);
entityManager2.getTransaction().commit();
entityManager2.close();
assertNotNull(result2); // succeeds
assertEquals(id, result2.id); // succeeds
assertEquals(name, result2.name); // succeeds
// Get all entities
EntityManager entityManager3 = EMF.createEntityManager();
entityManager3.getTransaction().begin();
String queryString3 = "SELECT e from TestEntity e";
TypedQuery<TestEntity> query3 = entityManager3.createQuery(queryString3, TestEntity.class);
List<TestEntity> result3 = query3.getResultList();
entityManager3.getTransaction().commit();
entityManager3.close();
assertNotNull(result3); // succeeds
assertFalse(result3.isEmpty()); // fails
assertEquals(1, result3.size());
assertNotNull(result3.get(0));
assertEquals(id, result3.get(0).id);
assertEquals(name, result3.get(0).name);
}
}
ご覧のとおり、find() による単一エンティティの取得はうまく機能しますが、SELECT クエリはうまく機能しません。同じコードが Hibernate でも問題なく動作します。
DataNucleus のコメントによる編集:
コンソール出力
Jan 30, 2013 2:41:10 PM org.datanucleus.metadata.MetaDataManager loadPersistenceUnit
WARNING: Class SimpleJpaTest was specified in persistence-unit google.transactions-optional but not annotated, so ignoring
Jan 30, 2013 1:41:11 PM com.google.appengine.api.datastore.dev.LocalDatastoreService init
INFO: Local Datastore initialized:
Type: High Replication
Storage: In-memory
Jan 30, 2013 1:41:11 PM org.datanucleus.TransactionImpl internalPreCommit
INFO: >> calling preCommit on org.datanucleus.store.connection.ConnectionManagerImpl$2@15b573da
Jan 30, 2013 1:41:11 PM org.datanucleus.TransactionImpl internalPreCommit
INFO: >> calling preCommit on org.datanucleus.ObjectManagerImpl@5ede1ffa
Jan 30, 2013 1:41:11 PM org.datanucleus.TransactionImpl preFlush
INFO: >> calling preFlush on org.datanucleus.store.connection.ConnectionManagerImpl$2@15b573da
Jan 30, 2013 1:41:11 PM org.datanucleus.TransactionImpl preFlush
INFO: >> calling preFlush on org.datanucleus.ObjectManagerImpl@5ede1ffa
Jan 30, 2013 1:41:11 PM com.google.appengine.datanucleus.MetaDataValidator validate
INFO: Performing appengine-specific metadata validation for TestEntity
Jan 30, 2013 1:41:11 PM com.google.appengine.datanucleus.MetaDataValidator validate
INFO: Finished performing appengine-specific metadata validation for TestEntity
Jan 30, 2013 1:41:11 PM org.datanucleus.TransactionImpl internalPreCommit
INFO: >> calling preCommit on org.datanucleus.store.connection.ConnectionManagerImpl$2@49c54f01
Jan 30, 2013 1:41:11 PM org.datanucleus.TransactionImpl internalPreCommit
INFO: >> calling preCommit on org.datanucleus.ObjectManagerImpl@69eeff74
Jan 30, 2013 1:41:11 PM org.datanucleus.TransactionImpl preFlush
INFO: >> calling preFlush on org.datanucleus.store.connection.ConnectionManagerImpl$2@49c54f01
Jan 30, 2013 1:41:11 PM org.datanucleus.TransactionImpl preFlush
INFO: >> calling preFlush on org.datanucleus.ObjectManagerImpl@69eeff74
Jan 30, 2013 1:41:11 PM org.datanucleus.TransactionImpl preFlush
INFO: >> calling preFlush on org.datanucleus.store.connection.ConnectionManagerImpl$2@16f650e5
Jan 30, 2013 1:41:11 PM org.datanucleus.TransactionImpl preFlush
INFO: >> calling preFlush on org.datanucleus.ObjectManagerImpl@71fcf7e2
Jan 30, 2013 1:41:11 PM org.datanucleus.TransactionImpl internalPreCommit
INFO: >> calling preCommit on org.datanucleus.store.connection.ConnectionManagerImpl$2@16f650e5
Jan 30, 2013 1:41:11 PM org.datanucleus.TransactionImpl internalPreCommit
INFO: >> calling preCommit on org.datanucleus.ObjectManagerImpl@71fcf7e2
Jan 30, 2013 1:41:11 PM org.datanucleus.TransactionImpl preFlush
INFO: >> calling preFlush on org.datanucleus.store.connection.ConnectionManagerImpl$2@16f650e5
Jan 30, 2013 1:41:11 PM org.datanucleus.TransactionImpl preFlush
INFO: >> calling preFlush on org.datanucleus.ObjectManagerImpl@71fcf7e2
ところで: この問題はGoogle App Engine + google cloud sql jpa query does not retrieve data from databaseに似ているようですが、まだ答えはありません。