Reputation: 20346
Can someone please explain to me @MapsId
in hibernate? I'm having a hard time understanding it.
It would be great if one could explain it with an example and in what kind of use cases is it most applicable?
Upvotes: 132
Views: 140563
Reputation: 131
The @MapsId
annotation is used in JPA to specify that an attribute should be part of a composite primary key. It is typically used with @EmbeddedId
to map the fields of the composite primary key to the corresponding entity relationships.
Consider this example:
@Entity
public class CompanyProduct {
@EmbeddedId
private CompanyProductId id = new CompanyProductId();
@ManyToOne
@MapsId("companyId")
private Company company;
@ManyToOne
@MapsId("productId")
private Product product;
private Integer quantity;
}
While the CompanyProductId (the composite key) definition is:
@Embeddable
public class CompanyProductId implements Serializable {
private Long companyId;
private Long productId;
// getter, setter, constructor and etc.
}
The CompanyProduct
class has a composite key, and when we are using @MapsId we are telling that this many-to-one relationship corresponds with the specified field inside the composite key.
For example, the product_id
column, has two purposes:
company_product
table.And when you look at the final created table (created with JPA Buddy plugin) you'll see this:
Which of course confirms what I've stated.
Upvotes: 0
Reputation: 41220
In the answer to my question about
Saving an entity with @ManyToOne JPA Composite Key used as Ids adds an extra parameter
It shows another use of @MapsId
to solve a mapping problem and what happens if you don't apply @MapsId
.
Upvotes: 0
Reputation: 6280
As he explained Vladimir in his tutorial, The best way to map a @OneToOne
relationship is to use @MapsId
. This way, you don’t even need a bidirectional association since you can always fetch the Child entity by using the Parent entity identifier.
Upvotes: 10
Reputation: 2359
IMHO, the best way to think about @MapsId
is when you need to map a composite key in a n:m entity.
For instance, a customer can have one or more consultant and a consultant can have one or more customer:
And your entites would be something like this (pseudo Java code):
@Entity
public class Customer {
@Id
private Integer id;
private String name;
}
@Entity
public class Consultant {
@Id
private Integer id;
private String name;
@OneToMany
private List<CustomerByConsultant> customerByConsultants = new ArrayList<>();
public void add(CustomerByConsultant cbc) {
cbc.setConsultant(this);
this.customerByConsultant.add(cbc);
}
}
@Embeddable
public class CustomerByConsultantPk implements Serializable {
private Integer customerId;
private Integer consultantId;
}
@Entity
public class CustomerByConsultant{
@EmbeddedId
private CustomerByConsultantPk id = new CustomerByConsultantPk();
@MapsId("customerId")
@JoinColumn(insertable = false, updatable = false)
private Customer customer;
@MapsId("consultantId")
@JoinColumn(insertable = false, updatable = false)
private Consultant consultant;
}
Mapping this way, JPA automagically inserts Customer
and Consultant
ids in the EmbeddableId
whenever you save a consultant. So you don't need to manually create the CustomerByConsultantPk
.
Upvotes: 13
Reputation: 3557
MapsId lets you use the same primary key between two different entities/tables. Note: when you use MapsId, the CASCADE.ALL
flag becomes useless, and you will need to make sure that your entities are saved manually.
Upvotes: 6
Reputation: 1138
I found this note also useful: @MapsId
in hibernate annotation maps a column with another table's column.
It can be used also to share the same primary key between 2 tables.
Example:
@Entity
@Table(name = "TRANSACTION_CANCEL")
public class CancelledTransaction {
@Id
private Long id; // the value in this pk will be the same as the
// transaction line from transaction table to which
// this cancelled transaction is related
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "ID_TRANSACTION", nullable = false)
@MapsId
private Transaction transaction;
....
}
@Entity
@Table(name = "TRANSACTION")
@SequenceGenerator(name = "SQ_TRAN_ID", sequenceName = "SQ_TRAN_ID")
public class Transaction {
@Id
@GeneratedValue(generator = "SQ_TRAN_ID", strategy = GenerationType.SEQUENCE)
@Column(name = "ID_TRANSACTION", nullable = false)
private Long id;
...
}
Upvotes: 40
Reputation: 11829
Here is a nice explanation from Object DB.
Designates a ManyToOne or OneToOne relationship attribute that provides the mapping for an EmbeddedId primary key, an attribute within an EmbeddedId primary key, or a simple primary key of the parent entity. The value element specifies the attribute within a composite key to which the relationship attribute corresponds. If the entity's primary key is of the same Java type as the primary key of the entity referenced by the relationship, the value attribute is not specified.
// parent entity has simple primary key
@Entity
public class Employee {
@Id long empId;
String name;
...
}
// dependent entity uses EmbeddedId for composite key
@Embeddable
public class DependentId {
String name;
long empid; // corresponds to primary key type of Employee
}
@Entity
public class Dependent {
@EmbeddedId DependentId id;
...
@MapsId("empid") // maps the empid attribute of embedded id
@ManyToOne Employee emp;
}
Read the API Docs here.
Upvotes: 65