вопрос уникальности столбца hibernate

Я все еще в процессе обучения hibernate/hql, и у меня есть вопрос, который наполовину лучший вопрос практики/наполовину проверка здравомыслия.

предположим, у меня есть класс A:

@Entity
public class A
{
    @Id @GeneratedValue(strategy=GenerationType.AUTO)
    private Long id;

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

    //getters, setters, etc. omitted for brevity
}

Я хочу обеспечить, чтобы каждый экземпляр A, который сохраняется, имел уникальное имя (следовательно, аннотацию @Column), но я также хочу иметь возможность обрабатывать случай, когда уже есть экземпляр a, сохраненный с этим именем. Я вижу два способа сделать это:--3-->

1) я могу поймать орг.зимовать.исключение.ConstraintViolationException, которое может быть создано во время сеанса.вызов saveOrUpdate () и попробуйте обработать его.

2) я могу запросить существующие экземпляры A, которые уже имеют это имя в DAO перед вызовом сеанса.saveOrUpdate().

прямо сейчас я склоняюсь к подходу 2, потому что в подходе 1 я не знаю, как программно выяснить, какое ограничение было нарушено (есть несколько других уникальных членов в A). Сейчас мой ДАО.код save () выглядит примерно так:

public void save(A a) throws DataAccessException, NonUniqueNameException
{
    Session session = sessionFactory.getCurrentSession();

    try
    {
        session.beginTransaction();

        Query query = null;

        //if id isn't null, make sure we don't count this object as a duplicate
        if(obj.getId() == null)
        {
            query = session.createQuery("select count(a) from A a where a.name = :name").setParameter("name", obj.getName());
        }
        else
        {
            query = session.createQuery("select count(a) from A a where a.name = :name " + 
                "and a.id != :id").setParameter("name", obj.getName()).setParameter("name", obj.getName());
        }

        Long numNameDuplicates = (Long)query.uniqueResult();
        if(numNameDuplicates > 0)
            throw new NonUniqueNameException();

        session.saveOrUpdate(a);
        session.getTransaction().commit();
    }
    catch(RuntimeException e)
    {
            session.getTransaction().rollback();
            throw new DataAccessException(e); //my own class
    }
}

Я иду об этом в правильном направлении? Может ли hibernate сказать мне программно (т. е. не как строка ошибки), какое значение нарушает ограничение уникальности? Отделяя запрос от фиксации, я приглашаю ошибки безопасности потока или я в безопасности? Как это обычно делается?

спасибо!

2 ответов


Я думаю, что ваш второй подход лучше.

чтобы иметь возможность поймать исключение ConstraintViolation с любой уверенностью, что этот конкретный объект вызвал его, вам нужно будет сбросить сеанс сразу после вызова saveOrUpdate. Это может привести к проблемам производительности, если вам нужно вставить несколько таких объектов одновременно.

даже если вы будете проверять, существует ли имя в таблице при каждом действии сохранения, это все равно будет быстрее, чем промывка после каждой вставки. (Вы всегда можете проверить, чтобы подтвердить.)

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


подход 1 будет в порядке, если:

  • в сущности существует только одно ограничение.
  • есть только один грязный объект в сессии.

помните, что объект не может быть сохранен до flush() вызывается или транзакция совершается.

для лучшей отчетности об ошибках я бы:

  1. используйте подход два для каждого нарушения ограничений, поэтому я могу дать конкретную ошибку для каждого из них..
  2. выполнить перехватчик, который в случае исключения ограничения повторяет транзакцию (максимальное количество раз), поэтому нарушение не может быть поймано в одном из тестов. Это необходимо только в зависимости от уровня изоляции транзакции.