Persisting object does not persist all its relational objects - java

I am have three entities with relations look like this:
Customer and Order: one-to-many
Order and Article: one-to-many
Here are my Java Classes:
#Entity
#Table
public class Order implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "order_id")
private long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "customer_id", nullable = false)
private Customer customer;
#OneToMany(mappedBy = "order")
private List<Article> orderedArticles;
}
#Entity
#Table
public class Customer implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "customer_id")
private long id;
#OneToMany(mappedBy = "customer")
private List<Order> orders;
}
#Entity
#Table
public class Article implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "article_id")
private long id;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "order_id", nullable = false)
private Order order;
}
And then I persist the order to the database:
Customer customer = createCustomer()
List<Article> articles = createArticles();
Order order = new Order(customer, articles)
entityManager.persist(order);
The order and customer were successfully persisted but strangely that the article not. Can anyone help me here what did i do wrong? How can I make one call to persist the order and parallel the customer and article will be also persisted?
Thank you very much!

Try change
#OneToMany(mappedBy = "order")
private List<Article> orderedArticles;
to
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<Article> orderedArticles;

As it mentioned in this article:
Cascading only makes sense for Parent – Child associations (the Parent entity state transition being cascaded to its Child entities). Cascading from Child to Parent is not very useful and usually, it’s a mapping code smell.
So, for example your Order - Article association should be corrected in this way:
#Entity
#Table
public class Order implements Serializable {
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<Article> orderedArticles;
}
#Entity
#Table
public class Article implements Serializable {
#ManyToOne // CascadeType.ALL should not be used here
#JoinColumn(name = "order_id", nullable = false)
private Order order;
}
The same correction should be made for the Customer - Order association.
You use bidirectional #OneToMany. So, as documentation states:
Whenever a bidirectional association is formed, the application developer must make sure both sides are in-sync at all times.
For example your Order entity should have the following methods:
#Entity
#Table
public class Order implements Serializable {
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List<Article> orderedArticles;
public void addArticle(Article article) {
orderedArticles.add(article);
article.setOrder(this);
}
public void removeArticle(Article article) {
orderedArticles.remove(article);
article.setOrder(null);
}
}
to make the bidirectional association Order - Article in-sync. The same correction should be made for the Customer - Order association.
Assuming that your Order and Customer entities have the appropriate helper methods, the valid example of persisting can look like this:
Article article1 = new Article();
// ...
Article article2 = new Article();
// ...
Order order = new Order();
order.addArticle(article1);
order.addArticle(article2);
Customer customer = new Customer();
customer.addOrder(order);
entityManager.persist(customer);
So, you should start from articles creation, then add them to the order (or orders), then add your order (or orders) to the customer entity and then persist the customer. Due to the usage of CascadeType.ALL all children entities will be persisted too.

Related

JPA Entity Class using class object as parameter

I have a class User which has a parameter of another class type ShoppingList.
As this...
#Entity
public class Employee implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false, updatable = false)
private Long id;
private String Name;
#?????
private ShoppingList[] shoppingList;
}
How can i make this ManyToOne relationship while the variable being an array?
The idea is to have a User table and another ShoppingList table, so the user can have multiple lists at the same time.
This would be the correct way:
One Employee has many ShoppingLists.
One ShoppingList has only one Employee.
#Entity
public class Employee implements Serializable {
....
#OneToMany(mappedBy = "employee", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private List<ShoppingList> shoppingList;
....
}
#Entity
public class ShoppingList implements Serializable {
....
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "employee_id", nullable = false)
private Employee employee;
....
}
You can fine-tune your entities as per your need.
For more info, I would refer to this tutorial, it has helped me a lot.

Found shared references to a collection: OneToMany empty collections refering to same PersistentBag

I've the following model : An Organization has a List<Contract>, each Contract may have a pricebookId and a collection of PricebookEntry. Multiple Contract entities can have the same pricebookId. The Collection<PricebookEntry> is the result of a #OneToMany association with the pricebookId as JoinColumn.
Here is the postulate: An organization has 2 contracts and both contracts have no pricebookId.
And here is my problem: During an Hibernate session where this particular Organization is involved, Hibernate throws this exception:
org.hibernate.HibernateException: Found shared references to a collection: Contract.pricebookEntries.
Indeed, both contracts have their pricebookEntries set to the same PersistentBag (same reference).
How can I fix this ?
Thank you for your help!
#Entity
public class Organization implements Serializable {
#Id
private String id;
#Fetch(FetchMode.SUBSELECT)
#OneToMany(fetch = FetchType.EAGER, mappedBy = "organization")
private final List<Contract> contracts = new ArrayList<>();
}
#Entity
public class Contract implements Serializable {
#Id
private String id;
#Column(name = "pricebook_id")
private String pricebookId;
#JoinColumn(name = "organization_id", referencedColumnName = "id")
#ManyToOne(fetch = FetchType.LAZY)
private Organization organization;
#JoinColumn(name="pricebookId", referencedColumnName = "pricebook_id")
#OneToMany(fetch = FetchType.EAGER)
#Fetch(FetchMode.SUBSELECT)
private final Collection<PricebookEntry> pricebookEntries = new ArrayList<>();
}
#Entity
public class PricebookEntry {
#Id
private String id;
private String pricebookId;
}
What you have here isn't a #OneToMany relationship, since multiple Contract entities may reference the same (set) of PricebookEntry instances.
To fix turn the Collection<PricebookEntry> into a proper entity Pricebook and have a many-to-one relationship from Contract to Pricebook.
Also lose the now redundant pricebookid since it is simply the id of the Pricebook.

JPA ManyToOne/OneToMany bidirectional get duplicate data

I am building a REST API with Spring 2.1 and I am getting duplicate data to consult the ManyToOne relationship.
Localidad:
#Entity
#Table(name = "localidad")
public class Localidad implements Serializable {
private static final long serialVersionUID = -7258462202419598287L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idLocalidad;
private String nombreLocalidad;
private BigDecimal precioEnvio;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "provinciaIdProvincia")
private Provincia provincia;
public Localidad() {}
public Localidad(String nombreLocalidad, BigDecimal precioEnvio) {
this.nombreLocalidad = nombreLocalidad;
this.precioEnvio = precioEnvio;
}
...
Provincia:
#Entity
#Table(name = "provincia")
public class Provincia implements Serializable {
private static final long serialVersionUID = 3324427184301992595L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idProvincia;
private String nombreProvincia;
#OneToMany(mappedBy= "provincia", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Localidad> localidades = new HashSet<Localidad>();
public Provincia() {}
public Provincia(String nombreProvincia) {
this.nombreProvincia = nombreProvincia;
}
...
I access information by implementing CrudRepository and Service #Autowired
Duplicate data HTTP GET Request:
Duplicate Data
Thanks
The problem is caused by Jackson doing cyclic serialization on the provincia and the localidades fields. This can be solved by using the #JsonIgnoreProperties annotation. so in the Localidad class or entity add the annotation as follows:
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "provinciaIdProvincia")
#JsonIgnoreProperties("localidades")
private Provincia provincia;
And in the Provincia class modifiy the Set<Localidad>(btw you can just use a List<Localidad> instead) like this:
#OneToMany(mappedBy= "provincia", cascade = CascadeType.ALL, fetch =
FetchType.LAZY)
#JsonIgnoreProperties("provincia")
private Set<Localidad> localidades = new HashSet<Localidad>();
With this changes your Rest API should now show no duplicates. You have to ignore the fields that define the association between the two classes or entities. In case you have used #JsonProperty to define the fields names, use the names used in the #JsonProperty definition for #JsonIgnoreProperties.

org.hibernate.PropertyAccessException while persisting ManyToMany relationship

Have a problem persisting a ManyToMany relationship mapped like that
Document.java
public class Document {
.......
#ManyToMany(targetEntity = Category.class, cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable(name = "fideuram_gup_documents_in_categories",
joinColumns = #JoinColumn(name="fk_document"),
inverseJoinColumns = #JoinColumn(name = "fk_category"))
private Set<Category> categories = new HashSet<Category>();
.......
}
where Category is one more entity of my model which I don't paste here since it doesn't carry a reverse mapping of this relation, and has just an ID and a name.
When I try to persist Document however I get the following error:
org.hibernate.PropertyAccessException: could not get a field value by reflection getter of it.ardesia.fideuram.gup.model.Category.id
I've surfed the web about it but no page relates to ManyToMany relations. Of course all the ManyToOne relations I have on the entity Document work fine.
I'm using:
spring-data-jpa:1.2.0.RELEASE
hibernate-core:4.2.2.Final
hibernate-entitymanager:4.2.2.final
UPDATE
All entities expose a default constructor and getter/setter for every field. Or,more preciselt, I'm using Spring Roo for creating the entity and it injects getters and setters automatically upon compilation.
You can instrument Hibernate how it must access your property using #javax.persistence.Access annotation; put on your mapped class with #Access.value set to
AccessType.FIELD for direct field access
AccessType.PROPERTY for accessing properties using accessors
Maybe it can help you, I already did the same, I put my code, it creates a join table:
#Entity
#Table(name = "custom_pizza")
public class CustomPizza extends BaseEntity {
private static final long serialVersionUID = 1L;
// ManyToMany instead of oneToMany in order to don't have the unique
// constraint on each primary key of the join table
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable(name = "custom_pizza_topping", joinColumns = #JoinColumn(name = "custom_pizza_id"), inverseJoinColumns = #JoinColumn(name = "topping_id"))
private Set<Topping> toppings = new HashSet<Topping>();
public void addTopping(Topping topping) {
toppings.add(topping);
}
public void removeTopping(Topping topping) {
toppings.remove(topping);
}
...
And my topping:
#Entity
#Table(name = "topping")
public class Topping extends BaseEntity {
private static final long serialVersionUID = 1L;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "price", nullable = false)
private float price;
....
and the BaseEntity
#MappedSuperclass
public abstract class BaseEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
...

JPA EclipseLink #OneToMany returns empty set

I am using an embedded id:
#Embeddable
public class EntityId {
private long id;
}
And I have the following tables:
#Entity
public class Master {
#EmbeddedId private EntityId id;
#OneToMany(mappedBy = "master")
Set<Config> configs;
}
#Entity
public class SlaveLeft {
private #EmbeddedId EntityId id;
}
#Entity
public class SlaveRight {
private #EmbeddedId EntityId id;
}
#Entity
public class Config {
#EmbeddedId private EntityId id;
#ManyToOne
#JoinColumn(name = "MASTER_ID", referencedColumnName = "id")
private Master master;
#ManyToOne
#JoinColumn(name = "LEFT_ID", referencedColumnName = "id")
private SlaveLeft slaveLeft;
#ManyToOne
#JoinColumn(name = "RIGHT_ID", referencedColumnName = "id")
private SlaveRight slaveRight;
}
I then populate the database:
final EntityManager em = injector.getInstance(EntityManager.class);
em.getTransaction().begin();
Master master = new Master(EntityId.next());
SlaveLeft slaveLeft = new SlaveLeft(EntityId.next());
SlaveRight slaveRight = new SlaveRight(EntityId.next());
Config config = new Config(EntityId.next(), master, slaveLeft, slaveRight);
em.persist(master);
em.persist(slaveLeft);
em.persist(slaveRight);
em.persist(config);
em.getTransaction().commit();
final Master master1 = em.find(Master.class, master.getId());
System.out.println(master1.getConfigs());
The problem is that the master1.getConfigs() returns empty.
What am I missing to get a working relation?
If you use another entity maneger to find your master, its configs collection will probably be populated. But here, the entity manager returns the master from its cache, and since you didn't put the config into the master's set of configs, it isn't there.
When using a bidirectional relationship, you are responsible for the maintenance of both sides of the relationship. The ORM only uses the owning side (the one without the mappedBy attribute) to decide if the relationship exists or not, but the coherence of the object graph is your responsibility.

Categories

Resources