このシナリオを捉えるキーワードが不足しているため、説明を続けさせてください。クラスは単純化されています。
これを考えると:
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 で例外がスローされます。
- その後の繰り返しで
- 前回の反復では、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;
}
}
私は何を間違っていますか?
私が試して動作したオプションのいくつかは次のとおりです。
- @Transactional に Dao メソッドの (readonly=true) を追加するのは、それらが単なるクエリであるためです (ただし、複雑なクエリのために意図的にトランザクション化されている可能性があり、ダーティ リードを防ぐためにロックが必要な場合があるという悪影響があります)。
- コントローラーで、変更用の別のループを作成します。次に、アイテムをループしてどれが割引されているかを確認するための 2 つのループがあり、それらの情報をどこかに保存して、後で前述の pojo の変更を行う 2 番目のループで参照できるようにします。
他のオプションを検討しています。コーディング方法に問題がある場合はコメントしてください。