Jpa eclipselink bug when try persist collection by another entity - jpa

Hello i have got one entity which contains collection of object:
#Entity
public class ResourceType{
#Id
public Integer id;
#ManyToMany(mappedBy = "resourcesTypes")
private Set<Resource> resources = new HashSet<>();
}
As you see this entity contains collection of Resource
#Entity
public class Resource{
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinTable(name = "RESOURCE_TYPE_CROSS", joinColumns = { #JoinColumn(name = "RESOURCE_ID") }, inverseJoinColumns = { #JoinColumn(name = "RESOURCE_TYPE_ID", referencedColumnName = "ID") })
private List<ResourceType> resourcesTypes = new ArrayList<>();
}
So i guess if i get all resourceTypes with all resources this collection is in local cache.
But if i try persist resource :
#Override
public ResourcesDTO createResource(ResourcesDTO resource) throws CreateResourceException {
if (resource.getResourcesTypes().isEmpty()) {
throw new CreateResourceException("Can not create resouce " + resource.getName()
+ " none resource types is set");
resourceDAO.create(map(resource));
log.debug("Created resource: " + r);
resource.setId(r.getId());
return resource
}
If i perssist Resource with ResourcesTypes and next i will get all ResourceTypes entity manager do not found new Resources. But in my cross table resource_type_cross everything is ok. i will try to do something like that after create new resource:
for(ResourceType rt : resource.getResourceTypes()){
em.refresh(rt);
}
But its not working properly. After i will restart server everything is fine. But why entity manager do not refresh resource type ??
This is what i use to read all ResourceTypes:
public ResourceType getAllResourceTypes(){
em.createQuery("Select n from Resource n left join fetch n.children");
return em.find(ResourceType.class, 0); //0 - ROOT
}
as follow article : http://www.tikalk.com/java/load-tree-jpa-and-hibernate
The Main question is that :
create or update entity by one side do not update another, so in my case i create and update resource type by resource entity, but i am using resouce type to get all resource.

First, there is a problem with your ManyToMany mappings. You have made both sides unidirectional accessing the same join table instead of bidirectional. One side should control the mapping, with the other set to mappedby the owning side.
Second, have you flushed or committed the changes when you refresh ResourceTypes? It is probably better to maintain both sides by setting the reference in code rather than query to refresh each and every ResourceType

Related

How can I force Hibernate to use joins to fetch data for instances

I have a Spring Boot application using Hibernate as JPA provider. My application has two entities connected with a #OneToMany / #ManyToOne relation. The relation is annotated with #Fetch(FetchMode.JOIN) on both directions, and fetch = FetchType.EAGER.
My entities are called Car and Driver:
#Entity
#Table(name = "car")
#Data
public class Car implements Serializable, Cloneable {
#Id
#GenericGenerator(name = "car_seq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = {
#Parameter(name = "sequence_name", value = "car_seq") })
#GeneratedValue(generator = "car_seq")
private Integer id;
#OneToMany(mappedBy = "car", fetch = FetchType.EAGER)
#Fetch(FetchMode.JOIN)
private List<Driver> drivers = new ArrayList<>();
#Column(name = "license_no", nullable = false)
private String licenseNo;
}
#Entity
#Table(name = "driver")
#Data
public class Driver implements Serializable, Cloneable {
#Id
#GenericGenerator(name = "driver_seq", strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator", parameters = {
#Parameter(name = "sequence_name", value = "driver_seq") })
#GeneratedValue(generator = "driver_seq")
private Integer id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "car_id", nullable = true)
#Fetch(FetchMode.JOIN)
private Car car;
#Column(name = "name", nullable = false)
private String name;
}
When selecting a care (e.g. by calling Car.findById()), Hibernate joins the the two tables in a single SQL, and returns a Car object with a list of Drivers.
But if I select a single driver, Hibernate will join the Driver and Car table to give me the Driver object with the Car property populated, but it will run a second query to fetch all the driver objects on for the list on the car object.
For performance reasons I would like all the involved objects to be fetched in a single query, as is the case when I fetch a car. But I cannot find a way to make Hibernate do this. There is a property, hibernate.max_fetch_depth, which is supposed to do this, but I have found that it only affects the behavior of fetching a car, not when I fetch a driver.
I know I can use an EntityGraph to control the fetching, and by using an EntityGraph I have successfully retrieved a driver object with its car and all the car's drivers in one query. But to do that, I have to explicitly use a graph when retrieving the object, and I cannot do that in all the various cases where a Car object is needed. There are lots of other entities that have a relation to Car, and I don't want to write an EntityGraph for each and every one of those.
So is there a way to tell Hibernate how you want the fetching to be done by default on an entity? I would have thought that the annotations would be enough, but it seems that there either has to be something more, or that this simply cannot be done.
Arndt
FetchType.EAGER is one of the most common reasons for performance problems. You should use
#OneToMany(mappedBy = "car")
private List<Driver> drivers = new ArrayList<>();
And fetch drivers If needed
SELECT c FROM Car c JOIN FETCH c.drivers

JPA EclipseLink Parent Child

I am currently using 2 classes that have a OneToMany relation. One class contains catalogs (you can think of it as book); an other class contains template (you can think of it as pages). In this scenario, one template can belong only to one catalog hence I used the OneToMany relation.
My application goes very well until I restart the service. It is currently running on Hana Cloud Platform under MaxDB. I am using JPA and eclipselink (I used #AdditionalCriteria to manage my multi-tenancy as the multi-tenancy offered by JPA does not allow me to make queries on multiple tenants).
Here is an extract of my code for the Catalog:
#Entity
#Table(name = "Catalog")
#AdditionalCriteria("(:adminAccess = 1 or this.customerId=:customerId) AND (:allStatus = 1 or this.statusRecord = :statusRecord)")
public class Catalog implements Serializable {
private static final long serialVersionUID = -3906948030586841482L;
#Id
#GeneratedValue
private long id;
[...]
#OneToMany(cascade = ALL, orphanRemoval = false, fetch = EAGER, mappedBy = "catalog")
private Set<Template> templates = new HashSet<Template>();
[...]
public void setTemplate(Template template) {
this.templates.add(template);
}
}
The code for Template is the following:
#Entity
#Table(name = "Template")
#AdditionalCriteria("(:adminAccess = 1 or this.customerId=:customerId) AND (:allStatus = 1 or this.statusRecord = :statusRecord)")
public class Template implements Serializable {
private static final long serialVersionUID = 5268250318899275624L;
#Id
#GeneratedValue
private long id;
[...]
#ManyToOne(cascade = ALL, fetch = EAGER)
#JoinColumn(name = "catalog_id", referencedColumnName = "id")
private Catalog catalog;
public void setCatalog(Catalog catalog) {
this.catalog = catalog;
if(!catalog.getTemplate().contains(this))
catalog.getTemplate().add(this);
}
}
In my Servlet, I use only the Catalog to make operations. If I have to save a template, I read it from the catalog, make the modifications in the template and persist the catalog.
It works very well until I restart my service.
The catalog does not have any references to the templates anymore BUT the template still have a reference to the catalog it used to belong to.
Can you please point me into the right direction?
Thanks

Jpa-One To Many and Restws

I have two classes, Customer and CustomerProduct. I want the customerId(from customer) to be mapped in CustomerProduct. I used OneToMany mapping as follows.
Customer.java
#XmlTransient
#OneToMany(fetch = FetchType.EAGER, mappedBy = "customer")
public Set<CustomerProducts> getCustomerProducts() {
return customerProducts;
}
CustomerProduct.java
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "customer_id", nullable = false)
public Customer getCustomer() {
return customer;
}
I was able to save the datas, but for selecting a customer, I used named query which returned a Customer with CustomerProduct as well. I used restws. I used Response.ok(cust).build(); where cust had all the values in the server side.
When I get the response in the client side, I am getting the customers but not the CustomerProduct.
Did I miss something?
You need to remove the #XmlTransient annotation, it prevents the customerProducts property from serialization to a response.
See: http://docs.oracle.com/javaee/6/api/javax/xml/bind/annotation/XmlTransient.html
And to avoid infinity loop during serialization you either need add #XmlTransient to the customer property of the CustomerProduct entity or fetch this property lazily.

jpa many to many inverse side cant perform merge operation after been cascaded

i have two entity classes with mapping #ManytoMany
Inverse Side
public class Users implements Serializable {
#ManyToMany(mappedBy = "usersList",cascade=CascadeType.MERGE)
private List<Tags> tagsList = new ArrayList();
Owning Side
public class Tags implements Serializable {
#JoinTable(name = "USERS_TAG_XREF", joinColumns = {
#JoinColumn(name = "TAG_ID", referencedColumnName = "TAG_ID")}, inverseJoinColumns = {
#JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")})
#ManyToMany
private List<Users> usersList = new ArrayList();
Now when i perform an operation on the owning side like this;
for(Tags t : tList){
Users u = em.find(Users.class, id);
t.getUsersList().clear();
em.merge(t);
}
It clears many users U relations with the Tag entity t, i assume this works because Tag entity is the owing side , but when i perform this with the inverse side ;
Users u = em.find(Users.class,id);
u.getTagList().clear();
em.merge(u);
It doesnt do anything on the table, i actually want the code to clear many Tags t relations with one User u, but i feel since this is an inverse side by adding the cascade=CascadeType.MERGE would allow merge operation as this em.merge(u); but it doesnt
please help
You should also remove User from Tag entity
Users u = em.find(Users.class,id);
{
for (Tag t : u.getTagList())
{
t.hetUserList().remove(u);
}
u.getTagList().clear();
em.merge(u);
}

GWT Resolver attribute of relationship can not be resolved

I'm working on a GXT project using JPA for persistence, but I'm facing an issue with bidirectionnal relationship persistence.
I have those two Entities :
#Entity
#Table(name = "ACTV_REQ", catalog = "erpdb")
#AttributeOverride(name = "id", column = #Column(name = "ID", nullable = false, columnDefinition = "BIGINT UNSIGNED"))
#NamedQueries(value = {
#NamedQuery(name = "findByPerson", query="select object(m) from ActvReq m where m.people= :people")
})
public class ActvReq extends BaseEntity {
#ManyToOne
#JoinColumn(name = "PPL_ID")
#NotNull
private People people;
#ManyToOne
#JoinColumn(name = "ACTV_TYP_ID")
private ActivityTyp actvTyp;
#ManyToOne
#JoinColumn(name= "PPL_ACTV_RIGHT_ID")
private PeopleActvRight pplActvRight;
#Column(name = "DESCR")
private String desc;
}
And :
#Entity
#Table(name = "PPL_ACTV_RIGHT", catalog = "erpdb")
#AttributeOverride(name = "id", column = #Column(name = "ID", nullable = false, columnDefinition = "BIGINT UNSIGNED"))
#PeopleActvRightBeanConstraint
#NamedQueries(value = {
#NamedQuery(name = "findByPeople", query="select object(m) from PeopleActvRight m where m.people= :people")
})
public class PeopleActvRight extends BaseEntity {
#ManyToOne
#JoinColumn(name="ACTV_TYP_ID")
#NotNull
ActivityTyp type;
#ManyToOne
#JoinColumn(name="PPL_ID")
#NotNull
People people;
#ManyToOne
#JoinColumn(name="ACTV_RIGHT_ID")
ActvRight actvRight;
#OneToMany(mappedBy="pplActvRight",cascade=CascadeType.ALL)
private List<ActvReq> actvRequests = new ArrayList<ActvReq>();
}
(I did not copy getters and setters but thoses methods exists.)
For the persistence of ActvReqProxy, it's basically done that way in my EditorPresenter :
getRequestContext().persistAndReturn(getModel()).with("actvTyp","people","pplActvRight").fire(new Receiver<M>() {
#Override
public void onSuccess(M response) {
unsetContext();
onSaveSuccess( response );
}
});
And the response pplActvRight is already null in the response I get, but in getModel() pplActvReqProxy is set.
On server side I've a service which calls the following method of my DAO :
public ActvReq persistAndReturn(ActvReq entity){
em.getTransaction().begin();
em.persist(entity);
em.close;
return entity;
}
And when I'm trying to persist a ActvReqProxy from my editor, using method with("pplActvRight","people",actvType"), I don't get any errors, but in my DB the entity is not entirely persisted. I mean a new ActvReq is created in the DB but field PPL_ACTV_RIGHT_ID remains null. (It works fine for people and actvTyp)
EDIT : In fact I assume the problem is located on GWT Resolver in resolveDomainValue, it can not resolve the attribute pplActvRight. It's as if my EntityProxy object doesn't exists on server-side.
Any ideas?
So at the beginning of persistAndReturn on server side it is already null? If so, then at least we know that it has nothing to do with JPA.
And you're sure that on client side it is set to something other than null on proxy before calling persistAndReturn? You can easily verify it: using Eclipse debugger it is possible to see JSON code to which proxy will be serialized (one of fields of proxy that you can see when you select proxy object in debugger). Please make sure that pplActvRight key is there with not-null value.
If so, maybe you should debug GWT source code that translates proxies to server-side entities to check what is being done with that pplActvRight property (why isn't it assigned to corresponding ActvReq server side instance). I can't remember what the class name doing this stuff was but if you won't be able to find it I can search it for you.