javahibernatejpaspring-data-jpaembeddable

Auto generated id not cascading on EmbeddedId in child entity in Hibernate


When trying to insert an Entity - Awith a set Another Entity B, B should get the Auto generated Id from A but its null.

Tried and failed:

  1. @MapsId("taskPKId.storyId.id") - Same error.
  2. @Embeddable class StoryId { @ManyToOne(fetch = FetchType.Lazy) @JoinColumn(name = "STORY_ID") Long id; } //Incomprehensible Null pointer exception
  3. mappedBy("story") - same error
    1. Tried with mappedBy('story') but getting an error with repeated column and so had to map it with insertable=false and updatable=false [Hibernate doesn't recognize insertable=false for @EmbeddedId]

I am getting STORY_ID = null and therefore saveAll fails on storyRepository.saveAll(stories) where storyRepository is a Spring Data repository

@Table(name = "STORY")
@EqualsAndHashCode
class Story {
   @Id
   @GeneratedValue(stratergy=GenerationType.Auto)
   @Column(name="STORY_ID")
   Long id;

   @Column(name="STORY_NAME")
   String name;

   //@OneToMany(cascade=ALL, mappedBy="taskPKId.storyId.id", fetch = FetchType.Lazy) // tried this as well
   @OneToMany(cascade=ALL, mappedBy="story", fetch = FetchType.Lazy)
   Set<Task> task;
}

@Table(name = "TASK_XREF")
@EqualsAndHashCode
Class Task {
   @EmbeddedId
   TaskPKId taskPKId;

   @Column(name = "TASK_NAME")
   String name;

   @ManyToOne (fetch = FetchType.Lazy, optional = false)
   @JoinColumn(name = "STORY_ID", referencedColumnName = "STORY_ID", nullable = false, insertable = false, updatable = false) 
   Story story;
}

@Embeddable
@EqualsAndHashCode
Class TaskPKId  implements Serializable {
   TaskId taskId;
   TaskTypeId taskTypeId;
   StoryId storyId;
}

@Embeddable
@EqualsAndHashCode
class StoryId implements Serializable {
   @Column(name = "STORY_ID")
   Long id;
}

Tables:

  1. STORY [STORY_ID, STORY_NAME]
  2. TASK_XREF [(TASK_ID(FK), TASK_TYPE_ID(FK), STORY_ID(FK)) PK,TASK_NAME]

Story gets inserted (before commit ofcourse), but fails because STORY_ID is sent as null to TASK_XREF for the next inserts


Solution

  • I'm not quite sure why your configuration does not work. I have a similar configuration in one of my projects that works just fine. I was able to find a solution however, by adding a @MapsId annotation to the ManyToOne in the Task class. (see can someone please explain me @MapsId in hibernate? for an explanation about MapsId) I also removed insertable=false and updatable=false. See below for the code.

    I didn't get MapsId to work with the StoryId class, so i changed the type of TaskPKID.storyId from StoryId to long. The StoryId class doesn't seem to add much, so hopefully this isn't to much of a problem. If you find a solution please let me know in the comments though!

    By the way, your code has a lot of problems. There's a bunch of typo's, and there is a OneToMany mapping on a property that is not a Collection (which isn't allowed) This made it more difficult for me to debug the problem. Please make sure to post better quality code in your questions next time.

    Here is the Task class the way I implemented it:

    @Entity
    @Table(name = "TASK_XREF")
    class Task {
        @EmbeddedId
        TaskPKId taskPKId;
    
        @Column(name = "TASK_NAME")
        String name;
        @MapsId("storyId")
        @ManyToOne(fetch = FetchType.LAZY, optional = false)
        @JoinColumn(name = "STORY_ID")
        Story story;
    
        //getters, setters
    
    }
    

    And here is the TaskPKID class:

    @Embeddable
    class TaskPKId  implements Serializable {
            long taskId;
            long taskTypeId;
            @Column(name="STORY_ID")
            long storyId;
    
        public long getTaskId() {
            return taskId;
        }
    
        public void setTaskId(long taskId) {
            this.taskId = taskId;
        }
    
        public void setTaskTypeId(long taskTypeId) {
            this.taskTypeId = taskTypeId;
        }
    }