3

i'm using spring 4.0.1 , hibernate 4.3.5 ,jackson 1.9.2 and STS IDE I'm creating a RESTful webservice that returns a data in JSON format when i use Hibernate code generator it generates getters and setter of associated entities annotated by @OneToMany(fetch = FetchType.LAZY, mappedBy = "user") for the source and @ManyToOne(fetch = FetchType.LAZY) for the reference which causes an infinite recursion during serialization. I tried using Jackson's @JsonIgnore and @JsonBackReference annotations to fix the problem but it seems as if they are being totally ignored and the infinite recursion is still occurring.

Caused by: com.fasterxml.jackson.databind.JsonMappingException: Infinite recursion (StackOverflowError)

This is my entity classes User.class

    //i get that suggestion from some sites
    @JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
    @Entity
    @Table(name = "user", catalog = "someSchema")    
    public class User implements java.io.Serializable {

        private String name;
        private String password;
        private String username;
        private Set<Telephone> telephones = new HashSet<Telephone>(0);
        @JsonManagedReference
        @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
        public Set<Telephone> getTelephones() {
            return this.telephones;
        }

        public void setTelephones(Set<Telephone> telephones) {
            this.telephones = telephones;
        }
    }

Telephone.class

@Entity
@Table(name = "telephone", catalog = "someSchema")
public class Telephone implements java.io.Serializable {


    private User user;
    private String telephone;

    @ManyToOne(fetch = FetchType.LAZY)
//tried @JsonIgnore only and both
    @JsonIgnore
//tried @JsonBackReference only and both
    @JsonBackReference
    @JoinColumn(name = "user_id", nullable = false)
    public User getUser() {
        return this.user;
    }


    @JsonIgnore
    @JsonBackReference
    public void setUser(User user) {
        this.user = user;
    }

}

concerning registering jackson to my application, i used xml config

   <mvc:annotation-driven>
        <mvc:message-converters>
            <bean
                class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                <property name="objectMapper">
                    <bean
                        class="web.jsonConverters.HibernateAwareObjectMapper" />
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>

and mapper class

public class HibernateAwareObjectMapper extends ObjectMapper {

    public HibernateAwareObjectMapper() {
        Hibernate4Module hm = new Hibernate4Module();
        registerModule(hm);
    }
}

Do you have any idea why the Jackson annotations are being ignored?

any help will be appreciated...

Ahmed Adel
  • 181
  • 1
  • 6
  • 15
  • possible duplicate of [@JsonIgnore and @JsonBackReference are being Ignored](http://stackoverflow.com/questions/16881382/jsonignore-and-jsonbackreference-are-being-ignored) – try-catch-finally Dec 29 '14 at 12:01
  • yup..but answers not sufficient could any one give better answer – Ahmed Adel Dec 29 '14 at 12:05
  • You should - *by all means* - provide more information on your current case (code, what error you get, what you've tried so far). You might link to that question if any of the three answers did not help (this is part of what you've tried so far). – try-catch-finally Dec 29 '14 at 12:09

2 Answers2

1

use

import com.fasterxml.jackson.annotation.JsonBackReference;

instead of

import org.codehaus.jackson.annotate.JsonBackReference;
Khalid
  • 4,730
  • 5
  • 27
  • 50
user104309
  • 690
  • 9
  • 20
-1

i found a way by annotating the setter by @Transient

idont know why but it works fine

User.class

    //i get that suggestion from some sites
    @JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
    @Entity
    @Table(name = "user", catalog = "someSchema")    
    public class User implements java.io.Serializable {

        private String name;
        private String password;
        private String username;
        private Set<Telephone> telephones = new HashSet<Telephone>(0);

        @OneToMany(fetch = FetchType.LAZY, mappedBy = "user")
        public Set<Telephone> getTelephones() {
            return this.telephones;
        }

        public void setTelephones(Set<Telephone> telephones) {
            this.telephones = telephones;
        }
    }

Telephone.class

   @Entity
   @Table(name = "telephone", catalog = "someSchema")
   public class Telephone implements java.io.Serializable {


    private User user;
    private String telephone;

    @ManyToOne(fetch = FetchType.LAZY)

    @JoinColumn(name = "user_id", nullable = false)
    public User getUser() {
        return this.user;
    }

    @Transient  
    public void setUser(User user) {
    this.user = user;
      }

    }

Another Naive Solution

I solved it manually in my RESTful Controller

by loop over the set of telephones and set user to null

    @RestController
    @RequestMapping("/user") 
    public class UserController extends ParentController {

        static final Logger logger = Logger.getLogger(UserController.class.getName());
        @Autowired
        IUserDao iuserdao;

            @RequestMapping(value = "/signin", method = RequestMethod.POST)
            public ResponseEntity<User> signin(@RequestBody LoginWrapper login) {
                System.out.println("==============GET USER==============");
                try {
                    User user = iuserdao.signin(login);
                    if (user == null) {
                        HttpHeaders httpHeaders = new HttpHeaders();
                        httpHeaders.set(ERR_HEADER_NAME, "user not exist");
                        return new ResponseEntity<User>(httpHeaders, HttpStatus.NOT_FOUND);
                    } else {
                        List<Telephone> tels=user.getTelephones();
                        for (Telephone telephone : tels) {
                            telephone.setUser(null);
                        }
                        return new ResponseEntity<User>(user, HttpStatus.OK);
                    }

                } catch (Exception e) {
                    System.out.println(e.getMessage());
                    return null;
                }

            }

still need a better answer concerning Jackson problem..

Ahmed Adel
  • 181
  • 1
  • 6
  • 15
  • 1
    The transient annotation 'works' by making it transient to JPA - JPA will not persist or read it to/from the database in anyway. You cannot mix and match annotations on fields and properties; they should all be of the same type (all field or all property), and all on the same method types where you would notice that the transient immediately conflicts with the ManyToOne annotation. You may have a similar situation occuring with Jackson such that it only looks at annotations on your fields, ignoring fields set on accessor methods. – Chris Jan 06 '15 at 16:58
  • so, to make jackson annotation works I've to make all my annotations on fields not accessor methods.??? – Ahmed Adel Jan 06 '15 at 18:26