[英]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有不同的
CascadeType
: https : //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.