Подтвердить что ты не робот

Как решить javax.persistence.EntityNotFoundException с JPA (не используя @NotFound)

Мы используем JPA для загрузки некоторых материалов из базы данных. Некоторые объекты могут иметь необязательные отношения между ними, например.

@Entity
public class First {
    ....
    @OneToOne(cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH})
    @JoinColumns(value = {
        JoinColumn(name = "A_ID", referencedColumnName = "A_ID", insertable = false, updatable = false), 
        JoinColumn(name = "B_ID", referencedColumnName = "B_ID", insertable = false, updatable = false)})
    private Second second;

Когда эта ассоциация присутствует в базе данных, все работает нормально. Когда это не так, я получаю javax.persistence.EntityNotFoundException
Я хочу вместо исключения иметь это поле как NULL, если ассоциация отсутствует.

Я пробовал несколько разных вещей, например. используя опцию = true в аннотации отношений (которая по умолчанию является опцией по умолчанию), помещая ее как Nullable и т.д. Кажется, что ничего подобного не делает, кажется, что все эти параметры игнорируются.

Я нашел много ссылок, в которых упоминается эта же проблема (и некоторые вопросы здесь, в stackoverflow), но во всех них предлагается использовать аннотацию @NotFound из Hibernate. Но мы НЕ хотим иметь никаких зависимостей от Hibernate (мы хотим сохранить все чисто JPA).

Кто-нибудь из вас знает какой-либо другой способ решить эту проблему?

Большое спасибо за вашу помощь!

4b9b3361

Ответ 1

Ниже приведено альтернативное решение этой проблемы. Мне пришлось строить поверх старой базы данных, где отношения иногда были повреждены. Вот как я решил это, используя только JPA.

@PostLoad
public void postLoad(){
    try {
        if(getObject() != null && getObject().getId() == 0){
            setObject(null);
        }
    }
    catch (EntityNotFoundException e){
        setObject(null);
    }
} 

Ответ 2

Я столкнулся с той же проблемой. Он не всегда воспроизводимый, поэтому я не могу его протестировать, но вот некоторые мысли:

  • Экземпляр вашего второго класса удаляется, а экземпляр класса First ничего не знает об этом.
  • Вам нужен способ, чтобы экземпляр First know, когда его экземпляр Second был удален.
  • Вариант каскада для удаления здесь не помогает.
  • Вы можете попытаться использовать двунаправленную связь, если экземпляр First существует внутри экземпляра Second. Это позволяет вам обновлять экземпляр Сначала через экземпляр Second перед удалением второй
  • Двунаправленные отношения - это зло. Я бы предположил в вашем случае, что Первый - это владелец Второго. Не разрешайте службе удалять Второй экземпляр напрямую. Пусть сервис, который работает с экземплярами Сначала удалите экземпляр Second. В этом случае вы можете сделать "второе" поле сначала нулевое, чем удаление экземпляра Second via EntityManager.
  • Вы можете получить исключение, когда выполняются запросы и кеш второго уровня, и запрос имеет подсказку, которая позволяет кэшировать его результат. я предложит вам получить результат запросов с помощью следующего метода:
private List<?> getQueryResult(final Query query)
{
    try
    {
        return query.getResultList();
    }
    catch (EntityNotFoundException e)
    {
        return query.setHint("javax.persistence.cache.storeMode", CacheStoreMode.REFRESH).getResultList();
    }
}
  1. Если вы работаете с объектами через EntityManger, но не через запросы, и вы получаете исключение, поскольку сущность кэшируется, вы можете аннулировать все объекты First в кеше, когда вы удаляете Second.

Я бы хотел обсудить это решение, так как я не могу его протестировать и не могу сделать sute он работает. Если кто-то попытается, пожалуйста, дайте мне знать.

PS: если у кого-то есть unit test для спящего режима, который воспроизводит эту проблему, не могли бы вы сообщить мне об этом. Я хочу продолжить исследование.

Ответ 3

Как насчет добавления теста в соответствующий класс сущностей:

public boolean getHasSecond() {
    if (this.Second != null) {
        return true;
    } else {
        return false;
    }
}

Таким образом, вы можете проверить, существует ли связь...

Ответ 4

Попытайтесь добавить опцию = true в аннотацию OneToOne.