【问题标题】:Spring Data JPA CRUD Repository Interface JPQL @Query generating custom query failsSpring Data JPA CRUD 存储库接口 JPQL @Query 生成自定义查询失败
【发布时间】:2021-04-13 03:41:27
【问题描述】:

我正在开发一个基于休息(不是休息)的 api,并遇到以下问题。尝试使用 JPQL 创建自定义查询以更新客户信息。实体客户中的成员电子邮件是唯一的,因此我必须编写自己的查询,否则会导致唯一约束违反异常。到目前为止,我发现了这一点,现在我正在尝试通过使用 JPQL 在 Spring CRUD 存储库接口中编写自定义查询来修复。

客户控制器

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

}

客户存储库

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$0(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 查询是正确的。我很困惑你知道我做错了什么吗?

【问题讨论】:

  • 你从哪里得到你在更新中使用的语法?
  • logicbig.com/tutorials/java-ee-tutorial/jpa/update-queries.html 从这里。我认为这是有效的 JPQL?
  • 既然您将Customer 实体传递给更新方法,为什么不直接更新整个实体呢?您真的打算只更新某些字段吗?
  • 我传递整个实体以避免手动编辑字段。我认为如果我动态地保持它会更好。是的,因为在我的情况下,电子邮件字段不应该是可更新的。也许这不是逻辑,但这是我应该学会处理的场景..
  • 不要这样做,除非你真的只想更新某些字段。

标签: java spring-boot jpa spring-data-jpa jpql


【解决方案1】:

命名

命名可能有问题。您没有包括您的客户实体。它真的像您的 JPQL 查询所建议的那样使用 snake_case 命名约定吗?

在 JPQL 中,您应该使用与 Java 类中完全相同的字段名称。通常是 camelCase 命名约定,而在数据库中使用的是 snake_case。

返回值

方法签名肯定有问题。修改查询只能使用void或int/Integer作为返回类型。

假设您确实为 Customer 使用了一个 snake_case,并且在将 updateCustomerByDTO 的返回类型更改为 void 之后,查询工作正常。

void updateCustomerByDTO(@Param("customer") Customer customer);

关于如何处理不可更新电子邮件问题的另一个提示。您可以只使用@Column 注释并将updatable 属性设置为false。如果您真的不想更新电子邮件,那会容易得多。

@Entity
class Customer {
   ...
   @Column(updatable = false)
   String email;
   ...
}

【讨论】:

  • 非常感谢您的反馈。是的,在我更正命名之后,因为在我的实体和 POJO 中我使用驼峰式(认为 JPQL 需要蛇形大小写,因为我看到的示例),查询有效,但数据库效果没有变化。因此,如果我使用不同的值发出 PUT 请求,它会保持相同。我进行了调试以查看 customerDTO 是否具有正确的值并且它是正确的。另一个地方出了问题。请问我是否使用 @Column(updateable = false) 注释,我是否必须编写自定义 JPQL 查询或者 Spring 是否为我处理了 constraintviolationsexception?
  • 我只需要从 Request 中获取额外的 id 并更新我的 Customerservice 和 CustomerRepository。你的帖子解决了我的问题。如果这样做是不好的做法,我很高兴再次收到您的来信,为什么我们应该使用 @Colum(updateable = false) 来代替......亲切的问候
  • 很高兴它成功了! @Column(updatable=false) 就是为这种情况设计的。有了这个注解,您就不再需要自定义查询,因此代码更少,更易于维护。
猜你喜欢
  • 2019-08-08
  • 2015-12-23
  • 2021-04-01
  • 2018-10-06
  • 2017-11-15
  • 2016-08-29
  • 1970-01-01
  • 1970-01-01
  • 2011-12-26
相关资源
最近更新 更多