What's the difference between @JoinColumn and mappedBy when using a JPA @OneToMany association

后端 未结 8 1932
日久生厌
日久生厌 2020-11-22 02:35

What is the difference between:

@Entity
public class Company {

    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
    @JoinColumn(name = \"c         


        
相关标签:
8条回答
  • 2020-11-22 02:42

    I disagree with the accepted answer here by Óscar López. That answer is inaccurate!

    It is NOT @JoinColumn which indicates that this entity is the owner of the relationship. Instead, it is the @ManyToOne annotation which does this (in his example).

    The relationship annotations such as @ManyToOne, @OneToMany and @ManyToMany tell JPA/Hibernate to create a mapping. By default, this is done through a seperate Join Table.


    @JoinColumn

    The purpose of @JoinColumn is to create a join column if one does not already exist. If it does, then this annotation can be used to name the join column.


    MappedBy

    The purpose of the MappedBy parameter is to instruct JPA: Do NOT create another join table as the relationship is already being mapped by the opposite entity of this relationship.



    Remember: MappedBy is a property of the relationship annotations whose purpose is to generate a mechanism to relate two entities which by default they do by creating a join table. MappedBy halts that process in one direction.

    The entity not using MappedBy is said to be the owner of the relationship because the mechanics of the mapping are dictated within its class through the use of one of the three mapping annotations against the foreign key field. This not only specifies the nature of the mapping but also instructs the creation of a join table. Furthermore, the option to suppress the join table also exists by applying @JoinColumn annotation over the foreign key which keeps it inside the table of the owner entity instead.

    So in summary: @JoinColumn either creates a new join column or renames an existing one; whilst the MappedBy parameter works collaboratively with the relationship annotations of the other (child) class in order to create a mapping either through a join table or by creating a foreign key column in the associated table of the owner entity.

    To illustrate how MapppedBy works, consider the code below. If MappedBy parameter were to be deleted, then Hibernate would actually create TWO join tables! Why? Because there is a symmetry in many-to-many relationships and Hibernate has no rationale for selecting one direction over the other.

    We therefore use MappedBy to tell Hibernate, we have chosen the other entity to dictate the mapping of the relationship between the two entities.

    @Entity
    public class Driver {
        @ManyToMany(mappedBy = "drivers")
        private List<Cars> cars;
    }
    
    @Entity
    public class Cars {
        @ManyToMany
        private List<Drivers> drivers;
    }
    

    Adding @JoinColumn(name = "driverID") in the owner class (see below), will prevent the creation of a join table and instead, create a driverID foreign key column in the Cars table to construct a mapping:

    @Entity
    public class Driver {
        @ManyToMany(mappedBy = "drivers")
        private List<Cars> cars;
    }
    
    @Entity
    public class Cars {
        @ManyToMany
        @JoinColumn(name = "driverID")
        private List<Drivers> drivers;
    }
    
    0 讨论(0)
  • 2020-11-22 02:42

    Let me make it simple.
    You can use @JoinColumn on either sides irrespective of mapping.

    Let's divide this into three cases.
    1) Uni-directional mapping from Branch to Company.
    2) Bi-direction mapping from Company to Branch.
    3) Only Uni-directional mapping from Company to Branch.

    So any use-case will fall under this three categories. So let me explain how to use @JoinColumn and mappedBy.
    1) Uni-directional mapping from Branch to Company.
    Use JoinColumn in Branch table.
    2) Bi-direction mapping from Company to Branch.
    Use mappedBy in Company table as describe by @Mykhaylo Adamovych's answer.
    3)Uni-directional mapping from Company to Branch.
    Just use @JoinColumn in Company table.

    @Entity
    public class Company {
    
    @OneToMany(cascade = CascadeType.ALL , fetch = FetchType.LAZY)
    @JoinColumn(name="courseId")
    private List<Branch> branches;
    ...
    }
    

    This says that in based on the foreign key "courseId" mapping in branches table, get me list of all branches. NOTE: you can't fetch company from branch in this case, only uni-directional mapping exist from company to branch.

    0 讨论(0)
  • 2020-11-22 02:43

    I'd just like to add that @JoinColumn does not always have to be related to the physical information location as this answer suggests. You can combine @JoinColumn with @OneToMany even if the parent table has no table data pointing to the child table.

    How to define unidirectional OneToMany relationship in JPA

    Unidirectional OneToMany, No Inverse ManyToOne, No Join Table

    It seems to only be available in JPA 2.x+ though. It's useful for situations where you want the child class to just contain the ID of the parent, not a full on reference.

    0 讨论(0)
  • 2020-11-22 02:53

    The annotation @JoinColumn indicates that this entity is the owner of the relationship (that is: the corresponding table has a column with a foreign key to the referenced table), whereas the attribute mappedBy indicates that the entity in this side is the inverse of the relationship, and the owner resides in the "other" entity. This also means that you can access the other table from the class which you've annotated with "mappedBy" (fully bidirectional relationship).

    In particular, for the code in the question the correct annotations would look like this:

    @Entity
    public class Company {
        @OneToMany(mappedBy = "company",
                   orphanRemoval = true,
                   fetch = FetchType.LAZY,
                   cascade = CascadeType.ALL)
        private List<Branch> branches;
    }
    
    @Entity
    public class Branch {
        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumn(name = "companyId")
        private Company company;
    }
    
    0 讨论(0)
  • 2020-11-22 03:02

    JPA is a layered API, the different levels have their own annotations. The highest level is the (1) Entity level which describes persistent classes then you have the (2) relational database level which assume the entities are mapped to a relational database and (3) the java model.

    Level 1 annotations: @Entity, @Id, @OneToOne, @OneToMany, @ManyToOne, @ManyToMany. You can introduce persistency in your application using these high level annotations alone. But then you have to create your database according to the assumptions JPA makes. These annotations specify the entity/relationship model.

    Level 2 annotations: @Table, @Column, @JoinColumn, ... Influence the mapping from entities/properties to the relational database tables/columns if you are not satisfied with JPA's defaults or if you need to map to an existing database. These annotations can be seen as implementation annotations, they specify how the mapping should be done.

    In my opinion it is best to stick as much as possible to the high level annotations and then introduce the lower level annotations as needed.

    To answer the questions: the @OneToMany/mappedBy is nicest because it only uses the annotations from the entity domain. The @oneToMany/@JoinColumn is also fine but it uses an implementation annotation where this is not strictly necessary.

    0 讨论(0)
  • 2020-11-22 03:03

    @JoinColumn could be used on both sides of the relationship. The question was about using @JoinColumn on the @OneToMany side (rare case). And the point here is in physical information duplication (column name) along with not optimized SQL query that will produce some additional UPDATE statements.

    According to documentation:

    Since many to one are (almost) always the owner side of a bidirectional relationship in the JPA spec, the one to many association is annotated by @OneToMany(mappedBy=...)

    @Entity
    public class Troop {
        @OneToMany(mappedBy="troop")
        public Set<Soldier> getSoldiers() {
        ...
    }
    
    @Entity
    public class Soldier {
        @ManyToOne
        @JoinColumn(name="troop_fk")
        public Troop getTroop() {
        ...
    } 
    

    Troop has a bidirectional one to many relationship with Soldier through the troop property. You don't have to (must not) define any physical mapping in the mappedBy side.

    To map a bidirectional one to many, with the one-to-many side as the owning side, you have to remove the mappedBy element and set the many to one @JoinColumn as insertable and updatable to false. This solution is not optimized and will produce some additional UPDATE statements.

    @Entity
    public class Troop {
        @OneToMany
        @JoinColumn(name="troop_fk") //we need to duplicate the physical information
        public Set<Soldier> getSoldiers() {
        ...
    }
    
    @Entity
    public class Soldier {
        @ManyToOne
        @JoinColumn(name="troop_fk", insertable=false, updatable=false)
        public Troop getTroop() {
        ...
    }
    
    0 讨论(0)
提交回复
热议问题