2

「デタッチされたインスタンスの削除」例外を取得する REST コントローラーから以下の Customer オブジェクトを削除しようとすると。

ログ:

org.springframework.dao.InvalidDataAccessApiUsageException: Removing a detached instance com.test.model.Customer#1750; nested exception is java.lang.IllegalArgumentException: Removing a detached instance com.test.model.Customer#1750

ドメイン:

@Entity
public class Customer{

@Id
private Long id;

@OneToOne(fetch = FetchType.EAGER)
@JoinColumn(name="COUNTRY_ID", nullable=false)
private Country country;

// other stuff with getters/setters

}

REST コントローラー:

@Controller
@RequestMapping("/shop/services/customers")
public class CustomerRESTController {

   /**
     * Deletes a customer
     */
    @RequestMapping( value="/{id}", method=RequestMethod.DELETE)
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteCustomer(@PathVariable Long id, HttpServletRequest request, HttpServletResponse response) throws Exception {

        Customer customer = customerService.getById(id);
        if(customer != null){
            customerService.delete(customer);
        }else{
            response.sendError(503, "No Customer found for ID : " + id);
        }
    }

    // other stuff
}

私はデータベースから顧客オブジェクトを取得していますが、まだ休止状態に不満があります。なにか提案を??

4

1 に答える 1

3

エンティティは、現在のセッション (またはより適切なトランザクション) で切り離されます。Spring 内にいるため、トランザクション動作にJava Transaction Service (JTS)を使用することは非常に一般的です。このような Hibernate を使用すると、コミット後に永続化コンテキストが自動的にクリアされます (JPA ソリューションとして使用される場合と同様)。

通常、Hibernate はセッションの永続化コンテキストをクリアしないため、通常、エンティティはコミット後に切り離されません。(分散環境では安全ではありませんが、Hibernate のみを使用してデータベースにアクセスし、Ehcache のような分散キャッシュを使用する場合は節約できます)。

解決策:session.merge(object)エンティティを現在のセッション オブジェクトの永続化コンテキストに再接続します。

これは実際にはマージではなく再接続であり、Hibernate がエンティティの現在の状態が適切なデータベース キャッシュを反映しているかどうか不明な場合、エンティティをリロードします。(さらに、バージョン プロパティ (@Version) が存在する場合の特別な動作を追加します)。

ところで、Hibernate のドキュメントには次のように記載されています。

指定されたオブジェクトの状態を、同じ識別子を持つ永続オブジェクトにコピーします。セッションに現在関連付けられている永続インスタンスがない場合は、ロードされます。

アップデート

コードを見ると、これはトランザクションの問題のように見えます。customerService.getById(id) および customerService.delete(customer) サービス呼び出しがトランザクションのコミットにつながるかどうかを確認してください。両方をまったく同じトランザクション内に配置する必要があります。

問題を解決するためにできることの1つは次のとおりです。

public void deleteCustomer(@PathVariable Long id, HttpServletRequest request, HttpServletResponse response) throws Exception {
    boolean wasDeleted = customerService.delete(id);
    if(!wasDeleted)
        response.sendError(503, "No Customer found for ID : " + id);
    }
}

この方法では、2 つのサービス コールは必要ありません。高レベルのサービス呼び出しで休止状態のエンティティを使用することは、実際には一般的ではありません (ただし、アーキテクチャによって異なる場合があります。私は Spring をあまり利用していません)。

于 2013-09-29T14:11:14.220 に答える