如何编写RestController来从XML请求更新JPA实体,springdatajpa方式?
我有一个数据库,其中有一个名为person的表:如何编写RestController来从XML请求更新JPA实体,springdatajpa方式?,spring,spring-mvc,spring-boot,spring-data-jpa,Spring,Spring Mvc,Spring Boot,Spring Data Jpa,我有一个数据库,其中有一个名为person的表: id | first_name | last_name | date_of_birth ----|------------|-----------|--------------- 1 | Tin | Tin | 2000-10-10 有一个名为Person的JPA实体映射到此表: @实体 @XmlRootElement(name=“person”) @XmlAccessorType(无) 公共阶层人士{
id | first_name | last_name | date_of_birth
----|------------|-----------|---------------
1 | Tin | Tin | 2000-10-10
有一个名为Person
的JPA实体映射到此表:
@实体
@XmlRootElement(name=“person”)
@XmlAccessorType(无)
公共阶层人士{
@身份证
@生成值
私人长id;
@xmltattribute(name=“id”)
私人长期外部化;
@XmlAttribute(name=“first name”)
私有字符串名;
@XmlAttribute(name=“last name”)
私有字符串lastName;
@xmltattribute(name=“dob”)
出生日期的私有字符串;
//二传手和接球手
}
实体还使用JAXB注释进行注释,以允许XML有效负载进入
要映射到实体实例的HTTP请求
我想实现一个端点,用于检索和更新具有给定id
的实体
根据,,
我需要做的就是实现如下处理程序方法:
@RestController
@请求映射(
path=“/persons”,
消费=应用程序\u XML\u值,
生成=应用程序\u XML\u值
)
公共类个人控制器{
私人最终个人储存库个人储存库;
@自动连线
公共人员控制器(最终人员存储库人员存储库){
this.personRepository=personRepository;
}
@PutMapping(value=“/{person}”)
公共人物savePerson(@modeldattribute Person){
返回personRepository.save(个人);
}
}
但是,这并没有按照预期工作,这可以通过以下失败测试用例进行验证:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment=RANDOM_端口)
公共类PersonControllerTest{
@自动连线
私有TestRestTemplate;
私有HttpHeader;
@以前
在()之前公开无效{
headers=新的HttpHeaders();
headers.setContentType(应用程序XML);
}
//测试失败
@试验
@肮脏的环境
public void testSavePerson(){
最终HttpEntity请求=新HttpEntity(“,标头);
final ResponseEntity response=restemplate.exchange(“/persons/1”,PUT,request,Person.class,“1”);
断言(response.getStatusCode(),equalTo(OK));
final Person body=response.getBody();
assertThat(body.getFirstName(),equalTo(“Tin”);//失败
资产(body.getLastName(),equalTo(“Herge”);
资产(body.getDateOfBirth(),equalTo(“1907-05-22”);
}
}
第一个断言失败,原因是:
java.lang.AssertionError:
Expected: "Tin Tin"
but: was "Tin"
Expected :Tin Tin
Actual :Tin
换言之:
- 没有发生服务器端异常(状态代码为
)200
- Spring成功加载id为1的Person实例
- 但它的属性不会得到更新
附注1 提供的解决方案不起作用 附注2 提供了演示问题的完整工作代码 更多细节 预期行为:
Jaxb2RootElementHttpMessageConverter
或MappingJackson2XmlHttpMessageConverter
person
参数传递给控制器的操作处理程序@PutMapping(value = "/{id}")
public Person savePerson(@RequestBody Person person, @PathVariable("id") Long id ) {
Person found = personRepository.findOne(id);
//merge 'found' from database with send person, or just send it with id
//Person merged..
return personRepository.save(merged);
}
问题是,当您调用
personRepository.save(person)
时,您的person实体没有主键字段(id),因此数据库最终有两条记录,新记录主键由db生成。修复方法是为id
字段创建一个setter,并在保存之前使用它设置实体的id:
@PutMapping(value=“/{id}”)
public Person savePerson(@RequestBody Person,@PathVariable(“id”)Long id){
person.setId(id);
返回personRepository.save(个人);
}
此外,正如@freakman所建议的,您应该使用@RequestBody
捕获原始json/xml并将其转换为域模型。另外,如果您不想为主键字段创建setter,另一个选项可能是支持基于任何其他唯一字段(如externalId)的更新操作,并调用它
@PutMapping(value = "/{id}")
public Person savePerson(@PathVariable Long id, @RequestBody Person person) {
Person persisted = personRepository.findOne(id);
if (persisted != null) {
persisted.setFirstName(person.getFirstName());
persisted.setLastName(person.getLastName());
persisted.setDateOfBirth(person.getDateOfBirth());
return persisted;
} else {
return personRepository.save(person);
}
}
更新
@PutMapping(value = "/{person}")
public Person savePerson(@ModelAttribute Person person, @RequestBody Person req) {
person.setFirstName(req.getFirstName());
person.setLastName(req.getLastName());
person.setDateOfBirth(req.getDateOfBirth());
return person;
}
要更新任何实体,加载和保存必须在同一事务中,否则它将在save()调用时创建一个新的实体,或者引发重复的主键约束冲突异常 要更新任何实体,我们需要将实体、load()/find()和save()放在同一事务中,或者在@Repository类中编写JPQL更新查询,并用@modify注释该方法 @修改注释不会触发额外的select查询来加载实体对象以更新它,而是假定