Spring 数据 JPA CRUD 存储库接口 JPQL @Query 生成自定义查询失败
Spring Data JPA CRUD Repository Interface JPQL @Query generating custom query fails
我正在研究基于休息的(不是 restful)api 并且遇到以下问题。尝试使用 JPQL 创建自定义查询以更新客户信息。实体客户中的成员电子邮件是唯一的,所以我必须编写自己的查询,否则会导致唯一约束违反异常。到目前为止我发现了这一点,现在我正尝试通过在 Spring CRUD 存储库接口中使用 JPQL 编写自定义查询来修复。
客户控制器
@RestController public class CustomerController {
@Autowired
private CustomerService customerService;
@GetMapping("/customers")
public List<CustomerDTO> allCustomer(){
return customerService.findAll();
}
@GetMapping("/customers/{id}")
public CustomerDTO oneCustomer(@PathVariable("id") long id) {
return customerService.findOneById(id);
}
@PostMapping("/customers")
public CustomerDTO addCustomer(@RequestBody CustomerDTO customerDTO) {
return customerService.saveCustomer(customerDTO);
}
@PutMapping("/customers/{id}")
public CustomerDTO updateCustomer(@RequestBody CustomerDTO customerDTO) {
return customerService.updateCustomer(customerDTO);
}
@DeleteMapping("/customers/{id}")
public void deleteCustomer(@PathVariable("id") long id) {
customerService.deleteCustomer(id);
}
}
客户服务
@Service
public class CustomerService {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private CustomerDTO customerDTO;
@Autowired
private ModelMapper modelMapper;
private final Logger log = LoggerFactory.getLogger(CustomerService.class);
// Gebe Liste von Kunden zurück
public List<CustomerDTO> findAll() {
var it = customerRepository.findAll();
var customerList = new ArrayList<CustomerDTO>();
for (Customer customer : it) {
customerDTO = convertToDto(customer);
customerList.add(customerDTO);
}
return customerList;
}
// Gebe einen bestimmten Kunden zurück
public CustomerDTO findOneById(long id) {
Customer customer = customerRepository.findById(id);
CustomerDTO customerDTO = convertToDto(customer);
return customerDTO;
}
// Speicher einen Kunden in der Datenbank und gebe diesen zurück
public CustomerDTO saveCustomer(CustomerDTO customerDTO) {
if (customerDTO != null) {
Customer savedObject = customerRepository.save(convertToEntity(customerDTO));
// Abrufen der gespeicherten Entity und Umwandlung in DTO, weil DTO nun weitere Werte enthält als zuvor (Id & timestamp)
CustomerDTO responseCustomer = convertToDto(customerRepository.findById(savedObject.getId()).get());
return responseCustomer;
} else {
log.info("Kunden speichern in die Datenbank fehlgeschlagen");
return null;
}
}
// Kundendaten bearbeiten
public CustomerDTO updateCustomer(CustomerDTO customerDTO) {
if (customerDTO != null) {
Customer updatedObject = customerRepository.updateCustomerByDTO(convertToEntity(customerDTO));
// Abrufen der gespeicherten Entity und Umwandlung in DTO
Customer getCustomer = customerRepository.findById(updatedObject.getId()).get();
CustomerDTO responseCustomer = convertToDto(getCustomer);
return responseCustomer;
} else {
log.info("Bearbeiten des Kunden in der Datenbank fehlgeschlagen!");
return null;
}
}
// Lösche Kunden aus der Datenbank
public void deleteCustomer(Long id) {
customerRepository.deleteById(id);
}
// Umwandlung von Entity zu DTO Objekt
public CustomerDTO convertToDto(Customer customer) {
CustomerDTO customerDTO = modelMapper.map(customer, CustomerDTO.class);
return customerDTO;
}
// Umwandlung von DTO zu Entity Objekt
private Customer convertToEntity(CustomerDTO customerDTO) {
Customer customer = modelMapper.map(customerDTO, Customer.class);
return customer;
}
}
CustomerRepository
public interface CustomerRepository extends CrudRepository<Customer, Long> {
/*
* Here we can create our custom search queries on CustomerRepository
*/
List<Customer> findBySurname(String surname);
Customer findById(long id);
Customer findByEmail(String email);
//Update Customer workaround email field ConstraintViolationException
@Transactional
@Modifying
@Query("UPDATE Customer c SET c.given_name = :#{#customer.given_name}, c.surname = :#{#customer.surname}, c.birthday= :#{#customer.birthday},"
+ " c.street_address = :#{#customer.street_address}, c.city = :#{#customer.city}, c.postal_code = :#{#customer.postal_code},"
+ " c.phone_number = :#{#customer.phone_number}, c.balance= :#{#customer.balance}, c.bonuspoints= :#{#customer.bonuspoints}"
+ " WHERE c.id = :#{#customer.id} ")
Customer updateCustomerByDTO(@Param("customer") Customer customer);
}
这导致跟踪 Stacktrace,到目前为止我没有找到任何解决方案。
堆栈跟踪
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerController': Unsatisfied dependency expressed through field 'customerService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerService': Unsatisfied dependency expressed through field 'customerRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerRepository' defined in com.yildiz.tradilianz.customer.CustomerRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Validation failed for query for method public abstract com.yildiz.tradilianz.customer.Customer com.yildiz.tradilianz.customer.CustomerRepository.updateCustomerByDTO(com.yildiz.tradilianz.customer.Customer)!
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643) ~[spring-beans-5.3.2.jar:5.3.2]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.2.jar:5.3.2]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.3.2.jar:5.3.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1415) ~[spring-beans-5.3.2.jar:5.3.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:608) ~[spring-beans-5.3.2.jar:5.3.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:531) ~[spring-beans-5.3.2.jar:5.3.2]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=14=](AbstractBeanFactory.java:335) ~[spring-beans-5.3.2.jar:5.3.2]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.2.jar:5.3.2]
at .......
Stacktrace 告诉我,验证是错误的,但我认为我的 JPQL 查询是正确的。我很困惑你知道我做错了什么吗?
命名
命名可能有问题。您没有包括您的客户实体。它真的像您的 JPQL 查询建议的那样使用 snake_case 命名约定吗?
在 JPQL 中,您应该使用 与 Java class 中一样的确切字段名称。通常,它是 camelCase 命名约定,而 snake_case 在数据库中使用。
Return值
方法签名肯定有问题。修改查询只能使用void或int/Integer作为return类型。
假设您确实为 Customer 使用了 snake_case,并且在将 updateCustomerByDTO
的 return 类型更改为 void 之后,查询工作正常。
void updateCustomerByDTO(@Param("customer") Customer customer);
关于如何处理不可更新电子邮件问题的另一个提示。您可以只使用 @Column
注释并将 updatable
属性设置为 false
。如果你真的不想更新电子邮件,那会容易得多。
@Entity
class Customer {
...
@Column(updatable = false)
String email;
...
}
我正在研究基于休息的(不是 restful)api 并且遇到以下问题。尝试使用 JPQL 创建自定义查询以更新客户信息。实体客户中的成员电子邮件是唯一的,所以我必须编写自己的查询,否则会导致唯一约束违反异常。到目前为止我发现了这一点,现在我正尝试通过在 Spring CRUD 存储库接口中使用 JPQL 编写自定义查询来修复。
客户控制器
@RestController public class CustomerController {
@Autowired
private CustomerService customerService;
@GetMapping("/customers")
public List<CustomerDTO> allCustomer(){
return customerService.findAll();
}
@GetMapping("/customers/{id}")
public CustomerDTO oneCustomer(@PathVariable("id") long id) {
return customerService.findOneById(id);
}
@PostMapping("/customers")
public CustomerDTO addCustomer(@RequestBody CustomerDTO customerDTO) {
return customerService.saveCustomer(customerDTO);
}
@PutMapping("/customers/{id}")
public CustomerDTO updateCustomer(@RequestBody CustomerDTO customerDTO) {
return customerService.updateCustomer(customerDTO);
}
@DeleteMapping("/customers/{id}")
public void deleteCustomer(@PathVariable("id") long id) {
customerService.deleteCustomer(id);
}
}
客户服务
@Service
public class CustomerService {
@Autowired
private CustomerRepository customerRepository;
@Autowired
private CustomerDTO customerDTO;
@Autowired
private ModelMapper modelMapper;
private final Logger log = LoggerFactory.getLogger(CustomerService.class);
// Gebe Liste von Kunden zurück
public List<CustomerDTO> findAll() {
var it = customerRepository.findAll();
var customerList = new ArrayList<CustomerDTO>();
for (Customer customer : it) {
customerDTO = convertToDto(customer);
customerList.add(customerDTO);
}
return customerList;
}
// Gebe einen bestimmten Kunden zurück
public CustomerDTO findOneById(long id) {
Customer customer = customerRepository.findById(id);
CustomerDTO customerDTO = convertToDto(customer);
return customerDTO;
}
// Speicher einen Kunden in der Datenbank und gebe diesen zurück
public CustomerDTO saveCustomer(CustomerDTO customerDTO) {
if (customerDTO != null) {
Customer savedObject = customerRepository.save(convertToEntity(customerDTO));
// Abrufen der gespeicherten Entity und Umwandlung in DTO, weil DTO nun weitere Werte enthält als zuvor (Id & timestamp)
CustomerDTO responseCustomer = convertToDto(customerRepository.findById(savedObject.getId()).get());
return responseCustomer;
} else {
log.info("Kunden speichern in die Datenbank fehlgeschlagen");
return null;
}
}
// Kundendaten bearbeiten
public CustomerDTO updateCustomer(CustomerDTO customerDTO) {
if (customerDTO != null) {
Customer updatedObject = customerRepository.updateCustomerByDTO(convertToEntity(customerDTO));
// Abrufen der gespeicherten Entity und Umwandlung in DTO
Customer getCustomer = customerRepository.findById(updatedObject.getId()).get();
CustomerDTO responseCustomer = convertToDto(getCustomer);
return responseCustomer;
} else {
log.info("Bearbeiten des Kunden in der Datenbank fehlgeschlagen!");
return null;
}
}
// Lösche Kunden aus der Datenbank
public void deleteCustomer(Long id) {
customerRepository.deleteById(id);
}
// Umwandlung von Entity zu DTO Objekt
public CustomerDTO convertToDto(Customer customer) {
CustomerDTO customerDTO = modelMapper.map(customer, CustomerDTO.class);
return customerDTO;
}
// Umwandlung von DTO zu Entity Objekt
private Customer convertToEntity(CustomerDTO customerDTO) {
Customer customer = modelMapper.map(customerDTO, Customer.class);
return customer;
}
}
CustomerRepository
public interface CustomerRepository extends CrudRepository<Customer, Long> {
/*
* Here we can create our custom search queries on CustomerRepository
*/
List<Customer> findBySurname(String surname);
Customer findById(long id);
Customer findByEmail(String email);
//Update Customer workaround email field ConstraintViolationException
@Transactional
@Modifying
@Query("UPDATE Customer c SET c.given_name = :#{#customer.given_name}, c.surname = :#{#customer.surname}, c.birthday= :#{#customer.birthday},"
+ " c.street_address = :#{#customer.street_address}, c.city = :#{#customer.city}, c.postal_code = :#{#customer.postal_code},"
+ " c.phone_number = :#{#customer.phone_number}, c.balance= :#{#customer.balance}, c.bonuspoints= :#{#customer.bonuspoints}"
+ " WHERE c.id = :#{#customer.id} ")
Customer updateCustomerByDTO(@Param("customer") Customer customer);
}
这导致跟踪 Stacktrace,到目前为止我没有找到任何解决方案。 堆栈跟踪
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerController': Unsatisfied dependency expressed through field 'customerService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customerService': Unsatisfied dependency expressed through field 'customerRepository'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'customerRepository' defined in com.yildiz.tradilianz.customer.CustomerRepository defined in @EnableJpaRepositories declared on JpaRepositoriesRegistrar.EnableJpaRepositoriesConfiguration: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: Validation failed for query for method public abstract com.yildiz.tradilianz.customer.Customer com.yildiz.tradilianz.customer.CustomerRepository.updateCustomerByDTO(com.yildiz.tradilianz.customer.Customer)!
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:643) ~[spring-beans-5.3.2.jar:5.3.2]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:119) ~[spring-beans-5.3.2.jar:5.3.2]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:399) ~[spring-beans-5.3.2.jar:5.3.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1415) ~[spring-beans-5.3.2.jar:5.3.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:608) ~[spring-beans-5.3.2.jar:5.3.2]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:531) ~[spring-beans-5.3.2.jar:5.3.2]
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=14=](AbstractBeanFactory.java:335) ~[spring-beans-5.3.2.jar:5.3.2]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-5.3.2.jar:5.3.2]
at .......
Stacktrace 告诉我,验证是错误的,但我认为我的 JPQL 查询是正确的。我很困惑你知道我做错了什么吗?
命名
命名可能有问题。您没有包括您的客户实体。它真的像您的 JPQL 查询建议的那样使用 snake_case 命名约定吗?
在 JPQL 中,您应该使用 与 Java class 中一样的确切字段名称。通常,它是 camelCase 命名约定,而 snake_case 在数据库中使用。
Return值
方法签名肯定有问题。修改查询只能使用void或int/Integer作为return类型。
假设您确实为 Customer 使用了 snake_case,并且在将 updateCustomerByDTO
的 return 类型更改为 void 之后,查询工作正常。
void updateCustomerByDTO(@Param("customer") Customer customer);
关于如何处理不可更新电子邮件问题的另一个提示。您可以只使用 @Column
注释并将 updatable
属性设置为 false
。如果你真的不想更新电子邮件,那会容易得多。
@Entity
class Customer {
...
@Column(updatable = false)
String email;
...
}