Как загрузить только указанные атрибуты в подграфе в @NamedEntityGraph

Я хочу загрузить объект UserReference из базы данных, но из атрибута verify я хочу загрузить только id, firstName и lastName, чтобы userReference выглядела примерно так:

{
  "id": 1,
  "company": "company1",
  "companyContactName": "some name",
  "companyPosition": "programmer",
  "referenceDate": "02/04/2005",
  "verifier": {
        "id":1
        "firstName": "Jane",
        "lastName": "Smith"
        "email":null,
        "username":null,
        "office:null,
        "department":null
  }
}

Я использовал граф сущностей для класса UserReference, но тот, который я использовал, загружает всю информацию, которая есть у пользователя, включая электронную почту, имя пользователя, офис и отдел. Есть ли способ указать что-то вроде EntityGraphType.FETCH для подграфа, чтобы он загружал только идентификатор, firstName и lastName для верификатора?

Это мой UserReferenceRepository:

public interface UserReferenceRepository extends JpaRepository<UserReference, Long>{

    @EntityGraph(value = "userReferenceGraph" ,  type = EntityGraphType.FETCH )
    UserReference findOne(Long id);
}

Класс UserReference:

@Getter
@Setter
@EqualsAndHashCode (exclude = {"id", "verifier"})
@ToString(exclude = {"id"})
@Entity
@NamedEntityGraphs({
    @NamedEntityGraph(
        name = "userReferenceGraph",      
        attributeNodes = {
            @NamedAttributeNode(value = "verifier", subgraph = "verifierGraph")
    },
    subgraphs = {
        @NamedSubgraph( 
            name = "verifierGraph",
            type = User.class,
            attributeNodes = {
                @NamedAttributeNode(value = "id"),
                @NamedAttributeNode(value = "firstName"),
                @NamedAttributeNode(value = "lastName")})})
})

public class UserReference {

    @Id
    @GeneratedValue
    private Long id;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id", referencedColumnName = "user_id", foreignKey = @ForeignKey (name = "FK_UserReference_UserHRDetails_user_id"))
    @JsonIgnore
    private UserHRDetails hrDetails;

    private String company;
    private String companyContactName;
    private String companyPosition;
    private Date referenceDate;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "verifier_id")
    private User verifier;
}

и пользователь:

@Getter @Setter
@EqualsAndHashCode(exclude = {"id", "department", "company", "authorities", "hrDetails"})
@ToString(exclude = {"password"})
@Entity
@AllArgsConstructor
@Builder
public class User implements Serializable{

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Access(value = AccessType.PROPERTY)
    private Long id;  

    @Size(max = 50)
    @Column(name = "first_name", length = 50)
    private String firstName;

    @Size(max = 50)
    @Column(name = "last_name", length = 50)
    private String lastName;

    @Column(length = 100, unique = true, nullable = false)
    private String email;

    @Column(length = 50, unique = true, nullable = false)
    private String username;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "department_id")
    private Department department;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "office_id")
    private Office office;
}

1 ответ

Я полагаю, вы используете Джексона для генерации JSON. В данном случае это битва Джексон против Entity Graph, и бывший не имеет шансов выиграть эту битву. Entity Graph - это просто подсказка для построения SQL-запроса, и вы можете только сказать Hibernate не загружать некоторые атрибуты. Hibernate по-прежнему не поддерживает графы сущностей при загрузке основных полей сущностей, см. https://hibernate.atlassian.net/browse/HHH-9270. Но главная проблема заключается в том, что Джексон будет вызывать каждый получатель в вашей сущности во время генерации JSON, а Hibernate будет лениво загружать их, не учитывая ваш Entity Graph. Я могу предложить только использование @JsonIgnore, но это может быть не так гибко, как вам нужно.

Я встретил ту же проблему, и я вижу 2 способа ее решения:

БЫСТРО: Вы можете выполнить какое-либо действие @PostLoad в своей сущности и обнулить то поле, которое вам не нужно.

@PostLoad
private void postLoad() {
    if (verifier != null) {
        verifier.email = null;
        verifier.office = null;
        verifier.department = null;
    }
}

ПРАВИЛЬНО: Другой способ - защитить ваши сущности, преобразовав их в DTO. Создайте отдельные POJO и преобразуйте ваши User и UserReference в эти классы DTO POJO. Там вы определенно будете иметь больше контроля над своим ответом.

Другие вопросы по тегам