CrudRepository 不会删除具有关系的对象
CrudRepository does not delete an Object with relationship
我有一个基本的 Spring 启动应用程序。使用 Spring 初始化程序、JPA、嵌入式 Tomcat、Thymeleaf 模板引擎,并将其打包为可执行 JAR 文件。
我创建了这个存储库 class:
@Repository
public interface MenuRepository extends CrudRepository<Menu, Long> {
..
}
和这项服务class
@Service
@Transactional(readOnly = true)
public class MenuService {
@Autowired
protected MenuRepository menuRepository;
@Transactional
public void delete (Menu menu) {
menuRepository.delete (menu);
}
..
}
和这个 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);
}
}
但是 Junit 失败了,因为对象没有被删除,也没有抛出异常!
按照建议,我在属性中有这个..
@OneToMany(mappedBy="menu", cascade = CascadeType.ALL, orphanRemoval = true, fetch=FetchType.LAZY)
private List<MenuPrice> price;
即使我在 运行 测试时在控制台中看到这个:
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`))
和菜单 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;
...
}
考虑一下:
@Transactional(readOnly = true)
public class MenuService {
但我的主要问题是:您在 spring 启动应用程序中使用 @Transactional
注释做什么?
确保在子对象中:MenuPrice、MenuPriceSummary 你有 CascadeType.ALL
,比如
@OneToMany(mappedBy="menu", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
private List<MenuPrice> price;
尝试从 MenuService
中删除 @Transactional(readOnly = true)。这可以防止冲洗。
否则我会尝试 en Lopes 的方法。有不同的CascadeType
:
https://docs.oracle.com/javaee/6/api/javax/persistence/CascadeType.html
您正在使用 @OneToMany
和 @ManyToOne
定义 Menu 和 MenuPrice 之间的双向关系。当你有双向关系时,你需要设置双方。在测试中,您在 MenuPrice 中设置 Menu,但没有将 MenuPrice 添加到 Menu 的 MenuPrice Set 中。在创建 menuPrice 后包括以下语句 menu.MenuPrice.add(menuPrice);
。您也不需要多个 save()
,因为您指定了 Cascade.All
。请尝试以下操作:
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);
}
在许多情况下,您不需要双向关系并且可以删除 @OneToMany
,但这取决于您的业务逻辑需要如何导航到子项或通过 JPA 查询它。
一个更简单的方法是将 @PreRemove
添加到将子值设置为 null
的父项:我的概念是 @OneToOne
关系,其中 Registration
对象有一个 Subscription
对象。他们都有彼此的外键。
里面 Registration
class:
@PreRemove
private void preRemove() {
setSubscription(null);
}
我有一个基本的 Spring 启动应用程序。使用 Spring 初始化程序、JPA、嵌入式 Tomcat、Thymeleaf 模板引擎,并将其打包为可执行 JAR 文件。 我创建了这个存储库 class:
@Repository
public interface MenuRepository extends CrudRepository<Menu, Long> {
..
}
和这项服务class
@Service
@Transactional(readOnly = true)
public class MenuService {
@Autowired
protected MenuRepository menuRepository;
@Transactional
public void delete (Menu menu) {
menuRepository.delete (menu);
}
..
}
和这个 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);
}
}
但是 Junit 失败了,因为对象没有被删除,也没有抛出异常!
按照建议,我在属性中有这个..
@OneToMany(mappedBy="menu", cascade = CascadeType.ALL, orphanRemoval = true, fetch=FetchType.LAZY)
private List<MenuPrice> price;
即使我在 运行 测试时在控制台中看到这个:
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`))
和菜单 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;
...
}
考虑一下:
@Transactional(readOnly = true)
public class MenuService {
但我的主要问题是:您在 spring 启动应用程序中使用 @Transactional
注释做什么?
确保在子对象中:MenuPrice、MenuPriceSummary 你有 CascadeType.ALL
,比如
@OneToMany(mappedBy="menu", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
private List<MenuPrice> price;
尝试从 MenuService
中删除 @Transactional(readOnly = true)。这可以防止冲洗。
否则我会尝试 en Lopes 的方法。有不同的CascadeType
:
https://docs.oracle.com/javaee/6/api/javax/persistence/CascadeType.html
您正在使用 @OneToMany
和 @ManyToOne
定义 Menu 和 MenuPrice 之间的双向关系。当你有双向关系时,你需要设置双方。在测试中,您在 MenuPrice 中设置 Menu,但没有将 MenuPrice 添加到 Menu 的 MenuPrice Set 中。在创建 menuPrice 后包括以下语句 menu.MenuPrice.add(menuPrice);
。您也不需要多个 save()
,因为您指定了 Cascade.All
。请尝试以下操作:
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);
}
在许多情况下,您不需要双向关系并且可以删除 @OneToMany
,但这取决于您的业务逻辑需要如何导航到子项或通过 JPA 查询它。
一个更简单的方法是将 @PreRemove
添加到将子值设置为 null
的父项:我的概念是 @OneToOne
关系,其中 Registration
对象有一个 Subscription
对象。他们都有彼此的外键。
里面 Registration
class:
@PreRemove
private void preRemove() {
setSubscription(null);
}