Hibernate UniqueConstraint несколько столбцов с возможными значениями null
У меня есть проблема, препятствующая вставке дублированных значений в базу данных MySQL (используя Hibernate). Проблема в том, что у меня есть категория, которая имеет родительскую категорию. Могут существовать две категории с одинаковым именем, но разными родителями (поэтому название категории не может использоваться в качестве businessId). Если категория не имеет родителя (т. е. категории верхнего уровня), значение parentId равно NULL. Я не могу понять, что я делаю неправильно в следующем коде, чтобы получить дубликат вставлен в база данных.
Вот мой класс сущностей.
@Entity
@Table(name = "category", uniqueConstraints = {@UniqueConstraint(columnNames = {"name","parentId"})})
public class Category implements Serializable {
@Id
@Column(name="id")
@GeneratedValue
private long id;
@Column(name="name")
private String name;
@ManyToOne(cascade={CascadeType.ALL})
@JoinColumn(name="parentId", nullable=true)
private Category parent;
...
}
Вот код, используемый для разбора новых категорий (и я считаю, что здесь что-то не так: -|)
for (...) {
Category parent = null;
String [] categories = somePath.split("\\");
for (String cat: categories) {
Category category = new Category();
category.setName(cat);
category.setParent(parent);
parent = categoryDB.insertCategory(category);
}
}
А вот функция insertCategory
public Category insertCategory (Category category) {
Session session = sessionFactory.openSession();
Transaction transaction = null;
try{
transaction = session.beginTransaction();
category.setId((Long) session.save(category));
transaction.commit();
} catch (HibernateException e) {
if (transaction!=null)
transaction.rollback();
...
} finally {
session.close();
}
return category;
}
После выполнения кода я получил следующие записи в базе данных:
select * from category;
+----+--------------+----------+
| id | name | parentId |
+----+--------------+----------+
| 1 | A | NULL |
| 4 | A | NULL |
| 2 | B | 1 |
| 3 | C | 2 |
| 5 | B | 4 |
| 6 | C | 5 |
+----+--------------+----------+
Когда я пытаюсь ограничить только по" имени", в базу данных вставляются только 3 целого, но я не могу наложить ограничение только по имени, из-за описанной ситуации выше.
EDIT: одной из возможностей, которые я видел, чтобы решить эту проблему, было использование функций в определениях ограничений (которые в случае MySQL могут быть IFNULL(parentId, 0). И кажется, что такая возможность возможна, например, в Oracle (согласно этому посту), но не в MySQL.
EDIT 2 : на самом деле я нашел в MySQL bug tracker людей с подобной проблемой. Поскольку я использую MySQL, я проверил, какой код генерируется при создании таблицы и она была практически идентична той, что была в Bug report (которая фактически считалась не багом в соответствии с некоторыми стандартами, упомянутыми в bugtracker. В любом случае, мой коллега попытался выполнить эквивалентный код на MS SQL Server, и это фактически помешало ему вставить строки с одинаковыми значениями в поле name и null parentId. Похоже, что на уровне базы данных нет возможности (по крайней мере, с помощью MySQL) сделать желаемое ограничение, что весьма разочаровывает. Принятие ответа, поскольку это один из возможных обходных путей для решения проблемы (используя magic numbers
, которого я не буду придерживаться, предпочитая сохранить один дополнительный запрос find
в моем коде)
Любая помощь будет оценена по достоинству, Спасибо!
1 ответ:
Простое решение-заставить
parentId
быть "собой".Таким образом, у вас никогда не может быть двух категорий "верхнего уровня". Сделайте+----+--------------+----------+ | id | name | parentId | +----+--------------+----------+ | 1 | A | 1 | | 2 | B | 1 | | 3 | C | 2 | | 4 | C | 3 | +----+--------------+----------+
parentId
не подлежащим аннулированию.Я уверен, что ваша проблема связана с тем, что нулевые значения не проверяются в ограничениях. Хотя вы можете попробовать с проверками hibernate, но это будет, безусловно, более неэффективно.
@org.hibernate.annotations.Check