Unidirectional @OneToOne with reverse foreign key
We would like to create a unidirectional mapping @OneToOne
with a foreign key that is not in the main table but in the subordinate one. By providing the following java code, Hibernate tries to find the column product_ID
in the table product
, but not in the productimage
. Is it possible to make it work with single annotation modifications?
All unnecessary fields and columns have been removed from the example.
JPA Objects:
@Entity
public class Product {
@Id
@Column(name = "ID", unique = true)
private String id;
// this doesn't work
@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "product_ID", nullable = false)
private ProductImage productImage;
// this works, but we want one-to-one
// @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
// @JoinColumn(name = "product_ID", nullable = false)
// private List<ProductImage> productImages;
// getters/setters omitted
}
@Entity
public class ProductImage {
@Id
@Column(name = "ID", unique = true)
private String id;
@Column
@Lob
private Blob data;
// getters/setters omitted
}
Database tables:
CREATE TABLE `product` (
`ID` varchar(255) NOT NULL,
PRIMARY KEY (`ID`)
)
CREATE TABLE `productimage` (
`ID` varchar(255) NOT NULL,
`data` longblob NOT NULL,
`product_ID` varchar(255) NOT NULL,
PRIMARY KEY (`ID`),
KEY `FK_123` (`product_ID`),
CONSTRAINT `FK_123` FOREIGN KEY (`product_ID`) REFERENCES `product` (`ID`)
)
source to share
@OneToOne is a little misleading. This is most likely different from what you want. A classic one-to-one relationship database is modeled using the @ManyToOne JPA annotation in most cases. Try it. Never had problems with unidirectional relationships via @ManyToOne and @JoinColumn. I would also explicitly specify the productId field in the second class.
source to share
Whether you map it as @OneToOne or @OneToMany, really neither here nor there: clients can only use what you show, so map it as @OneToMany and hide the implementation.
@Entity
public class Product {
@Id
@Column(name = "ID", unique = true)
private String id;
@OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "product_ID", nullable = false)
private List<ProductImage> productImages;
//no getters or setters for productImages;
public ProductImage getProductImage(){
return productImages.size() > 0 ? productImages.get(0) : null;
}
}
I also don't see any particular problem with making bidirectional communication as either 1-2-1 or 1-2-M. Again, don't expose the link from ProductImage> Product unless you want it to be publicly available.
source to share
It is a one-to-one inverse relationship. In a normal (not inverted) relationship, the source object stores a pointer (a column in the table) to the target object. But otherwise, the target object has a pointer to the original object.
Therefore, you need to use a property mappedBy
to communicate that the key to this relationship is on the other side.
In your case, you need to introduce annotation @OneToOne
on the target side:
@Entity
public class Product {
@Id
@Column(name = "ID")
private String id;
@OneToOne(mappedBy = "product")
private ProductImage productImage;
}
@Entity
public class ProductImage {
@Id
@Column(name = "ID", unique = true)
private String id;
@OneToOne
@JoinColumn(name = "product_ID")
private Product product;
@Column
@Lob
private Blob data;
}
source to share
JPA Objects, learn more about @PrimaryKeyJoinColumn
:
@Entity
public class Product {
@Id
private String id;
@PrimaryKeyJoinColumn
@OneToOne(orhanRemoval = true)
private ProductImage image;
}
@Entity
public class ProductImage {
@Id
private String productId; // this will be foreign key as well as primary
//other properties goes here
}
Database tables:
CREATE TABLE product (
id VARCHAR PRIMARY KEY
);
CREATE TABLE product_image (
product_id VARCHAR PRIMARY KEY REFERENCES product(id)
-- other columns goes here
);
source to share