Differences @Onetomany, @Manytomany, @Manytoone, @Onetoone

Asked

Viewed 27,253 times

15

I wanted to build a database with Java Hibernate as below:

Desenho do Banco

Doubts:

  • What’s the difference between @OneToMany, @ManyToMany, @ManyToOne, @OneToOne? And what would your statement look like in Java? Example, it has to be List<Objeto>.

  • I’m thinking of using the @OneToOne, an example in the evaluation, the user would be blocked from performing a new evaluation?

1 answer

24


Unidirectional vs bidirectional mapping

First of all, it should be noted that each of the relationships @OneToOne, @OneToMany, @ManyToOne and @ManyToMany may be unidirectional or bidirectional.

In the unidirectional relationship between two entities A and B, starting with entity A, I come easily to an instance of entity B, but I cannot easily do the opposite.

Already in the bi-directional relationship, I can also from the entity B, easily navigate back to the entity A.

Mapping @ManyToOne unidirectional

The @ManyToOne means many-to-1. In this your example (I will assume that the field categoria on the table evento should be called categoria_id), we would have it:

@Entity
@Table(name = "evento")
public class Evento {

    // ... Outros campos ...

    @ManyToOne
    @JoinColumn(name = "categoria_id")
    private Categoria categoria;

    // ... Outros campos e métodos ...

    public Categoria getCategoria() {
        return categoria;
    }
}

The side Many is the class that involves it all, in case Evento. The side One is that of the related entity, in the case of Categoria. That is, many events for a category. The same rule also applies to @ManyToOne, @OneToOne and @ManyToMany (we will see more about them below).

This is because an event has only one category, but a category can have many events. With this mapping, we can do this:

Evento e = ...;
Categoria c = e.getCategoria();

Mapping @OneToMany unidirectional

The @OneToMany is the opposite of @ManyToOne, ie is the 1-to-many. For example, we could do this:

@Entity
@Table(name = "categoria")
public class Categoria {

    // ... Outros campos ...

    @OneToMany
    @JoinColumn(name = "categoria_id") // Esta coluna está na tabela "evento".
    private List<Evento> eventos;

    // ... Outros campos e métodos ...

    public List<Evento> getEventos() {
        return eventos;
    }
}

With this mapping, we can do this:

Categoria c = ...;
List<Evento> eventos = c.getEventos();

Mappings @OneToMany and @ManyToOne bidirectional

If you have the above two cases at the same time, where from Evento I arrive in category and from Categoria I arrive in Evento, the result will be that the mapping will go wrong. Why? Because JPA will see two different mappings, one of them Evento for Categoria and a different mapping of Categoria for Evento. It turns out that these two mappings are one!

That’s where the field comes in mappedBy:

@Entity
@Table(name = "evento")
public class Evento {

    // ... Outros campos ...

    @ManyToOne
    @JoinColumn(name = "categoria_id")
    private Categoria categoria;

    // ... Outros campos e métodos ...

    public Categoria getCategoria() {
        return categoria;
    }
}

@Entity
@Table(name = "categoria")
public class Categoria {

    // ... Outros campos ...

    @OneToMany(mappedBy = "categoria")
    private List<Evento> eventos;

    // ... Outros campos e métodos ...

    public List<Evento> getEventos() {
        return eventos;
    }
}

In this bi-directional relationship, the mappedBy says that the other side of the relationship that owns it and that the field that shapes it is called categoria. Note that this is the field name of the Java class, not the field name in the database!

In general, it is recommended that the side of the relationship end with the toOne is the owner of the relationship.

It’s important in two-way relationships, to always connect the two sides of the relationship before persisting in the EntityManager:

Evento e = ...;
Categoria c = ...;
e.setCategoria(c);
c.eventos.add(e);

Mapping @OneToOne

If you use the @OneToOne You model case 1-to-1. You can make an evaluation belong to only one person, but in this type of relationship, you also have that one person can only have an evaluation.

You would do it like this:

@Entity
@Table(name = "avaliacao")
public class Avaliacao {

    // ... Outros campos ...

    @OneToOne
    @JoinColumn(name = "pessoa_id")
    private Pessoa pessoa;

    // ... Outros campos e métodos ...

    public Pessoa getPessoa() {
        return pessoa;
    }
}

With that, you can do it:

Avaliacao a = ...;
Pessoa avaliado = a.getPessoa();

To do otherwise, it is necessary that the relationship is bidirectional:

@Entity
@Table(name = "pessoa")
public class Pessoa {

    // ... Outros campos ...

    @OneToOne(mappedBy = "pessoa")
    private Avaliacao avaliacao;

    // ... Outros campos e métodos ...

    public Avaliacao getAvaliacao() {
        return avaliacao;
    }
}

And then, having the two-way relationship:

Pessoa p = ...;
Avaliacao a = p.getAvaliacao();

Again, in the case of two-way relationships, one should always connect the two sides of the relationship before persisting in the EntityManager:

Pessoa p = ...;
Avaliacao a = ...;
a.setPessoa(p);
p.setAvaliacao(a);

Mapping @ManyToMany

Your diagram has no case where a many-to-many relationship exists. So let’s come up with a:

One type of pizza have several types of ingredients.
One type of ingredient can be part of several types of pizza.

And let’s assume we have the table pizza, the table ingrediente and an intermediate table pizza_ingrediente, where each row contains the key of the other two tables.

@Entity
@Table(name = "pizza")
public class Pizza {

    // ... Outros campos ...

    @ManyToMany
    @JoinTable(
        name = "pizza_ingrediente",
        joinColumns = @JoinColumn(name = "pizza_id"),
        inverseJoinColumns = @JoinColumn(name = "ingrediente_id"),
    )
    private List<Ingrediente> ingredientes;

    // ... Outros campos e métodos ...

    public List<Ingrediente> getIngredientes() {
        return ingredientes;
    }
}

The annotation @JoinTable is responsible for mapping the intermediate table. The joinColumns represents the side of the entity that owns the relationship (Pizza) and the inverseJoinColumns the side of the related entity (Ingrediente). With all this, it is possible then to do this:

Pizza p = ...;
List<Ingrediente> ingredientes = p.getIngredientes();

To make the relationship bi-directional, again we have the mappedBy:

@Entity
@Table(name = "ingrediente")
public class Ingrediente {

    // ... Outros campos ...

    @ManyToMany(mappedBy = "ingredientes")
    private List<Pizza> pizzas;

    // ... Outros campos e métodos ...

    public List<Pizza> getPizzas() {
        return pizzas;
    }
}

And then we can do that too:

Ingrediente i = ...;
List<Pizza> pizzas = i.getPizzas();

And again, we have to remember to relate the two sides:

Ingrediente mussarela = ...;
Ingrediente tomate = ...;
Ingrediente presunto = ...;
Ingrediente ovo = ...;

Pizza napolitana = ...;
Pizza portuguesa = ...;

napolitana.ingredientes.add(mussarela);
napolitana.ingredientes.add(tomate);
napolitana.ingredientes.add(presunto);

portuguesa.ingredientes.add(mussarela);
portuguesa.ingredientes.add(ovo);
portuguesa.ingredientes.add(presunto);

mussarela.pizzas.add(napolitana);
mussarela.pizzas.add(portuguesa);

presunto.pizzas.add(napolitana);
presunto.pizzas.add(portuguesa);

tomate.pizzas.add(napolitana);

ovo.pizzas.add(portuguesa);

Finally remember this:

If the relationship ends with ToMany, then you have a list of related entities. It ends with ToOne, there is only one related entity.

  • In the case of Evaluation, if I put only one Onetomany works normally, but in case I put a Onetomany for Event and Person, being that the first says it needs to be a List, and I put in, no error appears

  • @Felipejunges At the time I was writing this confused me because of a phone I received and then I ended up changing things. Answer edited.

  • Thanks, in the question, I am also using targetEntity=Event.class, it is optional and what changes ?

  • @Felipejunges O targetEntity is unnecessary. It only serves for the case where you have List eventos instead of List<Evento> eventos. That is, it is only used when you work with crude types that do not have generics (which is not something that makes a lot of sense to do).

  • 1

    Marking answer as answered :), very good your answer!

  • That excellent link you saved as a favorite. Thanks man!

Show 1 more comment

Browser other questions tagged

You are not signed in. Login or sign up in order to post.