简体   繁体   English

CrudRepository 不删除具有关系的对象

[英]CrudRepository does not delete an Object with relationship

I have a basic SpringBoot app.我有一个基本的 SpringBoot 应用程序。 using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file.使用 Spring Initializer、JPA、嵌入式 Tomcat、Thymeleaf 模板引擎,并打包为可执行 JAR 文件。 I have created this Repository class:我创建了这个 Repository 类:

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

and this service class和这个服务类

@Service
@Transactional(readOnly = true)
public class MenuService {

     @Autowired
     protected MenuRepository menuRepository;

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

and this Junit Test:和这个 Junit 测试:

@ContextConfiguration(classes={TestSystemConfig.class})
@RunWith(SpringRunner.class)
@SpringBootTest(classes = MenuGestApplication.class) 
public class MenuServiceTests {
...
@Test
    public void testDelete () {

        Menu menu = new menu(); 
        menu.setmenuId("bacalla-amb-tomaquet");
        menuService.save(menu);

        MenuPrice menuPrice = new menuPrice(menu);
        menuPrice.setPrice((float)20.0);
        menuPriceService.save(menuPrice);

        MenuPriceSummary menuPriceSummary = new menuPriceSummary(menu);
        menuPriceSummary.setFortnightlyAvgPrice((float)20.0);

        menuPriceSummaryService.save(menuPriceSummary);

        menu = menuService.findBymenuId("bacalla-amb-tomaquet");

        assertNotNull (menu);

        menuService.delete (menu);

        menu = menuService.findBymenuId("bacalla-amb-tomaquet");

        assertNull (menu);

    }
}

But the Junit is failing because the object is not deleted and no exception is thrown !但是 Junit 失败了,因为对象没有被删除,也没有抛出异常!

I have this in the proerty, as suggested..正如建议的那样,我在财产中有这个。

@OneToMany(mappedBy="menu", cascade = CascadeType.ALL,  orphanRemoval = true, fetch=FetchType.LAZY)
    private List<MenuPrice> price;

even that I see this in the console when running the tests:即使我在运行测试时在控制台中看到这一点:

Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`elcormenu`.`t_menu_price`, CONSTRAINT `FK19d0sljpshu4g8wfhrkqj7j7w` FOREIGN KEY (`menu_id`) REFERENCES `t_menu` (`id`))

and the Menu class:和菜单类:

@Entity
@Table(name="t_menu")
public class Menu  implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @JsonProperty("id")
    private Long id;    

    @JsonProperty("MenuId")
    private String MenuId;

    @OneToMany(mappedBy = "Menu", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
    @JsonIgnore
    private Set<MenuPrice> MenuPrice = new HashSet<>();

    @OneToOne(mappedBy = "Menu", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JsonIgnore
    private MenuPriceSummary summary;
...
}

You are defining a Bidirectional relationship between Menu and MenuPrice by using both @OneToMany and @ManyToOne .您正在使用@OneToMany@ManyToOne定义 Menu 和 MenuPrice 之间的双向关系。 When you have a biodirectional relationship you need to set both sides.当您有双向关系时,您需要设置双方。 In the test you are setting Menu in MenuPrice but not adding the MenuPrice to Menu's MenuPrice Set.在测试中,您在 MenuPrice 中设置 Menu,但未将 MenuPrice 添加到 Menu 的 MenuPrice Set 中。 Include the following statement menu.MenuPrice.add(menuPrice);包括以下语句menu.MenuPrice.add(menuPrice); after you create menuPrice.创建 menuPrice 后。 You also do not need multiple save() since you have specified Cascade.All .由于您已指定Cascade.All您也不需要多个save() Try the following:请尝试以下操作:

public void testDelete () {

    Menu menu = new menu(); 
    menu.setmenuId("bacalla-amb-tomaquet");

    MenuPrice menuPrice = new menuPrice(menu);
    menuPrice.setPrice((float)20.0);

    // Not needed
    // menuPriceService.save(menuPrice);

    // Add menuPrice to menu's menuPrice set
    menu.menuPrice.add(menuPrice);

    MenuPriceSummary menuPriceSummary = new menuPriceSummary(menu);
    menuPriceSummary.setFortnightlyAvgPrice((float)20.0);

    // Set menuPriceSummary in menu        
    menu.summary = menuPriceSummary;

    // Not needed
    //menuPriceSummaryService.save(menuPriceSummary);

    // Saving menu will save it children too
    menuService.save(menu);

    menu = menuService.findBymenuId("bacalla-amb-tomaquet");

    assertNotNull (menu);

    menuService.delete (menu);

    menu = menuService.findBymenuId("bacalla-amb-tomaquet");

    assertNull (menu);

}

In may cases, you don't need bidirectional relationships and can remove the @OneToMany , but this depends on how your business logic needs to navigate to the children or query it via JPA.在可能的情况下,您不需要双向关系并且可以删除@OneToMany ,但这取决于您的业务逻辑需要如何导航到子项或通过 JPA 查询它。

make sure that in the child objects: MenuPrice, MenuPriceSummary you have CascadeType.ALL , something like确保在子对象中: MenuPrice, MenuPriceSummary 你有CascadeType.ALL ,类似于

@OneToMany(mappedBy="menu", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
private List<MenuPrice> price;

Think about that:考虑一下:

@Transactional(readOnly = true)
public class MenuService {

But my main question would be: For what did you use the @Transactional annotation within a spring boot app?但我的主要问题是:您在 Spring Boot 应用程序中使用@Transactional注释是为了什么?

Try removing the @Transactional( readOnly = true ) from MenuService .尝试从MenuService删除 @Transactional( readOnly = true )。 This could prevent the flushing.这可以防止冲洗。

Otherwise I would try the approach of en Lopes.否则我会尝试 en Lopes 的方法。 There are diffrent CascadeType : https://docs.oracle.com/javaee/6/api/javax/persistence/CascadeType.html有不同的CascadeTypehttps : //docs.oracle.com/javaee/6/api/javax/persistence/CascadeType.html

For a simpler thing that worked is to add @PreRemove to the parent that sets the child value to null : My concept was an @OneToOne relationship where a Registration object has a Subscription object.更简单的工作是将@PreRemove添加到将子值设置为null的父级:我的概念是@OneToOne关系,其中Registration对象具有Subscription对象。 They both had foreign key to each other.他们都有彼此的外键。

Inside Registration class:内部Registration类:

@PreRemove
private void preRemove() {
    setSubscription(null);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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