Solucionado (ver solução)
Solucionado
(ver solução)
6
respostas

Relacionamento - JPÀ @OneToMany - É um

Olá! É possível, criar uma relação de um campo da entidade com ela mesma? Seria uma relação de um para muitos sendo é um. Usuário tem vários contatos, um contato é um usuário. Poderia ser feito assim:

@Entity
public class Usuario{

@Id
@GeneratedValue
long int id;

@OneToMany(mappedBy = "id")
List<Usuario> contatos;
}

Ou seria necessário criar uma outra entidade, e qual seria o relacionamento? Desde já agradeço!

6 respostas

Eu acabei criando duas entidades

Entidade: Usuario

public class Usuario {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_usuario")
    private Long id;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "convidante", targetEntity = Amizade.class, fetch = FetchType.LAZY)
    private List<Amizade> convidantes;

    @OneToMany(cascade = CascadeType.ALL, mappedBy = "convidado", targetEntity = Amizade.class, fetch = FetchType.LAZY)
    private List<Amizade> convidados;
}

Entidade: Amizade

public class Amizade {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id_amizade")
    private Long id;

    @ManyToOne
    @JoinColumn(name = "id_convidante", nullable = false)
    private Usuario convidante;

    @ManyToOne
    @JoinColumn(name = "id_convidado", nullable = false)
    private Usuario convidado;
}

Porém não consigo realizar o insert corretamente e muitos menos consultas. O que estou fazendo de errado? A modelagem está certa? Como poderia corrigi-la?

solução!

Fala Diego, tudo bem ?

É possível sim fazer mapeamentos para a mesma entidade quando for necessário. Fiz um teste aqui pra poder exemplificar.

Modelo:

@Entity
public class Usuario {

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

    private String nome;

    @OneToMany
    private List<Usuario> contatos;

    /**
     * @deprecated
     */
    public Usuario() {
    }

    public Usuario(String nome) {
        this.nome = nome;
    }

    //getters and setters

    public void adicionaContato(Usuario usuario) {
        this.contatos.add(usuario);
    }

}

Repository:

@Repository
public class UsuarioDao {

    @PersistenceContext
    private EntityManager manager;

    public void save(Usuario usuario) {
        manager.persist(usuario);
    }

    public Usuario buscaPorId(Integer id) {
        return manager.find(Usuario.class, id);
    }

    public List<Usuario> buscaContatoDoUsuario(int id) {
        String jpql = "select contatos from Usuario u join u.contatos contatos where u.id = :id";
        return manager.createQuery(jpql, Usuario.class)
            .setParameter("id", id)
            .getResultList();
    }

}

Controller pra teste:

@Controller
public class UsuarioController {

    @Autowired
    private UsuarioDao usuarioDao;

    @GetMapping("/teste/adicao/usuarios")
    @ResponseBody
    @Transactional
    public String adicionaUsuarios() {

        List<Usuario> list = Arrays.asList(
                new Usuario("Usuário 1"),
                new Usuario("Usuário 2"),
                new Usuario("Usuário 3"),
                new Usuario("Usuário 4"));

        list.stream().forEach(usuarioDao::save);
        return "Usuarios adicionados";
    }

    @GetMapping("teste/adicao/contatos")
    @ResponseBody
    @Transactional
    public String adicionaContato() {

        Usuario usuario1 = usuarioDao.buscaPorId(1);
        Usuario usuario2 = usuarioDao.buscaPorId(2);
        Usuario usuario3 = usuarioDao.buscaPorId(3);
        Usuario usuario4 = usuarioDao.buscaPorId(4);

        //usuario 1 adiciona os demais como contatos
        usuario1.adicionaContato(usuario2);
        usuario1.adicionaContato(usuario3);
        usuario1.adicionaContato(usuario4);
        //como objetos estao gerenciados pela JPA no fim desta transação a alteraçao será refletida na base

        return "Contatos adicionados ao usuario1";
    }

    @GetMapping("/teste/query/contatos")
    @ResponseBody
    public List<Usuario> buscaContatosDeUmUsuario() {

        return usuarioDao.buscaContatoDoUsuario(1);
    }
}
  • Subindo a app temos a seguinte representação no banco:
mysql> desc usuario;
+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(11)      | NO   | PRI | NULL    | auto_increment |
| nome  | varchar(255) | YES  |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+
2 rows in set (0.01 sec)

mysql> desc usuario_contatos
    -> ;
+-------------+---------+------+-----+---------+-------+
| Field       | Type    | Null | Key | Default | Extra |
+-------------+---------+------+-----+---------+-------+
| usuario_id  | int(11) | NO   | MUL | NULL    |       |
| contatos_id | int(11) | NO   | PRI | NULL    |       |
+-------------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)
  • Acessando primeiro endpoint /teste/adicao/usuarios
mysql> select * from usuario;
+----+------------+
| id | nome       |
+----+------------+
|  1 | Usuário 1  |
|  2 | Usuário 2  |
|  3 | Usuário 3  |
|  4 | Usuário 4  |
+----+------------+
4 rows in set (0.00 sec)
  • Acessando endpoint que testa inserção dos usuarios como contato /teste/adicao/contatos
mysql> select * from usuario_contatos;
+------------+-------------+
| usuario_id | contatos_id |
+------------+-------------+
|          1 |           2 |
|          1 |           3 |
|          1 |           4 |
+------------+-------------+
3 rows in set (0.00 sec)
  • Por fim acessando endpoint que busca contatos de um usuario /teste/query/contatos
[{
    "id": 2,
    "nome": "Usuário 2",
    "contatos": []
}, {
    "id": 3,
    "nome": "Usuário 3",
    "contatos": []
}, {
    "id": 4,
    "nome": "Usuário 4",
    "contatos": []
}]

Espero ter ajudado. Abraço!

Rafael Rollo, cara, não tenho palavras, muito obrigado mesmo... era o que eu estava pensando, porque a proposta do JPA é transformar os relacionamentos das entidades em orientação à objetos, na qual seu exemplo acima mostra isso ocorrendo de fato, essa transição. Muito obrigado, muito mesmo!

Opa Diego,

De nada! Que bom que ajudou.

Abraço!

Olá Rafael,

to tendo um problema. Só consigo salvar um registro na tabela de relacionamento. Será que a relação no caso seria muitos pra muitos?

Boa Diego,

Isso mesmo. OneToMany não vai deixar cadastrar um contato a mais de um usuário.

ManyToMany deve resolver.