I have a class with a
#OneToMany(cascade = CascadeType.PERSIST)
#OrderBy(value="minAantal") Dit lijkt niet te werken.
private Collection<PrijsTabelItem> prijsTabelItems;
The property minAantal is an integer. When I persist an Object and recall it from the database, the Collection prijsTabelItem is not sorted.
I already tried to change the OneToMany to:
#OneToMany(cascade = CascadeType.PERSIST, fetch=FetchType.EAGER)
but that does not make any difference.
Is this a problem in openJPA or have I done something wrong?
Related
I'm writing some entity relationships using Spring Data and Java. I have this pair of classes (edited):
Subject:
#Entity
#Table(name = "SUBJECT")
// Lombok, etc., attributes removed
public class Subject {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "ID", updatable = false, nullable = false)
#JsonProperty("id")
private Long id;
#OneToMany(targetEntity = SubjectResource.class, mappedBy = "subject", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<SubjectResource> resources;
}
SubjectResource:
#Entity
#Table(name = "SUBJECT_RESOURCE")
// Lombok, etc., attributes removed
public class SubjectResource {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#Column(name = "ID", updatable = false, nullable = false)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "SUBJECT_ID")
private Subject subject;
}
I'm trying to solve these issues:
Question 1: Can I manipulate #OneToMany or #ManyToOne to NOT have the child class recurse its parent?
Fetch of resources returns subject data:
/subject/101:
{"id":101,"resources":[{"id":1001,"subject":101},{"id":1002,"subject":101},{"id":1003,"subject":101},{"id":1004,"subject":101}]}
/subjectResource/1001:
{id:1001,subject:{"id":101,"resources":[{"id":1001,"subject":101},{"id":1002,"subject":101},{"id":1003,"subject":101},{"id":1004,"subject":101}]}}
That is, /subjectResource/1001 returns its ID and the entire /subject/101 query.
How can I have just the subjectResource data, without its parent?
Question 2: Through #OneToMany or #ManyToOne can I get Hibernate to fetch on a "1" (O(1)) basis?
When /subjects does its thing, it works with Hibernate on a "n+1" (O(n)) basis: 1 fetch of subjects, n fetches of resources, one for each subject ID.
I could force a single fetch through a fancy repository #Query annotation ("select s from subject s left join fetch s.resources"). But that means putting the subject : subject_resource definitions in two places, etc.
Can JPA implementation / Hibernate be forced to do a join, and thus make only one database call, through annotation within an entity class?
Question 3: How do I get my Spring Data / Spring Repository to cooperate with Ignite, and have the cache return the data it already had on the first call?
I'm usng FetchType.LAZY, as all good pupils do. I'm also storing things in Apache Ignite. For /subject/101 the initial call fetches everything OK, returning it in JSON. But the second call gets from the Ignite cache, which complains about being out of transaction.
How do get my LAZY fetches to cooperate with Ignite?
Thanks,
Jerome.
It seems I have a problem with OneToMany, ManyToOne mapping.
I'm using a CrudRepository named "ur" here:
ur.save(new User("zx","z", "a", "email#email.com", "Baa")); //userRepo save
User u1 = ur.findOneByUserName("Bx");
MyToken t1 = new MyToken("X5");
u1.addToken(t1);
ur.save(u1);
MyToken t2 = TokenRepo.findOneByToken("X5"); // a different crudRepository
String foundUser = t2.getUser().getUserName(); // THIS "user" is null.
relevant sections of User.java (extends AbstractPersistable<Long>):
#OneToMany(fetch = FetchType.EAGER, cascade={CascadeType.ALL}, mappedBy = "user")
private Set<Role> roles = new HashSet<Role>(1);
#OneToMany(fetch = FetchType.EAGER, cascade={CascadeType.ALL}, mappedBy = "user")
private Set<MyToken> token = new HashSet<MyToken>();
MyToken.java (extends AbstractPersistable<Long>):
#ManyToOne(fetch = FetchType.EAGER, cascade={CascadeType.ALL})
#JoinColumn(name="user") // commenting this out or not does nothing...
private User user;
My debugger says 'user' is 'null' at line "String foundUser" even though that should be completely false according to the code.
As you can see, all is "eager" so I don't see why MyToken.setUser() is not automatically done. How are they not linked already? AnyInitiatedToken.getUser() should not be null if you already did User.addToken, and UserRepo.save().
NOTE: I have also tried .LAZY for the MyToken.java and Role.java class (but still doesn't work).
Since you did not post the setters, I gonna assume they look like default setter.
User.token is set, but it has a mappedby, so it is really irrelevant for what is stored in the DB. Token.user matters, but that is still an NULL so that's what get's saved and retrieved.
You have two options:
Change User.setToken() to update Token.user of the passed in Token (and of the one that was previously set.
Whenever you call User.setToken() also update Token.users to make both directions of the relationship match.
I think it's not related to the fetch strategy whether it's eager or lazy fetch type. I think you gave column alias for the user's id and missed that the name atributte's value of the #JoinColumn should reference that primary key column alias.
Like in the User you have:
#Id
#Column(name = "userid", unique = true, nullable = false)
public String getUserId() {
return this.userId;
}
then in the Token should be
#ManyToOne(fetch = FetchType.EAGER, cascade={CascadeType.ALL})
#JoinColumn(name="userid") // commenting this out or not does nothing...
private User user;
I have a manytomany relation mapped with these 3 entities :
#Entity
public class ApplicatifDo {
.....
#OneToMany(cascade = CascadeType.ALL, mappedBy = "applicatifDo", fetch = FetchType.EAGER, orphanRemoval = true)
private Set<ApplicatifTerminalDo> applicatifTerminalSet;
.....
}
#Entity
public class ApplicatifTerminalDo {
......
#ManyToOne
#JoinColumn(name = "idApplicatif", nullable = false)
private ApplicatifDo applicatifDo;
#ManyToOne
#JoinColumn(name = "idTerminal", nullable = false)
private TerminalDo terminalDo;
#Column
private String remarques;
......
}
#Entity
public class TerminalDo {
......
#OneToMany(cascade = CascadeType.ALL, mappedBy = "terminalDo", fetch = FetchType.EAGER, orphanRemoval = true)
private Set<ApplicatifTerminalDo> applicatifTerminalSet;
......
}
I try to do many cascading tests on the join table ApplicatifTerminalDo from the two entities ApplicatifDo and TerminalDo. When I create or update a ApplicatifTerminalDo in the Set the cascading works well, but when it comes to orphanRemoval or delete It's not working
First :
For the delete I get the error :
Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`softtwo`.`applicatifterminal`, CONSTRAINT `FK_295tcnx7wjuvv5se1g3vldxxn` FOREIGN KEY (`idApplicatif`) REFERENCES `applicatif` (`id`))
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:409)
at com.mysql.jdbc.Util.getInstance(Util.java:384)
.....
I would like that when I delete an entity ApplicatifDo or TerminalDo all rows related to them in the ApplicatifTerminal join table get deleted as well.
Second :
For the orphanRemoval, when I delete an element from the Set of ApplicatifTerminalSet in my ApplicatifDo entity and do a merge, then to test it I do a find by his Id of parent entity to get a new exact same entity and count the number of elements in the Set I get the good number (the number at the biginning with one less). But in my database I still have all my datas of my Set.
The code :
//My applicatifDo1 has 4 elements in the ApplicatifTerminalSet here
Assert.assertEquals(applicatifDo1.getApplicatifTerminalSet().size(), 4);
Iterator<ApplicatifTerminalDo> iterator = applicatifDo1
.getApplicatifTerminalSet().iterator();
boolean first = true;
while (iterator.hasNext()) {
ApplicatifTerminalDo element = iterator.next();
if (!first) {
element.setRemarques("remarques updated");
} else {
iterator.remove();
first = false;
}
}
// updateApplicatifDo do just a merge
applicatifDao.updateApplicatifDo(applicatifDo1.getId(), applicatifDo1);
ApplicatifDo applicatifDo = applicatifDao
.findApplicatifDo(applicatifDo1.getId());
Assert.assertEquals(applicatifDo.getApplicatifTerminalSet().size(), 3);
When I do that the update of setRemarques() works well. I have no error in the console when I do that. Then the remove seems to work because I retrieve the same object by his Id it still says I have 3 elements, then the fourth has been deleted : BUT, when I look in phpmyadmin my 4 elements/relations in my appplicationterminal table are still there.
If I do another TestNg later, just retrieving my applicatifDo by his Id this time it gets the four elements.
Then there a big integrity problem here, and still I always use eagerly fetching. Any idea why such problem here ? And how could I make my cascading works ?
Third :
More globally, I have another cascading + orphanRemoval rules on other two entities and that works very well, but the entity target of these cascades does not have other relations with other entities.
Clearly, there is specific rules (or maybe limitations) when cascading on entities with other relationships.
Please do you know tutorials/rules explaining the best practices for such mappings ?
Thanks in advance. I'm stuck on this for too long.
the question and problem is pretty simple, though annoying and I am looking for a global solution, because it's application-wide problem for us.
The code below is really not interesting but I post it for clarification!
We use PostgreSQL database with JPA 2.0 and we generated all the facades and entities, of course we did some editing but not much really.
The problem is that every entity contains a Collection of its children, which however (for us only?) is NOT updated after creation a children element.
The objects are written to database, you can select them easily, but what we really would like to see is the refreshed collection of children in parent object.
Why is this happening? If we (manually) refresh the entity of parent em.refresh(parent) it does the trick but it would mean for us a lot of work in Facades I guess. But maybe there is no other way?
Thanks for support!
/* EDIT */
I guess it has to be some annotation problem or cache or something, but I've already tried
#OneToMany(mappedBy = "idquestion", orphanRemoval=true, fetch= FetchType.EAGER)
and
#Cacheable(false)
didn't work properly.
/* EDIT */
Some sample code for understanding.
Database level:
CREATE TABLE Question (
idQuestion SERIAL,
questionContent VARCHAR,
CONSTRAINT Question_idQuestion_PK PRIMARY KEY (idQuestion)
);
CREATE TABLE Answer (
idAnswer SERIAL,
answerContent VARCHAR,
idQuestion INTEGER,
CONSTRAINT Answer_idAnswer_PK PRIMARY KEY (idAnswer),
CONSTRAINT Answer_idQuestion_FK FOREIGN KEY (idQuestion) REFERENCES Question(idQuestion)
);
Than we have generated some Entities in Netbeans 7.1, all of them look similar to:
#Entity
#Table(name = "question", catalog = "jobfairdb", schema = "public")
#XmlRootElement
#NamedQueries({ BLAH BLAH BLAH...})
public class Question implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name = "idquestion", nullable = false)
private Integer idquestion;
#Size(max = 2147483647)
#Column(name = "questioncontent", length = 2147483647)
private String questioncontent;
#OneToMany(mappedBy = "idquestion", orphanRemoval=true)
private Collection<Answer> answerCollection;
Getters... setters...
We use (again) generated facades for them, all implementing AbstractFacade like:
public abstract class CCAbstractFacade<T> {
private Class<T> entityClass;
public CCAbstractFacade(Class<T> entityClass) {
this.entityClass = entityClass;
}
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getEntityManager().persist(entity);
}
The father entity is updated automatically if you use container managed transactions and you fetch the collection after the transaction is complete. Otherwise, you have to update yourself the collection.
This article explains in detail this behaviour: JPA implementation patterns: Bidirectional associations
EDIT:
The simplest way to use Container Managed Transactions is to have transaction-type="JTA" in persistence.xml and use Container-Managed Entity Managers.
You seem to be setting the ManyToOne side, but not adding to the OneToMany, you have to do both.
In JPA, and in Java in general you must update both sides of a bi-directional relationship, otherwise the state of your objects will not be in sync. Not doing so, would be wrong in any Java code, not just JPA.
There is no magic in JPA that will do this for you. EclipseLink does have a magic option for this that you could set through a customizer (mapping.setRelationshipPartnerAttributeName()), but it is not recommended, fixing your code to be correct is the best solution.
See,
http://en.wikibooks.org/wiki/Java_Persistence/Relationships#Object_corruption.2C_one_side_of_the_relationship_is_not_updated_after_updating_the_other_side
I have an entity VM with a relationship to another entity BP. The relationship is eagerly fetched. First I load a VM. After loading the VM is detached, serialized and changed at the client side. Now I want to update the changed entity so I use the EntityManager.merge() method from JPA. Now I run into the following error from OpenJPA:
"Encountered new object in persistent field "Vm.bp" during attach. However, this field does not allow cascade attach. Set the cascade attribute for this field to CascadeType.MERGE or CascadeType.ALL (JPA annotations) or "merge" or "all" (JPA orm.xml). You cannot attach a reference to a new object without cascading."
Why do I have to add a Cascade.MERGE to a relationship to another entity that will never change? And why does JPA think that BP is a new object ("...cannot attach reference to a new object...")?
When using ManyToOne relationships do I always have to add Cascade.MERGE in order to update the entity or is this because of the EAGER fetch type?
Here's my entity:
#Entity
#Table(name = "VM")
public class Vm extends BaseEntity implements Serializable {
public static final long serialVersionUID = -8495541781540291902L;
#Id
#SequenceGenerator(name = "SeqVm", sequenceName = "SEQ_VM")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SeqVm")
#Column(name = "ID")
private Long id;
// lots of other fields and relations
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "BP_ID")
private Bp bp;
// ...
}
I found the reason why this error message comes up: The #Version annotated database field of the related Bp entity was initialized with "0". Apparently OpenJPA (1.2.3) is not able to cope with entity versions of zero.
Setting the version to 1 solved my issue.