简体   繁体   中英

org.hibernate.NonUniqueObjectException. What the cause?

In my model are 3 entities(in real more than 3, but 3 is actually at this situation)

Candidate
Vacancy
Event

They all have relation many to many(Candidate can have may Vacancies and many Events, Vacancy ...candidates and... events.....).

If I update Candidate - good result. if I update Vacancy - good result BUT if I update Event I have org.hibernate.NonUniqueObjectException

It shows when I change collection of vacancy in Event. If I comment place, where I change vacancies of event I have no problem(place ONE in my code) Go to code: Model mapping:

Vacancy:

@Entity
@Table(name = "vacancy")
@XmlRootElement(name="vacancy")
public class Vacancy {

    private Integer id;

    private String name;

    private String description;

    private Date date;

    private User author;

    @XmlTransient
    private Set<Candidate> candidates = new HashSet<Candidate>();
    private Set<VacancyStatus> statusList = new HashSet<VacancyStatus>();
    private Set<Skill> skills = new HashSet<Skill>();
    private Set<Note> comments = new HashSet<Note>();
    private Set<Event> events = new HashSet<Event>();

    public Vacancy() {
        super();
    }

    @ManyToMany(mappedBy = "vacancies", fetch = FetchType.EAGER)
    public Set<Event> getEvents() {
        return events;
    }

    public void setEvents(Set<Event> events) {
        this.events = events;
    }




    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }



    @ManyToMany(mappedBy = "vacancies", fetch = FetchType.EAGER)
    public Set<Candidate> getCandidates() {
        return candidates;
    }

    public void setCandidates(Set<Candidate> candidates) {
        this.candidates = candidates;
    }


    @Override
    public boolean equals(Object obj) {
        if(obj!= null && ((Vacancy)obj).getId() == id ){
            return true;
        }
        return false;
    }
    @Override
    public int hashCode() {
        Integer id = getId();
        return id != null ? id.intValue() : super.hashCode();
    }
    ...
}

Candidate:

@Entity
@Table(name = "candidate")
@XmlRootElement(name = "candidate")
public class Candidate extends Person {

    @Size(min=3,max=12)
    @Pattern(regexp="[0-9]*",message="phone format must be without + and - (for example: 89123353456)")
    private String phone;

    @Past
    private Date date;

    private User author;

    @Size(min=4, max=100)
    @URL()
    private String resumeUrl;

    private List<CandidateStatus> statusList = new LinkedList<CandidateStatus>();

    private Set<Vacancy> vacancies= new HashSet<Vacancy>();

    private Set<Skill> skills = new HashSet<Skill>();

    private List<Note> comments = new LinkedList<Note>();

    private Set<Event> events = new HashSet<Event>();

    public Candidate() {
        super();
    }

    @ManyToMany(mappedBy = "candidates", fetch = FetchType.EAGER)
    //@LazyCollection(LazyCollectionOption.FALSE)
    public Set<Event> getEvents() {
        return events;
    }

    public void setEvents(Set<Event> events) {
        this.events = events;
    }


    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinTable(name = "candidate_vacancy", joinColumns = @JoinColumn(name = "candidate_id"), inverseJoinColumns = @JoinColumn(name = "vacancy_id"))
    @XmlTransient
    public Set<Vacancy> getVacancies() {
        return vacancies;
    }

    public void setVacancies(Set<Vacancy> vacancies) {
        this.vacancies = vacancies;
    }
...

}

@MappedSuperclass
public abstract class Person {

    @Size(min=3)
    @Pattern(regexp="[a-zA-Z]*")    
    private String name;

    @Size(min=3)
    @Pattern(regexp="[a-zA-Z]*")
    private String surname;

    private Integer id;

    public Person() {
    }

    public Person(String name, String surname) {
        super();
        this.name = name;
        this.surname = surname;
    }

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY )
    @Column (name = "id")
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Column(name = "name")
    //@NotEmpty
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Column(name = "surname")
    //@NotEmpty
    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }
    @Override
    public boolean equals(Object obj) {
        if(obj!=null && ((Person)obj).getId() == id ){
            return true;
        }
        return false;
    }

    @Override
    public int hashCode() {
        Integer id = getId();
        return id != null ? id.intValue() : super.hashCode();
    }




}

Event:

@Entity
@Table(name = "event")
@XmlRootElement
public class Event {
    private Integer id;

    @Size(min=3 )
    @Pattern(regexp="[a-zA-Z]*")    
    private String name;

    @Size(min=5)
    @Pattern(regexp="[a-zA-Z]*")    
    private String description;

    private Date date;

    @Future
    private Date eventDate;

    private User author;

    private Set<Candidate> candidates;
    private Set<Vacancy> vacancies;

    private EventType eventType;
    private EventStatus eventStatus;

    public Event() {
        super();
    }



    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name ="id")
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }


    @Override
    public boolean equals(Object obj) {
        if(obj!=null && ((Event)obj).getId() == id ){
            return true;
        }
        return false;
    }
    @Override
    public int hashCode() {
        Integer id = getId();
        return id != null ? id.intValue() : super.hashCode();
    }

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    public Set<Candidate> getCandidates() {
        return candidates;
    }

    public void setCandidates(Set<Candidate> candidates) {
        this.candidates = candidates;
    }

    @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    public Set<Vacancy> getVacancies() {
        return vacancies;
    }

    public void setVacancies(Set<Vacancy> vacancies) {
        this.vacancies = vacancies;
    }
}

In my @Controller class I write so method:

@RequestMapping(value = "/updateEvent", method = RequestMethod.POST)
    public String updateEvent(Model model,
            @Valid @ModelAttribute("existedEvent") Event event,
            BindingResult result,
            @ModelAttribute("linkedCandidates") Set<Candidate> candidates,
            @ModelAttribute("linkedvacancies") Set<Vacancy> vacancies) {
        if (result.hasErrors()) {
            model.addAttribute("idEvent", event.getId());
            return "eventDetails";
        }
        if (vacancies != null) {
            for (Vacancy vacancy : vacancies) {
                vacancy.getEvents().add(event);
            }
        }
        if (candidates != null) {
            for (Candidate candidate : candidates) {
                candidate.getEvents().add(event);
            }
        }
        event.setVacancies(vacancies);//place ONE (if comment this line - will //work)
        event.setCandidates(candidates);
        eventService.update(event);//error here
        return "redirect:goToEventMenu";

    }

update:

    @Transactional
    @Service
    public class EventService {
         public void update(Event event) {
            eventDao.update(event);
        }
    ....
    }

@Repository("eventDaoImpl")
public class EventDaoImpl extends DaoAbstract implements EventDao {
    @Autowired
    CandidateDao candidateDao;

    @Autowired
    VacancyDao  vacancyDao;



    @Override
    public boolean update(Event event) {
        Session session = sessionFactory.getCurrentSession();
        if (event == null) {
            return false;
        }
        session.update(event);
        return true;
    }
...
}

Sorry for a lot of code/ But I don't know that write here)

stackTrace:

10.09.2013 11:46:14 org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [appServlet] in context with path [/ui] threw exception [Request processing failed; nested exception is org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.epam.hhsystem.model.vacancy.Vacancy#6]] with root cause
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session: [com.epam.hhsystem.model.vacancy.Vacancy#6]
    at org.hibernate.engine.internal.StatefulPersistenceContext.checkUniqueness(StatefulPersistenceContext.java:697)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:296)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:241)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:109)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
    at org.hibernate.internal.SessionImpl.fireSaveOrUpdate(SessionImpl.java:735)
    at org.hibernate.internal.SessionImpl.saveOrUpdate(SessionImpl.java:727)
    at org.hibernate.engine.spi.CascadingAction$5.cascade(CascadingAction.java:258)
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:388)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:331)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:209)
    at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:418)
    at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:358)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:334)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:209)
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:166)
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:132)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.cascadeOnUpdate(DefaultSaveOrUpdateEventListener.java:364)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.performUpdate(DefaultSaveOrUpdateEventListener.java:338)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.entityIsDetached(DefaultSaveOrUpdateEventListener.java:241)
    at org.hibernate.event.internal.DefaultUpdateEventListener.performSaveOrUpdate(DefaultUpdateEventListener.java:55)
    at org.hibernate.event.internal.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:90)
    at org.hibernate.internal.SessionImpl.fireUpdate(SessionImpl.java:786)
    at org.hibernate.internal.SessionImpl.update(SessionImpl.java:778)
    at org.hibernate.internal.SessionImpl.update(SessionImpl.java:774)
    at com.epam.hhsystem.jpa.EventDaoImpl.update(EventDaoImpl.java:45)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
    at $Proxy38.update(Unknown Source)
    at com.epam.hhsystem.services.EventService.update(EventService.java:24)
    at com.epam.hhsystem.services.EventService$$FastClassByCGLIB$$3653e1b6.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:698)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:631)
    at com.epam.hhsystem.services.EventService$$EnhancerByCGLIB$$8266fd26.update(<generated>)
    at com.epam.hhsystem.web.controllers.EventMenuController.updateEvent(EventMenuController.java:164)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:647)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:728)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:305)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:183)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:947)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1009)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:312)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)

I noticed, that I have no problem if I write change mapping Vacancy in Event:

@ManyToMany(/*cascade = CascadeType.ALL,*/ fetch = FetchType.EAGER)// I comment cascade
    public Set<Vacancy> getVacancies() {
        return vacancies;
    }

Why? for simmetrical entity Candidate this mapping works:

@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    public Set<Candidate> getCandidates() {
        return candidates;
    }

If I delete relation between Event and Vacancy in initally code variant- it works good

If I delete relation between Event and Candidate in initally code variant- it DOESN'T works

UPDATE

if I replace update on merge I have:

10.09.2013 13:33:08 org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [appServlet] in context with path [/ui] threw exception [Request processing failed; nested exception is java.lang.IllegalStateException: Error occurred while storing entity [com.epam.hhsystem.model.vacancy.Vacancy@6]. An entity copy [com.epam.hhsystem.model.vacancy.Vacancy#6] was already assigned to a different entity [com.epam.hhsystem.model.vacancy.Vacancy@6].] with root cause
java.lang.IllegalStateException: Error occurred while storing entity )

Looking at your code, you have cascade = CascadeType.ALL on both Candidate.getVacancies() and Event.getVacancies() . I guess this is what that causing problem. Because in your controller you are setting candidates and vacancies in the event and then asking Hibernate to persists the event. So Hibernate is trying to save vacancies once through an event object and then through candidate object (or vice versa). Removing cascade from candidate will fix the issue. Also consider using session.merge() instead of session.update() as merge has same functionality.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM