如何一键挽救父母和孩子(JPA和Hibernate) [英] How to save parent and child in one shot (JPA & Hibernate)

查看:113
本文介绍了如何一键挽救父母和孩子(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")
  • cartItems 私有设置
  • 设置设置器.由于Hibernate使用的是自己的List实现,因此永远不要直接通过setter
  • 对其进行更改.
  • 初始化您的列表private List<CartItem> cartItems = new ArrayList<>();
  • 使用@ManyToOne(optional = false)代替@JoinColumn
  • 中的nullable = false
  • 更喜欢fetch = FetchType.LAZY来收藏
  • 最好使用辅助方法来设置关系.例如.类Cart应该具有一个方法:

  • remove @Column(name="id") from both entities
  • make setter for cartItems private. Since Hibernate is using its own implementation of the List, and you should never change it directly via setter
  • initialize you list private List<CartItem> cartItems = new ArrayList<>();
  • use @ManyToOne(optional = false) instead of nullable = 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆