java 在 Spring MVC 应用程序中将 DAO 对象转换为 DTO 对象

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14442639/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 16:16:56  来源:igfitidea点击:

Converting DAO objects to DTO objects in a Spring MVC Application

javaspringspring-mvcdaodto

提问by paddy

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.

背景:我在教育环境中工作,去年夏天,我们的一位开发人员使用 Spring MVC 和 Hibernate 设计并构建了一个 Java Web 应用程序。它在 9 月推出了新术语,因为它取代了尘土飞扬的旧 Blackboard 插件,因此深受用户欢迎。应用程序的主要功能用于为学生设定目标、给他们留言并为学生创建报告。

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.

经过调查,我想我已经找到了原因。我参加了一个示例课程并查看了报告以了解发生了什么。我发现数据库以毫秒为单位返回相关数据,浏览器以毫秒为单位呈现它,但浏览器等待从它返回的数据之间有 12 秒。在数据库查询完成和前端接收响应之间对对象所做的唯一事情是转换为 DTO。

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

代码:这是报表对象在DAO层的样子

@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:

因此,主要区别在于日期对象已成为日期格式的字符串,而不是 GregorianCalendar 对象,因此日期的前端显示是一种很好的可读格式。以下是转换为 DTO 所涉及的内容的示例。服务层中的 Single 方法接受 DAO 对象,从中获取相关字段,将它们设置在新构造的 DTO 对象中,根据需要进行转换(例如,公历转换为日期格式的字符串)并返回 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.

问题:毕竟,我的问题是,这是从 DAO 转换为 DTO 的正确方法吗?自从他离开后,我一直在遵循他的代码,任何新添加的东西都是以同样的方式完成的。将对象返回到前端而不转换它们我看到的结果 > 300 毫秒而不是 12 秒。

I know he learned Spring MVC from herefor 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.

我知道他从这里为项目学习了 Spring MVC,所以他不是一个有经验的 Spring 开发人员,我也不是,并且从我们看到如此大的请求时间来判断我们一定做错了什么。

采纳答案by paddy

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

好的,正如 beny23 提到的 Hibernate 是延迟加载的(最初加载 PK 列表,然后在对数据执行某些操作时加载其余部分)

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.

我使用的解决方案是创建一个非休眠连接来使用普通的 JDBC 连接读取数据,查询还转换了数据,以便它以我需要的格式返回(日期为字符串等),所以我没有t 必须转换为 dto。通过这种方式,我将一些工作卸载到数据库中,并为我的应用程序省去了做这件事的麻烦。

回答by Jerome Dalbert

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

这可能不是您问题的原因(12s 很大),但仍然值得一提。

(Simple)DateFormatclasses are not thread-safe :

简单DateFormat类不是线程安全的:

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)DateFormatjust before using it.

所以不要将它们存储在全局类属性中,否则你可能会遇到奇怪的问题。一个简单的事情就是在使用之前实例化一个 ( Simple) DateFormat

Also see this interesting blog postabout SimpleDateFormat.

另请参阅有关 SimpleDateFormat 的这篇有趣的博客文章