我有一个父-> 子关系,有一个@ManyToOne
/@OneToMany
关系。
我正在处理对父级的更新,代码大致如下:
- 获取父级
- 从(按顺序 - ehCache、db 或如果未找到则创建)检索
- 处理更新,如果未找到,则在父级上创建一个子级
- 保存到数据库
- 存储在缓存中
运行时,我发现出现以下顺序
- 第一次更新完成 - 父母和孩子都创建了一个缓存
- 第二次更新 - 从缓存中检索父级,添加新子级
- 当第二次更新完成时,孩子的 id 仍然为空。但是,更新确实成功完成。(针对休眠日志和数据库验证)
- 第三次更新 -
DataIntegrityViolationException
被抛出,因为第二次更新的孩子再次被插入。
我假设这必须与父级缓存的事实有关,而不是从数据库返回。我不确定这里的正确过程应该是什么。
相关信息:
- 父 <--> 子后向引用已正确定义和注释。
- 在父项的初始 INSERT 之后,我尝试从数据库中重新获取父项,并缓存它,看看它是否有所作为——它没有。
- 事务边界必须在这里发挥作用,因为这最初在我注释为
@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;
}
}