0

在我的模型中是 3 个实体(实际上超过 3 个,但实际上是 3 个)

Candidate
Vacancy
Event

它们都具有多对多的关系(候选人可以有可能的空缺和许多事件,空缺......候选人和......事件......)。

如果我更新候选人 - 好结果。如果我更新空缺 - 好的结果但是如果我更新事件我有 org.hibernate.NonUniqueObjectException

当我更改 Event 中的空缺集合时,它会显示。如果我评论地方,我改变事件的空缺我没有问题(在我的代码中放置一个)转到代码:模型映射:

空缺:

@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();
    }
    ...
}

候选人:

@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();
    }




}

事件:

@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;
    }
}

在我的@Controller 类中,我编写了这样的方法:

@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";

    }

更新:

    @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;
    }
...
}

对不起很多代码/但我不知道写在这里)

堆栈跟踪:

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)

我注意到,如果我在 Event 中编写更改映射 Vacancy,我没有问题:

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

为什么?对于对称实体候选者,此映射有效:

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

如果我在初始代码变体中删除事件和空缺之间的关系-效果很好

如果我在初始代码变体中删除 Event 和 Candidate 之间的关系 - 它不起作用

更新

如果我在合并时替换更新,我有:

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 )
4

1 回答 1

3

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.

于 2013-09-10T09:00:28.663 回答