私のシナリオをお見せします。
これは私の親オブジェクトです:
@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 ofテーブルcart(親オブジェクト)。
これは私がオブジェクトを保存する方法です:
1)JSONオブジェクトを読み取るrestControllerがあります。
@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です。これは単なるInterfaceです。
public interface CartService {
void create(CartDto cartDto);
}
これは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は別のインターフェースであり、その実装のみを示します。
@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およびそのcart_item sを保存しようとすると、次のエラーが発生します。
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]
エラーはHibernateがa cart_itemを保存しようとしたときにidcartの事実に依存すると思います=まだ存在しません!
親オブジェクトとその子をオンショットで保存する正しい方法は何ですか?ありがとうございました
これがルールのリストです。親エンティティとその子をワンショットで格納できるようにするために、従う必要があります。
PERSIST
を有効にする必要があります(_CascadeType.ALL
_も問題ありません)マッピングの問題:
@Column(name="id")
を削除しますcartItems
private。 HibernateはList
の独自の実装を使用しているため、セッターを介して直接変更しないでくださいprivate List<CartItem> cartItems = new ArrayList<>();
nullable = false
_内で_@JoinColumn
_の代わりに@ManyToOne(optional = false)
を使用しますfetch = FetchType.LAZY
_を優先関係を設定するには、ヘルパーメソッドを使用することをお勧めします。例えば。クラスCart
にはメソッドが必要です:
_public void addCartItem(CartItem item){
cartItems.add(item);
item.setCart(this);
}
_
設計の問題:
save
のような定型文を避ける方がはるかに優れています。誰がこの関係を所有しているかというこの質問にとって非常に重要である議論から、1つの重要なことが明らかに欠落しています。この関係の所有者が子エンティティに行くことを意味する親エンティティにmappedByを配置すると、明示的にプロパティを設定してこの関係を埋める必要があります。そうしないと、この関係シップは構築されません。 JoinColumnアノテーションを親の上に置きます。これにより、関係の所有者が親であることが保証され、親エンティティが自動的に保存されるときにこの関係が確立されます
この投稿を確認しましたか? 行は別のトランザクションによって更新または削除されました(または未保存の値のマッピングが正しくありませんでした)
あなたはこれで適切な答えを見つけるかもしれません、あなたの問題はあなたのgetCurrentSessionから来ていると思います、たとえhibernateがスレッドセーフではないのでセッションを使用していても、セッションは軽量で非スレッドセーフなオブジェクトです。ここから何かを掘る必要があります。
実際、あるスレッド/セッションがオブジェクトをデータベースに保存するときに、別のスレッド/セッションが同じ操作を試みると、IDがすでに存在するため操作が不可能であるため、この種のエラーが発生します。
乾杯!
メソッドがトランザクションであることを確認してください。メソッドシグネチャの上に@Transactional
アノテーションを使用して、メソッドをトランザクション対応にすることができます。
サービスが使用されているので、これが質問に直接関連しないことはわかっていますが、同様の問題が発生したときにgoogleがここに連れてきました。私の場合、Spring JPAリポジトリを使用していました。 @ org.springframework.transaction.annotation.Transactionalを使用してリポジトリインターフェイスに注釈を付けてください。