【问题标题】:Creating a New Custom Object vs Setting Multiple Values to Null of the Same Object in Java在 Java 中创建新的自定义对象与将多个值设置为 Null 的同一对象
【发布时间】:2023-03-20 12:45:02
【问题描述】:

所以,我遇到过这种情况

我有一个 Restful 控制器方法,它返回 list 对象 Match

@RequestMapping(value = "/fetchFootballMatchesToday", method = RequestMethod.GET)
public @ResponseBody List<Match> fetchMatchesScheduledToday() throws ParseException {
    return matchesToday;
   }

Match 对象有 15 个属性,我只需要 UI 上的 3 个属性。我不希望用户看到对象的其他属性。

有两种方法:

  1. 运行一个 for 循环并为每个 Match 对象显式设置属性为 null,这些对象并不打算发送到 UI。像这样

    List<Match> matchesToday = matchInvService.fetchMatchByDate(today);
    
    for (Match stats : matchesToday) {
     if (stats != null) {
    /* user should not know these details, so they are being set to null */
        stats.setMatchSuccessIndex(null);
        stats.setTeamRanking(null);
        stats.setStadiumInfrastructureLevel(null); 
        ..... & so on
      }
    }
    

这种方法的问题在于未来属性的数量会增加。此代码也需要更新。此外,如果对象的属性数量很大。我需要在这里多次设置 null。

  1. 第二种方法是在循环中创建一个新的 Match 对象并为其设置 3 个必需的值。

    List<Match> matchesToday = matchInvService.fetchMatchByDate(today);
    List<Match> responseList = new ArrayList<Match>();  
    
    for (Match stats : matchesToday) {
     if (stats != null) {
       Match tmpMatch = new Match();
       match.setProperty1(stats.getProperty1());
      }
     responseList.add(tmpMatch); 
    }
    
     return responseList;
    

每次循环运行时,这种方法都会创建额外的 Match 对象。此外,如果过于频繁地调用该特定方法,对象的创建也会激增。 尽管这些对象将被垃圾收集,但我不确定这是否是最佳方式。

需要你们的建议。这是编写更多代码与节省内存之间的权衡吗?解决这个问题的最佳方法是什么?

【问题讨论】:

  • Jersey 使用标准做法,即每个控制器方法都返回一个 Response 对象。这是一个包装器对象,其中包含实际的响应实体。您可以通过使用自定义地图来使用类似的方法。或具有键、值属性的自定义对象。然后,您可以只设置您关心的所需属性并作为响应发送。
  • 我假设Match也是一个数据库实体?
  • @22kar 但又一次,我需要创建那么多对象,然后将它们添加到列表中。你说的第二种方法对吗?
  • @Aris 是的
  • 查看下面的答案

标签: java spring performance


【解决方案1】:

对象 Match 有 15 个属性,我只需要 3 个属性 用户界面。我不希望用户看到其他属性 目的。

将所有字段设置为null 确实很麻烦且容易出错。
所以我会避免第一种方式。

第二种方法似乎朝着更好的方向发展。
但是您不应该害怕将对象映射到其他对象,因为客户端需要查看它们的特定视图。
“合理”列表大小的简单映射操作很便宜。而且我认为你不会在 UI 中显示数百万行,所以大小应该是合理的。
否则,您应该通过考虑分页概念来重新考虑整个 UI 设计。

我会使用第三个选项:创建一个类,声明客户端需要知道的 3 个属性并将Match 映射到这个类。它使事情变得更加清晰。

List<Match> matchesToday = matchInvService.fetchMatchByDate(today);
List<MatchDTO> responseList = new ArrayList<Match>();  

for (Match stats : matchesToday) {
 if (stats != null) {
    MatchDTO match = new MatchDTO(stats);
    responseList.add(match); 
  } 
}

return responseList;

其中MatchDTO(Match) 是一个构造函数,它从Match 实例复制三个所需字段:

public MatchDTO(Match match){
  this.foo = match.getFoo();
  this.bar = match.getBar();
  this.fooBar = match.getFooBar();
}

或在 Java 8 中:

List<MatchDTO> responseList =   
    matchInvService.fetchMatchByDate(today)
                   .stream()
                   .filter(Objects::nonNull)
                   .map(MatchDTO::new)
                   .collect(Collectors.toList);

【讨论】:

    【解决方案2】:

    您可以创建一个新的传输对象(一个简单的 POJO),您可以在其中定义要发送到 UI 的字段。

    这样您就可以将您的响应与实际的数据库实体分离。因此,从这个意义上说,您的服务层将根据您拥有的各种条件检索匹配项并将它们存储在一个列表中。

    然后您可以继续使用 Function 或 Spring 的 Converter 接口的实现将列表映射到新的传输对象(前面提到的 POJO)。

    因此,完成后,您的响应将包含一个传输对象列表,其中包含您要发送到 UI 的字段。

    【讨论】:

      【解决方案3】:

      如果原来的列表不再使用了。可以设置为null,迟早会被回收。我觉得考虑内存没什么意义。

      【讨论】:

        【解决方案4】:

        最好将查询限制为仅询问您需要的属性。

        所以不是matchInvService.fetchMatchByDate(today);而是返回整个Match 例如,您可以实现本机查询。此外,最好不要返回实体来返回 DTO。

        一个使用SqlResultSetMapping注解的小例子如下:

        MatchDTO match = (MatchDTO) em.createNativeQuery("select m.property1, m.property2, m.property3 from match where m.date=aDate", "MatchDTOMapping").getSingleResult();
        

        拥有

        class MatchDTO {
        
            private String property1
            private String property2;
            private String property3;
        
          //omit other attributes 
        
           public MatchDTO(String property1, String property2, String property3) {
              this.property1 = property1;
              this.property2 = property2;
              this.property3 = property3;
        
           }
        
        }
        

        注释

        @SqlResultSetMapping(name="MatchDTOMapping",
            classes="@ConstructorResult(
            targetClass= MatchDTO.class,
            columns = {@ColumnResult(name="property1"),
                       @ColumnResult(name="property2"),
                       @ColumnResult(name="property3")}))
        

        Here您还可以使用本机和 JPQL 查询找到解决方案。

        【讨论】:

          【解决方案5】:

          服务应该只回答所需的详细信息。 Java's GC 可以很好地处理不再需要的小对象的删除。所以我会继续创建代表所需状态的小对象并传递它们作为响应。例如

          return matchesToday.stream().map(MatchInfoResponse::new).collect(Collectors.toList());
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2021-11-04
            • 1970-01-01
            • 2013-09-19
            • 2011-10-13
            • 1970-01-01
            • 2023-01-08
            相关资源
            最近更新 更多