0

このシナリオを捉えるキーワードが不足しているため、説明を続けさせてください。クラスは単純化されています。

これを考えると:

public ItemController {
    @Autowired
    ItemDtoService ItemDtoService;

    @Autowired
    DiscountService discountService;
    @RequestMapping(value = "/viewItems", method = RequestMethod.POST)
    public void process() {
        List<ItemDto> ItemDtos = ItemDtoService.getItemDtos();
        for(ItemDto i: ItemDtos) {
            boolean isDiscounted = discountService.hasDiscount(i); //throws exception here on iteration 2 and the last iteration, ItemDto was discounted
            if (isDiscounted) {
                i.setPrice(discountService.getDiscountedPrice(i));
                //do some other i.setter, basically modify the pojo
            }
        }        
    }
}

次の場合、discountService.hasDiscount で例外がスローされます。

  1. その後の繰り返しで
  2. 前回の反復では、ItemDto が割引されました。

例外は次のとおりです。

Caused by: org.hibernate.exception.SQLGrammarException: could not update: [somepackage.ItemDto#364] 

そして、スタックトレースのどこかにこれが表示されます:

at org.springframework.orm.jpa.JpaTransactionManager.doCommit(JpaTransactionManager.java:456)"

問題は、メソッド呼び出しが @Transactional であるその下にある dao メソッドを使用することです (そして、それは単なるクエリであり、複雑なクエリであっても、おそらく正当な理由があります)。JPA Tx マネージャーは、メソッド呼び出しの終了時にジョブを実行するときに、pojo が変更されていることを確認し、同期を試みます。ItemDtoService.getItemDtos 内で getEntityManager().createNativeQuery(nativeSql, ItemDto.class) を使用するため、ItemDto pojo には @Entity があります。他の 5 つのクラスの詳細は次のとおりです。

@Entity
public class ItemDto{
    //body
}


@Service
public class ItemService {
    @Autowired
    ItemDao itemDao;

    public List<ItemDto> getItems() {
        return itemDao.getItems(); //for sake of simplicity     
    }
}

@Repository
@Transactional
public class ItemDaoImpl {
    public List<ItemDto> getItems() {       
        String nativeSql = "select...."
        return getEntityManager().createNativeQuery(nativeSql, ItemDto.class);      
    }

}

@Service
public class DiscountService {
    @Autowired
    DiscountDao discountDao;

    public boolean hasDiscount(ItemDto i) {     
        boolean hasDiscount = discountDao.hasDiscount(i);
        //do other service stuff that might influence the hasDiscount flag
        return hasDiscount;     
    }
}

@Repository
@Transactional
public class DiscountDaoImpl {
    public boolean hasDiscount(ItemDto i) {     
        String nativeSql = "select...."
        boolean hasDiscount;
        //in reality the query is a complicated joins, executes and returns if has discount or not
        return hasDiscount;
    }

}

私は何を間違っていますか?

私が試して動作したオプションのいくつかは次のとおりです。

  1. @Transactional に Dao メソッドの (readonly=true) を追加するのは、それらが単なるクエリであるためです (ただし、複雑なクエリのために意図的にトランザクション化されている可能性があり、ダーティ リードを防ぐためにロックが必要な場合があるという悪影響があります)。
  2. コントローラーで、変更用の別のループを作成します。次に、アイテムをループしてどれが割引されているかを確認するための 2 つのループがあり、それらの情報をどこかに保存して、後で前述の pojo の変更を行う 2 番目のループで参照できるようにします。

他のオプションを検討しています。コーディング方法に問題がある場合はコメントしてください。

4

1 に答える 1

0

私が見つけた別のオプションは、ItemDto のリストを返す Dao 内にあります。リストを返す前に、これを実行します。

getEntityManager().clear();

リストはとにかく Dto であり、これらは DB 同期を必要としないと予想されるため、正常に機能します。同時に、一貫した読み取りに必要なロックのために @Transactional が保持されます。

それはもう1つの選択肢ですが、実際に最も適切な方法は何ですか?

于 2011-09-15T05:46:42.770 に答える