1 つの都市に複数の郊外が含まれるように、GAE データストアにエンティティ グループを作成したいと考えています。以下は私のコードです:-
//city.java
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
@Entity
public class City
{
@Id
private String name;
@OneToMany(mappedBy="city", cascade=CascadeType.ALL)
private Suburban[] suburbans;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public Suburban[] getSuburbans()
{
return suburbans;
}
public void setSuburbans(Suburban[] suburbans)
{
this.suburbans = suburbans;
}
}
//郊外.java
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
@Entity
public class Suburban
{
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Key id;
private String name;
@ManyToOne(fetch = FetchType.LAZY)
private City city;
public City getCity()
{
return city;
}
public void setCity(City city)
{
this.city = city;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public Key getId()
{
return id;
}
}
Eclipse オプション「Generate Cloud Endpoint Class」の google-plugin を使用して、cityendpoint クラスを自動生成しています。
//CityEndpoint.java
@Api(name = "cityendpoint", namespace = @ApiNamespace(ownerDomain = "zestbuds.com", ownerName = "zestbuds.com", packagePath = "android"))
public class CityEndpoint
{
/**
* This method lists all the entities inserted in datastore.
* It uses HTTP GET method and paging support.
*
* @return A CollectionResponse class containing the list of all entities
* persisted and a cursor to the next page.
*/
@SuppressWarnings({ "unchecked", "unused" })
@ApiMethod(name = "listCity")
public CollectionResponse<City> listCity(@Nullable @Named("cursor") String cursorString, @Nullable @Named("limit") Integer limit)
{
EntityManager mgr = null;
Cursor cursor = null;
List<City> execute = null;
try
{
mgr = getEntityManager();
Query query = mgr.createQuery("select from City as City");
if (cursorString != null && cursorString != "")
{
cursor = Cursor.fromWebSafeString(cursorString);
query.setHint(JPACursorHelper.CURSOR_HINT, cursor);
}
if (limit != null)
{
query.setFirstResult(0);
query.setMaxResults(limit);
}
execute = (List<City>) query.getResultList();
cursor = JPACursorHelper.getCursor(execute);
if (cursor != null)
cursorString = cursor.toWebSafeString();
// Tight loop for fetching all entities from datastore and accomodate
// for lazy fetch.
for (City obj : execute)
;
} finally
{
mgr.close();
}
return CollectionResponse.<City> builder().setItems(execute).setNextPageToken(cursorString).build();
}
/**
* This method gets the entity having primary key id. It uses HTTP GET method.
*
* @param id the primary key of the java bean.
* @return The entity with primary key id.
*/
@ApiMethod(name = "getCity")
public City getCity(@Named("id") String id)
{
EntityManager mgr = getEntityManager();
City city = null;
try
{
city = mgr.find(City.class, id);
} finally
{
mgr.close();
}
return city;
}
/**
* This inserts a new entity into App Engine datastore. If the entity already
* exists in the datastore, an exception is thrown.
* It uses HTTP POST method.
*
* @param city the entity to be inserted.
* @return The inserted entity.
*/
@ApiMethod(name = "insertCity")
public City insertCity(City city)
{
EntityManager mgr = getEntityManager();
try
{
if (containsCity(city))
{
throw new EntityExistsException("Object already exists");
}
mgr.persist(city);
} finally
{
mgr.close();
}
return city;
}
/**
* This method is used for updating an existing entity. If the entity does not
* exist in the datastore, an exception is thrown.
* It uses HTTP PUT method.
*
* @param city the entity to be updated.
* @return The updated entity.
*/
@ApiMethod(name = "updateCity")
public City updateCity(City city)
{
EntityManager mgr = getEntityManager();
try
{
if (!containsCity(city))
{
throw new EntityNotFoundException("Object does not exist");
}
mgr.persist(city);
} finally
{
mgr.close();
}
return city;
}
/**
* This method removes the entity with primary key id.
* It uses HTTP DELETE method.
*
* @param id the primary key of the entity to be deleted.
*/
@ApiMethod(name = "removeCity")
public void removeCity(@Named("id") String id)
{
EntityManager mgr = getEntityManager();
try
{
City city = mgr.find(City.class, id);
mgr.remove(city);
} finally
{
mgr.close();
}
}
private boolean containsCity(City city)
{
EntityManager mgr = getEntityManager();
boolean contains = true;
try
{
City item = mgr.find(City.class, city.getName());
if (item == null)
{
contains = false;
}
} finally
{
mgr.close();
}
return contains;
}
private static EntityManager getEntityManager()
{
return EMF.get().createEntityManager();
}
}
最初は @JsonIdentityInfo を使用していなかったため、java.io.IOException: com.google.appengine.repackaged.org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError)を取得していました。スレッドを読んだ後、自分のエラーがジャクソンによるものであることに気付きました。
Threadを読んだ後、@JsonIdentityInfo を使用することにしました。今、私は java.io.IOException: com.google.appengine.repackaged.org.codehaus.jackson.map.JsonMappingException: フィールド "suburbans" にアクセスしようとしましたが、オブジェクトをデタッチしたときにこのフィールドはデタッチされませんでした。このフィールドにアクセスしないか、オブジェクトをデタッチするときにデタッチしてください。(参照チェーン経由: com.google.api.server.spi.response.CollectionResponse["items"]->com.google.appengine.datanucleus.query.StreamingQueryResult[0]->com.zestbuds.android.City["郊外"])
Cascade.ALL を使用しているのに、suburban が切り離されないのはなぜですか?