Converting DAO objects to DTO objects in a Spring MVC Application

14,082

Solution 1

Okay as beny23 mentioned Hibernate was lazy-loading (loading a list of PKs initially then loading the rest when actioning something on the data)

The solution I used was to create a non-hibernate connection for reading data using a normal JDBC connection, the query also converted the data so that it came back in the format I needed it in (Dates as Strings etc) so and I didn't have to convert to dto. That way I unloaded some of the work to the database and saved my application the hassle of doing it.

Solution 2

This is probably not the cause of your problem (12s is huge), but it is still worth saying.

(Simple)DateFormat classes are not thread-safe :

Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.

So don't store them in global class attributes, otherwise you could have weird issues. A simple thing to do is instantiating a (Simple)DateFormat just before using it.

Also see this interesting blog post about SimpleDateFormat.

Share:
14,082
patrickm
Author by

patrickm

Updated on June 04, 2022

Comments

  • patrickm
    patrickm almost 2 years

    Background: I work in an education environment and last summer one of our developers designed and built a Java web application using Spring MVC and Hibernate. It launched with the new term in September to much joy from the users as it replaced a dusty old Blackboard plugin. The primary functions of the applications are used for setting targets for a student, leaving them messages and creating reports for students.

    Fast forward a few months, the original developer has moved on and the application is having some growing pains.

    Use Case Scenario: A Teacher logs in and they are presented with their home screen which contains a list of courses which they teach on, with an overview of Targets, messages and Reports for the currently selected course as well as a list of student enrolments for the course. If a course contains a small number of targets etc then it's quick to return the information. But as the amount of information grows, the time taken to load it seems to increase exponentially.

    After investigating I think I've found out why. I took a sample course and took a look at Reports to see what was going on. and I found that the Database returned the relevant data in milliseconds, the browser rendered it in milliseconds but there was 12 seconds in between when the browser was waiting for data to be returned from it. The only thing that gets done to the objects between the database query finishing and the front end receiving the response is a convert to DTO.

    Code: This is what the Report Object looks like in the DAO layer

    @Entity
    @Table(name = "REPORTS")
    public class Report implements Serializable
    {
    
        /**
         * 
         */
        private static final long   serialVersionUID    = -7659637777880535914L;
    
        @Id
        @GeneratedValue
        @Column(name = "REPORT_ID", insertable = true, updatable = false, nullable = false, unique=true)
        private Integer             reportID;
    
        @Column(name = "DATE_CREATED", insertable = true, updatable = false, nullable = false)
        private GregorianCalendar   dateCreated;
    
        @Column(name = "DATE_MODIFIED", insertable = true, updatable = true, nullable = true)
        private GregorianCalendar   dateModified;
    
        @Column(name = "TITLE", insertable = true, updatable = true, nullable = false, length=1000)
        private String              title;
    
        @Column(name = "CURRENT_PERFORMANCE_GRADE", insertable = true, updatable = true, nullable = false)
        private String              currentPerformanceGrade;
    
        @Column(name = "TARGET_GRADE", insertable = true, updatable = true, nullable = false)
        private String              targetGrade;
    
        //VARCHAR(MAX) as this is the main body of the tutor report comments. Here the tutor can write as much content as they like.
        @Column(name = "TUTOR_COMMENTS", insertable = true, updatable = true, nullable = false, columnDefinition="VARCHAR(MAX)")
        private String              tutorComments;
    //getters and setters below
    }
    

    There are other fields in there too like the user the report is linked to, the course, the tutor who wrote it etc. but I left them out for simplicity's sake here.

    public class ReportDTO implements Serializable
    {
    
    /**
     * 
     */
    private static final long   serialVersionUID    = 2795129355073929139L;
    
    private Integer             reportID;
    
    private String              dateCreated;
    
    private String              dateModified;
    
    private String              title;
    
    private String              currentPerformanceGrade;
    
    private String              targetGrade;
    
    private String              tutorComments;
    //getters and setters below
    }
    

    So the main differences are with the date objects have become date formatted strings as opposed to GregorianCalendar objects so that the front end display of the date is in a nice readable format. Here's an example of what the converting to DTO involves. A Single method in the service layer which takes the DAO object, gets the relevant fields from it, sets them in the newly constructed DTO object, converts as needed (e.g. Gregorian Calendar to date formatted String) and returns the DTO:

    public ReportDTO convertToDto(Report daoReport) throws Exception
    {
    
        ReportDTO dtoReport = new ReportDTO();
        try
        {
                        if(daoReport.getReportID() != null)
            {
                dtoReport.setReportID(daoReport.getReportID());
            }
                        if(daoReport.getDateCreated() != null)
            {
                dtoReport.setDateCreated(ReportServiceImpl.ISO_DATE_TIME_FORMAT.format(daoReport.getDateCreated().getTime()));
    
            }
    
            if(daoReport.getDateModified() != null)
            {
                 dtoReport.setDateModified(ReportServiceImpl.ISO_DATE_TIME_FORMAT.format(daoReport.getDateModified().getTime()));
    
            }
    
            if(daoReport.getTitle() != null)
            {
                dtoReport.setTitle(daoReport.getTitle());
    
            }
                         if(daoReport.getCurrentPerformanceGrade() != null)
            {
                  dtoReport.setCurrentPerformanceGrade(daoReport.getCurrentPerformanceGrade());
    
            }
    
            if(daoReport.getTargetGrade() != null)
            {
                dtoReport.setTargetGrade(daoReport.getTargetGrade());
    
            }
    
            if(daoReport.getTutorComments() != null)
            {
                dtoReport.setTutorComments(daoReport.getTutorComments());
    
            }
                        return dtoReport;
        }
        catch(Exception e)
        {
            Exception myException = new Exception("Exception was thrown while converting a persistent Report object to it's data transport equivalent", e);
            throw myException;
        }
    

    Question: So after all that, my question is, is this the correct way of converting from DAO to DTO? Since he left I had been following his code and anything new added was done in the same way. Returning the Objects to the front end without converting them I see the results in >300 milliseconds instead of 12 seconds.

    I know he learned Spring MVC from here for the project so he wasn't an experienced Spring Developer and neither am I and judging by the fact we're seeing such large request times we must be doing something wrong.