简体   繁体   中英

Spring Data JPA CRUD Repository Interface JPQL @Query generating custom query fails

I am working on a rest-based (not restful) api and have the following problem. Trying to create a custom query with JPQL to update customer information. Member email in entity customer is unique, so I have to write my own query otherwise it causes unique constraintviolationsexception. I found this out so far and now I am trying to fix by writing a Custom query in Spring CRUD Repository Interface with JPQL.

CustomerController

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

}

CustomerService

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

This causes following Stacktrace and I dont found any solution so far. 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 is telling me, that validation is false but I Think my JPQL Query is correct. I am confused do you have an idea what I am doing wrong?

Naming

There might be a problem with naming. You didn't include your Customer entity. Does it really use a snake_case naming convention as your JPQL query suggests?

In JPQL you should use the exact field names as in a Java class . Usually, it's camelCase naming convention, while snake_case is used in a database.

Return value

Definitely there is a problem with a method signature. Modifying queries can only use void or int/Integer as a return type.

Assuming you really did use a snake_case for Customer and after changing the return type of updateCustomerByDTO to void, the query works fine.

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

Another hint of how you may approach the non-updatable email problem. You can just use a @Column annotation with the updatable attribute set to false . If you really never want to update an email, it will be much easier.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM