Marty Pitt
Marty Pitt

Reputation: 29300

Hibernate, @ManyToOne inserts, and ehCache

I have a parent -> child relationship, with a @ManyToOne / @OneToMany relationship.

I'm processing updates to the parent, in code that goes roughly like this:

When running through, I find the following sequence occurs

I assume that this must be related to the fact the parent is cached, rather than returned from the database. I'm not sure what the correct process here should be.

Relevant information:

Whats the correct way to handle this - specifically, to avoid having to load the Parent from the db every time, while still having child entities tracked correctly?

Code example shown below.

@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;
    }
}

And, the update processor code:

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;
}

}

Upvotes: 1

Views: 720

Answers (1)

Marty Pitt
Marty Pitt

Reputation: 29300

The answer to this was frustratingly simple.

In the update method, I was ignoring the result of the save() operation. Often, this is fine, if you're not planning on using object again. (which is common, as you save right at the end of your unit of work).

However, as I was continuing to use my 'parent' again, I needed to observe the returned value:

So this:

fixtureRepository.save(fixture);
fixtureCache.put(new Element(fixtureId, fixture));      

becomes this:

fixture = fixtureRepository.save(fixture);
fixtureCache.put(new Element(fixtureId, fixture));      

Upvotes: 2

Related Questions