2015-10-29 3 views
0

Рассмотрим следующие объекты JPA:Предотвращение удаления от каскадного к экземпляру объекта

@Entity @Table(name = "product") class Product { ... } 

@Entity @Table(name = "stock") class Stock { 
    @JoinColumn(name = "product_id", updatable = false) 
    @ManyToOne(fetch = FetchType.LAZY) 
    private Product product; 

    @Column(name = "quantity") 
    private Long quantity; 
} 

@Entity @Table(name = "cart") class Cart { 
    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "cart", orphanRemoval = true) 
    private List<CartItem> items = new ArrayList<>(); 

    public void addItem(CartItem item) { items.add(item); } 
    public void removeItem(CartItem item) { items.remove(item); } 
} 

@Entity @Table(name = "cart_item") class CartItem { 
    @JoinColumn(name = "cart_id", updatable = false) 
    @ManyToOne(fetch = FetchType.LAZY) 
    private Cart cart; 

    @JoinColumn(name = "product_id", updatable = false) 
    @ManyToOne(fetch = FetchType.LAZY) 
    private Product product; 

    @Column(name = "quantity") 
    private Long quantity; 

    @JoinColumn(name = "stock_id", updatable = false) 
    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) 
    private Stock stock; 

    public void setQuantity(Long quantity) { 
    final Long delta = this.quantity - Math.max(0L, quantity); 

    this.quantity += delta; 
    this.stock.setQuantity(this.stock.getQuantity() - delta); 

    if(quantity < 1) { cart.removeItem(this); } 
    } 
} 

Обратите внимание на ассоциацию с CartItem к Stock. Эта ассоциация была аннотирована таким образом, что изменение количества предметов корзины влияет на ее доступный запас в другом направлении, то есть, если количество предметов корзины увеличивается, доступное количество запаса для продукта уменьшается и наоборот.

Это позволяет мне стрелять cartRepository.save(cart), сохраняя все элементы корзины и обновления их запасов в то же время (в связи с Cascade.ALL от Cart к CartItem). Это отлично работает, если элемент корзины имеет ненулевое количество.

Однако, когда cartRepository.save(cart) вызывается после вызова cart.removeItem(item), каскад также пытается удалить запас для позиции корзины, что не является целью. Элемент корзины должен быть удален, но связанный с ним запас должен быть просто обновлен. Есть ли способ каскадного обновления от CartItem до Stock, но каскад удаляет по CartItem как обновления на Stock?

ответ

0

Изменение

class CartItem { 
    @JoinColumn(name = "stock_id", updatable = false) 
    @ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY) 
    private Stock stock; 
} 

к

class CartItem { 
    @JoinColumn(name = "stock_id", updatable = false) 
    @ManyToOne(cascade = { CascadeType.MERGE, CascadeType.PERSIST }, fetch = FetchType.LAZY) 
    private Stock stock; 
} 

(CascadeType.ALL к { CascadeType.MERGE, CascadeType.PERSIST }) решить эту проблему. Ранее следующие SQL запросы были выполнены, когда cartItem.setQuantity(0) был вызван:

delete from cart_item where id=? 
delete from stock where id=? 

С изменением следующие SQL запросы выполняются в соответствии с требованиями:

update stock set quantity=? where id=? 
delete from cart_item where id=? 

Sample приложение available on Github для проверки правильности решения.

0

Ваш подход очень проблематичен, если не сказать больше. Что произойдет, если несколько потоков изменят количество одного и того же продукта? JPA не защищает от одновременного доступа! И даже если вы не столкнетесь с техническими проблемами синхронизации, вы получите их логически, потому что (постоянное) количество будет либо перезаписано с неправильными значениями (или вы получите OptimisticLockException).

Вы должны избегать такой головной боли и обрабатывать количество в качестве динамического значения и вычислять текущее количество продукта с помощью оператора select.

Если вы настаиваете на количестве кеширования, вы должны использовать отдельный сервис для этой задачи, который использует правильную синхронизацию, параллельную карту или тому подобное.

Смежные вопросы