简体   繁体   中英

Hibernate and Jackson (java.lang.IllegalStateException: Cannot call sendError() after the response has been committed)

Hello have some problem with Hibernate and Jackson. Have two POJOs

@Entity
@Table(name = "USERS")
public class User implements Serializable {
static final long serialVersionUID = 4235637833262722256L;

@Id
@GeneratedValue
private int id;

@Column(name = "CREATION_DATE")
private Date creationDate;

@Column(name = "FIRST_NAME")
private String firstName;

@Column(name = "LAST_NAME")
private String lastName;

@Version
@Column(name = "VERSION")
private int version;

@Column(name = "E_MAIL")
private String email;

@Column(name = "ENABLED")
private boolean enabled;

@Column(name = "LOGIN")
private String login;

@Column(name = "PASSWORD")
private String password;

@JsonManagedReference("role")
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Set<UserRole> userRole;

User's and his Role that's made for spring security integration.

@Entity
@Table(name = "USER_ROLES")
public class UserRole {

@Version
@Column(name = "VERSION")
int version;
@Id
@GeneratedValue
private int id;

@JsonBackReference("role")
@ManyToOne()
@JoinColumn(name = "USER_ID", referencedColumnName = "ID")
private User user;

@Column(name = "ROLE")
private String role;

And have a controller which make from User Json via Jackson. With method

  @RequestMapping(value = "/login", method = RequestMethod.GET, produces = "application/json")
private @ResponseBody User checkLoginAvailability(@RequestParam String login) {
    User user = appContext.getUserService().readUserByLogin(login);
    if (user != null) {
        return user;
    }
    return new User();
}

When this method return user without null fields it throws that:

26-Oct-2014 14:31:17.652 WARNING [http-nio-8080-exec-4] 
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver.doResolveException 
Handling of [org.springframework.http.converter.HttpMessageNotWritableException]
resulted Exception java.lang.IllegalStateException: Cannot call sendError() after the response has been committed

That's problem came when i added @OneToMany(mappedBy = "user", cascade = CascadeType.ALL,fetch = FetchType.EAGER), previously it was just fetch = FetchType.EAGER, but i wasn't able to save User with his role in this case the table USER_ROLE was empty. I've red some solution that problem in infinite loop somewhere in Jackson, added some anotation @JsonBackReference("role") and @JsonManagedReference("role") but nothing changed. How can i solve this problem? Thank you!

i recommend adding @jsonignore on the property that is causing the circular reference. this will tell jackson not to serialize that property.

Use an exception breakpoint for IllegalStateException to halt execution when sendError is called, then inspect the local variables on the stack to get the exception that could not be reported, and invoke its printStackTrace method to see its stacktrace.

Hopefully, the root cause of the exception will then be clear, and with it how to fix whatever the problem was.

Sometimes complexes beans cause this kind of problem. As I saw your relationship @OneToMany is EAGER, that is correct cause if you're using LAZY when you serialize the list to JSON that dependencies objects are losted. Your problem is a serialization problem, I have no doubt.

Yur can try:

@Entity
@Table(name = "USERS")
//To suppress serializing properties with null values
@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
//Ignoring new fields on JSON objects
@JsonIgnoreProperties(ignoreUnknown = true)
public class User implements Serializable {
...

But I'll recommend you to use a good practice called: Data transfer Object (DTO). DTOs are bean used in the middle layer of the persistent beans and the form. Most commonly used by the services layer or controller in an (N)tier application to transfer data between itself and the UI layer. You can create the DTO in service layer or may directly in controller (better in service) and send it to form, it also permit you to binding the form entries to the validation directly in the controller (basically you need to add the parameter BindingResult result in the signature of the controller method). DTOs allow you to create annotations that do the validation like @NotEmpty, @PasswordsNotEqual, @Size(max = 100), these annotation going in the bean, and you can also put these annotations in your persistence bean, but not recommended cause you are creating a very complex bean. The messages of validation are showed in the the form See how:

<form:errors id="error-password" path="password" cssClass="help-block"/>

Believe in me, DTO are fantastic to your scenario cause it's allow you to do the validation of the form entries in the right way.

Best Regards

The best solution which I found is not to return the Entity directly. In your case you are returning the 'user' entity directly in your login controller. Instead just return user.id or user.firstname, etc. It will work fine. When we return any entity the compiler goes into infinite recursive loop.

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