Вопрос об уникальности спящего столбца - PullRequest
6 голосов
/ 23 марта 2010

Я все еще нахожусь в процессе изучения 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 с таким именем.Я вижу два способа сделать это:

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

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

Сейчас я склоняюсь к подходу 2, потому что в подходе 1 я нене знаю, как программно выяснить, какое ограничение было нарушено (в A есть пара других уникальных членов).Прямо сейчас мой код DAO.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 ]

3 голосов
/ 23 марта 2010

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

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

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

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

1 голос
/ 23 марта 2010

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

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

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

Для лучшего сообщения об ошибках я бы:

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