У меня есть такая таблица "регионы":

Id | имя | parent_id

1 | что угодно | 100000

Где parent_id должен ссылаться на идентификатор, то есть эта строка географически принадлежит 100000.

Однако из-за того, что импортируемые вначале данные грязные, строка с идентификатором 100000 не существует.

Следовательно, в данной Entity:

@Entity("regions")
public class Region {
    private int id;
    private String name;
    private Region parent;

    ...

    @ManyToOne()
    @JoinColumn(name = "parent_id")
    public Region getParent() {
        return parent;
    }

    public void setParent(Region parent) {
        this.parent = parent;
    }
}

Когда я делаю список с гибернацией:

    Session session = sessionHandler.getSession(); //gets current session
    Transaction tx = session.beginTransaction();
    try {
        return (List<T>)session.createQuery("FROM regions").list();
    }
    catch(HibernateException ex) {
        ex.printStackTrace();
        if (tx!=null) tx.rollback();
        throw ex;
    }finally {
        sessionHandler.close(); 
    }

Это вызовет исключение:

org.hibernate.ObjectNotFoundException: No row with the given identifier exists: [whatever.entities.Region#6046193]

Это означает, что регион с идентификатором 6046193 не существует. Как объяснялось ранее, я ожидаю, что что-то подобное произойдет.

Мои вопросы, учитывая, что я не могу изменить столбец parent_id на значение NULL, это способ обработать это исключение, чтобы система игнорировала исключение и продолжала работу программы?

3
jamesdeath123 6 Янв 2016 в 07:57

2 ответа

Лучший ответ

Вы можете попробовать установить для типа выборки отношения «многие к одному» значение «ленивый».

...
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
public Region getParent() {
    return parent;
}
...

Я предполагаю, что тогда спящий режим не будет выдавать ошибку во время «выбрать все», и вы можете обработать ошибку при первом вызове метода получения, если вы сделаете это так.

Но я не уверен на 100%, работает ли это, и не думаю, что это действительно хорошее решение . Я действительно думаю, что вам следует дезинфицировать свои данные во время / после импорта.

Если вы не дезинфицируете свои данные, вам придется держать навсегда временные решения проблем в коде. Что, если в будущем кто-то удалит fetch = FetchType.LAZY, потому что думает, что это приведет к повышению производительности? Ваше приложение неожиданно сломается только потому, что ваши сущности не отражают правильно то, что находится в вашей базе данных.

Вы сказали, что не можете установить для parent_id значение NULL, поскольку столбец не допускает значения NULL. Но как насчет создания фиктивных записей для отсутствующих идентификаторов? Вы можете сделать это сразу после импорта грязных данных, прежде чем запускать приложение в первый раз.

Кроме того, простое изменение столбца на допускающее значение NULL (при условии, что вы можете сделать это на мгновение) все равно не сработает. Вам все равно придется дезинфицировать данные - в этом случае вам придется установить для всех parent_id значение null, если строка с указанным идентификатором не существует.

1
David Tanzer 6 Янв 2016 в 05:34

Я бы сказал, что ваша конструкция модели данных ошибочна.

С точки зрения отношений, вы используете parent_id для представления внешнего ключа, ссылающегося на себя, не допускающего значения NULL, для id. Это означает, что любое значение, которое вы помещаете внутри parent_id, должно иметь соответствующую строку с тем же значением id. Это должно автоматически привести к нарушению ограничения внешнего ключа путем вставки недопустимых строк, которые вы цитируете в своем сообщении.

Если поле должно оставаться nullable=false, вы можете создать контрольную строку, и любые устаревшие данные, которые вы загружаете с недопустимыми ссылками parent_id, можно изменить, чтобы использовать id контрольной строки, чтобы модель данных является действительным. Если модель данных можно немного изменить, legacy_parent_id может содержать устаревшую ссылку, а ваш код может иметь разные логические пути в зависимости от контрольной строки.

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

В унаследованной модели у вас будет legacy_parent_id, который вы заполняете, это просто целое число. В модели, отличной от устаревшей, у вас может быть проверенная взаимосвязь внешнего ключа parent_id, которая не допускает значения NULL.

1
Naros 6 Янв 2016 в 05:20