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.
Related
See the following relations:
The Table RECIPE_USERCATEGORY_REL has an ON UPDATE CASCADE trigger, so if I would execute the following command in psql console, the value of ruc_ucat_category will also be updated automatically.
update usercategory set ucat_category = 'OldCategory' where ucat_category = 'NewCategory';
This works.
The problem is now Hibernate. I have this method in my service class:
public void renameCategory(String userId, String fromCategory, String toCategory)
{
TypedQuery<UserCategory> query = entityManager.createNamedQuery("UserCategory.findAllCaseSensitiveByUserIdAndPrefix", UserCategory.class);
query.setParameter(ApplicationConstants.PARAM_USER_ID, userId);
query.setParameter("category", fromCategory);
List<UserCategory> resultList = query.getResultList();
if (resultList == null || resultList.isEmpty())
{
return;
}
UserCategory userCategory = resultList.get(0);
userCategory.setCategory(toCategory);
}
I can assure that userCategory has the value 'OldCategory'. In my opinion, the update should work, because the trigger of the database should update the value of the relation table, but nothing happens. Why is this so?
Additional information: In my Entities, there is no #OneToMany and #ManyToOne declaration on the USERCATEGORY <-> RECIPE_USERCATEGORY_REL relationship (only on RECIPE <-> RECIPE_USERCATEGORY_REL relationship it is). This is because RECIPE_USERCATEGORY_REL is not a real join table. USERCATEGORY is similar to a growing lookup table, so Hibernate must not interfere the workflow here. The only relation of USERCATEGORY <-> RECIPE_USERCATEGORY_REL is the referential integrity in the database.
This is what the entity looks like, but as I said, there is no hibernate relation to the category table since Hibernate should not take care about this relation:
#Table(name = "RECIPE_USERCATEGORY_REL")
public class RecipeUserCategoryRel implements Serializable
{
private static final long serialVersionUID = 1L;
#EmbeddedId
private RecipeUserCategoryRelPk recipeUserCategoryRelPk = new RecipeUserCategoryRelPk();
#MapsId("rcpId")
#ManyToOne
#JoinColumn(name = "ruc_rcp_id", referencedColumnName = "rcp_id", insertable = false, updatable = false)
private Recipe recipe;
...
}
and...
#Embeddable
public class RecipeUserCategoryRelPk implements Serializable
{
private static final long serialVersionUID = 1L;
#Column(name = "ruc_rcp_id")
private Long rcpId;
#Column(name = "ruc_ucat_category")
private String category;
#Column(name = "ruc_ucat_acc_identifier")
private Long identifier;
public RecipeUserCategoryRelPk()
{
}
...
//getters, setters, hashcode, equals
}
I read in some other postings that it is not allowed in JPA to change the primary key. However, my use case is definitely changing the primary key, but in my case, it's not a common use case and no 'real' part of the application, but there are cases where users need to modify old data, so I need to provide this functionality.
(As workaround, I made a native update query)
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
I'm using JPA on RAD7.5. Entities i'm using are defined below. Everytime I'm accesisng Pgm joined Typ is returning as null.
#Entity
public class Typ implements Serializable {
#Id
#Column(name="TYP_ID")
private int TypId;
#OneToMany(mappedBy="TypId")
private Set<Pgm> pgmCollection;
}
#Entity
public class Pgm implements Serializable {
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name="TYP_ID")
private Typ TypId;}
I referenced following link -
https://stackoverflow.com/a/10702626/1483063 and tried 'generic' option as well, no gain though.
Tried query Select F.* from PGM F with 'Fetch JOIN' that didn't help either.
Any advice is greatly appreciated. Thanks.
What is the data on the database, is the foreign key null?
Your #JoinColumn does not make sense,
insertable = false, nullable = false, updatable = false
How is this column inserted if you have insertable = false?
Got GlassFish v3. I have an one-to-many entity. The problem is, that EclipseLink seems to ignore the fetch EAGER mode.
Here is my entities.
#Entity
public class Person implements Serializable
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#OneToMany(mappedBy = "person", fetch = FetchType.EAGER)
private List<Hobby> hobbies;
// getter and setter
}
A 1:n relationship
#Entity
public class Hobby
{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne
#JoinColumn
private Person person;
// getter and setter
}
And the bean
#javax.ejb.Remote
public interface Testing
{
public void addTestData();
public List<Person> getTestData();
}
#javax.ejb.Stateless
public class TestingBean implements Testing
{
#javax.persistence.PersistenceContext
private EntityManager entityManager;
public void addTestData()
{
Person p = new Person();
p.setName("JOE");
entityManager.persist(p);
Hobby h1 = new Hobby();
h1.setName("h1");
h1.setPerson(p);
entityManager.persist(h1);
}
public List<Person> getTestData()
{
TypedQuery<Person> gridQuery = entityManager.createQuery("SELECT e FROM Person e", Person.class);
return gridQuery.getResultList();
}
}
EDIT Client:
InitialContext context = new InitialContext();
Testing test = (Testing)context.lookup("java:global/dst2_1/TestingBean");
test.addTestData();
for(Person p: test.getTestData()) {
System.out.println(p.getName());
for(Hobby b : p.getHobbys()) {
System.out.println(b.getName());
}
}
context.close();
Using MySQL - Storing the data works. But if I fetch the data only the person is returned - not hobbies. Coudld you tell me what is wrong in my code?
EDIT sorry have tried so many things ... The code shown as above produces:
Exception Description: An attempt was made to traverse a
relationship using indirection that had a null Session. This often
occurs when a n entity with an uninstantiated LAZY relationship is
serialized and that lazy relationship is traversed after
serialization. To avoid this issue, ins tantiate the LAZY
relationship prior to serialization.
But the Person is returned correctly. Why does it specify LAZY while I am using EAGER?
You code looks correct. I can't see any way that the EAGER could be ignored.
Are you sure you get the error with this attribute, not another one?
Also ensure you recompile and deployed your code correctly. You most like have an old version deployed.
Make the eager object Serializable
I have an entity class that contains a map of key-value pairs which live in a different table and there may be no such pairs for a given entity. The relevant code for the entity classes is below.
Now, when I insert such an entity with persist(), then add key-value pairs, and then save it with merge(), I get duplicate entry errors for the related table that stores the key-value pairs. I tried to hold back insertion until the keys were added, to have one call to persist() only. This led to duplicate entry errors containing an empty (zero) id in the foreign key column (ixSource).
I followed the process in the debugger, and found that eclipselink seems to be confused about the cascading. While it is updating the entity, it executes calls that update the related table. Nonetheless, it also adds those operations to a queue that is processed afterwards, which is when the duplicate entry errors occur. I have tried CascadeType.ALL and MERGE, with no difference.
I'm using static weaving, if it matters.
Here's the entities`code, shortened for brevity:
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
#DiscriminatorColumn(name = "sType")
#Table(name = "BaseEntity")
public abstract class BaseEntity extends AbstractModel
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ix")
private long _ix;
}
#Entity
#Table(name = "Source")
public class Source extends BaseEntity
{
#OneToMany(cascade = CascadeType.MERGE)
#JoinTable(name = "SourceProperty", joinColumns = { #JoinColumn(name = "ixSource") })
#MapKey(name = "sKey")
private Map<String, SourceProperty> _mpKeys;
// ... there's more columns that probably don't matter ...
}
#Entity
#Table(name = "SourceProperty")
#IdClass(SourcePropertyKey.class)
public class SourceProperty
{
#Id
#Column(name = "sKey", nullable = false)
public String sKey;
#Id
#Column(name = "ixSource", nullable = false)
public long ixSource;
#Column(name = "sValue", nullable = true)
public String sValue;
}
public class SourcePropertyKey implements Serializable
{
private final static long serialVersionUID = 1L;
public String sKey;
public long ixSource;
#Override
public boolean equals(Object obj)
{
if (obj instanceof SourcePropertyKey) {
return this.sKey.equals(((SourcePropertyKey) obj).sKey)
&& this.ixSource == ((SourcePropertyKey) obj).ixSource;
} else {
return false;
}
}
}
I can't see how those errors would occur. Could you include the SQL and ful exception.
What version of EclipseLink are you using, did you try the latest release?
Why are you calling merge? Are you detaching the objects through serialization, if it is the same object, you do not need to call merge.
It could be an issue with the #MapKey, does it work if you remove this?