0

I havea spring based java app that I am currently developing Long story short - here is the code I use to retrieve an object from the db, do some calculations on the same and render it

@RequestMapping(value = { "/mapping" }, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public Object getMasterAppMappingById(
        @PathVariable(value = "masterAppId") Integer masterAppId) {  

    RestBaseVO masterAppMappingRestBaseVO = new RestBaseVO();

    MasterAppMappingTreeDetailsVO masterAppMappingTreeDetailsById = applicationDeviceServices.getMasterAppMappingTreeDetails(masterAppId, true);

    return masterAppMappingTreeDetailsById;
}

The problem I have is, the code is fine right up until jackson kicks in and converts it. At the return statement is my bottleneck

The method getMasterAppMappingTreeDetails works perfectly and performs well

The json that is rendered by jackson is shown at the following url on pastebin http://pastebin.com/erRDtweZ

As you can see - it is fairly big

The classes being serialized are as follows

public class MasterAppMappingTreeDetailsVO {

@JsonProperty("id")
private Integer id;

@JsonProperty("mappingId")
private Integer mappingId;

@JsonProperty("parentMappingId")
private Integer parentMappingId;



 @JsonProperty("isQuestion")
    private boolean isQuestion;

    @JsonProperty("isAnswer")
    private boolean isAnswer;

    @JsonProperty("isApplication")
    private boolean isApplication;

    @JsonProperty("displayLabel")
    private String displayLabel;

    @JsonProperty("additionalText1")
    private String additionalText1;

    @JsonProperty("imageUrl")
    private String imageUrl;

    @JsonProperty("imageDateUpdated")
    private Long imageDateUpdated;

    @JsonProperty("appId")
    private Integer appId;

    @JsonProperty("appName")
    private String appName;

    @JsonProperty("children")
    private List<MasterAppMappingTreeDetailsVO> children;

    @JsonProperty("menuStyle")
    private MenuStyleVO menuStyle;

}


    @Entity
@Table(name = "menu_style")
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class MenuStyleVO extends BaseDAOVO implements Serializable{

    /**
     * 
     */
    private static final long serialVersionUID = 3697798179195096156L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    private Integer id;

    @Column(name = "menuStyleName", unique = false, nullable = false, length = 200)
    private String menuStyleName;

    @Column(name = "menuTemplate", unique = false, nullable = false, length = 200)
    private String menuTemplate;

    @OneToOne(fetch = FetchType.EAGER)
    @Cascade({ CascadeType.SAVE_UPDATE })
    @JoinColumn(name="logo_id")
    @JsonProperty("logo")
    private ApplicationImageVO logo;

    @Column(name = "logoAlignment", unique = false, nullable = true, length = 20)
    private String logoAlignment;

    @Column(name = "backArrowColor", unique = false, nullable = true, length = 7)
    private String backArrowColor;    

    @OneToOne(fetch = FetchType.EAGER)
    @Cascade({ CascadeType.SAVE_UPDATE })
    @JoinColumn(name="backArrowIcon_id")
    @JsonProperty("backArrowIcon")
    private ApplicationImageVO backArrowIcon;

    @Column(name = "questionLabelTextColor", unique = false, nullable = true, length = 7)
    private String questionLabelTextColor;  

    @Column(name = "headerBackgroundColor", unique = false, nullable = true, length = 7)
    private String headerBackgroundColor;      

    @Column(name = "headerBackgroundOpacity", unique = false, nullable = true)
    private Integer headerBackgroundOpacity; 

    @Column(name = "mainBackgroundColor", unique = false, nullable = true, length = 7)
    private String mainBackgroundColor;      

    @Column(name = "mainBackgroundOpacity", unique = false, nullable = true)
    private Integer mainBackgroundOpacity; 

    @OneToOne(fetch = FetchType.EAGER)
    @Cascade({ CascadeType.SAVE_UPDATE })
    @JoinColumn(name="backgroundImageLandscape_id")
    @JsonProperty("backgroundImageLandscape")
    private ApplicationImageVO backgroundImageLandscape;

    @OneToOne(fetch = FetchType.EAGER)
    @Cascade({ CascadeType.SAVE_UPDATE })
    @JoinColumn(name="backgroundImagePortrait_id")
    @JsonProperty("backgroundImagePortrait")
    private ApplicationImageVO backgroundImagePortrait;

    @Column(name = "containerColor", unique = false, nullable = true, length = 7)
    private String containerColor;      

    @Column(name = "containerOpacity", unique = false, nullable = true)
    private Integer containerOpacity; 

    @Column(name = "containerLineDividerColor", unique = false, nullable = true, length = 7)
    private String containerLineDividerColor;      

    @Column(name = "containerLineDividerOpacity", unique = false, nullable = true)
    private Integer containerLineDividerOpacity; 

    @Column(name = "optionIconSize", unique = false, nullable = true)
    private Integer optionIconSize; 

    @Column(name = "optionLabelTextColor", unique = false, nullable = true, length = 7)
    private String optionLabelTextColor;     

    @Column(name = "optionTaglinePosition", unique = false, nullable = true, length = 20)
    private String optionTaglinePosition;   

    @Column(name = "optionTaglineTextColor", unique = false, nullable = true, length = 7)
    private String optionTaglineTextColor; 

    @Column(name = "optionSelectionArrowColor", unique = false, nullable = true, length = 7)
    private String optionSelectionArrowColor;

    @OneToOne(fetch = FetchType.EAGER)
    @Cascade({ CascadeType.SAVE_UPDATE })
    @JoinColumn(name="optionSelectionArrowIcon_id")
    @JsonProperty("optionSelectionArrowIcon")
    private ApplicationImageVO optionSelectionArrowIcon;

}

Can anyone offer any advise on how to improve the performance of this json call or how to improve jackson performance in general for my application?

Damien Gallagher
  • 981
  • 4
  • 13
  • 25
  • 1
    I'd start by using [Jackson's AfterBurner module](https://github.com/FasterXML/jackson-module-afterburner) – fps Sep 02 '15 at 20:33
  • unfortunately this library had no impact. Can you offer any further advice? – Damien Gallagher Sep 06 '15 at 19:21
  • Could this have anything to with the number of nested levels I have? I have 1 class called MasterAppMappingTreeDetailsVO that has contains a list of MasterAppMappingTreeDetailsVO objects. The MasterAppMappingTreeDetailsVO also has a menuStyle object and I believe the bottleneck is here. The MenuStyleVO is loaded from the database using hibernate in an eager fashion. Any assistance would be appreciated – Damien Gallagher Sep 06 '15 at 22:35
  • 1
    Check if you have a [N+1 problem](http://stackoverflow.com/questions/97197/what-is-the-n1-selects-issue). Maybe you're not loading every possible object in an eager fashion. Jackson is fast, despite the number of nested levels. – fps Sep 06 '15 at 23:39
  • Yes I removed a few objects last night and it loaded faster so I have it narrowed down slightly. I noticed I had the JsonIgnore on some attributes last night, and with the nested objects, JsonIgnore wasnt taking effect further down the tree and attibutes marked as jsonIgnore were being included – Damien Gallagher Sep 07 '15 at 07:36
  • OK, check another thing... Sometimes it can happen that you have both jackson v1 and jackson v2 jars in your classpath (maybe due to transitive dependencies). If this is the case, check that you're not mixing different jackson versions for annotations and data binding. – fps Sep 07 '15 at 18:16
  • Unfortunately I only have the one version of jackson on the classpath. I get the impression now that the JsonIgnore annotation is not taking effect correctly down in the nested classes – Damien Gallagher Sep 07 '15 at 20:31
  • One thing i noticed - rthe class that has the problems has the annotation JsonIgnoreProperties(value={"hibernateLazyInitializer", "handler"})at the class level. Would this affect the JsonIgnore annotation? – Damien Gallagher Sep 07 '15 at 21:32

1 Answers1

4

Problem with Jackson
If you are serializing your entity classes using Jackson API, then putting @JsonIgnore annotation may suffice one case but not all.
If you have some lazy loaded properties, then those will be loaded once Jackson serializes the objects or you would have to load them eagerly, which would definitely have adverse impact on the performance.
If you have Relations between your classes(one-many;many-many etc), then Jackson will load all dependent entities as well when serializing object and you may also get into a circular dependency issue i.e. parent loading child and child loading parent.
Of course, there is a way to overcome that using Jackson API, but in long term, it will become a bottleneck.

Solution
The best way according to me is to create model class for what you want to return and load and return that class before returning from the controller.

This has some pros and cons.

Cons:
1. Of course there would be extra classes and extra code to be maintained.

Pros:
1. Model classes would be separate and there would not be any dependency on your entity classes.
2. Any change in any of the classes will not effect the other class.
3. Properties which are not needed to be rendered can be avoided.
4. You can easily modify your entity class and until your model class is changed there would not be a change in the view.

Hope this helps!!

Vipul Agarwal
  • 1,513
  • 3
  • 14
  • 24
  • I tried that yesterday but it also loaded lazy associations for some reason. I think i only created the model for 1 attribute and not all - would you recommend not returned any database entities in complicated json? – Damien Gallagher Sep 08 '15 at 17:33
  • 1
    It would be good if you create model class having only those attributes that are to be rendered. If those attributes require some lazy loaded attributes to be loaded, then it can not be avoided. Recheck your associations and the relation between the model attributes vs lazy loaded attributes. – Vipul Agarwal Sep 09 '15 at 09:36
  • thanks Vipul - i think my problem lies in some MayToOne lazy associations that I need to sort out – Damien Gallagher Sep 09 '15 at 15:07
  • I am glad I could help. All the best!! – Vipul Agarwal Sep 09 '15 at 15:24
  • No bother - any advice on getting hibnernate ManyToOne lazy associations working correctly? :) – Damien Gallagher Sep 09 '15 at 15:29