¿Alguien puede explicar mappedBy en JPA e Hibernate?

175

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 Posee 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 aéreos:

@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 hace que AirlineFlights sea el propietario / entidad de identificación.

Teóricamente, me gustaría que la aerolínea sea la propietaria de airlineFlights.

ingrese la descripción de la imagen aquí

brainydexter
fuente

Respuestas:

151

Al especificar el @JoinColumnen ambos modelos, no tiene una relación bidireccional. Tienes dos relaciones unidireccionales y un mapeo muy confuso de eso. Le está diciendo a ambos modelos que "poseen" la columna IDAIRLINE. ¡Realmente solo uno de ellos debería hacerlo! Lo 'normal' es quitar @JoinColumncompletamente el @OneToManylado y agregar mappedBy al @OneToMany.

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

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

Affe
fuente
2
Estoy un poco confundido por su descripción al final sobre mappedBy. ¿Importa cómo se organizan las cosas en db? @DB: AirlineFlights tiene idAirline como la clave externa. Airline solo tiene idAirline como clave principal y no mantiene información sobre AirlineFlights @ DB.
brainydexter
10
Si, importa. El nombre en mappedBy le dice a Hibernate dónde encontrar la configuración para JoinColumn. (En el método getAirline () de AirlineFlight.) La forma en que lo mapeó colocando JoinColumn en la aerolínea, le está diciendo a Airline que es responsable de mantener los valores en la otra tabla. Es posible decirle a una entidad que "posee" una columna en una tabla diferente y que es responsable de actualizarla. No es algo que normalmente desee hacer y puede causar problemas con el orden en que se ejecutan las instrucciones SQL.
Affe
Por favor vea la edición. En el nivel de base de datos, la tabla airlineFlight posee idAirline como una columna de clave externa. Por lo tanto, JoinColumn debe colocarse en la clase / tabla airlineFlight en el ManytoOne correspondiente, ya que posee esa columna?
brainydexter
Sí, recomendaría hacerlo de esa manera. Es la opción menos complicada y no parece necesitar nada más.
Affe
"quita la @JoinColumn del lado @OneToMany por completo" te refieres al @ManyToOnelado, ¿verdad?
nbro
284

MappedBy señala que hiberna que la clave de la relación está del otro lado.

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

Kurt Du Bois
fuente
3
¿puedes por favor aclarar un poco más?
Alexander Suraphel el
1
@ Kurt Du Bois, ¿por qué usarías en mappedBylugar de definir un bidireccional (con restricciones de teclas extranjeras en cada lado)?
Kevin Meredith
66
Porque a veces simplemente no tiene sentido poner una llave en ambos lados. Digamos, por ejemplo, que tiene una empresa y un portátil. Un portátil solo pertenecerá a una compañía, pero una compañía tendrá múltiples portátiles.
Kurt Du Bois
Perdón por el tipo de edición por revertir mi respuesta, pero en realidad no hubo ningún valor agregado en su edición. La última oración ni siquiera tenía sentido.
Kurt Du Bois
@KurtDuBois, así mapeado, solo ven en la imagen cómo crear su base de datos, es decir, si usa mappedby o no hiberna en el lado de Java, se comporta de manera similar.
22

mappedbyhabla por sí mismo, le dice a hibernate que no asigne este campo. ya está asignado por este campo [nombre = "campo"].
campo está en la otra entidad (name of the variable in the class not the table in the database)..

Si no hace eso, hibernate asignará estas dos relaciones, ya que no es la misma relación

así que necesitamos decirle a hibernate que haga el mapeo solo en un lado y coordine entre ellos.

Charif DZ
fuente
es mapeado ¿Por qué es opcional? Porque sin usar mappedBy obtengo el mismo resultado, es decir, mapeo bidireccional de objetos
Freelancer
no puedes usar on2many y many2one sin usar mappedBy en uno de los lados lo mismo para many2many tienes que usar mappedBy en un lado
Charif DZ
Gracias por señalar lo que significa el valor del atributo, que es el nombre del campo en la otra tabla.
Gab 是 好人
2
Tal vez la hibernación no siempre habla por sí misma, pero cuando lo hace, al menos usa puntuación
Amalgovinus
1
Para mí, no habla por sí mismo; Por el contrario, es muy confuso. Basta con mirar la cantidad de preguntas sobre lo que realmente es mappedByy inversedBy. Otros ORM utilizan mucho más inteligentes belongsToMany, hasManylos atributos.
Jan Bodnar
12

mappedby = "objeto de entidad de la misma clase creado en otra clase”

Nota: -Mapeado solo se puede usar en una clase porque una tabla debe contener una restricción de clave externa. si mapeado puede aplicarse en ambos lados, entonces elimina la clave externa de ambas tablas y sin clave externa no hay relación entre dos tablas.

Nota: - se puede usar para las siguientes anotaciones: - 1. @ OneTone 2. @ OneToMany 3. @ ManyToMany

Nota --- No se puede usar para la siguiente anotación: - 1. @ ManyToOne

En uno a uno: - Realice en cualquier lado de la asignación, pero realice en un solo lado. Eliminará la columna adicional de restricción de clave externa en la tabla en la que se aplica la clase.

Por ej. Si aplicamos mapeado en la clase Empleado en el objeto empleado, se eliminará la clave externa de la tabla Empleado.

ks gujjar
fuente
12

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 <code> one-to-many </code>

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

Entonces, hay una sola fuente de verdad cuando se trata de administrar una one-to-manyrelación de tabla.

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

Asociación bidireccional de entidades <code> One-To-Many </code>

Si observa el diagrama anterior, puede ver que hay dos formas de administrar esta relación.

En la Postentidad, tiene la commentscolección:

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

Y, en el 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 Clave externa, debe definir cuál es la fuente de la verdad cuando se trata de traducir el cambio de estado de asociación en su modificación equivalente del valor de la columna Clave externa.

MappedBy

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

Sincronice ambos lados de una asociación bidireccional

Ahora, incluso si definió el mappedByatributo y la @ManyToOneasociación del lado secundario maneja la columna 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 removeCommentaseguran que ambos lados estén sincronizados. Entonces, si agregamos una entidad secundaria, la entidad secundaria debe apuntar al elemento primario y la entidad principal debe tener el elemento secundario contenido en la colección secundaria.

Para obtener más detalles sobre la mejor manera de sincronizar todos los tipos de asociación de entidades bidireccionales, consulte este artículo .

Vlad Mihalcea
fuente
0

Comenzó con el mapeo ManyToOne, luego colocó el mapeo OneToMany también de manera bidireccional. Luego, en el lado de OneToMany (generalmente su tabla / clase principal), debe mencionar "mappedBy" (la asignación se realiza por y en la tabla / clase secundaria), por lo que hibernate no creará una tabla de asignación EXTRA en DB (como TableName = parent_child).

Akshay N. Shelke
fuente