Получить с нетерпением коллекцию объектов, содержащих другие с нетерпением полученные коллекции

#jpa #entity-relationship #fetching-strategy

#jpa #сущность-отношение #выборка-стратегия

Вопрос:

Я застрял в отношении M: N между сущностью и строками. У пользователя может быть более одной роли, и каждая роль может быть назначена более чем одному пользователю. Роль — это просто строка. Роли содержатся в таблице с двумя столбцами: roleId и roleName .

Я создал две сущности, но я абсолютно не могу заставить их работать. Первый объект — это пользователь:

 @Entity
@Table(name="appUsers")
public class UserEntity {
    @Id
    private String login;
    private String password;
    @OneToMany(fetch=FetchType.EAGER,mappedBy="user") //we always need to load user's roles
    private Collection<UsersToRoles> roles;
    @Transient
    private Collection<String> roleNames;

    public String getLogin() {
        return login;
    }

    public String getPassword() {
        return password;
    }

    @PostLoad
    void prepareRoleNames() {
        roleNames = new HashSet<String>(roles.size());
        for (UsersToRoles mapping : roles)
            roleNames.add(mapping.getNameOfRole());
    }

    public Collection<String> getRoles() {
        return roleNames;
    }
}
  

Вторая — это объект, связанный с таблицей соединений:

 @Entity
@IdClass(UsersToRolesId.class)
public class UsersToRoles {
    @Id
    @SuppressWarnings("unused")
    @Column(name="login")
    private String login;
    @Id
    @SuppressWarnings("unused")
    @Column(name="roleId")
    private int roleId;
    @ElementCollection(fetch=FetchType.EAGER)
    @CollectionTable(name="userRoles", joinColumns={@JoinColumn(name="roleId")})
    private List<String> roleName;
    @ManyToOne
    @JoinColumn(name="login")
    @SuppressWarnings("unused")
    private UserEntity user;

    public String getNameOfRole() {
        if (roleName.isEmpty())
            throw new CommonError("Role name for roleId="   roleId, AppErrors.ACCESSOR_UNAVAILABLE);
        return roleName.get(0);
    }
}

class UsersToRolesId {
    private String login;
    private int roleId;

    /**
     * Implicit constructor is not public. We have to
     * declare public non-parametric constructor manually.
     */
    public UsersToRolesId() {
    }

    @Override
    public int hashCode() {
        return 17*login.hashCode()   37*roleId;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof UsersToRolesId))
            return false;
        UsersToRolesId ref = (UsersToRolesId)obj;
        return (this.login.equals(ref.login) amp;amp; this.roleId == ref.roleId);
    }
}
  

И проблема в том, что roleName коллекция всегда равна нулю. Я не могу заставить это работать. Когда я делаю ошибку в имени таблицы в @CollectionTable аннотации, она все еще работает. JPA вообще не извлекает вложенную коллекцию. Это позволяет выбрать из таблицы пользователя, соединенного с таблицей UsersToRoles , но соединение с таблицей userRoles отсутствует.

Смогу ли я когда-нибудь это сделать? Могу ли я получить нетерпеливую коллекцию объектов, содержащих другие нетерпеливо извлеченные коллекции?

Ответ №1:

Ваше отображение совершенно неверно. UsersToRoles имеет roleId столбец. Таким образом, это относится к одной роли. Как у него может быть коллекция имен ролей? Столбец login дважды отображается в сущности. Более того, для меня это выглядит как простая таблица соединений, без каких-либо других атрибутов, кроме RoleId и login, которые являются внешними ключами к идентификаторам пользователя и роли соответственно.

У вас должно быть две сущности: User и Role , с ManyToMany ассоциацией, использующей UsersToRoles таблицу в качестве таблицы соединений. Вот и все. Таблица UsersToRoles не должна отображаться как объект: это чистая таблица соединений.

Ответ №2:

Поставщики JPA обычно имеют свойство конфигурации, обозначающее глубину выборки по умолчанию, т. Е. hibernate.max_fetch_depth для режима гибернации. Проверьте, сможете ли вы увидеть больше, когда увеличите его.

Кроме того, подумайте о своем дизайне. Извлечение вложенных коллекций коллекции с нетерпением может быть хорошей идеей только в ограниченных сценариях (с точки зрения производительности). Когда вы аннотируете свою сущность таким образом, вы будете использовать нетерпеливую выборку во всех вариантах использования. Возможно, вам было бы лучше использовать «ленивый» и извлекать его только явно, с запросом с предложением JOIN FETCH ?