1

I've been working with RESTful service built by Spring MVC + Hibernate + MySQL.

In the database, I have two tables - topic and comment. ID column of topic table is the foreign key of the comment table. I generate the models and DAOs using Hibernate Tools:

Topic model:

...

@OneToMany(fetch = FetchType.LAZY, mappedBy = "topic")
public Set<Comment> getComments() {
    return this.comments;
}

...

Comment model:

...

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "topic_id", nullable = false)
public Topic getTopic() {
    return this.topic;
}

...

Here comes the question. I can get the detached topic object DT using findById(). However, every time I want to send DT as the response body to the frontend, 'no session' exception is thrown because LAZY object - comments, can only be accessed within the session.

1) if I don't change to EAGER, my solution is to create new Topic object NT and retrieve values (except comments) from the DT. Then return NT as the response body. Is this the best practice?

2) if I change the @OneToMany part to EAGER, the problem is still unsolved. Because when I send DT, set of comments are included, and every comment has relationship to topic as declared by @ManyToOne. So still 'no session' exception is thrown.

3) if I change both @OneToMany and @ManyToOne to EAGER, computer will stuck as it will go from topic to comment and back to topic, iteratively, never stop.

So, how you guys suggest to return topic as response body.

Steven Luo
  • 2,350
  • 3
  • 18
  • 35
  • 1
    Creating a View Object or a DTO (Data Transfer Object) to return information to the front end is not unusual or bad practice. You can get rid of the 'no session' by setting the comments of the Topic to null or reinitializing to an empty array. I'm not sure you need the ManyToOne bidirectional relationship annotation in the comment model. The fact the you have a list of comments in the topic and have it annotated there should be sufficient. – K.Nicholas Dec 28 '15 at 02:51
  • Checkout [jackson-datatype-hibernate](https://github.com/FasterXML/jackson-datatype-hibernate) to work with lazy-loading aspects – nickmesi Dec 28 '15 at 03:16
  • For handling cyclic object graphs see [JsonIdentityInfo](http://wiki.fasterxml.com/JacksonFeatureObjectIdentity) – nickmesi Dec 28 '15 at 03:26
  • I don't understand why `Topic` is fully mapped in `Comment`. It makes no sense, if you regard `Topic` as an aggregate. So what's the use case? Including it in the response is like a webpage about a car that contains all information about its manufacturer. – a better oliver Dec 28 '15 at 14:42
  • @zeroflagL Maybe I misunderstand something. Does foreign key must come with a `@OneToMany` or `@ManyToOne` relationship? Is it good practice to have foreign key without any relationship? – Steven Luo Dec 29 '15 at 07:22
  • Possible use case: You have a list of topics and load all comments for one topic. With your solution you would load the topic again and do that for every comment. What is the purpose of a comment knowing about the topic? Do you ever load a comment without already having a topic? Would you ever load comment no. 12345 and then ask yourself "What topic does that comment belong to"? I don't think so. As far as I can see it the comment does not need a reference to the topic. – a better oliver Dec 29 '15 at 07:55

1 Answers1

0

After long time of research, I finally have acceptable solution.

But first thing first, the reason of using bidirectional relationship can be found here.

My solution is using jackson-datatype-hibernate, which thanks to @ZeeMesi. Here's the steps:

Step 1: dependency

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

Step 2: spring context

<mvc:annotation-driven>
    <mvc:message-converters>
        <!-- Use the HibernateAware mapper instead of the default -->
        <beans:bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
            <beans:property name="objectMapper">
                <beans:bean class="com.hersbitcloud.cancercloud.HibernateAwareObjectMapper" />
            </beans:property>
        </beans:bean>
    </mvc:message-converters>
</mvc:annotation-driven>

Step 3: create new class

public class HibernateAwareObjectMapper extends ObjectMapper {

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

Then all the lazily fetched object will be null.

Community
  • 1
  • 1
Steven Luo
  • 2,350
  • 3
  • 18
  • 35