【发布时间】:2017-07-27 15:08:01
【问题描述】:
我是 Spring Boot 的初学者,无法解决问题。我有一个实体类 (Customer) 和一个 REST 存储库 (CustomerRepository)。该类包含一些我不想被 REST 存储库公开的敏感字段。所以,我用@JsonIgnore 注解对这些字段进行了如下注解:
package br.univali.sapi.entities;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
public class Customer
{
@Id
@GeneratedValue
private Long id = null;
private String login;
private String name;
@JsonIgnore
private String password;
@JsonIgnore
private String email;
public Customer()
{
}
public Long getId()
{
return id;
}
public void setId(Long id)
{
this.id = id;
}
public String getLogin()
{
return login;
}
public void setLogin(String login)
{
this.login = login;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public String getPassword()
{
return password;
}
public void setPassword(String password)
{
this.password = password;
}
public String getEmail()
{
return email;
}
public void setEmail(String email)
{
this.email = email;
}
}
一切正常,我的 REST API 返回了预期的结果。但是,当我向 API 发出 POST 请求以插入新实体时,我收到数据库错误:"column password can't be null", "column email can't be null"。
密码和电子邮件与其他参数一起在 POST 请求中发送到服务器,但似乎被忽略了。如果我删除 @JsonIgnore 注释,实体将正常保留。
我知道我可以使用投影来隐藏这些字段。但投影是 URL 中的可选参数。这样,有经验的用户将能够从请求 URL 中删除参数并看到这些字段。
如果我可以隐式执行投影,那将解决问题,但这似乎只使用 Spring 框架是不可能的。我可以使用 Apache URL 重写来实现这一点,但维护会很糟糕。
有人知道我该如何解决这个问题吗? 谢谢!
编辑 1:
我相信我找到了使用 DTO/投影(数据传输对象)的解决方案。您必须创建两个 DTO,一个用于显示实体,另一个用于更新实体,如下所示:
public interface CustomerViewDTO
{
public Long getId();
public String getLogin();
public String getName();
}
public class CustomerUpdateDTO
{
private String login;
private String name;
private String password;
private String email;
// Getters and Setters ommited for breviety
}
现在,在您使用 DTO 的存储库上,Spring 会发挥它的魔力:
@Transactional(readOnly = true)
public interface CustomerRepository extends JPARepository<Customer, Long>
{
// Using derived query
public CustomerViewDTO findByIdAsDTO(Long id);
// Using @Query annotation
@Query("SELECT C FROM Customer C WHERE C.id = :customerId")
public CustomerViewDTO findByIdAsDTO(@Param("customerId") Long id);
}
对于更新,您会在控制器上收到 DTO,并将其映射到服务上的实体,如下所示:
@RestController
public class CustomerController
{
@Autowired
private CustomerService customerService;
@RequestMapping(method = "PATCH", path = "/customers/{customerId}")
public ResponseEntity<?> updateCustomer(@PathVariable Long customerId, @RequestBody CustomerUpdateDTO customerDTO)
{
CustomerViewDTO updatedCustomer = customerService.updateCustomer(customerId, customerDTO);
return ResponseEntity.ok(updatedCustomer);
}
@RequestMapping(method = GET, path = "/customers/{customerId}")
public ResponseEntity<?> findCustomerById(@PathVariable Long customerId)
{
return ResponseEntity.ok(customerService.findByIdAsDTO(Long));
}
}
@Service
public class CustomerService
{
@Autowired
private CustomerRepository customerRepository;
// Follow this tutorial:
// https://www.baeldung.com/entity-to-and-from-dto-for-a-java-spring-application
@Autowired
private ModelMapper modelMapper;
@Transactional(readOnly = false)
public CustomerViewDTO updateCustomer(Long customerId, CustomerUpdateDTO customerDTO)
{
Customer customerEntity = customerRepository.findById(customerId);
// This copies all values from the DTO to the entity
modelMapper.map(customerDTO, customerEntity);
// Now we have two aproaches:
// 1. save the entity and convert back to DTO manually using the model mapper
// 2. save the entity and call the repository method which will convert to the DTO automatically
// The second approach is the one I use for several reasons that
// I won't explain here
// Here we use save and flush to force JPA to execute the update query. This way, when we do the select the entity will come with the fields updated
customerEntity = customerRepository.saveAndFlush(customerEntity);
// First aproach
return modelMapper.map(customerEntity, CustomerViewDTO.class);
// Second approach
return customerRepository.findByIdAsDTO(customerId);
}
@Transactional(readOnly = true)
public CustomerViewDTO findByIdAsDTO(Long customerId)
{
return customerRepository.findByIdAsDTO(customerId);
}
}
【问题讨论】:
-
也许this question 可以帮助你。 cmets 也很有趣。
-
将@JsonSetter 注释添加到您的密码和电子邮件设置器中。即使相关字段被标记为忽略,它也应该允许反序列化。或者,按照here 的建议,仅将
@JsonIgnore应用于吸气剂
标签: java spring-data spring-data-jpa spring-data-rest