我想在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;
}
}
// suburban.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;
}
}
我使用google-plugin为eclipse选项“Generate Cloud Endpoint Class”自动生成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:无限递归(StackOverflowError)< / em>的。阅读thread后,我发现我的错误是由于杰克逊。
阅读Thread后,我决定使用@JsonIdentityInfo。现在我来了 java.io.IOException:com.google.appengine.repackaged.org.codehaus.jackson.map.JsonMappingException:您刚刚尝试访问字段“suburbans”但是在分离对象时此字段未分离。要么不访问此字段,要么在分离对象时将其分离。 (通过参考链:com.google.api.server.spi.response.CollectionResponse [“items”] - &gt; com.google.appengine.datanucleus.query.StreamingQueryResult [0] - &gt; com.zestbuds.android.City [ “居住在郊区”])
为什么我的郊区并没有脱离,即使我使用的是Cascade.ALL?
答案 0 :(得分:4)
终于解决了问题。
无需使用@JsonIdentityInfo。我只需要删除具有@ManyToOne注释的类成员的getter和setter方法(在我的例子中,我删除了getCity()和setCity())。
Here是datanucleus为双向一对多映射提供的示例。