背景:我在教育环境中工作,去年夏天,我们的一位开发人员使用Spring MVC和Hibernate设计并构建了一个Java Web应用程序。它在九月推出了新术语,让用户感到非常高兴,因为它取代了一个尘土飞扬的旧Blackboard插件。应用程序的主要功能用于为学生设置目标,为学生留下信息和创建报告。
快进几个月,最初的开发人员继续前进,应用程序正在经历一些成长的痛苦。
用例场景:教师登录并显示其主屏幕,其中包含他们教授的课程列表,其中包含当前所选课程,消息和报告的概述课程以及课程的学生入学名单。如果课程包含少量目标等,则可以快速返回信息。但随着信息量的增长,加载信息的时间似乎呈指数级增长。
调查后我想我已经找到了原因。我参加了一个示例课程并看了一下Reports,看看发生了什么。我发现数据库以毫秒为单位返回相关数据,浏览器以毫秒为单位返回相关数据,但浏览器等待从中返回数据之间有12秒。对数据库查询完成和接收响应的前端之间的对象进行的唯一操作是转换为DTO。
代码:这是报告对象在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
}
还有其他字段,例如报告链接到的用户,课程,编写它的导师等等,但为了简单起见,我把它们留在了这里。
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
}
因此,主要区别在于日期对象已成为日期格式化字符串而不是GregorianCalendar对象,因此日期的前端显示采用了可读的格式。以下是转换为DTO所涉及的示例。服务层中的单个方法接受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;
}
问题:毕竟,我的问题是,这是从DAO转换为DTO的正确方法吗?自从他离开后,我一直在关注他的代码,所有新添加的内容都以同样的方式完成。将对象返回到前端而不进行转换我看到的结果大于300毫秒而不是12秒。
我知道他从here学习了Spring MVC这个项目所以他不是一个经验丰富的Spring开发者,也不是我和我们看到这么大的请求时间我们必须做错的事实判断
答案 0 :(得分:3)
好的,因为beny23提到Hibernate是懒惰加载(最初加载PK列表然后在对数据执行操作时加载其余的)
我使用的解决方案是创建一个非休眠连接,使用普通的JDBC连接读取数据,查询还转换了数据,使其以我需要的格式(日期为字符串等)返回,以便我没有必要转换成dto。这样我将一些工作卸载到数据库并保存我的应用程序这样做的麻烦。
答案 1 :(得分:1)
这可能不是你问题的原因(12秒很大),但仍然值得一说。
(Simple)DateFormat类不是线程安全的:
日期格式未同步。建议创建 每个线程的单独格式实例。如果有多个线程访问 同时格式化,必须在外部同步。
所以不要将它们存储在全局类属性中,否则你可能会遇到奇怪的问题。
一件简单的事情就是在使用之前实例化(Simple
)DateFormat
。
另请参阅有关SimpleDateFormat的this interesting blog post。