Unidirectional @OneToOne with reversed foreign key

13.2k views Asked by At

We'd like to create unidirectional @OneToOne mapping with foreign key that is not in the master table, but in the slave. By providing following java code, Hibernate tries to find column product_ID in the table product but not in productimage. Is it possible to make it working with the only modifications to annotations?

All unnecessary fields and columns have been stripped from the example.

JPA entities:

@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`)
)
3

There are 3 answers

2
Alex Rogachevsky On

@OneToOne is a little misleading. It is most likely different from what you want. Classic database "one to one" relationship is modeled via @ManyToOne JPA annotation in most cases. Try it. Never had a problem with uni-directional relationships via @ManyToOne and @JoinColumn. I'd also explicitly specify productId field in the second class.

2
Alan Hay On

Whether you map it as @OneToOne or @OneToMany is really neither here nor there: clients can only use what you expose 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 do not see any particular issue with making the relationship bidirectional either as 1-2-1 or 1-2-M. Again, don't expose the reference from ProductImage > Product if you don't want it publicly available.

1
Ostap On

JPA entities, more on @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
);