Search code examples
spring-bootjunitspring-data-jpaspring-datajunit4

spring data jpa: Insert instead of delete


I have a basic SpringBoot app. using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file. I have created this Repository class:

@Repository
public interface MenuRepository extends CrudRepository<Menu, Long> {

      @Cacheable("menus")
      @Query("select cur from Menu cur where cur.MenuId = ?1")
      Menu findMenuByMenuId (String MenuId);

      @Cacheable("menus")
      @Query("select cur from Menu cur where lower (cur.symbol) = lower (?1) ")
      Menu findMenuBySymbol (String symbol);

and this service:

@Service
public class MenuService {

        @Autowired
    protected MenuRepository menuRepository;    

    @Autowired
    MenuPriceService menuPriceService;


     @Transactional
     public Menu save (Menu menu) {              
         return menuRepository.save (menu);
     }


     @Transactional
     public void delete (Menu menu) {
         menuRepository.delete  (menu);
     }
     ..
}


}

and this JUNIT test:

    @Test
    @Transactional
    public void testDelete () {

        String menuName = System.currentTimeMillis()+"";

        Menu menu = new Menu(); 
        menu.setMenuId(menuName);
        menuService.delete (menu);
}

but this is what I see in the console

Hibernate: 
    insert 
    into
        t_menu
        (...) 
    values
        (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)

Solution

  • While removing @Transactional as suggested in another answer might make the behavior go away, I'd question the validity of the test.

    The question really is: why do you try to delete an entity that is not in the database?

    Solution:

    If you simulate the case where don't know if the entity exists, use deleteById it will throw an exception when the id is not present in the database and you can, of course, catch that.

    Some background to how and why this insert happens:

    delete(T entity) is implemented through SimpleJpaRepository.delete(T entity) which basically contains this single line of code.

    em.remove(em.contains(entity) ? entity : em.merge(entity));
    

    As you can see it does a merge on the entity if the entity isn't present in the EntityManager. Without the merge you'd might end up with duplicate entities in the session if another instance with same class and Id does exist in the EntityManager. But since your entity doesn't exist at all this triggers an insert into the database.

    You might think it as this: You create an instance, you make the EntityManager know about it (by telling it to delete it) and you delete it, so these are the steps that need to occur in the database -> insert + delete.