Hibernate - 多对多 - 连接表+额外列(注释)

时间:2017-04-11 23:03:33

标签: java json spring hibernate

尝试在http://www.mkyong.com/hibernate/hibernate-many-to-many-example-join-table-extra-column-annotation/调整示例,我遇到了json和递归循环的问题。

(代码已更改为保护) Resource.java

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.Type;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonManagedReference;

@Entity
@Table(name = "resources")
public class Resource implements TestableSerializable, Syncable<Resource> {
private static final long serialVersionUID = -1L;

private UUID resourceID;
private String name;
private Date lastUpdated;
private String lastAction;

@JsonManagedReference
private Set<ResourceEvents> events = new HashSet<ResourceEvents>(0);
@JsonIgnore
private Set<Credential> credentials = new HashSet<Credential>(0);

public Resource() {
    super();
    this.resourceID = UUID.randomUUID();
    this.lastAction = Syncable.ADD;
    this.lastUpdated = new Date();
}

public Resource(UUID resourceID) {
    super();
    this.resourceID = (resourceID == null ? UUID.randomUUID() : resourceID);
    this.lastAction = Syncable.ADD;
    this.lastUpdated = new Date();
}

@SyncConstructor(fields={"resourceID", "name"})
public Resource(UUID resourceID, String name) {
    super();
    this.resourceID = (resourceID == null ? UUID.randomUUID() : resourceID);
    this.name = name;
    this.lastUpdated = new Date();
    this.lastAction = Syncable.ADD;
}

public Resource(UUID resourceID, String name, Set<Event> events, Set<Credential> credentials) {
    super();
    this.resourceID = (resourceID == null ? UUID.randomUUID() : resourceID);
    this.name = name;
    this.lastUpdated = new Date();
    this.lastAction = Syncable.ADD;
    //this.events = events;
    this.credentials = credentials;
}

public void changeToMatch(Resource resource) {
    this.resourceID = resource.getResourceID();
    this.name = resource.getName();
    if (resource.getLastAction().equals(Syncable.DELETE)) {
        this.markDeleted();
    }
}

@Override
public int compareTo(Resource anotherResource) {
    return this.lastUpdated.compareTo(anotherResource.getLastUpdated());
}

@Override
public void markDeleted() {
    this.lastUpdated = new Date();
    this.lastAction = DELETE;
}

@Override
public void markUpdated() {
    this.lastUpdated = new Date();
    this.lastAction = UPDATE;
}


@Id
@Type(type="uuid-char")
@Column(name = "id", unique = true, nullable = false, length = 36)
public UUID getResourceID() {
    return this.resourceID;
}

public void setResourceID(UUID resourceID) {
    this.resourceID = resourceID;
}

@Column(name = "name")
public String getName() {
    return this.name;
}

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

@Column(name = "lastUpdated", length = 23)
@Type(type = "****************.UtcTimestampType")
public Date getLastUpdated() {
    return this.lastUpdated;
}

public void setLastUpdated(Date lastUpdated) {
    this.lastUpdated = lastUpdated;
}

@Column(name = "lastAction", length = 10)
public String getLastAction() {
    return this.lastAction;
}

public void setLastAction(String lastAction) {
    this.lastAction = lastAction;
}

@OneToMany(fetch = FetchType.EAGER, mappedBy = "resourceEventId.resource", cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.PERSIST})
public Set<ResourceEvents> getResourceEvents() {
    return this.events;
}

public void setResourceEvents(Set<ResourceEvents> events) {
    this.events = events;
}

@Override
public List<Error> test() {
    // TODO Auto-generated method stub
    return null;
}

@Override
public int hashCode() {
    int result = 1;
    //result = prime * result + ((events == null) ? 0 : events.hashCode());
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    result = prime * result
            + ((resourceID == null) ? 0 : resourceID.hashCode());
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Resource other = (Resource) obj;
    if (events == null) {
        if (other.events != null)
            return false;
    } else if (!events.equals(other.events))
        return false;
    if (flags == null) {
        if (other.flags != null)
            return false;
    } else if (!flags.equals(other.flags))
        return false;
    if (name == null) {
        if (other.name != null)
            return false;
    } else if (!name.equals(other.name))
        return false;
    if (resourceID == null) {
        if (other.resourceID != null)
            return false;
    } else if (!resourceID.equals(other.resourceID))
        return false;
    return true;
}

@Override
public String toString() {
    return "Resource [resourceID=" + resourceID + ", name=" + name
            + ", lastUpdated="
            + lastUpdated + ", lastAction=" + lastAction + ", events="
            + events + "]";
}
}

Events.java

import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.Type;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonManagedReference;

@Entity
@Table(name = "events")
public class Event implements TestableSerializable, Syncable<Event> {
private static final long serialVersionUID = -1L;

private UUID id;
private String name;
private Date lastUpdated;
private String lastAction;

@JsonManagedReference
private Set<ResourceEvents> resources = new HashSet<ResourceEvents>(0);

public Event() {
    this.lastAction = Syncable.ADD;
    this.lastUpdated = new Date();
}

public Event(UUID id, String name) {
    super();
    this.id = id;
    this.lastAction = Syncable.ADD;
    this.lastUpdated = new Date();
}

@SyncConstructor(fields={"id", "name"})
public Event(UUID id, String name, Set<Resource> resources) {
    super();
    this.id = id;
    this.name = name;
    //this.resources = resources;
    this.lastAction = Syncable.ADD;
    this.lastUpdated = new Date();
}

public void changeToMatch(Event event) {
    this.id = event.getId();
    this.name = event.getName();
    if (event.getLastAction().equals(Syncable.DELETE)) {
        this.markDeleted();
    }
}

@Override
public int compareTo(Event anotherEvent) {
    return this.lastUpdated.compareTo(anotherEvent.getLastUpdated());
}

@Override
public void markDeleted() {
    this.lastUpdated = new Date();
    this.lastAction = DELETE;
}

@Override
public void markUpdated() {
    this.lastUpdated = new Date();
    this.lastAction = UPDATE;
}

@Id
@Type(type="uuid-char")
@Column(name = "id", unique = true, nullable = false, length = 36)
public UUID getId() {
    return id;
}

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

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

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

@Column(name = "lastUpdated", length = 23)
@Type(type = "com.cuadra.lup.persistence.types.UtcTimestampType")
public Date getLastUpdated() {
    return this.lastUpdated;
}

public void setLastUpdated(Date lastUpdated) {
    this.lastUpdated = lastUpdated;
}

@Column(name = "lastAction", length = 10)
public String getLastAction() {
    return this.lastAction;
}

public void setLastAction(String lastAction) {
    this.lastAction = lastAction;
}

@OneToMany(fetch = FetchType.EAGER, mappedBy = "resourceEventId.event", cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.PERSIST})
public Set<ResourceEvents> getResourceEvents() {
    return this.resources;
}

public void setResourceEvents(Set<ResourceEvents> resources) {
    this.resources = resources;
}

@Override
public List<Error> test() {
    // TODO Auto-generated method stub
    return null;
}

@Override
public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((id == null) ? 0 : id.hashCode());
    result = prime * result + ((name == null) ? 0 : name.hashCode());
    return result;
}

@Override
public boolean equals(Object obj) {
    if (this == obj)
        return true;
    if (obj == null)
        return false;
    if (getClass() != obj.getClass())
        return false;
    Event other = (Event) obj;
    if (id == null) {
        if (other.id != null)
            return false;
    } else if (!id.equals(other.id))
        return false;
    if (name == null) {
        if (other.name != null)
            return false;
    } else if (!name.equals(other.name))
        return false;
    return true;
}

}

ResourceEvents.java

import java.util.Date;
import java.util.List;

import javax.persistence.AssociationOverride;
import javax.persistence.AssociationOverrides;
import javax.persistence.Column;
import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

@Entity
@Table(name = "resourceEvents")
@AssociationOverrides({
    @AssociationOverride(name = "resourceEventId.resource",
        joinColumns = @JoinColumn(name = "resource", nullable = false, updatable = true)),
    @AssociationOverride(name = "resourceEventId.event",
        joinColumns = @JoinColumn(name = "event", nullable = false, updatable = true)) })
public class ResourceEvents implements TestableSerializable {

private static final long serialVersionUID = 1L;
private ResourceEventId resourceEventId = new ResourceEventId ();
private Date lastUpdated;
private String lastAction;

public ResourceEvents () {
}

public ResourceEvents (Resource resource, Event event) {
    getResourceEventId().setResource(resource);
    getResourceEventId().setEvent(event);       
    this.lastAction = Syncable.ADD;
    this.lastUpdated = new Date();
}

@EmbeddedId
public ResourceEventId getResourceEventId() {
    return resourceEventId;
}

public void setResourceEventId(ResourceEventId resourceEventId) {
    this.resourceEventId = resourceEventId;
}

@Transient
public Resource getResource() {
    return getResourceEventId().getResource();
}

public void setResource(Resource resource) {
    getResourceEventId().setResource(resource);
}

@Transient
public Event getEvent() {
    return getResourceEventId().getEvent();
}

public void setEvent(Event event) {
    getResourceEventId().setEvent(event);
}

@Temporal(TemporalType.DATE)
@Column(name = "lastUpdated", nullable = false, length = 25)
public Date getLastUpdated() {
    return this.lastUpdated;
}

public void setLastUpdated(Date lastUpdated) {
    this.lastUpdated = lastUpdated;
}

@Column(name = "lastAction", nullable = false, length = 10)
public String getLastAction() {
    return this.lastAction;
}

public void setLastAction(String lastAction) {
    this.lastAction = lastAction;
}

public boolean equals(Object o) {
    if (this == o)
        return true;
    if (o == null || getClass() != o.getClass())
        return false;

    ResourceEvents that = (ResourceEvents) o;

    if (getResourceEventId() != null ? !getResourceEventId().equals(that.getResourceEventId())
            : that.getResourceEventId() != null)
        return false;

    return true;
}

public int hashCode() {
    return (getResourceEventId() != null ? getResourceEventId().hashCode() : 0);
}

@Override
public List<Error> test() {
    // TODO Auto-generated method stub
    return null;
}
}

ResourceEventId

import java.io.Serializable;
import java.util.List;

import javax.persistence.ManyToOne;

import com.fasterxml.jackson.annotation.JsonBackReference;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class ResourceEventId implements TestableSerializable {

ObjectMapper mapper = new ObjectMapper();

private static final long serialVersionUID = 1L;
@JsonBackReference
private Resource resource;
@JsonBackReference
private Event event;

ResourceEventId () {
    mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
}

@ManyToOne
public Resource getResource() {
    return resource;
}

public void setResource(Resource resource) {
    this.resource = resource;
}

@ManyToOne
public Event getEvent() {
    return event;
}

public void setEvent(Event event) {
    this.event = event;
}

public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    ResourceEventId that = (ResourceEventId) o;

    if (resource != null ? !resource.equals(that.resource) : that.resource != null) return false;
    if (event != null ? !event.equals(that.event) : that.event != null)
        return false;

    return true;
}

public int hashCode() {
    int result;
    result = (resource != null ? resource.hashCode() : 0);
    result = 31 * result + (event != null ? event.hashCode() : 0);
    return result;
}

@Override
public List<Error> test() {
    // TODO Auto-generated method stub
    return null;
}
}

错误消息

11-Apr-2017 15:45:38.210 WARNING [https-nio-8443-exec-1] org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.doResolveException Handling of [org.springframework.http.converter.HttpMessageNotWritableException] resulted in Exception
java.lang.IllegalStateException: Cannot call sendError() after the response has been committed
at org.apache.catalina.connector.ResponseFacade.sendError(ResponseFacade.java:478)
at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:127)
at javax.servlet.http.HttpServletResponseWrapper.sendError(HttpServletResponseWrapper.java:127)
at org.springframework.security.web.context.SaveContextOnUpdateOrErrorResponseWrapper.sendError(SaveContextOnUpdateOrErrorResponseWrapper.java:87)
at org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.sendServerError(DefaultHandlerExceptionResolver.java:313)
at org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.handleHttpMessageNotWritable(DefaultHandlerExceptionResolver.java:370)
at org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.doResolveException(DefaultHandlerExceptionResolver.java:140)
at org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.resolveException(AbstractHandlerExceptionResolver.java:138)
at org.springframework.web.servlet.handler.HandlerExceptionResolverComposite.resolveException(HandlerExceptionResolverComposite.java:75)
at org.springframework.web.servlet.DispatcherServlet.processHandlerException(DispatcherServlet.java:1164)
at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1005)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:959)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:876)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:852)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:622)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:232)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
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:154)
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.www.BasicAuthenticationFilter.doFilter(BasicAuthenticationFilter.java:201)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
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.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
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:344)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:261)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:165)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:105)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:506)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:620)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:1078)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:760)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1524)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)

11-Apr-2017 15:45:38.215 SEVERE [https-nio-8443-exec-1] org.apache.catalina.core.StandardWrapperValve.invoke Servlet.service() for servlet [slaveService] in context with path [] threw exception [Request processing failed; nested exception is 
org.springframework.http.converter.HttpMessageNotWritableException: Could not write JSON: No serializer found for class 
com.******.common.model.ResourceEventId and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: java.util.ArrayList[303]->    com.*****.model.Resource["resourceEvents"]->org.hibernate.collection.internal.PersistentSet[0]-> 
com.*****.model.ResourceEvents["resourceEventId"]); nested exception is 
com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class com.*****.model.ResourceEventId and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: java.util.ArrayList[303]->com.cuadra.lup.persistence.common.model.Resource["resourceEvents"]->org.hibernate.collection.internal.PersistentSet[0]->com.*****.model.ResourceEvents["resourceEventId"])] with root cause
 com.fasterxml.jackson.databind.JsonMappingException: No serializer found for class com.*****.common.model.ResourceEventId and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) (through reference chain: java.util.ArrayList[303]->com.*****.model.Resource["resourceEvents"]->org.hibernate.collection.internal.PersistentSet[0]->com.*****.common.model.ResourceEvents["resourceEventId"])
at 

:::: EDIT :::: 可序列化错误已得到修复,但现在是主要问题

Could not write JSON: Infinite recursion (StackOverflowError) 

1 个答案:

答案 0 :(得分:0)

我会回答我自己的问题。

正如评论中所述,我忘记了导致序列化错误的@Embeddable。

至于无限递归,我只需要添加com.fasterxml.jackson.annotation.JsonIdentityInfo;

@Entity
@Table(name = "events")
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, 
property="@id")

public class Event implements TestableSerializable, Syncable<Event> {

@Entity
@Table(name = "resources")
@JsonIdentityInfo(generator=ObjectIdGenerators.IntSequenceGenerator.class, property="@id")
public class Resource implements TestableSerializable, Syncable<Resource> {