Reputation: 1717
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> {
..
}
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:
@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 !
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;
...
}
Upvotes: 5
Views: 5958
Reputation: 6808
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. They both had foreign key to each other.
Inside Registration
class:
@PreRemove
private void preRemove() {
setSubscription(null);
}
Upvotes: 0
Reputation: 1600
You are defining a Bidirectional relationship between Menu and MenuPrice by using both @OneToMany
and @ManyToOne
. 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. Include the following statement menu.MenuPrice.add(menuPrice);
after you create menuPrice. You also do not need multiple save()
since you have specified Cascade.All
. 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.
Upvotes: 3
Reputation: 105
Try removing the @Transactional(readOnly = true) from MenuService
. This could prevent the flushing.
Otherwise I would try the approach of en Lopes. There are diffrent CascadeType
:
https://docs.oracle.com/javaee/6/api/javax/persistence/CascadeType.html
Upvotes: 0
Reputation: 26
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?
Upvotes: 0
Reputation: 2113
make sure that in the child objects: MenuPrice, MenuPriceSummary you have CascadeType.ALL
, something like
@OneToMany(mappedBy="menu", cascade = CascadeType.ALL, fetch=FetchType.LAZY)
private List<MenuPrice> price;
Upvotes: 1