【问题标题】:Spring + Hibernate RESTful ResourceSpring + Hibernate RESTful 资源
【发布时间】:2014-04-06 08:44:36
【问题描述】:

我是 Spring 新手,我正在尝试创建一个 RESTful 资源以在我的 API 上使用。到目前为止,我能够列出元素 (GET /people.json)、显示特定元素 (GET /people/{id}.json)、创建新元素 (POST /people.json) 和删除元素 (删除 /people/{id}.json)。我的最后一步是处理更新方法(PUT /people/{id}.json,但我还不知道该怎么做。

这是我的控制器的代码:

包 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\": \"\"}";
    }
}

这是我的服务的代码:

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);
        }
    }   
}

那么,对于 Spring,那里的 hibernate 专家,什么是完成我需要的最佳方法?同样重要的是资源更新只更新实际更改的属性

谢谢,

【问题讨论】:

    标签: java spring hibernate rest


    【解决方案1】:

    更改您的PersonService 更新方法签名,使其返回Person,此外,您不需要单独的id,只需使用Person 对象来保存ID。他们实现这样的方法:

    public Person update(Person person) {
        // you might want to validate the person object somehow
        return em.merge(person);
    }
    

    也相应地更新您的网络服务层。要么更改它以使 personId 不再存在,要么保留它并使用它设置 Person id:

    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 + "}";
    }   
    

    一个小问题,合并也可能会保留新实体。因此,如果您想确保 update 仅更新现有的 ids,请将其正文更改为:

    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);
    }
    

    同样重要的是资源更新只更新实际改变的属性

    如果您希望 hibernate 仅更新您的 JSON 对象和数据库之间不同的属性,@org.hibernate.annotations.Entity 上有一个方便的组合 dynamicUpdate 和 selectBeforeUpdate 属性(查看 Hibernate Manual)。仅当您确实需要该行为时才启用此功能(这是非标准行为,可能会降低性能)。

    @org.hibernate.annotations.Entity(dynamicUpdate = true, selectBeforeUpdate = true)
    

    在 Hibernate 4.0 上,那些不推荐使用的属性,您可以使用单独的注释来代替:

    @DynamicUpdate
    @SelectBeforeUpdate
    

    【讨论】:

    • 谢谢你,安东尼。这看起来像答案,但也许仍然缺少一些东西。当我使用参数 {"firstName": "John"} 在 API 上调用 put 时,我会返回带有无效名字和姓氏的 json(尽管 id 没有)。该记录也没有在数据库中更新。我需要在 person 对象上设置所有其他参数吗?
    • marcel,您正在发送具有多个 null 属性的部分对象,Hibernate 将此解释为“更新所有缺失字段的记录设置 null。如果您的 id 不存在,它将插入一个新纪录。
    • 查看我的更新以了解如何处理动态更新。如果它没有削减它,您将被困在查询最后一个持久实体状态 (find) 并手动更新非空属性:if (person.getFirstName() != null &amp;&amp; !dbPerson.getFirstName().equals(person.getFirstName())) dbPerson.setFirstName(person.getFirstName()); 但这是丑陋而冗长的代码。
    • Anthony,我收到了您的最新消息,谢谢。但是,问题似乎在此之前。我的控制器方法签名: public String update(@PathVariable("personId") Integer personId, @ModelAttribute("person") Person person, BindingResult result) 可能不正确,因为当我打印 person 对象时它没有 firstName或姓氏填充。它适用于我的 create 方法,所以我想知道这是否是进行更新的正确方法(它们之间的区别是 personId 路径变量)
    • Id 进来就好了。这正是我正在做的: PUT "localhost:8080/api/people/7.json", {firstName: "Lara", lastName: "Croft"} 并且 person 对象带有 firstName null 和 lastName null。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-06-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多