Anotação Hibernate @WhereJoinTable

Anotação Hibernate @WhereJoinTable

1. Visão geral

O uso de uma ferramenta Mapeamento Relacional a Objetos, como o Hibernate, facilita a leitura de nossos dados em objetos, mas pode dificultar a formação de nossas consultas com modelos de dados complexos.

O relacionamento de muitos para muitos é sempre desafiador, mas pode ser mais desafiador quando desejamosacquire related entities based on some property of the relation itself.

Neste tutorial, veremos como resolver este problema usando a anotação@WhereJoinTable do Hibernate.

2. Relação@ManyToMany básica

Vamos começar com um simples@ManyToMany relationship. Precisaremos de entidades de modelo de domínio, uma entidade de relação e alguns dados de teste de amostra.

2.1. Modelo de domínio

Vamos imaginar que temos duas entidades simples,UsereGroup, que estão associadas como@ManyToMany:

@Entity
public class User {

    @Id
    @GeneratedValue
    private Long id;
    private String name;

    @ManyToMany
    private List groups = new ArrayList<>();

    // standard getters and setters

}
@Entity
public class Group {

    @Id
    @GeneratedValue
    private Long id;
    private String name;

    @ManyToMany(mappedBy = "groups")
    private List users = new ArrayList<>();

    // standard getters and setters

}

Como podemos ver, nossa entidadeUser pode ser membro de mais de uma entidadeGroup. Da mesma forma, uma entidadeGroup pode conter mais de uma entidadeUser.

2.2. Entidade de relação

For @ManyToMany associations, we need a separate database table called a relation table. A tabela de relação precisa conter pelo menos duas colunas: As chaves primárias das entidadesUsereGroup relacionadas.

Com apenas as duas colunas de chave primária,our Hibernate mapping can represent this relation table.

No entanto, se precisarmos colocar dados adicionais na tabela de relação,we should also define a relation entity for the many-to-many relationship itself.

Vamos criar a classeUserGroupRelation para fazer isso:

@Entity(name = "r_user_group")
public class UserGroupRelation implements Serializable {

    @Id
    @Column(name = "user_id", insertable = false, updatable = false)
    private Long userId;

    @Id
    @Column(name = "group_id", insertable = false, updatable = false)
    private Long groupId;

}

Aqui, nomeamos a entidader_user_group para que possamos referenciá-la mais tarde.

Para nossos dados extras, digamos que queremos armazenar todas as funções deUser para cadaGroup. Então, vamos criar a enumeraçãoUserGroupRole:

public enum UserGroupRole {
    MEMBER, MODERATOR
}

A seguir, adicionaremos uma propriedaderole aUserGroupRelation:

@Enumerated(EnumType.STRING)
private UserGroupRole role;

Finalmente, para configurá-lo corretamente, precisamos adicionar a anotação@JoinTable na coleçãoUser'sgroups. Aqui, especificaremos o nome da tabela de junção usandor_user_group, o nome da entidadeUserGroupRelation:

@ManyToMany
@JoinTable(
    name = "r_user_group",
    joinColumns = @JoinColumn(name = "user_id"),
    inverseJoinColumns = @JoinColumn(name = "group_id")
)
private List groups = new ArrayList<>();

2.3. Dados de amostra

Para nossos testes de integração, vamos definir alguns dados de amostra:

public void setUp() {
    session = sessionFactory.openSession();
    session.beginTransaction();

    user1 = new User("user1");
    user2 = new User("user2");
    user3 = new User("user3");

    group1 = new Group("group1");
    group2 = new Group("group2");

    session.save(group1);
    session.save(group2);

    session.save(user1);
    session.save(user2);
    session.save(user3);

    saveRelation(user1, group1, UserGroupRole.MODERATOR);
    saveRelation(user2, group1, UserGroupRole.MODERATOR);
    saveRelation(user3, group1, UserGroupRole.MEMBER);

    saveRelation(user1, group2, UserGroupRole.MEMBER);
    saveRelation(user2, group2, UserGroupRole.MODERATOR);
}

private void saveRelation(User user, Group group, UserGroupRole role) {

    UserGroupRelation relation = new UserGroupRelation(user.getId(), group.getId(), role);

    session.save(relation);
    session.flush();
    session.refresh(user);
    session.refresh(group);
}

Como podemos ver,user1 euser2 estão em dois grupos. Além disso, devemos notar que enquantouser1 éMODERATOR emgroup1, ao mesmo tempo ele tem uma funçãoMEMBER emgroup2.

3. Buscando@ManyToMany Relações

Agora que configuramos nossas entidades corretamente, vamos buscar os grupos da entidadeUser.

3.1. Busca Simples

Para buscar grupos, podemos simplesmente invocar o métodogetGroups() deUser dentro de uma sessão ativa do Hibernate:

List groups = user1.getGroups();

Nossa saída paragroups será:

[Group [name=group1], Group [name=group2]]

Mas como podemos obter os grupos de um usuário cuja função de grupo é apenasMODERATOR?

3.2. Filtros personalizados em uma entidade de relação

Podemosuse the @WhereJoinTable annotation to directly acquire only filtered groups.

Vamos definir uma nova propriedade comomoderatorGroupse colocar a anotação@WhereJoinTable nela. Quando acessamos as entidades relacionadas por meio desta propriedade, ela conterá apenas grupos dos quais nosso usuário éMODERATOR.

Precisaremos adicionar uma cláusula SQL where para filtrar os grupos pela funçãoMODERATOR:

@WhereJoinTable(clause = "role='MODERATOR'")
@ManyToMany
@JoinTable(
    name = "r_user_group",
    joinColumns = @JoinColumn(name = "user_id"),
    inverseJoinColumns = @JoinColumn(name = "group_id")
)
private List moderatorGroups = new ArrayList<>();

Assim, podemos facilmente obter os grupos com a cláusula where SQL aplicada:

List groups = user1.getModeratorGroups();

Nossa saída serão os grupos nos quais o usuário tem apenas a função deMODERATOR:

[Group [name=group1]]

4. Conclusão

Neste tutorial, aprendemos comofilter @ManyToMany collections based on a property of the relation table using Hibernate’s @WhereJoinTable annotation.

Como sempre, todos os exemplos de código fornecidos neste tutorial estão disponíveisover on GitHub.