4

I have a very simple class, with a @ManyToOne association.

@Entity
public class Place {
  ...

    @ManyToOne
    @RestResource(exported = false)
    private Category category;

  ...
}

Both Place and Category have their own respective Repositories that are exported. But for our case, we need to have the Category field not exported here. IDs are exposed for Category.

However, when I try to update an existing Place with an existing Category, Hibernate fails the update.

Example PUT to /places/foo

{
  ...
  "category": {
    "name": "Farm",
    "id": 4
  },
  ...
}

Caused by: org.hibernate.HibernateException: identifier of an instance of com.phrankly.places.model.Category was altered from 2 to 4

I'm not sure how that's even happening, given I don't set any Cascade options - Categories are managed elsewhere.

I don't want to have to write a custom Controller for Place. I also tried using an EventHandler to manually set the field to see if that helped.

@Component
@RepositoryEventHandler(Place.class)
public class PlaceEventHandler {

    @Autowired
    private CategoryRepository categoryRepository;

  ...

    @HandleBeforeSave
    public void onUpdateExisting(Place place) {
        if(place.getCategory() != null){
            // or find by another field if you're not exposing IDs
            place.setCategory(categoryRepository.findOne(place.getCategory().getId()));
        }
    }

  ...
}

But, no change in functionality. I know I'm going outside of what Spring Data REST suggests, but what can I do here? Is this even a SDR issue or am I mapping this incorrectly?

bvulaj
  • 5,023
  • 5
  • 31
  • 45
  • Can you try `{... "category": 4 ...}`? – Alan Hay Dec 19 '16 at 09:19
  • @AlanHay No luck with that, `Can not construct instance of Category: no int/Int-argument constructor/factory method to deserialize from Number value (4)` – bvulaj Dec 19 '16 at 18:34
  • I have reread you question and would expect that the standard SDR functionality would work in this case unless I am missing something. To update you should making a PATCH request to /places/foo/123 with body of, for example, `{"category" : "http://localhost:9090/api/categories/4" }` where the URL is obviously pointing the resource of the Category you wish to set. There is no requirement to send the other fields. – Alan Hay Dec 19 '16 at 18:46
  • @AlanHay, Negative, since that is not exported, it expects a Category object. Passing a URI as a field param is just interpreted as a String value, which doesn't deserialize. I'm not doing anything special here, so I'm having trouble understanding why this isn't working. It's a little outside of recommended SDR practices, but still. – bvulaj Dec 19 '16 at 19:03
  • I came across this comment before posting, but I've got to believe there *is* a way to handle this. http://stackoverflow.com/a/34767212/621686 – bvulaj Dec 19 '16 at 21:53
  • And for what it's worth, if I intercept the PUT in my own custom Controller method, and just save the entity through my Repository, it works fine. So, I believe this is a spring data rest issue moreso than anything else. – bvulaj Dec 19 '16 at 22:06
  • This would probably work even if you just sent `{... "category": 4 ...}` I originally suggested trying this because I observed that in recent Spring MVC versions it is smart enough when processing a reuest body to fetch the referenced entity (i.e. the Category with ID 4), and set the association without having to register any converter - check your SQL logs. It is sometimes hard to know where the Spring MVC, HATEOAS and Data Rest libraries overlap and what part is doing what! One thing around an MVC rest controller is no PATCH support though http://stackoverflow.com/a/39424421/1356423 – Alan Hay Dec 19 '16 at 22:25
  • Yeah, the no PATCH support is a bummer. I think your suggestion might work, but I don't want to resort to that quite yet I guess. I feel like this should still work, but there is no case where this setup does work. I've opened a bug in the SDR project for now. https://jira.spring.io/browse/DATAREST-963 – bvulaj Dec 19 '16 at 22:39

0 Answers0