We have the following mapping:
#Entity
public class A {
private B b;
#OneToOne
public B getB() {
return b;
}
}
When we delete an object of class A it must not delete the referenced object B. At the moment we get an exception when we try to delete A because of the existing relationship to B. How is the correct mapping?
You should disable cascade delete
#OneToOne(cascade = {})
or you can try
#OneToOne(orphanRemoval=false)
Related
If we have an entity A which contains a List of instances of entity B and entity B contains a List of instances of entity C, something like:
#Entity
class C { ... }
#Entity
class B {
#OneToMany
List<C> c;
}
#Entity
class A {
#OneToMany
List<B> b;
}
Is there a way to delete instances of C and update an instance of C from SimpleJPARepository based on entity A?
I am trying to delete a Child Entity using Child's Repository. I do not want to load the whole Collection of Child in Parent and remove a Child from there because the collection is huge in some cases and can cause memory issues. But after I delete a child when I load the Parent using Parent Repository I get an error that says "Deleted Entity passed to persists".
#Entity
#Table(name="USR")
public class User {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "user", orphanRemoval=true)
private Set<UserApplication> userApplications = new HashSet<UserApplication>();
}
#Entity
#Table(name="USR_2_APL")
public class UserApplication {
#ManyToOne
#JoinColumn(name = "USR_SK")
private User user;
}
#Test
public void testDeleteUserApp() {
List<UserApplication> removedUserApp = userApplicationRepository.findByUserSkAndApplicationSk(1, 5);
userApplicationRepository.delete(removedUserApp);
//*****This is where I see an error that says
//org.springframework.orm.jpa.JpaObjectRetrievalFailureException: deleted entity passed to persist: [UserApplication#<null>]; nested exception is javax.persistence.EntityNotFoundException
userRepository.findByUserLoginName(loginId);
}
I donot know if this will help you but I have something similar and this is what I do to delete the data...
In the repository I have a method like this:-
#Transactional
public Long deleteByByUserSkAndApplicationSk(int userSk, int applicationSk);
The output of the method is the number of rows deleted.
Then you can directly call the method where ever you want to delete.
I am using Spring Boot to implement rest api. There are three entities SeqTb, PairTb, and GroupTb and they are nested. SeqTb has manytoone with PairTb. PairTb has onetomany relationship with SeqTb and also manytoone with GroupTb.
//SeqTb.java
#Entity
#Table(name="SEQ_TB")
public class SeqTb implements Serializable {
.......
#ManyToOne
#JoinColumn(name="PAIR_ID")
private PairTb pairTb;
......
}
// PairTb.java
#Entity
#Table(name="PAIR_TB")
#NamedQuery(name="PairTb.findAll", query="SELECT p FROM PairTb p")
public class PairTb implements Serializable {
#ManyToOne
#JoinColumn(name="GROUP_ID")
private GroupTb groupTb;
#OneToMany(mappedBy="pairTb", cascade=CascadeType.ALL)
private List<SeqTb> seqTbs;
}
//GroupId.java
#Entity
#Table(name="GROUP_TB")
public class GroupTb implements Serializable {
//bi-directional many-to-one association to PairTb
#OneToMany(mappedBy="groupTb", cascade=CascadeType.ALL)
private List<PairTb> pairTbs;
}
In my controller GET request with analysisId was handled in the following way:
#RequestMapping(
value = "/api/seqs/{analysis_id}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<SeqTb> getSeqByAnalysisId(#PathVariable("analysis_id") String analysis_id) {
SeqTb seq = seqService.findByAnalysisId(analysis_id);
return new ResponseEntity(seq, HttpStatus.OK);
}
I also create a bean class SeqServiceBean that extends the interface SeqService which in turn calls methods from the following JPA repository for query.
//SeqRepository.java
#Repository
public interface SeqRepository extends JpaRepository<SeqTb, Integer> {
#Override
public List<SeqTb> findAll();
public List<SeqTb> findByAnalysisId(String analysisId);
}
When I query a SeqTb object with SeqTb.PairTb == null, the api works just fine. However, if the analysisId I put in the url belongs to a SeqTb record that associates with a pairId which in turn belongs to a groupId, the program would go nuts. Below is the output, the first part output is correct (bold text). After that it keeps printing PairTb and GroupTb in loops (repeating keywords pairTb, groupTb).
{"rowId":8,"analysisId":"cce8d2c2-a6dc-4ee9-ba97-768f058abb50","analyteCode":"D","center":"UCSC",
"pairTb":{"rowId":4,"pairCode":"01ad975d-c2ed-4e4d-bd3b-c9512fc9073c","groupTb":{"rowId":1,"groupName":"PAWG_pilot-50","pairTbs":[{"rowId":1,"pairCode":"00ad0ffe-2105-4829-a495-1c2aceb5bb31","groupTb":{"rowId":1,"groupName":"PAWG_pilot-50","pairTbs":
Meanwhile I got lots of errors from tomcat server:
Caused by: java.lang.IllegalStateException: getOutputStream() has already been called for this response
at org.apache.catalina.connector.Response.getWriter(Response.java:565) ~[tomcat-embed-core-8.0.32.jar:8.0.32]
at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:212) ~[tomcat-embed-core-8.0.32.jar:8.0.32]
How do I ignore the nested entity object inside an entity and get only the meaning columns?
You can also annotate a property with #JsonIgnore in order to not output that field.
Found the solution. Created a value object that only contains the specific columns from entity and leave out the nested entity object. And it works.
My question is probably so simple, that I can't find an answer for it.
I want to do something like this:
#Entity
public class EntityA {
#Transient
#SomeQueryAnnotation(query="select b from EntityB where b.id=1")
private EntityB entityB;
}
EntityB is kind of static resource. It should not be saved back to the database. There is also no mapping between the entities.
[EDIT]
Do you think it was ok, when I do this:
#Entity
public class EntityA {
private EntityB getEntityB() {
ServiceRemote service = (ServiceRemote)context.lookup("ejb:ServiceRemote");
return service.getEntityB();
}
}
Than it should still be possible to use remoting, because the connection can be configured in each clients' jndi.properties file. what is your prefered method when you need to access the database from your entities?
Best recommendation - Unless these objects have an in-database relation, then there shouldn't be an entity relationship.
Second best - I would recommend you create a data transfer object to fetch your object.
#Entity
public class EntityA {
#Transient
private EntityB entityB;
}
#Stateless
public class EntityADTO {
EntityManager em;
public EntityA findA(Object pkey) {
EntityA a = em.find(okey, EntityA.class);
a.entityB = em.find(1, EntityB.class);
return a;
}
}
How do I delete an entity in a OneToMany relationship.
#Entity
#NamedQueries({
#NamedQuery(name="User.findByUserNamePassword",
query="select c from User c where c.userName = :userName AND c.password = :password")
})
#Table(name="\"USER\"")
public class User implements Serializable {
#OneToMany(mappedBy="user", cascade=CascadeType.ALL, orphanRemove=true)
private List<Profession> professions;
public List<Profession> getProfessions() {
return professions;
}
public void setProfessions(List<Profession> professions) {
this.professions = professions;
}
public void addProfession(Profession profession){
if(this.professions == null){
this.professions = new ArrayList<Profession>();
}
this.professions.add(profession);
profession.setUser(this);
}
public void removeProfession(Profession profession){
if(this.professions != null){
professions.remove(profession);
profession.setUser(null);
}
}
}
Inside Profession Entity
#Entity
public class Profession implements Serializable {
#ManyToOne
#JoinColumn(name="UserId", nullable=false)
private User user;
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
Then inside my EJB I have this
#Stateless
#LocalBean
#TransactionAttribute(TransactionAttributeType.REQUIRED)
public class ScholarEJB{
/**
* Add a profession to a target user
* #param user
* #param profession
*/
public void addProfession(User user, Profession profession){
//Put the user in a managed state. It is important to do this before
//adding a new profession onto user
user = find(User.class, user.getId());
user.addProfession(profession);
this.create(user); //This is persist action
}
public void removeProfession(User user, Profession profession){
//Put the user in a managed state. It is important to do this before
//adding a new profession onto user
user = find(User.class, user.getId());
user.remove(user);
this.update(user); //merge action
//this.create(user) //also try this as well, but it does not work
}
}
Now addProfession work beautifully, but removeProfession does not work. Not sure why? Help please. Do I need to evict caches?
If professions are only part of this relationship, then you can guarantee that when a profession is removed from the User's set it will also be removed from the database by turning on orphanRemoval on the OneToMany side of the relationship.
#OneToMany(mappedBy="user", cascade=CascadeType.ALL, orphanRemoval=true)
private List<Profession> professions;
This is what the JPA 2.0 specification states
The JPA 2.0 specification states that
Associations that are specified as
OneToOne or OneToMany support use of
the orphanRemoval option. The
following behaviors apply when
orphanRemoval is in effect:
If an entity that is the target of the
relationship is removed from the
relationship (by setting the
relationship to null or removing the
entity from the relationship
collection), the remove operation will
be applied to the entity being
orphaned. The remove operation is
applied at the time of the flush
operation. The orphanRemoval
functionality is intended for entities
that are privately "owned" by their
parent entity. Portable applications
must otherwise not depend upon a
specific order of removal, and must
not reassign an entity that has been
orphaned to another relationship or
otherwise attempt to persist it. If
the entity being orphaned is a
detached, new,or removed entity, the
semantics of orphanRemoval do not
apply.
If the remove operation is applied to
a managed source entity, the remove
operation will be cascaded to the
relationship target in accordance with
the rules of section 3.2.3, (and hence
it is not necessary to specify
cascade=REMOVE for the
relationship)[20].
My guess as to what is occurring is that your User has a OneToMany relationship to Profession and you user object has the profession. When you delete the Profession the user still has the reference. Because the mapping is cascade persist, it re persists the Profession.
You need to ensure that you remove the profession from the user's professions before deleting it.
If you are using EclipseLink there is a property that may also help, but fixing your code to maintain your model correctly is the best solution. You could also remove the cascade persist.
"eclipselink.persistence-context.persist-on-commit"="false"
or,
"eclipselink.persistence-context.commit-without-persist-rules"="true"
I just added orphanRemoval = true in the OneToMany relationship and I resolved it.
Class SolicitudRetorno:
#OneToMany(mappedBy = "solicitud", cascade = CascadeType.ALL, orphanRemoval = true)
#LazyCollection(LazyCollectionOption.FALSE)
#NotAudited
private List<RetornoMenor> hijosRetorno;
Class RetornoMenor:
#OneToMany(mappedBy = "solicitud", cascade = CascadeType.ALL, orphanRemoval = true)
#LazyCollection(LazyCollectionOption.FALSE)
#NotAudited
private List<RetornoMenor> hijosRetorno;
You might try clearing the user field in profession:
public void removeProfession(Profession profession){
if(this.professions != null){
professions.remove(profession);
profession.setUser(null); // disassociate profession from user
}
}
To be on the safe side, I would also check that the passed in profession's current user equals this, just in case someone passes in a profession belonging to another user.
This is the solution to my original question, however, I do not know if this is the best
My EJB bean
#PersistenceContext(unitName="Bridgeye2-ejbPU")
private EntityManager em;
public <T> T create(T t) {
em.persist(t);
return t;
}
public <T> T find(Class<T> type, Object id) {
return em.find(type, id);
}
public <T> void delete(T t) {
t = em.merge(t);
em.remove(t);
}
public <T> void removeAndClearCaches(T t){
this.delete(t);
clearCaches();
}
public <T> T update(T t) {
return em.merge(t);
Now in my Managed Bean, I do this
/**
* Add a new profession
*/
public void addNewProfession(){
Profession profession = new Profession();
newProfessions.add(profession);
}
/**
* Remove the profession
* #param profession
*/
public void removeProfession(Profession profession){
//This will remove the `profession` of the list
//at the presentation layer
this.myProfessions.remove(profession);
//This will remove the `profession` of the list
//at the persistence layer
scholarEJB.removeAndClearCaches(profession);
}