Onetoone JPA 2.2 Unidirectional Mapping

Asked

Viewed 3,140 times

4

Hello, everybody.

I am using JPA + Hibernate 4 above the database structure (Mysql 5.6) below. I am in doubt in the relationship @OneToOne.

What’s going on:

The entity BodyPart has a relationship @OneToOne with BodyPartHit loaded EAGER. In the other entity, BodyPartHit, I wish I had just the id of BodyPart, as it is already mapped in the entities below. By persisting the entity BodyPart, she does not persist BodyPartHit even if you are marked with CascadeType.All. From there I had to do as it is usually done, persisting the parent entity and setting the id in the daughter entity and works. By recovering an object BodyPart, is not carried out the lazy loading of BodyPartHit.

What I would like if possible:

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

What I tried to solve the problem:

I checked out a post from Wow talking a little bit about this kind of mapping, but he uses the annotation @PrimaryKeyJoinColumn that does not require an id on the daughter entity (BodyPartHit in the case) because the key of the parent entity (BodyPart) is migrated to the daughter, but the problem was using this approach is that I have an id in the daughter 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
}

2 answers

1

There’s no way to do it. The way you mapped the class, it would be correct if the side that carried the key was Bodypart, which is not the case. To be able to do the bidirectional relationship, you need to map Bodypart in Bodyparthit, and then use mappedBy in Bodyhit. Joincolumn is to be used in the entity that carries the key, not in the one that has the key carried. See this post: https://stackoverflow.com/questions/11938253/jpa-joincolumn-vs-mappedby

Would that way

@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
}
  • This does not answer the question. When you have reputation enough, you’ll be able to leave comments on any post but until then, write only answer that no depend on more information of who asked. - From Review

  • I disagree, my friend.

  • Even before the issue did not require additional information. Knowledge building is more important than dots, hug.

  • @Leonardolana I’m sorry if your reception to Stackoverflow in Portuguese was a bit turbulent. But don’t be discouraged, your contribution is welcome.

  • @Leonardolana, if I cannot find a solution in the context I explained in the question, I will implement it in this way. However, as I use these entities in a JSON response, an infinite recursion problem may occur which there are solutions that are/can be explained in other topics.

  • In this case you would note, in the Bodypart class, the attribute with Jsonmanagedreference, and in Hit with Jsonbackreference

Show 1 more comment

0

To solve the problem, simply make use of Annotation @Prepersist, and invert entity mapping ( @Onetoone must be in the Bodyparthit class.java)

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
}

This way, when JPA persists the daughter entity (Bodyparthit) via Scade, it will already possess the parent identity id (Bodypart).

I hope I’ve helped.

Here’s an example project I went up on github: https://github.com/enikolas/jpa-one-to-one-cascade

  • Thanks Nikolas, I will take a look later, if it works out I give Solution in your reply.

  • @diegohsi for nothing, await feedback on see if this solves your problem.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.