Reputation: 2035
I am new to Spring, and I am trying to create a RESTful resource to use on my API. So far, I was able to list elements (GET /people.json), show a specific element (GET /people/{id}.json), create a new element (POST /people.json), and delete an element (DELETE /people/{id}.json). My last step is to work on the update method (PUT /people/{id}.json, but I have no idea how to do it yet.
Here is the code I have for my controller:
package com.example.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.example.model.Person;
import com.example.service.PersonService;
import java.util.Map;
@Controller
public class PersonController {
@Autowired
private PersonService personService;
@ResponseBody
@RequestMapping(value = "/people.json", method = RequestMethod.GET)
public String all(Map<String, Object> map) {
return "{\"people\": " + personService.all() + "}";
}
@ResponseBody
@RequestMapping(value = "/people/{personId}.json", method = RequestMethod.GET)
public String show(@PathVariable("personId") Integer personId) {
Person person = personService.find(personId);
return "{\"person\": "+person+"}";
}
@ResponseBody
@RequestMapping(value = "/people.json", method = RequestMethod.POST)
public String create(@ModelAttribute("person") Person person, BindingResult result) {
personService.create(person);
return "{\"person\": "+person+"}";
}
@ResponseBody
@RequestMapping(value = "/people/{personId}.json", method = RequestMethod.PUT)
public String update(@PathVariable("personId") Integer personId, @ModelAttribute("person") Person person, BindingResult result) {
personService.update(personId, person);
return "{\"person\": "+person+"}";
}
@ResponseBody
@RequestMapping(value = "/people/{personId}.json", method = RequestMethod.DELETE)
public String delete(@PathVariable("personId") Integer personId) {
personService.delete(personId);
return "{\"status\": \"ok\", \"message\": \"\"}";
}
}
And here is the code for my service:
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.example.model.Person;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import java.util.List;
@Service
public class PersonServiceImpl implements PersonService {
@PersistenceContext
EntityManager em;
@Transactional
public List<Person> all() {
CriteriaQuery<Person> c = em.getCriteriaBuilder().createQuery(Person.class);
c.from(Person.class);
return em.createQuery(c).getResultList();
}
@Transactional
public Person find(Integer id) {
Person person = em.find(Person.class, id);
return person;
}
@Transactional
public void create(Person person) {
em.persist(person);
}
public void update(Integer id, Person person) {
// TODO: How to implement this?
}
@Transactional
public void delete(Integer id) {
Person person = em.find(Person.class, id);
if (null != person) {
em.remove(person);
}
}
}
So, for the Spring, hibernate experts out there, what is the best way to accomplish what I need? It is also important that the resource update only updates the actual changed attributes
Thanks,
Upvotes: 2
Views: 1844
Reputation: 22461
Change your PersonService
update method signature so that it returns a Person
, also, you don't need an separate id
, just use the Person
object to hold the id. Them implement the method like this:
public Person update(Person person) {
// you might want to validate the person object somehow
return em.merge(person);
}
Also update your web service layer accordingly. Either change it so that personId
is no longer there, or keep it and set the Person
id with it:
public String update(@PathVariable("personId") Integer personId,
@ModelAttribute("person") Person person, BindingResult result) {
person.setId(personId);
Person updated = personService.update(person);
// You may want to use a JSON library instead of overriding toString.
return "{\"person\": " + updated + "}";
}
One small gotcha, merge may also persist new entities. So, if you want to make sure that update
only updates existing id
s, change its body to something like:
public Person update(Person person) {
if(em.find(Person.class, person.getId()) == null) {
throw new IllegalArgumentException("Person " + person.getId()
+ " does not exists");
}
return em.merge(person);
}
It is also important that the resource update only updates the actual changed attributes
If you want hibernate to update only attributes that are different between your JSON object and the database there is a handy combo of dynamicUpdate
and selectBeforeUpdate
properties on @org.hibernate.annotations.Entity
(check out the Hibernate Manual). Only enable this if you really need the behavior (which is non standard behavior and may decrease performance).
@org.hibernate.annotations.Entity(dynamicUpdate = true, selectBeforeUpdate = true)
On Hibernate 4.0 those properties where deprecated, you can use individual annotations instead:
@DynamicUpdate
@SelectBeforeUpdate
Upvotes: 1