Внутренняя ошибка сервера на Jackson @ResponseBody - PullRequest
3 голосов
/ 07 марта 2012

Я просто хочу вернуть свой пользовательский объект как JSON для использования при вызове ajax на стороне клиента.

Это работало в один момент, и после некоторых обновлений (а именно, изменения приложения для развертывания в / в Jetty) теперь это не так.

Я не выкидываю исключение из кода, он возвращается просто отлично, но, кажется, взрывается где-то в корневом коде в Джексоне при попытке преобразовать объект в JSON.

Как я уже сказал, я не получаю исключения, мой ajax-вызов просто взрывается, сообщая код ошибки «500, Internal Server Error».

/* User contains information about a user of this site, that exists only
 * in the context of this site (no names, addresses).
 */
@Entity(name="User")
@Table(name="USER")
@NamedQuery(
    name="findUserByName",
    query="SELECT OBJECT(u) FROM User u WHERE u.name = :name"
)
public class User extends AuditableEntity implements Serializable {

private static final long serialVersionUID = -1308795024222223320L;

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name="id")
private Long id;


@NotEmpty
@MinSkipEmpty(value=6)
@MaxSkipEmpty(value=32)
@Column(name="name", length=32)
private String name;

@NotEmpty
@MinSkipEmpty(value=4)
@MaxSkipEmpty(value=40)
@Column(name="password", length=40)
private String password;

@Column(name="salt", length=40)
private String salt;


@ManyToOne(fetch=FetchType.LAZY, cascade={CascadeType.PERSIST})
@JoinColumn(name="person_id")
private Person person;

@Column(name="last_login")
private Date lastLogin;

@ElementCollection(fetch=FetchType.EAGER)
@CollectionTable(name ="USER_AUTHORITY")
@Column(name="authority")
private List<Integer> authorities;


public Long getId() {
    return id;
}

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

public String getName() {
    return name;
}

public void setName(String name) {
    this.name = (name == null ? name : name.trim());

}

public String getPassword() {
    return password;
}

public void setPassword(String password) {
    this.password = (password == null ? password : password.trim());
}

public String getSalt() {
    return salt;
}

public void setSalt(String salt) {
    this.salt = salt;
}

public Person getPerson() {
    return person;
}

public void setPerson(Person person) {
    this.person = person;
}

public Date getLastLogin() {
    return lastLogin;
}

public void setLastLogin(Date lastLogin) {
    this.lastLogin = lastLogin;
}

public List<Integer> getAuthorities() {
    return authorities;
}

public void setAuthorities(List<Integer> authorities) {
    this.authorities = authorities;
}

}

Это сущность человека

@Entity(name = "Person")
@Table(name = "PERSON")
public class Person extends AuditableEntity implements Serializable {

    private static final long serialVersionUID = -1308795024262635690L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    @NotEmpty
    @MaxSkipEmpty(value=64)
    @Column(name = "firstName", length=64)
    private String firstName;

    @NotEmpty
    @MaxSkipEmpty(value=64)
    @Column(name = "lastName", length=64)
    private String lastName;

    @NotEmpty
    @Email
    @MaxSkipEmpty(value=256,  message="")
    @Column(name = "email", length=256)
    private String email;

    @DateTimeFormat(pattern="MM/dd/yyyy")
    @NotNull(message = "Required field")
    @Column(name = "date")
    private Date birthday;

    @ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST })
    @JoinColumn(name = "location_id")
    private Location location;


    public Person() {

    }

    public Person(String firstName, String lastName) {
        super();
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public Long getId() {
        return id;
    }

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

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Location getLocation() {
        return location;
    }

    public void setLocation(Location location) {
        this.location = location;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    @Override
    public String toString() {

        return super.toString() + " name = " + firstName + " " + lastName
                + " id = " + id;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result
                + ((firstName == null) ? 0 : firstName.hashCode());
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        result = prime * result
                + ((lastName == null) ? 0 : lastName.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;
        Person other = (Person) obj;
        if (firstName == null) {
            if (other.firstName != null)
                return false;
        } else if (!firstName.equals(other.firstName))
            return false;
        if (id == null) {
            if (other.id != null)
                return false;
        } else if (!id.equals(other.id))
            return false;
        if (lastName == null) {
            if (other.lastName != null)
                return false;
        } else if (!lastName.equals(other.lastName))
            return false;
        return true;
    }

}

@Entity(name = "Location")
@Table(name = "LOCATION")
public class Location extends AuditableEntity implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    //name of person/place/thing

    @Column(name = "name", length=128)
    String name;

    //street address, p.o. box, company name, c/o
    @NotEmpty
    @MaxSkipEmpty(value=128)
    @Column(name = "line_1", length=128)
    String line1;

    // apt., suite, building, floor, entrance, etc.
    @Column(name = "line_2", length=128)
    String line2;

    @NotEmpty
    @MaxSkipEmpty(value=64)
    @Column(name = "city", length=64)
    String city;

    // state, providence, region
    @NotEmpty
    @MaxSkipEmpty(value=40)
    @Column(name = "state", length=40)
    String state;

    // postal code
    @NotEmpty
    @MaxSkipEmpty(value=16)
    @Column(name = "zip", length=16)
    String zip;

    @Column(name = "country")
    String country;

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getLine1() {
        return line1;
    }

    public void setLine1(String line1) {
        this.line1 = line1;
    }

    public String getLine2() {
        return line2;
    }

    public void setLine2(String line2) {
        this.line2 = line2;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getZip() {
        return zip;
    }

    public void setZip(String zip) {
        this.zip = zip;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    private static final long serialVersionUID = -178898928354655555L;
}

@RequestMapping(value="user/{documentId}", method=RequestMethod.GET)
public @ResponseBody User getUserForDocument( Model model,     @PathVariable("documentId") Long docId){

    Document doc = null;
    try{
        doc = dService.find(docId);
    }catch(Exception e){
        Logger logger = Logger.getLogger(DocumentController.class);
        logger.error(e.getMessage());
    }
    User user = doc.getUser();

    user.getPerson();
    user.getPerson().getLocation();
    return user;
}

@Repository()
public class DocumentDaoImpl implements DocumentDao {

    @PersistenceContext
    private EntityManager entityManager;

    @Transactional
    public Document find(Long id) {

        Document doc = entityManager.find(Document.class, id);

        Hibernate.initialize(doc.getUser());
        Hibernate.initialize(doc.getUser().getPerson());
        Hibernate.initialize(doc.getUser().getPerson().getLocation());

        return doc;
    }

    @SuppressWarnings("unchecked")
    @Transactional
    public List<Document> getUnassignedDocumentsForUser(User user) {

        Query query = entityManager.createQuery(new StringBuffer()
                .append("select d from Document d WHERE d.user = :user ")
                .append("AND NOT d IN( SELECT d from Book b, IN(b.docs) bd WHERE bd.id = d.id )")
                .append("").toString());
        query.setParameter("user", user);
        List<Document> tmp = (ArrayList<Document>) query.getResultList();
        for(Document doc : tmp){
            Hibernate.initialize(doc);
            Hibernate.initialize(doc.getUser());
            Hibernate.initialize(doc.getUser().getPerson());
            Hibernate.initialize(doc.getUser().getPerson().getLocation());
            entityManager.detach(doc);
            entityManager.detach(doc.getUser());
            entityManager.detach(doc.getUser().getPerson());
            entityManager.detach(doc.getUser().getPerson().getLocation());
        }
        return tmp;
    }

    @Transactional
    public Document save(Document doc) {

        if (doc.getId() == null) {
            entityManager.persist(doc);
            return doc;
        } else {
            return entityManager.merge(doc);
        }
    }

    public EntityManager getEntityManager() {
        return entityManager;
    }

    public void setEntityManager(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

}

ErrorExceptionResolver:

@Component
public class BWHandlerExceptionResolver extends SimpleMappingExceptionResolver implements InitializingBean{

    public void afterPropertiesSet() throws Exception {
        Properties props = new Properties();
        props.put(Exception.class.getName(),"error");
        this.setExceptionMappings(props);

    }

Ответы [ 5 ]

5 голосов
/ 06 июня 2013

У меня возникла почти идентичная проблема, но я хотел знать, что именно заставило Джексона потерпеть неудачу, вместо того, чтобы получать только 500 внутренних серверов без трассировки стека исключений где-либо в журналах. После проверки источников Spring MVC, первое место, где ловится исключение, находится в ServiceInvocableHandlerMethod.java (строки 109-116):

...
try {
    returnValueHandlers.handleReturnValue(returnValue, getReturnType(), mavContainer, request);
} catch (Exception ex) {
    if (logger.isTraceEnabled()) {
        logger.trace(getReturnValueHandlingErrorMessage("Error handling return value", returnValue), ex);
    }
    throw ex;
}
...

handleReturnValue (...) выдает исключение, и оно перехватывается здесь, но не распечатывается, потому что logger.isTraceEnabled () возвращает false в конфигурации по умолчанию. Чтобы увидеть трассировку стека исключений в журналах, мне нужно было включить ведение журнала уровня TRACE в вышеупомянутом классе. В моем конкретном случае (я использую log4j) вот соответствующая часть log4j.properties:

...
log4j.logger.org.springframework.web.servlet.mvc.method.annotation=TRACE

log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Threshold=TRACE
...
3 голосов
/ 30 июля 2012

Настройка типа загрузки или пометки @Transactional controller не является хорошим решением.Используйте Джексона Гиберате модуль , чтобы решить эту проблему.Это не будет пытаться инициализировать объекты, которые являются прокси.

1 голос
/ 30 июля 2012

Проблема в том, что Джексон разрешает / сериализует ваш объект после закрытия транзакции. Одно простое решение этой проблемы - установить все ваши отношения на FetchType.EAGER.

Я все еще ищу лучшее решение: (

0 голосов
/ 29 июля 2014

Я столкнулся с подобной проблемой, но получил решение распечатать исключения после написания кода вручную для анализа объекта в JSON.

for example:

 public @ResponseBody String  getObject() {

  ObjectMapper mapper = new ObjectMapper();
  try {
          return mapper.writeValueAsString(object);

      } catch (IOException ex) {
          ex.printStackTrace();      
          }


   }
0 голосов
/ 14 марта 2012

Ответом было обернуть метод контроллера в транзакцию, указав аннотацию @Transactional.Это исправило мою проблему.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...