Jeff Higgins Jeff Higgins - 16 days ago 4
Java Question

Spring JpaRepositroy.save() does not appear to throw exception on duplicate saves

I'm currently playing around on Spring boot 1.4.2 in which I've pulled in Spring-boot-starter-web and Spring-boot-starter-jpa.

My main issue is that when I save a new entity it works fine (all cool).

However if I save a new product entity with the same id (eg a duplicate entry), it does not throw an exception. I was expecting ConstrintViolationException or something similar.

Given the following set up:

Application.java

@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}


ProductRepository.java

@Repository
public interface ProductRepository extends JpaRepository<Product, String> {}


JpaConfig.java

@Configuration
@EnableJpaRepositories(basePackages = "com.verric.jpa.repository" )
@EntityScan(basePackageClasses ="com.verric.jpa")
@EnableTransactionManagement
public class JpaConfig {

@Bean
JpaTransactionManager transactionManager() {
return new JpaTransactionManager();
}
}


Note JpaConfig.java and Application.java are in the same package.

ProductController.java

@RestController
@RequestMapping(path = "/product")
public class ProductController {

@Autowired
ProductRepository productRepository;

@PostMapping("createProduct")
public void handle(@RequestBody @Valid CreateProductRequest request) {
Product product = new Product(request.getId(), request.getName(), request.getPrice(), request.isTaxable());
try {
productRepository.save(product);
} catch (DataAccessException ex) {
System.out.println(ex.getCause().getMessage());
}
}
}


and finally Product.java

@Entity(name = "product")
@Getter
@Setter
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
public class Product {

protected Product() { /* jpa constructor*/ }

@Id
private String id;

@Column
private String name;

@Column
private Long price;

@Column
private Boolean taxable;
}


The getter, setter and equalsHashcode.. are lombok annotations.

Miscellaneous:

Spring boot : 1.4.2

Hibernate ORM: 5.2.2.FINAL

This issue happens regardless if I annotate the controller with or without
@Transactional


The underlying db shows the exception clearly

2016-11-15 18:03:49 AEDT [40794-1] verric@stuff ERROR: duplicate key value violates unique constraint "product_pkey"
2016-11-15 18:03:49 AEDT [40794-2] verric@stuff DETAIL: Key (id)=(test001) already exists


I know that is better (more common) to break the data access stuff into its own service layer instead of dumping it in the controller

The semantics of the controller aren't ReST

Things I've tried:

Spring CrudRepository exceptions

I've tried implementing the answer from this question, unfortunately my code never ever hits the DataAccesException exception

Does Spring JPA throw an error if save function is unsuccessful?

Again similar response to the question above.

http://www.baeldung.com/spring-dataIntegrityviolationexception

I tried adding the bean to my JPAconfig.java class that is:

@Bean
public PersistenceExceptionTranslationPostProcessor exceptionTranslation(){
return new PersistenceExceptionTranslationPostProcessor();
}


But nothing seemed to happen.

Sorry for long post, ty in advance

Answer

I think you are aware of CrudRepository.save() is used for both insert and update. If an Id is non existing then it will considered an insert if Id is existing it will be considered update. You may get an Exception if your send the Id as null.

Since you don't have any other annotations apart from @Id on your id variable, The Unique Id generation must be handled by your code Or else you need to make use of @GeneratedValue annotation.

Comments