【问题标题】:Setting the value with condition inside for each applying to all items instead of just 1为每个应用于所有项目而不是仅设置 1 的条件设置值
【发布时间】:2016-04-28 17:02:15
【问题描述】:

我在这里有一个非常奇怪的问题,我在列表中的对象内对列表进行迭代的问题,并且有一个条件,问题是第二次迭代有条件设置值,但它甚至适用于所有项目如果条件为假。

我有以下课程:

EventFeedKey(用作 hashmap 的键):

public class EventFeedKey {
    private final int eventId;
    private final int triggerId;

    public EventFeedKey(int eventId, int triggerId) {
        this.eventId = eventId;
        this.triggerId = triggerId;
    }

    @Override
    public boolean equals(Object object) {
        if (!(object instanceof EventFeedKey)) {
            return false;
        }

        EventFeedKey otherKey = (EventFeedKey) object;
        return this.eventId == otherKey.eventId && this.triggerId == otherKey.triggerId;
    }

    @Override
    public int hashCode() {
        int result = 17; // any prime number
        result = 31 * result + Integer.valueOf(this.eventId).hashCode();
        result = 31 * result + Integer.valueOf(this.triggerId).hashCode();
        return result;
    }
}

事件:

@Entity
@Table(name = "events")
public class Event {
    @Id
    @Column
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column(nullable = false)
    private String title;

    @ManyToOne(optional = false)
    @JoinColumn(name = "type_id")
    private EventType type;

// Setters and Getters
}

事件类型:

@Entity
@Table(name = "event_types")
public class EventType {
    @Id
    @Column
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column(nullable = false)
    private String title;

    @OneToMany(fetch = FetchType.LAZY, mappedBy = "eventType", cascade = CascadeType.ALL)
    @JsonManagedReference  
    private List<EventTypeTrigger> triggers = new ArrayList<>();

// Setters and Getters
}

事件类型触发器:

@Entity
@Table(name = "event_type_triggers")
public class EventTypeTrigger  {
    @Id
    @Column
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column(nullable = false)
    private String title;

    @Transient
    private int count;

// Setters and Getters
}

以下必须返回列表和由triggersFeed 中的计数值填充的触发器。

    List<Event> eventList = new ArrayList<Event>();
    if (eventList != null) {
        HashMap<EventFeedKey, Integer> triggersFeed = getTriggersWithCounts();
        eventList.stream().forEach(event -> {
            event.getType().getTriggers().stream().forEach(eventTypeTrigger -> {
                EventFeedKey key = new EventFeedKey(event.getId(), eventTypeTrigger.getId());
                eventTypeTrigger.setCount(triggersFeed.get(key) != null ? (Integer) triggersFeed.get(key) : 0);
            });
        });
        return eventList;
    }

getTriggersWithCounts() 内部:

public HashMap<EventFeedKey, Integer> getTriggersWithCounts() {
    HashMap<EventFeedKey, Integer> eventTriggers = new HashMap<>();
    // event id : 1, trigger id 7
    eventTriggers.put(new EventFeedKey(1,7), 5);
    return eventTriggers;
}

假设我有以下数据:

对于eventList

[  
   {  "id":1, "title":"Team A vs Team X", 
      "type":{  
         "id":3,
         "title":"Baseball",
         "triggers":[  
            {  
               "id":7,
               "title":"Base Reached",
               "count":0
            },
            {  
               "id":8,
               "title":"Out",
               "count":0
            }
         ]
      }
   },
      {  
      "id":2, "title":"Team A vs Team C",
      "type":{  
         "id":3,
         "title":"Baseball",
         "triggers":[  
            {  
               "id":7,
               "title":"Base Reached",
               "count":0
            },
            {  
               "id":8,
               "title":"Out",
               "count":0
            }
         ]
      }
   },
]

我从该方法得到的结果(这是错误的)是:

[  
   {  "id":1, "title":"Team A vs Team X", 
      "type":{  
         "id":3,
         "title":"Baseball",
         "triggers":[  
            {  
               "id":7,
               "title":"Base Reached",
               "count":5
            },
            {  
               "id":8,
               "title":"Out",
               "count":0
            }
         ]
      }
   },
      {  
      "id":2, "title":"Team A vs Team C",
      "type":{  
         "id":3,
         "title":"Baseball",
         "triggers":[  
            {  
               "id":7,
               "title":"Base Reached",
               "count":5
            },
            {  
               "id":8,
               "title":"Out",
               "count":0
            }
         ]
      }
   },
]

最烦人的是,如果我添加了这个:

if (triggersFeed.get(key) != null) {
    System.out.print(triggersFeed.get(key).toString());
}

它只会打印一个结果!

所以问题是:为什么修改了id:2 的事件?

编辑: 我认为这个问题现在与 Ebean 相关:http://ebean-orm.github.io/ 我在 Playframework 中使用。

【问题讨论】:

  • 您是如何构建数据的?我的感觉是,您在两个 Event 实例中都有指向相同 EventType 的指针,当您从一个事件更新它时,它是另一个 Event 中存在的同一个对象
  • @ArturBiesiadowski 这些是我从数据库中获得的实体,我还有另一个列表,但在 1 级(不是嵌套循环)中没有这样的问题。我在考虑指针,但在调试中我没有看到这个。
  • 我执行了您提供的示例(手动构造了每个输入事件)并且输出完全符合预期(仅修改了事件 id=1 的 EventTypeTrigger id=7)。几乎可以肯定,您可能会遇到两个事件中使用相同 EventTypeTrigger 对象的问题。
  • 好吧,调试对象的问题看起来如此不同,甚至没有意义,有实体,我正在使用带有 ebean 的播放框架
  • @ArturBiesiadowski 谢谢,我明白了,你说的提示让我照顾 ebean 框架,这是问题所在,ebean 是引用相同的实体实例,它们具有相同的“id” ,我不确定这是否是错误,但这是问题所在。

标签: java for-loop playframework ebean


【解决方案1】:

首先,感谢@ArturBiesiadowski 的提示,问题只是来自引用实体实例的 Ebean,因为它们具有相同的 @Id ,所以即使我设置了 1 个实体的值,但是当我得到实体时,我得到了同样的实例,即使我没有保留该实体,并且在我检索列表之前它们是不同的,但是当我在修改后取回列表时,Ebean 似乎使用 getter 获得了修改后的实体。

解决方案只是使用另一个对象(如 DTO),我之前已经拥有它们,但是我修改了列表,然后返回了 DTO,我所做的是将列表转换为 DTO,然后修改了列表,这使它起作用了。

我使用http://mapstruct.org/在实体和DTO之间进行映射,我的DTO类与Entity类相同,但没有注释。

EventDTO:

public class EventDTO {
    private Integer id;
    private String title;
    private EventTypeDTO type;

// Setters and Getters
}

EventTypeDTO:

public class EventTypeDTO {
    private Integer id;
    private String title;
    private List<EventTypeTriggerDTO> triggers = new ArrayList<>();

// Setters and Getters
}

EventTypeTriggerDTO:

public class EventTypeTriggerDTO  {
    private Integer id;
    private String title;
    private int count;

// Setters and Getters
}

现在的代码是:

    List<Event> eventList = new ArrayList<Event>();

    if (eventList != null) {
        List<EventDTO> eventDTOList = new ArrayList<>(EventMapper.INSTANCE.map(eventList));
        HashMap<EventFeedKey, Integer> triggersFeed = getTriggersWithCounts();
        eventList.stream().forEach(event -> {
            event.getType().getTriggers().stream().forEach(eventTypeTrigger -> {
                EventFeedKey key = new EventFeedKey(event.getId(), eventTypeTrigger.getId());
                eventTypeTrigger.setCount(triggersFeed.get(key) != null ? (Integer) triggersFeed.get(key) : 0);
            });
        });
        return eventDTOList;
    }

当然,您需要访问 MapStruct 文档以获取 Mappers,但如果您不想使用 mapstruct 的 Mapper,那么这个想法只是将实体类映射到 DTO 类,请使用您想要的任何 Mapper,但我确定是 MapStruct 在这种情况下运行良好。

【讨论】:

    猜你喜欢
    • 2019-09-11
    • 1970-01-01
    • 1970-01-01
    • 2019-07-18
    • 1970-01-01
    • 2016-03-28
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    相关资源
    最近更新 更多