如何一键挽救父母和孩子(JPA和Hibernate) [英] How to save parent and child in one shot (JPA & Hibernate)
问题描述
我开始向您展示我的情况.
I start showing you my scenario.
这是我的父对象:
@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;
...
}
这是我的子对象:
@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;
...
}
您可以看到数据库,在表 cart_item (子对象)中,字段 cart_id 具有指向字段 id 的外键表购物车(父对象)中的"strong".
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)有一个 restController 可以读取JSON对象:
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)这是 CartService ,它只是一个界面:
2) This is the CartService, that's just an Interface:
public interface CartService {
void create(CartDto cartDto);
}
这是CartService的实现:
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 只是另一个界面,我仅向您展示其实现:
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);
}
}
当我尝试保存新的购物车及其 cart_item 时,出现此错误:
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]
我认为该错误取决于以下事实:当休眠尝试保存购物车的 cart_item , id 还不存在!
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
推荐答案
这是您应遵循的规则列表,以便能够将父实体及其子实体一起存储射击:
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
(也可以使用CascadeType.ALL
) - 双向关系应该在双方上正确设置.例如.父级包含其集合字段中的所有子级,每个子级都有对其父级的引用. 在事务范围内执行
- 数据处理. 不允许使用自动提交模式.
- 仅应手动保存父实体(由于级联模式,子实体将自动保存)
- cascade type
PERSIST
should be enabled (CascadeType.ALL
is also fine) - a bidirectional relationship should be set correctly on both sides. E.g. parent contains all children in its collection field and each child has a reference to its parent.
- data manipulation is performed in the scope of a transaction. NO AUTOCOMMIT MODE IS ALLOWED.
- only parent entity should be saved manually (children will be saved automatically because of the cascade mode)
映射问题:
- 从两个实体中删除
@Column(name="id")
为 - 设置设置器.由于Hibernate使用的是自己的
List
实现,因此永远不要直接通过setter 对其进行更改.
- 初始化您的列表
private List<CartItem> cartItems = new ArrayList<>();
- 使用
@ManyToOne(optional = false)
代替@JoinColumn
中的 - 更喜欢
fetch = FetchType.LAZY
来收藏 -
最好使用辅助方法来设置关系.例如.类
Cart
应该具有一个方法:
cartItems
私有设置nullable = false
- remove
@Column(name="id")
from both entities - make setter for
cartItems
private. Since Hibernate is using its own implementation of theList
, and you should never change it directly via setter - initialize you list
private List<CartItem> cartItems = new ArrayList<>();
- use
@ManyToOne(optional = false)
instead ofnullable = false
inside the@JoinColumn
- prefer
fetch = FetchType.LAZY
for collections it'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);
}
设计问题:
- 将DTO传递到DAO层是不好的.最好在服务层以上进行DTO与实体之间的转换.
- 最好使用 Spring Data JPA存储库
这篇关于如何一键挽救父母和孩子(JPA和Hibernate)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!