@ManyToOne
/の関係で、親 - > 子の関係があり@OneToMany
ます。
おおよそ次のようなコードで、親への更新を処理しています。
- 親を取得する
- 取得元 (順番に - ehCache、db、または見つからない場合は作成)
- 更新を処理し、見つからない場合は親に子を作成します
- データベースに保存
- キャッシュに保存
実行すると、次のシーケンスが発生することがわかります
- 最初の更新が完了しました - 親と子の両方がキャッシュを作成しました
- 2 回目の更新 - 親がキャッシュから取得され、新しい子が追加されます
- 2 番目の更新が完了すると、子の ID はまだ null のままです。ただし、更新は正常に完了しました。(休止状態のログとデータベースの両方に対して検証済み)
- 3 番目の更新 -
DataIntegrityViolationException
2 番目の更新の子が再度 INSERT されるため、スローされます。
これは、データベースから返されるのではなく、親がキャッシュされているという事実に関連しているに違いないと思います。ここでの正しいプロセスがどうあるべきかわかりません。
関連情報:
- Parent <--> child 後方参照が正しく定義され、注釈が付けられています。
- 親の最初の INSERT の後、db から親を再フェッチし、これをキャッシュして、違いが生じるかどうかを確認しました --- 違いはありませんでした。
- として注釈が付けられた私のテストでは最初は失敗しなかったため、トランザクション境界がここで役割を果たす必要があります
@Transactional
。(苦労して学んだ教訓)
これを処理する正しい方法は?
以下にコード例を示します。
@Entity // Parent
class Fixture {
@OneToMany(cascade=CascadeType.ALL, mappedBy="fixture", fetch=FetchType.EAGER) @Getter @Setter
@MapKey(name="instrumentPriceId")
private Map<String,Instrument> instruments = Maps.newHashMap();
private Instrument addInstrument(Instrument instrument)
{
instruments.put(instrument.getInstrumentPriceId(), instrument);
instrument.setFixture(this);
log.info("Created instrument {}",instrument.getInstrumentPriceId());
return instrument;
}
/**
* Returns an instrument with the matching instrumentId.
* If the instrument does not exist, it is created, appended to the internal collection,
* and then returned.
*
* This method is guaranteed to always return an instrument.
* This method is thread-safe.
*
* @param instrumentId
* @return
*/
public Instrument getInstrument(String instrumentId)
{
if (!instruments.containsKey(instrumentId))
{
addInstrument(new Instrument(instrumentId));
}
return instruments.get(instrumentId);
}
}
@Entity // Child
public class Instrument {
@Column(unique=true)
@Getter @Setter
private String instrumentPriceId;
@ManyToOne(optional=false)
@Getter @Setter @JsonIgnore
private Fixture fixture;
public Instrument(String instrumentPriceId)
{
this.instrumentPriceId = instrumentPriceId;
}
}
そして、更新プロセッサ コード:
class Processor {
@Autowired
@Qualifier("FixtureCache")
private Ehcache fixtureCache;
@Autowired
private FixtureRepository fixtureRepository;
void update(String fixtureId, String instrumentId) {
Fixture fixture = getFixture(fixtureId);
// Get the instrument, creating it & appending
// to the collection, if it doesn't exist
fixture.getInstrument(instrumentId);
// do some updates...ommitted
fixtureRepository.save(fixture);
fixtureCache.put(new Element(fixtureId, fixture));
}
/**
* Returns a fixture.
* Returns from the cache first, if present
* If not present in the cache, the db is checked.
* Finally, if the fixture does not exist, a new one is
* created and returned
*/
Fixture getFixture(String fixtureId) {
Fixture fixture;
Element element = fixtureCache.get(fixtureId);
if (element != null)
{
fixture = element.getValue();
} else {
fixture = fixtureRepostiory.findOne(fixtureId);
if (fixture == null)
{
fixture = new Fixture(fixtureId);
}
}
return fixture;
}
}