OneToOne Mapping JPA 2.2 Unidirectional

4

Hello, everyone.

I'm using JPA + hibernate 4 on top of the database structure (MySQL 5.6) below. I'm in doubt about the @OneToOne relationship.

What's happening:

Entity BodyPart has a @OneToOne relation with BodyPartHit with EAGER load. In the other entity, BodyPartHit , I would like to have only the id of BodyPart , as it is already mapped in the entities below. When BodyPart persists, it does not persist BodyPartHit even though it is marked with CascadeType.All . So I had to do it the way it is usually done, persisting the parent entity and setting id on the daughter entity and it works. Retrieving a BodyPart object does not perform lazy loading of BodyPartHit .

What I would like if possible:

Keep the mapping of the entities below, have only the idBodyPart in the BodyPartHit entity and in the BodyPart entity have a reference @OneToOne to BodyPartHit with CascadeType.All , using fetch = FetchType.EAGER .

What I tried to solve the problem:

I checked out a post from the UaiHebert talking about this type of mapping, however he uses the annotation @PrimaryKeyJoinColumn that does not need an id in the child entity ( BodyPartHit in the case) because the key of the parent entity ( BodyPart ) is migrated to the daughter, but the problem was to use this approach is that I have an id in the child table.

The database (MySQL 5.6) is structured as follows:

CREATE TABLE IF NOT EXISTS 'body_part' (
  'id' INT NOT NULL AUTO_INCREMENT,
  'description' VARCHAR(45) NOT NULL,
  PRIMARY KEY ('id'));


CREATE TABLE IF NOT EXISTS 'body_part_hit' (
  'id' INT NOT NULL AUTO_INCREMENT,
  'id_body_part' INT NOT NULL,
  'perc_damage_hit' DECIMAL(4,2) NOT NULL,
  PRIMARY KEY ('id'),
  INDEX 'fk_body_part_hit_1_idx' ('id_body_part' ASC),
  CONSTRAINT 'fk_body_part_hit_1'
    FOREIGN KEY ('id_body_part')
    REFERENCES 'body_part' ('id')
    ON DELETE NO ACTION
    ON UPDATE NO ACTION); 
@MappedSuperclass
public abstract class BaseEntity<ID> implements Serializable{
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    // get and set
}

Entity
@Table(name = "body_part")
@XmlRootElement(name = "bodyPart")
public class BodyPart extends BaseEntity<Integer> {
    private static final long serialVersionUID = 1L;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
    @JoinColumn(name = "id", unique = true, nullable = false, updatable = false)
    private BodyPartHit bodyPartHit;

    private String description;
    // get and set
}

@Entity
@Table(name = "body_part_hit")
@XmlRootElement(name = "bodyPartHit")
public class BodyPartHit extends BaseEntity<Integer> {
    private static final long serialVersionUID = 1L;

    @Column(name = "perc_damage_hit")
    private BigDecimal percentDamageHit;

    @Column(name = "id_body_part)
    private Integer idBodyPart;

    // get and set
}
    
asked by anonymous 04.11.2016 / 14:52

2 answers

1

There's no way. The way you mapped the class would be correct if the side that carried the key was the BodyPart, which is not the case. To get the bidirectional relationship, you need to map BodyPart to BodyPartHit, and then use the mappedBy in BodyHit. JoinColumn is to be used in the entity that carries the key, not in the one that has the key transported. See this post: link

It would look like this

@Entity
@Table(name = "body_part")
@XmlRootElement(name = "bodyPart")
public class BodyPart extends BaseEntity<Integer> {
    private static final long serialVersionUID = 1L;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "bodyPart")
    private BodyPartHit bodyPartHit;

    private String description;
    // get and set
}
@Entity
@Table(name = "body_part_hit")
@XmlRootElement(name = "bodyPartHit")
public class BodyPartHit extends BaseEntity<Integer> {
    private static final long serialVersionUID = 1L;

    @Column(name = "perc_damage_hit")
    private BigDecimal percentDamageHit;

    @OneToOne
    @JoinColumn(name = "id_body_part", unique = true, nullable = false, updatable = false)
    private BodyPartHit bodyPart;

    // get and set
}
    
04.11.2016 / 18:11
0

To solve the problem, just make use of annotation @PrePersist, in addition to inverting the entity mapping (@OneToOne must be in the BodyPartHit.java class)

BodyPartHit.java

@Entity
@Table(name = "body_part_hit")
@XmlRootElement(name = "bodyPartHit")
public class BodyPartHit {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "perc_damage_hit")
    private BigDecimal percentDamageHit;

    @OneToOne
    @JoinColumn(name = "id_body_part", unique = true, nullable = false, updatable = false)
    private BodyPart bodyPart;

    @Column(name = "id_body_part", insertable = false, updatable = false, nullable = false)
    private Integer idBodyPart;

    // equals, hashCode, toString, getters and setters
}

BodyPart.java

@Entity
@Table(name = "body_part")
@XmlRootElement(name = "bodyPart")
public class BodyPart {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "bodyPart")
    private BodyPartHit bodyPartHit;

    private String description;

    @PrePersist
    public void prePersist() {
        if (bodyPartHit != null) {
            bodyPartHit.setBodyPart(this);
        }
    }

    // equals, hashCode, toString, getters and setters
}

In this way, when JPA persists the child entity (BodyPartHit) via cascade, it will already have the parent entity id (BodyPart).

I hope I have helped.

Here is an example project I uploaded to github: link

    
21.11.2016 / 17:41