Bidirectional one to one mapping in GAE using JDO? - rest

How can I implement a bidirectional one-to-one mapping using Google Application Engine (GAE) using Java Data Objects (JDO)?
I have a User class which holds contactInfo object and a ContactInfo class that holds a user object
#PersistenceCapable(identityType ="APPLICATION", detachable = "true")
public class User{
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
private String name;
#Persistent(dependent = "true")
private ContactInfo child;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ContactInfo getChild() {
return child;
}
public void setChild(ContactInfo child) {
this.child = child;
}
}
#PersistenceCapable(identityType ="APPLICATION", detachable = "true")
public class ContactInfo {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key id;
#Persistent(mappedBy = "child")
private User parent;
private String contactDetail;
public Key getId() {
return id;
}
public void setId(Key id) {
this.id = id;
}
public String getContactDetail() {
return contactDetail;
}
public void setContactDetail(String contactDetail) {
this.contactDetail = contactDetail;
}
}
Following error i am getting while testing API from API explorer
com.google.appengine.repackaged.org.codehaus.jackson.map.JsonMappingException: Infinite recursion (StackOverflowError) (through reference chain: com.demo.jdo.ContactInfo[\"user\"]->com.demo.jdo.User[\"contactInfo\"]->com.demo.jdo.ContactInfo[\"user\"]-

Standard JDO 1-1 bidir is simply found from http://www.datanucleus.org/products/accessplatform_3_1/jdo/orm/one_to_one.html#bi
GAE ought to be no different in this respect; last time I used it (maybe 3 yrs ago) they had some tests, think those under here http://code.google.com/p/datanucleus-appengine/source/browse/#svn%2Ftrunk%2Ftests%2Fcom%2Fgoogle%2Fappengine%2Fdatanucleus
Your question provides no definition of what you have tried in terms of annotations

Got the Solution, problem was with wrong use of mappedBy & presence of getter and setter of parent object in child.
#Persistent(mappedBy = "") annotation should only be at non-owner side
In on-owner/ child side there should not be any getter/setter present for owner/parent object.
Working code:
User.java
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, detachable = "true")
public class User {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Long id;
private String name;
#Persistent(dependent = "true")
private ContactInfo contactInfo;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public ContactInfo getContactInfo() {
return contactInfo;
}
public void setContactInfo(ContactInfo contactInfo) {
this.contactInfo = contactInfo;
}
}
ContactInfo.java
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;
import com.google.appengine.api.datastore.Key;
#PersistenceCapable(identityType = IdentityType.APPLICATION, detachable = "true")
public class ContactInfo {
#PrimaryKey
#Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
private Key id;
#Persistent(mappedBy = "contactInfo")
/*
* Important: Do not create getter and setters for this object else
* bidirectional mapping gives error
*/
private User user;
private String contactDetail;
public Key getId() {
return id;
}
public void setId(Key id) {
this.id = id;
}
public String getContactDetail() {
return contactDetail;
}
public void setContactDetail(String contactDetail) {
this.contactDetail = contactDetail;
}
}

Related

Convert Java object to BigQuery TableRow

I am exploring Google Cloud Dataflow.
I was wondering if automatic conversion between java object or JSON to TableRow can be done.
Just like we can automatically parse JSON to POJO class.
I could not find relevant information.
Hope not to duplicate question.
Will be grateful for any info!
Greetings
I've looking for examples for the same with no luck. I created a POJO class that almost match the schema of the bigquery table and matches the structure of the JSON objects that are the input for the pipeline. Finally, when I have to convert those objects to TableRow, for the nested and repeated values I made something like below, and the conversion was made by the API
TableRow row = new TableRow()
.set("items", c.element().getItems())
.set("orderDate", c.element().getOrderDate())
.set("orderNumber", c.element().getOrderNumber());
Where Item class is part of the Order object :
#JsonProperty("items")
private List<Item> items = null;
This is the code for Item class:
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({
"id",
"code",
"detail",
"name",
"shortName",
"description",
"sku",
"quantity",
"category",
"products"
})
public class Item implements Serializable
{
#JsonProperty("id")
private Integer id;
#JsonProperty("code")
private String code;
#JsonProperty("detail")
private String detail;
#JsonProperty("name")
private String name;
#JsonProperty("shortName")
private String shortName;
#JsonProperty("description")
private String description;
#JsonProperty("sku")
private String sku;
#JsonProperty("quantity")
private Integer quantity;
#JsonProperty("category")
private Category category;
#JsonProperty("products")
private List<Product> products = null;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
private final static long serialVersionUID = -5644586446669059821L;
#JsonProperty("id")
public Integer getId() {
return id;
}
#JsonProperty("id")
public void setId(Integer id) {
this.id = id;
}
#JsonProperty("code")
public String getCode() {
return code;
}
#JsonProperty("code")
public void setCode(String code) {
this.code = code;
}
#JsonProperty("detail")
public String getDetail() {
return detail;
}
#JsonProperty("detail")
public void setDetail(String detail) {
this.detail = detail;
}
#JsonProperty("name")
public String getName() {
return name;
}
#JsonProperty("name")
public void setName(String name) {
this.name = name;
}
#JsonProperty("shortName")
public String getShortName() {
return shortName;
}
#JsonProperty("shortName")
public void setShortName(String shortName) {
this.shortName = shortName;
}
#JsonProperty("description")
public String getDescription() {
return description;
}
#JsonProperty("description")
public void setDescription(String description) {
this.description = description;
}
#JsonProperty("sku")
public String getSku() {
return sku;
}
#JsonProperty("sku")
public void setSku(String sku) {
this.sku = sku;
}
#JsonProperty("quantity")
public Integer getQuantity() {
return quantity;
}
#JsonProperty("quantity")
public void setQuantity(Integer quantity) {
this.quantity = quantity;
}
#JsonProperty("category")
public Category getCategory() {
return category;
}
#JsonProperty("category")
public void setCategory(Category category) {
this.category = category;
}
#JsonProperty("products")
public List<Product> getProducts() {
return products;
}
#JsonProperty("products")
public void setProducts(List<Product> products) {
this.products = products;
}
#JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
#JsonAnySetter
public void setAdditionalProperty(String name, Object value) {
this.additionalProperties.put(name, value);
}
}
And this is the schema of the BigQuery table in regards Items, where Item is a RECORD and REPEATED field and also contain a nested RECORD and REPEATED field: products. See the screenshot of the schema
Item schema fields in BQ

Spring boot CrudRepository save - exception is org.hibernate.type.SerializationException: could not serialize

Not sure why I have an issue here, but when I save with a CrudRepository with these objects, I get the SerializationException (with no further information). Can someone take a look at my objects and offer me some insight into why they can't serialize? My pom.xml is attached last as well in case that helps somehow. I'm using a Postgres database.
EDIT: The database and now - tables are created, but objects are not creating rows.
The actual CrudRepository interface:
public interface AccountRepository extends CrudRepository<ZanyDishAccount, String> {}
ZanyDishAccount entity:
#Entity
public class ZanyDishAccount {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id; // internal id of the customer account for a Zany Dish subscription
private String status;
#OneToOne(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "company_id")
private Company company;
#OneToOne(cascade=CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "order_id")
private Order order;
public ZanyDishAccount() {}
public ZanyDishAccount(Company company, Order order) {
this.company = company;
this.order = order;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public Order getOrder() {
return order;
}
public void setOrder(Order order) {
this.order = order;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
#Override
public String toString()
{
return "ClassPojo [id = "+id+ ", company = " + company + ", status = " + status + "]";
}
}
Company entity:
#Entity
public class Company {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
Long id;
private String phoneNumber;
private String website;
private String name;
private String uuid;
private String country;
public Company() {}
public Company(String phoneNumber, String website, String name, String uuid, String country) {
this.phoneNumber = phoneNumber;
this.website = website;
this.uuid = uuid;
this.country = country;
}
public String getPhoneNumber ()
{
return phoneNumber;
}
public void setPhoneNumber (String phoneNumber)
{
this.phoneNumber = phoneNumber;
}
public String getWebsite ()
{
return website;
}
public void setWebsite (String website)
{
this.website = website;
}
public String getName ()
{
return name;
}
public void setName (String name)
{
this.name = name;
}
public String getUuid ()
{
return uuid;
}
public void setUuid (String uuid)
{
this.uuid = uuid;
}
public String getCountry ()
{
return country;
}
public void setCountry (String country)
{
this.country = country;
}
#Override
public String toString()
{
return "ClassPojo [phoneNumber = "+phoneNumber+", website = "+website+", name = "+name+", uuid = "+uuid+", country = "+country+"]";
}
}
Order entity:
#Entity
#Table(name = "_order")
public class Order {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
Long id;
private String pricingDuration;
private Items[] items;
private String editionCode;
public Order() {}
public Order(String pricingDuration, Items[] items, String editionCode) {
this.pricingDuration = pricingDuration;
this.items = items;
this.editionCode = editionCode;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPricingDuration ()
{
return pricingDuration;
}
public void setPricingDuration (String pricingDuration)
{
this.pricingDuration = pricingDuration;
}
public Items[] getItems ()
{
return items;
}
public void setItems (Items[] items)
{
this.items = items;
}
public String getEditionCode ()
{
return editionCode;
}
public void setEditionCode (String editionCode)
{
this.editionCode = editionCode;
}
#Override
public String toString()
{
return "ClassPojo [pricingDuration = "+pricingDuration+", items = "+items+", editionCode = "+editionCode+"]";
}
}
Thanks for your help!
Mike
Hm, this seems multi-faceted. Let's see if I can help at all. Last thing first...
No tables being created automatically.
I would take a look at this section in Spring's docs for the most basic approach: Initialize a database using Hibernate. For example, spring.jpa.hibernate.ddl-auto: create-drop will drop and re-create tables each time the application runs. Simple and easy for initial dev work. More robust would be leveraging something like Flyway or Liquibase.
Serialization issue
So without logs, and the fact that you have no tables created, the lack of a persistence layer would be the assumed culprit. That said, when you have tables and data, if you do not have a repository for all of the related tables, you'll end up with a StackOverflow error (the serialization becomes circular). For that, you can use #JsonBackReference (child) and #JsonManagedReference (parent). I have been successful using only #JsonBackReference for the child.
Items[]
I'm not sure what Item.class looks like, but that looks like an offensive configuration that I missed the first round.
Change private Items[] items; to private List<Item> items = new ArrayList<Item>();. Annotate with #ElementCollection.
Annotate Item.class with #Embeddable.

JPA OneToOne cascade delete

i have a rellationship between 2 classes Document and Medecin
#Entity
public class Document implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private String annee;
private Date dateVisite;
private String secteur;
private String typeVisite;
#OneToOne( fetch=FetchType.LAZY,cascade=CascadeType.REMOVE)
#JoinColumn(name = "idMedecin")
private Medecin medecin;
public Document(String annee,
Date dateVisite, String secteur, String typeVisite) {
super();
this.annee = annee;
this.dateVisite = dateVisite;
this.secteur = secteur;
this.typeVisite = typeVisite;
}
public String getSecteur() {
return secteur;
}
public void setSecteur(String secteur) {
this.secteur = secteur;
}
public String getTypeVisite() {
return typeVisite;
}
public void setTypeVisite(String typeVisite) {
this.typeVisite = typeVisite;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getAnnee() {
return annee;
}
public void setAnnee(String annee) {
this.annee = annee;
}
public Date getDateVisite() {
return dateVisite;
}
public void setDateVisite(Date dateVisite) {
this.dateVisite = dateVisite;
}
}
and the medecin entity is
#Entity
public class Medecin implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private long id;
private String nom;
private String secteur;
private int telephone;
private int specialite;
public Medecin() {
super();
// TODO Auto-generated constructor stub
}
public Medecin(String nom, String secteur, int telephone, int specialite) {
super();
this.nom = nom;
this.secteur = secteur;
this.telephone = telephone;
this.specialite = specialite;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public String getSecteur() {
return secteur;
}
public void setSecteur(String secteur) {
this.secteur = secteur;
}
public int getTelephone() {
return telephone;
}
public void setTelephone(int telephone) {
this.telephone = telephone;
}
public int getSpecialite() {
return specialite;
}
public void setSpecialite(int specialite) {
this.specialite = specialite;
}
}
the problem is that after i generate the database i want if i delete the document record from the database i want the medecin record will be deleted also but in my case if i delete the document record the medecin record dont be deleted
Based on your configuration, Hibernate will generate Document table with foreign key pointing to Medicine table.
To achieve your requirement, it should be like:
public class Document {
#OneToOne(mappedBy = "document", cascade = CascadeType.REMOVE)
private Medicine medicine;
}
public class Medicine {
#OneToOne
private Document document;
}
Updated
public void delete(int id){
Document document = entityManager.find(Document.class, id);
entityManager.remove(document);
entityManager.flush();
}

AnnotationException: mappedBy reference an unknown target entity property

I have one exception, which yold what I have no mapping on table. But I have this
Exeption is : \
AnnotationException: mappedBy reference an unknown target entity property: Relative.people in Person.relations
Relative entity is here:
#Entity
#Table(name = "relation")
public class Relative extends AbstractModel<UUID> implements Model<UUID> {
private UUID id;
private Person person;
private RelationTypeEnum relation;
public Relative() {
}
#Override
public void assignId() {
id = UUID.randomUUID();
}
#Override
#Id
#Column(name = "id")
public UUID getId() {
return id;
}
#ManyToOne
#JoinColumn(name="person_id", nullable=false)
public Person getPerson() {
return person;
}
#Column(name = "relation")
public RelationTypeEnum getRelation() {
return relation;
}
public void setId(UUID id) {
this.id = id;
}
public void setPerson(Person person) {
this.person = person;
}
public void setRelation(RelationTypeEnum relation) {
this.relation = relation;
}
}
And Person entity is here:
#Entity
#Table(name = "people")
public class Person extends AbstractModel<UUID> implements Model<UUID> {
private UUID id;
private String name;
private List<Relative> relations;
#Override
public void assignId() {
id = UUID.randomUUID();
}
#Override
#Id
#Column(name = "id")
public UUID getId() {
return id;
}
#Column(name = "name")
public String getName() {
return name;
}
#OneToMany(targetEntity=Relative.class, cascade=CascadeType.ALL,
mappedBy="people")
public List<Relative> getRelations() {
return relations;
}
public void setId(UUID id) {
this.id = id;
}
public void setName(String username) {
this.name = username;
}
public void setRelations(List<Relative> relations) {
this.relations = relations;
}
}
Solved.
Just changed
#Table(name = "people")
to
#Table(name = "person")
In my case there was a project which included a copy of the jar causing this issue. It was a web project which is including the jar inside its lib i.e. 2 copies of the same jar one with a different class version. Only discovered this when I physically opened the main ear and found the 2nd jar inside a web project.

Copy Entity ID at persist time

I want to copy the entity's UUID, generated at run time to another field.
The entity id is generated via the code described bellow:
package eclipselink.example;
public class UUIDSequence extends Sequence implements SessionCustomizer {
public UUIDSequence() {
super();
}
public UUIDSequence(String name) {
super(name);
}
#Override
public Object getGeneratedValue(Accessor accessor,
AbstractSession writeSession, String seqName) {
return UUID.randomUUID().toString().toUpperCase();
}
...
public void customize(Session session) throws Exception {
UUIDSequence sequence = new UUIDSequence("system-uuid");
session.getLogin().addSequence(sequence);
}
}
Persitence.xml:
property name="eclipselink.session.customizer" value="eclipselink.example.UUIDSequence"
The entity:
public abstract class MyEntity{
private String id;
private String idCopy;
#Id
#Basic(optional = false)
#GeneratedValue(generator="system-uuid")
#XmlElement(name = "ID")
public String getId() {
return id;
}
}
How can I instruct JPA (Eclipse-link) to copy the UUID generated at runtime to idCopy field as well?
I'm not 100% sure this will work (I don't know if EclipseLink calls the setter or assigns the field directly), but give this a try:
public abstract class MyEntity{
private String id;
private String idCopy;
#Id
#Basic(optional = false)
#GeneratedValue(generator="system-uuid")
#XmlElement(name = "ID")
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
this.idCopy = id;
// or
// this.setIdCopy(id);
}
}