Search code examples
spring-data-jpahibernate-mapping

How to keep data in sync with JPA across entities in session cache?


Im trying to figure out how to keep data in sync in the session cache. I have the following example:

@Table (name = "language")
@Entity
public class Language
{
    @Id
    @Column (name = "key", unique = true, nullable = false)
    private String key;

    @Column (name = "name", unique = true, nullable = false)
    private String name;

    @OneToMany (mappedBy = "language", cascade = { CascadeType.DETACH, CascadeType.REFRESH, CascadeType.REMOVE })
    private Set<Translation> translations;
}


@Table (name = "translation")
@Entity
public class Translation 
{
    @Id
    @GeneratedValue (strategy = GenerationType.SEQUENCE)
    private Long id;

    @ManyToOne(optional = false)
    private Language language;

    @ManyToOne (optional = false)
    @JoinColumn(referencedColumnName = "key")
    private TranslatableEntity translatable;

    @Column(name = "value")
    private String value;
}


@Table (name = "translatable")
@Entity
public class Translatable
{
    @Id
    @GeneratedValue (strategy = GenerationType.SEQUENCE)
    private Long id;

    @NotNull
    @Size (max = 255)
    @Column (name = "key", unique = true, nullable = false)
    private String key;

    @OneToMany (mappedBy = "translatable", cascade = { CascadeType.DETACH, CascadeType.REFRESH, CascadeType.REMOVE, CascadeType.PERSIST })
    @MapKey (name = "language")
    private Map<Language, Translation> translations;
}

So basically I do:

// Print current translations for en language
Language language = languageRepository.getOne("en");
printTranslations(language);

// Add new translatable object with translation for english language
Translatable translatable = new Translatable();
translatable.addTranslation("en", "...")
translatableRepository.saveAndFlush(translatable)

// Again print translations for en language
// This still prints outdated information
language = languageRepository.getOne("en");
printTranslations(language);

So my question is how to keep data consistent.

When inserting/removing a new translatable with some translations the translation list in Language instances are not updated in the session cache. I could not find any satisfactory answer to this. This one came closest: Keeping entity relationship in sync when deleting child in JPA.

Thanks


Solution

  • JPA doesn't maintain the two sides of a bidirectional relationship for you. And the purpose of the first level cache is that within a transaction entities get loaded only once.

    This gives you two options to solve the problem:

    1. maintain both sides of the relationship yourself, for example by implementing the Translatable.add method so that it updates Language.translations and Translation.language

    2. force a reload of language by either evicting it or by closing the session (i.e. the transaction) before calling languageRepository.getOne("en")