5

i use Hibernate 4 and Spring 3.

i have two entity.

Book entity

@Entity
@Table(name = "book")
public class Book implements Serializable {

    public Book() {
    }

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue( strategy = GenerationType.IDENTITY)
    private int id;

    @ManyToOne()
    @JoinColumn( name = "author_id" )
    private Author author;

    private String name;
    private int pages;

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

    public int getId() {
        return id;
    }

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

    public Author getAuthor() {
        return author;
    }

    public void setAuthor(Author author) {
        this.author = author;
    }

    public String getName() {
        return name;
    }

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

    public int getPages() {
        return pages;
    }

    public void setPages(int pages) {
        this.pages = pages;
    }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }


}

and Author entity

@Entity
@Table(name = "author")
public class Author implements Serializable {

    public Author() {
    }

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue( strategy = GenerationType.IDENTITY)
    private int id;
    private String name;

    @OneToMany( mappedBy = "author", cascade = CascadeType.ALL, orphanRemoval = true)
    private Set<Book> books = new HashSet<Book>();


    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public Set<Book> getBooks() {
        return books;
    }
    public void setBooks(Set<Book> books) {
        this.books = books;
    }

    public void addBook(Book book) {
        book.setAuthor(this);
        getBooks().add(book);
    }

    public void removeBook(Book book) {
        getBooks().remove(book);        
    }

}

and JSON depend in pom.xml

<dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.1.2</version>
        </dependency>


        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-hibernate4</artifactId>
            <version>2.1.2</version>
        </dependency>

My Root-context is here -

    <!-- Root Context: defines shared resources visible to all other web components -->
    <context:annotation-config/>

    <context:component-scan base-package="org.jar.libs.dao" />
    <context:component-scan base-package="org.jar.libs.service" />

    <tx:annotation-driven transaction-manager="transactionManager" />

     <bean id="jspViewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/views/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource"
        p:driverClassName="com.mysql.jdbc.Driver" p:url="jdbc:mysql://localhost:3306/hibernate"
        p:username="root" p:password="root" />

    <bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="annotatedClasses">
            <list>
                <value>org.jar.libs.domain.Book</value>
                <value>org.jar.libs.domain.Author</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>
    </bean>

    <bean id="transactionManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>

</beans>

...servlet-context.xml

<!-- Enables the Spring MVC @Controller programming model -->
    <annotation-driven />

    <!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
    <resources mapping="/resources/**" location="/resources/" />

    <!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
    <beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <beans:property name="prefix" value="/WEB-INF/views/" />
        <beans:property name="suffix" value=".jsp" />
    </beans:bean>

    <context:component-scan base-package="org.jar.libs.controller" />

Controller.

@Controller
@RequestMapping (value = "books/rest")
public class BookController {

    @Autowired
    private BookService bookService;

    // logger
    private static final Logger logger = LoggerFactory.getLogger(BookController.class);


    @SuppressWarnings("unchecked")
    @RequestMapping( method = RequestMethod.GET )
    public @ResponseBody List<Book> getBook() {


        List<Book> res = bookService.findAll();
        return res;


    }

}

findAll in my DAO :

public List<Book> findAll() {
        Session session = sessionFactory.getCurrentSession();
        List<Book> result = (List<Book>) session.createQuery("select c from Book c").list();
        return result;
    }

in debug i see that method return 2 records, but Spring can not convert result to JSON and return 406 HTTP error. What's wrong?

I attach image what i see in debug. - http://tinypic.com/view.php?pic=35kvi9i&s=6

Benjamin
  • 531
  • 2
  • 6
  • 18
  • are you getting `LazyInitialization` exceptions? – Nandkumar Tekale Jan 03 '13 at 15:29
  • no. in chrome debug i see 500 Internal Server Error, i can't see exception in SpringToolSuite console. – Benjamin Jan 03 '13 at 15:58
  • What version of Spring are you using? If you're using 3.2 or little less then the thing with JSON will be different. Are you experiencing such problems as mentioned in this [question](http://stackoverflow.com/a/14102773/1037210)? 406 HTTP error means the resource which is returned by the server cannot be accepted. – Lion Jan 03 '13 at 16:55
  • This may answer your problem too http://stackoverflow.com/questions/21177191/spring-mvc-rest-jpa-hibernate-one-to-many-json-error – loyalBrown Feb 02 '14 at 16:53

3 Answers3

4

Generally, when you call getter methods of entity classes(which returns relation object) out of transaction, then you get LazyInitializationExceptions.

That's what might be happening in your case if you are converting entity class objects(retrieved from query) to json out of transaction.

I had same issue, I converted my entity object retrieved by hibernate to json in controller. As controller was out of transaction(Transaction at service layer), while converting to json, getter methods of entity class objects are called and I got LazyInitializationException. Which obstructed object conversion to json, and response was not returned.

My solution, Try this :

@SuppressWarnings("unchecked")
@RequestMapping( method = RequestMethod.GET )
public @ResponseBody List<Book> getBook() {
    List<Book> res = bookService.findAll();
    for(Book book : res) {
       book.getAuthor().setBooks(null);
    }
    return res;
}
Nandkumar Tekale
  • 16,024
  • 8
  • 58
  • 85
  • It's work. Fine. But now i should call "setter method (null); " for all my entity who contains relation object ? – Benjamin Jan 03 '13 at 16:07
  • how to avoid it? ( set null for relation object ) – Benjamin Jan 03 '13 at 17:01
  • yes, You need to set null for relation object but only in case of `Set`s as above we did for `books` which is Set. Other way is to use bean mapping. – Nandkumar Tekale Jan 03 '13 at 17:54
  • Sorry, i am new in Spring/Hibernate. Where i can learn more about bean mapping? just i don't understand it. Thanks a lot! – Benjamin Jan 03 '13 at 20:46
  • In Bean mapping, you have different classes(called view beans) similar to your pojo/entity classes. What you do is copy values from entity/pojo class objects to view beans. Convert these view beans to json/xml and send json/xml in response. – Nandkumar Tekale Jan 04 '13 at 09:13
  • My guess is this is really blowing up is you're overflowing the stack. In your model you have a circular reference between your books and authors. When the JSON mapper tries to serialize this it will hit an infinite loop and overflow the stack. Unfortunately I haven't found a way around this yet either. – loyalBrown Feb 02 '14 at 16:45
1


As others have suggested, I would really not advise you to try to JSON serialize (or actually perform any serialization) of hibernate entities.
You must remember that the fetched entities are actually "proxified" objects (Hibernate uses ASM, CGLIB and other "dynamic proxiy" frameworks).
As a result for example, collections get replaced with [PersistenceBags] which may be initialized "lazily" , and cause you hibernate exceptions 1.

But the problems do not stop there, you may see issues when trying to serialize an Hibernate custom type
I know this might sound you like writing "boillerplate" code but you might end up coding DTOs - data transfer objects which will take the entity returned from your DAL, and transform them to an object that can be serialized.

You can use a framework like dozer in order to ease development of serialization between an entity to a DTO.

Yair Zaslavsky
  • 4,091
  • 4
  • 20
  • 27
0

Try using these two Jackson artifacts instead

<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.9</version>
</dependency>

<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.9</version>
</dependency>

Also on your controller try by changing it to -

 @SuppressWarnings("unchecked")
@RequestMapping( method = RequestMethod.GET,  produces = MediaType.APPLICATION_JSON_VALUE )
public @ResponseBody List<Book> getBook() {

Lastly, make sure your view is making a json request.

Mukus
  • 4,870
  • 2
  • 43
  • 56
  • thanks, but this don't work. i use curl for test it. It's my request - C:\>curl.exe -i -H "Accept: application/json" http://localhost:8080/libs/books/rest/ .. – Benjamin Jan 03 '13 at 16:12