0

I am getting this null pointer exception on the id of the newly created object that I'm putting into the DB, even though it should be auto generated.

@Entity
@Table(name = "REQUEST")
@EntityListeners(DatabaseListener.class)
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "requestId", scope = Request.class)
public class Request implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "REQUEST_ID", nullable = false)
    private Long requestId;

    @Column(name = "REQUEST_NAME")
    private String name;

    @Column(name = "CATEGORY")
    private String category;

    @Column(name = "REQUEST_MESSAGE")
    private String requestMessage;

    @Column(name = "CREATED_ON")
    private Date createdOn;

    @ManyToOne
    private User createdBy;

    @ManyToOne
    private User assignedTo;

    @ManyToOne
    private Status status;

    @OneToMany(mappedBy = "request", fetch = FetchType.EAGER)
    private List<Response> replies;

The endpoint that I am calling...

@RequestMapping(value = "/requests/addRequest", method = RequestMethod.POST)
public ResponseEntity<Request> addRequest(@RequestBody final Request request) {
    if(request.getStatus() == null){
        request.setStatus(request.getAssignedTo() == null ? statusRepo.findByType(StatusType.UNASSIGNED) : statusRepo.findByType(StatusType.ASSIGNED));
    }

    final Request saved = requestRepo.save(request);

    return new ResponseEntity<>(saved, HttpStatus.CREATED);
}

I can add more of the other classes if needed as well. But I can not figure out why requestId would be null. I'm calling that enpoint from an app and passing it a Request object, so obviously there it is null but it shouldn't be null when it's going into the DB. Right?

org.springframework.http.converter.HttpMessageNotReadableException: Could not read document: (was java.lang.NullPointerException) (through reference chain: Request["requestId"]); nested exception is com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain: Request["requestId"])
...
Caused by: com.fasterxml.jackson.databind.JsonMappingException: (was java.lang.NullPointerException) (through reference chain:Request["requestId"])
...
Caused by: java.lang.NullPointerException: null
    at com.fasterxml.jackson.annotation.ObjectIdGenerator$IdKey.<init>(ObjectIdGenerator.java:125) ~[jackson-annotations-2.4.0.jar:2.4.0]

Edit: To be clear, I know what a null pointer exception is, I understand that the requestId is null and that is causing a problem, so I don't think this is a duplicate question. What I don't understand is why it is null. It should be auto generated, but as Jason pointed out, I may have to determine the id application side first.

John
  • 1,808
  • 7
  • 28
  • 57
  • 1
    I can't answer your question as I'm unfamiliar with the API, but I know that in other persistence architectures e.g. hibernate, it's not unreasonable for ID fields to remain null until *after* the object is persisted, for generation strategies where the underlying DBMS generates the id, since the new id isn't known until after the object is committed. For those you either explicitly commit the object before accessing the id, or use an application-side generation strategy. So maybe this is a helpful hint. – Jason C Sep 12 '16 at 14:52
  • 1
    @Jared No, having NPE in the title does not make this a duplicate and I've reopened it. This one's quite possibly about the timing of generated ids in a persistence architecture, it's a decent question. That said there might be a different dupe elsewhere, although I can't easily find one on my phone. – Jason C Sep 12 '16 at 14:56
  • @JasonC Thanks, that did push me in the right direction I think. I did a quick test with assigning an arbitrary number (300) and that worked and in the DB it still auto generated properly. But then if I made the call again, still with that same arbitrary number (300) I got an exception `Already had POJO for id...`. So it seems I may need something to retrieve the next correct id and then I can post it. – John Sep 12 '16 at 15:09
  • @JasonC `.save()` is a part of Spring Data http://docs.spring.io/spring-data/data-commons/docs/1.6.1.RELEASE/reference/html/repositories.html – John Sep 12 '16 at 15:11
  • Ah look, http://stackoverflow.com/questions/13898982/why-is-id-not-set-in-entity-returned-from-spring-data-jpa-save is it possible auto is not the right strategy? Try identity, which according to fhe answer there forces an underlying select to retrieve the generated id. – Jason C Sep 12 '16 at 15:15
  • Unfortunately no, the problem in that users case was they didn't have any `GenerationType`, `AUTO` should work just as well, as it will select to use `IDENTITY``. Just to be sure though I tried it, and still the same – John Sep 12 '16 at 15:35
  • Try saveAndFlush instead of save? Or an explicit flush() after saving? – Jason C Sep 12 '16 at 15:41
  • 1
    Nope. The underlying issue is that `requestId` can't be null but when I make that POST the id has to be null since I wont know the value. I just remembered I ran into this before and solved it by passing the parts and not the object but that was for a much smaller object than this. – John Sep 12 '16 at 15:53
  • 1
    Ohhhh, i had the problem backwards. Well why dont you just let the id be nullable? It is nullable after all: a request *can* exist in your application without an id (yet), e.g. a freshly posted one. Either that or some sort of intermediate bean to receive the post before packing it into a request, although that adds a lot of tedious code. – Jason C Sep 12 '16 at 15:56
  • It can't be nullable in the DB though because it's the primary key. I tried adding @Nullable to allow it to be nullable before it's in the DB but that didn't work either, still NullPointerException on requestId – John Sep 12 '16 at 16:04

1 Answers1

1

Not the most elegant but so far the only solution I could find.

I created a new endpoint that creates a blank Request, saves it in the DB and returns it back to the app. This way the app now has a blank Request with a real and correct id.

Then in my addRequest endpoint I do a query for that id, and instead of creating a new Request I just update the existing one.

Edit: Actually the addRequest method will stay the same, as the DB will just update the existing entry.

John
  • 1,808
  • 7
  • 28
  • 57