Java 弹簧:can';t用@JsonIgnore注释的持久化实体
我是Spring Boot的初学者,无法解决问题。我有一个实体类(Customer)和一个REST存储库(CustomerRepository)。该类包含一些我不想被REST存储库公开的敏感字段。因此,我用@JsonIgnore注释对这些字段进行了注释,如下所示:Java 弹簧:can';t用@JsonIgnore注释的持久化实体,java,spring-data,spring-data-jpa,spring-data-rest,Java,Spring Data,Spring Data Jpa,Spring Data Rest,我是Spring Boot的初学者,无法解决问题。我有一个实体类(Customer)和一个REST存储库(CustomerRepository)。该类包含一些我不想被REST存储库公开的敏感字段。因此,我用@JsonIgnore注释对这些字段进行了注释,如下所示: package br.univali.sapi.entities; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import j
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;
}
}
public interface CustomerViewDTO
{
public Long getId();
public String getLogin();
public String getName();
}
一切正常,我的restapi返回了期望的结果。但是,当我向API发出POST请求以插入新实体时,我会收到数据库错误:“列密码不能为空”、“列电子邮件不能为空”
密码和电子邮件将与其他参数一起发送到POST请求中的服务器,但是它似乎被忽略。如果删除@JsonIgnore注释,实体将正常持久化
我知道我可以用投影来隐藏这些字段。但投影是URL中的可选参数。这样,有经验的用户就可以从请求URL中删除参数,并查看这些字段
如果我可以隐式地强制执行一个投影,这将解决问题,但仅使用Spring框架似乎不可能做到这一点。我可以使用ApacheURL重写来实现这一点,但是维护会很糟糕
有人知道我如何解决这个问题吗?
谢谢
编辑1:
我相信我找到了一个使用DTO/projections(数据传输对象)的解决方案。您必须创建两个DTO,一个用于显示实体,另一个用于更新实体,如下所示:
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;
}
}
public interface CustomerViewDTO
{
public Long getId();
public String getLogin();
public String getName();
}
现在,在存储库上使用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);
}
我想您在db级别上没有空约束。基本上,当您对字段执行
@JsonIgnore
操作时,您只是不传递它,数据库无法插入该值,这一点非常清楚
因此,我在这里看到的解决方案如下:
1) 删除@JsonIgnore
注释,以便能够执行POST
请求
2) 使用GET
时使用投影。您可以将Spring-Data-REST
配置为始终使用投影,但是默认情况下Spring-Data-REST
始终在单个资源上返回完整的JSON
(在您使用电子邮件和密码的情况下),这对我来说非常奇怪。我写了这个问题的解决方案,因为我也有同样的问题。只需使用ProjectingProcessor
,并为所有投影使用一些默认名称。不要忘记使用config.getProjectionConfiguration().addProjection
方法在配置中添加投影
3) 避免步骤2的唯一方法是禁止
在Spring数据REST
级别的单个资源上获取,并使用自定义控制器。但是我会使用步骤2中的解决方案来避免样板控制器。也许可以帮助您。注释也很有趣。将注释添加到密码和电子邮件设置器中。即使相关字段被标记为已忽略,也应允许反序列化。或者,根据建议,仅将@JsonIgnore
应用于getter您可以使用@RepositoryRestResource
的摘录投影
参数将投影设置为默认投影。
@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);
}
}