¿Alguien puede explicar mappedBy en JPA e Hibernate?

Resuelto brainydexter asked hace 12 años • 7 respuestas

Soy nuevo en hibernación y necesito usar relaciones uno a muchos y muchos a uno. Es una relación bidireccional en mis objetos, de modo que puedo atravesar desde cualquier dirección. mappedByes la forma recomendada de hacerlo, sin embargo, no pude entenderlo. ¿Alguien puede explicar:

  • ¿Cuál es la forma recomendada de usarlo?
  • ¿Qué propósito resuelve?

Por el bien de mi ejemplo, aquí están mis clases con anotaciones:

  • Airline DUEÑO DE MUCHOS AirlineFlights
  • Muchos AirlineFlights pertenecen a UNO Airline

Aerolínea :

@Entity 
@Table(name="Airline")
public class Airline {
    private Integer idAirline;
    private String name;

    private String code;

    private String aliasName;
    private Set<AirlineFlight> airlineFlights = new HashSet<AirlineFlight>(0);

    public Airline(){}

    public Airline(String name, String code, String aliasName, Set<AirlineFlight> flights) {
        setName(name);
        setCode(code);
        setAliasName(aliasName);
        setAirlineFlights(flights);
    }

    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name="IDAIRLINE", nullable=false)
    public Integer getIdAirline() {
        return idAirline;
    }

    private void setIdAirline(Integer idAirline) {
        this.idAirline = idAirline;
    }

    @Column(name="NAME", nullable=false)
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = DAOUtil.convertToDBString(name);
    }

    @Column(name="CODE", nullable=false, length=3)
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = DAOUtil.convertToDBString(code);
    }

    @Column(name="ALIAS", nullable=true)
    public String getAliasName() {
        return aliasName;
    }
    public void setAliasName(String aliasName) {
        if(aliasName != null)
            this.aliasName = DAOUtil.convertToDBString(aliasName);
    }

    @OneToMany(fetch=FetchType.LAZY, cascade = {CascadeType.ALL})
    @JoinColumn(name="IDAIRLINE")
    public Set<AirlineFlight> getAirlineFlights() {
        return airlineFlights;
    }

    public void setAirlineFlights(Set<AirlineFlight> flights) {
        this.airlineFlights = flights;
    }
}

Vuelos de aerolíneas:

@Entity
@Table(name="AirlineFlight")
public class AirlineFlight {
    private Integer idAirlineFlight;
    private Airline airline;
    private String flightNumber;

    public AirlineFlight(){}

    public AirlineFlight(Airline airline, String flightNumber) {
        setAirline(airline);
        setFlightNumber(flightNumber);
    }

    @Id
    @GeneratedValue(generator="identity")
    @GenericGenerator(name="identity", strategy="identity")
    @Column(name="IDAIRLINEFLIGHT", nullable=false)
    public Integer getIdAirlineFlight() {
        return idAirlineFlight;
    }
    private void setIdAirlineFlight(Integer idAirlineFlight) {
        this.idAirlineFlight = idAirlineFlight;
    }

    @ManyToOne(fetch=FetchType.LAZY)
    @JoinColumn(name="IDAIRLINE", nullable=false)
    public Airline getAirline() {
        return airline;
    }
    public void setAirline(Airline airline) {
        this.airline = airline;
    }

    @Column(name="FLIGHTNUMBER", nullable=false)
    public String getFlightNumber() {
        return flightNumber;
    }
    public void setFlightNumber(String flightNumber) {
        this.flightNumber = DAOUtil.convertToDBString(flightNumber);
    }
}

EDITAR:

Esquema de base de datos:

AirlineFlights tiene idAirline como ForeignKey y Airline no tiene idAirlineFlights. ¿Esto convierte a AirlineFlights en el propietario/entidad identificadora?

En teoría, me gustaría que la aerolínea fuera propietaria de AirlinesFlights.

ingrese la descripción de la imagen aquí

brainydexter avatar Feb 02 '12 13:02 brainydexter
Aceptado

MappedBy indica en hibernación que la clave de la relación está del otro lado.

Esto significa que aunque vincule 2 tablas, solo 1 de esas tablas tiene una restricción de clave externa para la otra. MappedBy le permite seguir vinculando desde la tabla que no contiene la restricción a la otra tabla.

Kurt Du Bois avatar Feb 02 '2012 07:02 Kurt Du Bois

Al especificar @JoinColumnen ambos modelos, no tiene una relación bidireccional. Tienes dos relaciones unidireccionales y, además, un mapeo muy confuso. Le estás diciendo a ambos modelos que "son dueños" de la columna IDAIRLINE. ¡Realmente sólo uno de ellos debería hacerlo! Lo "normal" es quitar @JoinColumnel @OneToManylado por completo y, en su lugar, agregar mappedBy al archivo @OneToMany.

@OneToMany(cascade = CascadeType.ALL, mappedBy="airline")
public Set<AirlineFlight> getAirlineFlights() {
    return airlineFlights;
}

Eso le dice a Hibernate "Vaya a revisar la propiedad del bean llamada 'aerolínea' en lo que tengo una colección para encontrar la configuración".

Affe avatar Feb 02 '2012 07:02 Affe

Relación de tabla versus relación de entidad

En un sistema de base de datos relacional, una one-to-manyrelación de tabla tiene el siguiente aspecto:

relación de tabla uno a muchos

Tenga en cuenta que la relación se basa en la columna de clave externa (por ejemplo, post_id) en la tabla secundaria.

Por lo tanto, existe una única fuente de verdad cuando se trata de gestionar una one-to-manyrelación en la mesa.

Ahora, si toma una relación de entidad bidireccional que se asigna a la one-to-manyrelación de tabla que vimos anteriormente:

Asociación bidireccional de entidades uno a muchos

Si observa el diagrama anterior, podrá ver que hay dos formas de gestionar esta relación.

En la Postentidad tienes la commentscolección:

@OneToMany(
    mappedBy = "post",
    cascade = CascadeType.ALL,
    orphanRemoval = true
)
private List<PostComment> comments = new ArrayList<>();

Y, en PostComment, la postasociación se asigna de la siguiente manera:

@ManyToOne(
    fetch = FetchType.LAZY
)
@JoinColumn(name = "post_id")
private Post post;

Debido a que hay dos formas de representar la columna de clave externa, debe definir cuál es la fuente de verdad cuando se trata de traducir el cambio de estado de la asociación en su modificación del valor de la columna de clave externa equivalente.

Mapeado por

El mappedByatributo indica que el @ManyToOnelado está a cargo de administrar la columna de clave externa, y la colección se usa solo para recuperar las entidades secundarias y para transmitir en cascada los cambios de estado de la entidad principal a las secundarias (por ejemplo, eliminar la entidad principal también debería eliminar las entidades secundarias).

Sincronizar ambos lados de una asociación bidireccional

Ahora, incluso si definió el mappedByatributo y la asociación del lado secundario @ManyToOneadministra la columna de clave externa, aún necesita sincronizar ambos lados de la asociación bidireccional.

La mejor manera de hacerlo es agregar estos dos métodos de utilidad:

public void addComment(PostComment comment) {
    comments.add(comment);
    comment.setPost(this);
}

public void removeComment(PostComment comment) {
    comments.remove(comment);
    comment.setPost(null);
}

Los métodos addCommenty removeCommentgarantizan que ambos lados estén sincronizados. Entonces, si agregamos una entidad secundaria, la entidad secundaria debe apuntar a la entidad principal y la entidad principal debe tener al hijo contenido en la colección secundaria.

Vlad Mihalcea avatar Mar 27 '2020 15:03 Vlad Mihalcea