Obtenga con entusiasmo la colección de entidades que contienen otras colecciones con entusiasmo.

Me he quedado atascado en la relación M: N entre entidad y cadenas. Un usuario puede tener más de un rol y cada rol puede asignarse a más de un usuario. El rol es solo una cuerda. Los roles están contenidos en la tabla con dos columnas: roleId y roleName .

He creado dos entidades, pero no puedo hacer que funcione. La primera entidad es el usuario:

@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 roles;
    @Transient
    private Collection roleNames;

    public String getLogin() {
        return login;
    }

    public String getPassword() {
        return password;
    }

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

    public Collection getRoles() {
        return roleNames;
    }
}

El segundo es la entidad asociada con la tabla de conexión:

@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 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) && this.roleId == ref.roleId);
    }
}

Y el problema es que la colección roleName siempre es nula. No puedo conseguir que funcione. Cuando cometo un error en el nombre de la tabla en la anotación @CollectionTable , todavía funciona. La JPA no recupera la subcolección en absoluto. Hace una selección de la tabla de usuarios unidos con la tabla UsersToRoles , pero falta la unión a la tabla userRoles .

¿Puedo hacer eso alguna vez? ¿Puedo obtener con entusiasmo la colección de entidades que contienen otras colecciones que se buscan con entusiasmo?

1

2 Respuestas

Tu mapeo está completamente equivocado. UsersToRoles tiene una columna roleId . Por lo tanto, se refiere a un solo papel. ¿Cómo podría tener una colección de nombres de roles? La columna de inicio de sesión se asigna dos veces en la entidad. Además, esto parece una simple tabla de unión para mí, sin ningún otro atributo que roleId y login, que son claves foráneas para los ID de usuario y rol, respectivamente.

Debe tener dos entidades: User y Role , con una asociación ManyToMany utilizando la tabla UsersToRoles como tabla de unión. Eso es. La tabla UsersToRoles no debe asignarse como una entidad: es una tabla de unión pura.

1
agregado

Los proveedores de JPA generalmente tienen una propiedad de configuración que denota la profundidad de búsqueda de deseos predeterminada, es decir, hibernate.max_fetch_depth para Hibernate. Compruebe si puede ver más cuando lo aumenta.

Además, piensa en tu diseño. La obtención de subcolecciones de una colección con entusiasmo podría ser una buena idea solo en escenarios limitados (en cuanto al rendimiento). Cuando realice anotaciones en su entidad de esa manera, utilizará la búsqueda ansiosa en todos los casos de uso. ¿Tal vez estaría mejor con "perezoso" y obtenerlo con entusiasmo solo explícitamente, con una consulta con una cláusula JOIN FETCH?

1
agregado