1

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

Dexters
  • 2,419
  • 6
  • 37
  • 57

2 Answers2

1

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;
    }
}
Joost Lambregts
  • 380
  • 2
  • 7
0

I'm not sure what you want to achieve, but it looks like you have combined @OneToMany annotation with @OneToOne-like implementation (which can lead to unexpected behavior like this one).

Possible solutions:

  1. If one story owns multiple tasks

    // Story.java     
    @OneToMany(cascade=ALL, mappedBy="story", fetch = FetchType.Lazy)
    Set<Task> task; // basically Set, List or any other collection type
    // Task.java
    @ManyToOne (fetch = FetchType.Lazy, optional = false)
    @JoinColumn(name = "STORY_ID", referencedColumnName = "STORY_ID") 
    Story story;
    
  2. If one story owns only one task

    // Story.java 
    @OneToOne(cascade=ALL, mappedBy="story", fetch = FetchType.Lazy)
    Task task;
    // Task.java
    @OneToOne (fetch = FetchType.Lazy, optional = false)
    @JoinColumn(name = "STORY_ID", referencedColumnName = "STORY_ID") 
    Story story;
    

Further reading: @OneToOne @OneToMany

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
Kamil Bęben
  • 1,064
  • 8
  • 12
  • you just repeated what I have mentioned as not working in my question. – Dexters May 19 '19 at 04:00
  • I don't see where you have mentioned it. The problem is, that you are trying to map single type (`Task`) field to collection type (`Collection`) relation, you wrote about multiple ways of indicating how is `Story story;` field mapped. – Kamil Bęben May 19 '19 at 13:19
  • You are right, sorry - its set only, a typo here. – Dexters May 20 '19 at 11:34