【问题标题】:Is it possible to replace or modify values from projections or anonymous classes?是否可以替换或修改投影或匿名类的值?
【发布时间】:2020-04-25 11:39:55
【问题描述】:

在我的应用程序中,我使用projections 将*Entity 对象映射到数据库中实际记录的简化或修改版本。

但是,我有一个特殊的用例,我需要从其中一个嵌套投影中替换某个值。由于这些是接口并且也被 Spring 代理,我不确定我想要的是否真的可能,但将其归结为一个非常简单的示例:

假设我有一个UserEntity 和一个User 投影。对于我的User 投影,我可以简单地执行:

User user = this.userEntityRepository.findById(userId);

但是,如果我想更改某些内容,我不确定这是否可行。也就是说,我不能这样做:

if (user.getAge() < 18) {
  user.setDisplayName(null);
}

现在,我知道我可以创建一个匿名类 new User() { .. } 并传入我需要的值,但在我的情况下,对象是嵌套的,因此这不是一个选项。

问题

是否有另一种方法来替换值,例如displayName 同上,不使用匿名类?


详尽的例子

阅读以下内容并不是真正必要的,但为了更详细地说明我的问题,我已经伪编码了一个示例,该示例更接近于我的特定情况下的问题。

我们有一个简单的UserEntity:

@Entity
@Table(name = "app_user")
public class UserEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @Column
  private String firstName;

  @Column 
  private String lastName;

  @Column 
  private Integer age;

  // Setter & Getter ..
}
@Entity
@Table(name = "event")
public class EventEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @OneToMany(mappedBy = "event")
  private List<EventAttendeeEntity> attendees;

  // ..
}

我们有一个将用户映射到事件的表:

@Entity
@Table(name = "attendee")
public class AttendeeEntity {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private Long id;

  @ManyToOne
  private EventEntity event;

  @ManyToOne
  private UserEntity user;


  // ..
}

此外,我们对这些实体进行了预测,并为我们的客户准备了视图:

/*
 * Projection for User
 */
public interface User {
  // All the properties ..  
}
/*
 * Projection for AttendeeEntity
 */
public interface Attendee {

  Long getId();

  User getUser();

}
/*
 * Projection for EventEntity
 */
public interface Event {

  Long getId();

  String getName();

  List<Attendee> getAttendees();

}

在我们获取UserEvent 的一项服务中。在这里,假设我们要删除所有 18 岁以下用户的姓名,仍然返回我们刚刚获取的 userEvent。

public Event getEvent(Long id, Boolean anonymize) {

  Event event = this.eventRepository.findById(id);

  // The "anonymize" is to highlight that I cannot 
  // simply solve this in a User-projection
  if (!anonymize) {
    return event;
  }

  event
    .getAttendees();
    .stream()
    .peek(attendee -> {

       User user = attendee.getUser();

       if(user.getAge() < 18) {

         // Here we create a new user object without a name

         User newUser = new User() {
            @Override
            String getDisplayName() { return null; }
            @Override
            Integer getAge() { return user.getAge(); }
         }

         // !! This is where we hit the problem since we cannot
         // !! replace the old user object like this
         attendee.setUser(newUser);
       }        
     });

  return event;
}

【问题讨论】:

  • 我不认为这是可能的。如果可能,那么接口和实现类之间没有区别
  • @Eklavya 恐怕是这样。我真的开始后悔去预测了。它们适用于简单的东西,例如“按原样”获取实体,但当它变得更复杂时,它们就不是一种选择。例如。现在我有一个项目列表,我想确定用户喜欢哪些项目。我认为通过预测无法做到这一点。

标签: java spring-boot spring-data-jpa spring-data


【解决方案1】:

一种解决方案是在投影选择器中使用 SPEL。请尝试

public interface Attendee {

  Long getId();

  @Value("#{target.user.age >= 18 ? target.user : new your.package.UserEntity()}")
  User getUser();

}

将 you.package 替换为 UserEntity 的包。注意放置 new UserEntity() 而不是 new User()。这样一个空模型将被投影为一个空界面用户。

【讨论】:

  • 不幸的是,这不是我的选择。在我的实际示例中,我实际上需要将一个投影属性替换为另一个。我意识到我的问题不是很清楚。
  • 我相应地更新了代码。现在应该更清楚为什么 SPEL 不是一个选项。
  • 我仍然确认可以使用spel。在您更新代码后,我修改了我的答案。请立即重试。
  • 现在它只是一个对象,所有内容都是null。如果我还需要UserEntity 的年龄怎么办?
  • 并且不能将其传递给构造函数。我的实际示例比这要复杂一些。为了更接近我的示例:假设您想删除名称并为所有 18 岁以下的用户设置一个随机年龄。恐怕这更接近我的要求。跨度>
【解决方案2】:

您不能使用投影来更新您的代码。

最后一点,请务必记住,投影和摘录仅用于只读目的。

API Data Rest Projections Baeldung

【讨论】:

  • Alink 可能会在此处提供问题的解决方案,但建议您使用您自己的描述正确发布答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-20
相关资源
最近更新 更多