I start showing you my scenario.
This is my parent object:
@Entity
@Table(name="cart")
public class Cart implements Serializable{
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Id
@Column(name="id")
private Integer id;
@OneToMany(mappedBy="cart", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private List<CartItem> cartItems;
...
}
This is my child object:
@Entity
@Table(name="cart_item")
public class CartItem implements Serializable{
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Id
@Column(name="id")
private Integer id;
@ManyToOne
@JoinColumn(name="cart_id", nullable=false)
private Cart cart;
...
}
As you can see looking at the database, in the table cart_item (child object) the field cart_id has a foreign key to the field id of the table cart (parent object).
This is how I save the object:
1) there's a restController that reads a JSON object:
@RestController
@RequestMapping(value = "rest/cart")
public class CartRestController {
@Autowired
private CartService cartService;
@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.CREATED)
public void create(@RequestBody CartDto cartDto) {
cartService.create(cartDto);
}
}
2) This is the CartService, that's just an Interface:
public interface CartService {
void create(CartDto cartDto);
}
This is the implementation of CartService:
import org.springframework.transaction.annotation.Transactional;
@Service
@Transactional
public class CartServiceImpl implements CartService {
@Autowired
private CartDao cartDao;
@Override
public void create(CartDto cartDto) {
cartDao.create(cartDto);
}
}
CartDao is just another interface, I show you only its implementation:
@Repository
public class CartDaoImpl implements CartDao {
@Autowired
private SessionFactory sessionFactory;
// in this method I save the parent and its children
@Override
public void create(CartDto cartDto) {
Cart cart = new Cart();
List<CartItem> cartItems = new ArrayList<>();
cartDto.getCartItems().stream().forEach(cartItemDto ->{
//here I fill the CartItem objects;
CartItem cartItem = new CartItem();
...
cartItem.setCart(cart);
cartItems.add(cartItem);
});
cart.setCartItems(cartItems);
sessionFactory.getCurrentSession().save(cart);
}
}
When I try to save a new cart and its cart_items I get this error:
SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/webstore] threw
exception [Request processing failed; nested exception is
org.springframework.orm.hibernate5.HibernateOptimisticLockingFailureException: Object of
class
[com.depasmatte.webstore.domain.CartItem] with identifier [7]: optimistic locking failed;
nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by
another transaction (or unsaved-value mapping was incorrect) :
[com.depasmatte.webstore.domain.CartItem#7]] with root cause
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction
(or unsaved-value mapping was incorrect) : [com.depasmatte.webstore.domain.CartItem#7]
I suppose the error depends on the fact that when Hibernate try to save the a cart_item, the id of the cart doesn't exist yet!
What's the correct way to save a parent object and its childer in on shot? Thank you
My parent class : @Entity @DynamicUpdate @Table(name = "person") public class Person { @Id @GeneratedValue(strategy = GenerationType. IDENTITY) @Column(name="person_pk", nullable=false) private Long personPK; @OneToMany(fetch = FetchType. EAGER, mappedBy = "person",cascade = CascadeType.
save(…)- Method. It will persist or merge the given entity using the underlying JPA EntityManager. If the entity has not been persisted yet Spring Data JPA will save the entity via a call to the entityManager.
Save and saveAndFlush both can be used for saving entities. They both are both belong to the Spring data library. save may or may not write your changes to the DB straight away. When we call saveAndFlush system are enforcing the synchronization of your model state with the DB.
The findById() method is used to retrieves an entity by its id and it is available in CrudRepository interface. The CrudRepository extends Repository interface. In Spring Data JPA Repository is top-level interface in the hierarchy.
Here's the list of rules you should follow, in order to be able to store a parent entity along with its children in a one shot:
PERSIST
should be enabled (CascadeType.ALL
is also fine)Mapping issues:
@Column(name="id")
from both entitiescartItems
private. Since Hibernate is using its own implementation of the List
, and you should never change it directly via setter private List<CartItem> cartItems = new ArrayList<>();
@ManyToOne(optional = false)
instead of nullable = false
inside the @JoinColumn
fetch = FetchType.LAZY
for collectionsit's better to use helper method for setting relationships. E.g. class Cart
should have a method:
public void addCartItem(CartItem item){
cartItems.add(item);
item.setCart(this);
}
Design issues:
save
with Spring Data JPA repositories
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With