Невозможно изменить отношение @ManyToOne
У меня есть отношение "много-к-одному", которое я хочу иметь значение "nullable":
@ManyToOne(optional = true)
@JoinColumn(name = "customer_id", nullable = true)
private Customer customer;
К сожалению, JPA продолжает устанавливать столбец в моей базе данных как NOT NULL. Может кто-нибудь объяснить это? Есть ли способ заставить его работать? Обратите внимание, что я использую JBoss 7, JPA 2.0 с Hibernate в качестве поставщика непрерывности и базу данных PostgreSQL 9.1.
ИЗМЕНИТЬ
Я нашел причину своей проблемы. По-видимому, это связано с тем, как я определил первичный ключ в ссылочном объекте Customer
:
@Entity
@Table
public class Customer {
@Id
@GeneratedValue
@Column(columnDefinition="serial")
private int id;
}
Похоже, что использование @Column(columnDefinition="serial")
для первичного ключа автоматически устанавливает внешние ключи, ссылающиеся на него NOT NULL
в базе данных. Действительно ли это ожидаемое поведение при указании типа столбца как serial
? Есть ли обходной путь для включения обнуляемых внешних ключей в этом случае?
Спасибо заранее.
Ответы
Ответ 1
Я нашел решение своей проблемы. Способ определения первичного ключа в объекте Customer
в порядке, проблема заключается в объявлении внешнего ключа. Его следует объявить следующим образом:
@ManyToOne
@JoinColumn(columnDefinition="integer", name="customer_id")
private Customer customer;
Действительно, если атрибут columnDefinition="integer"
опущен, внешний ключ по умолчанию будет установлен как исходный столбец: не-нулевой последовательный номер со своей собственной последовательностью. Это, конечно, не то, что мы хотим, поскольку мы просто хотим, чтобы ссылка на автоинкрементный идентификатор, а не на создание нового.
Кроме того, кажется, что атрибут name=customer_id
также требуется, как я заметил при выполнении некоторых тестов. В противном случае столбец внешнего ключа по-прежнему будет установлен в качестве столбца источника. По-моему, это странное поведение. Комментарии или дополнительная информация, чтобы прояснить это, приветствуются!
Наконец, преимущество этого решения заключается в том, что идентификатор генерируется базой данных (а не JPA), и поэтому нам не нужно беспокоиться об этом при вставке данных вручную или через скрипты, которые часто происходят при миграции данных или обслуживании.
Ответ 2
Я столкнулся с этой проблемой, но я смог решить ее так:
@ManyToOne
@JoinColumn(nullable = true)
private Customer customer;
Возможно, проблема возникла из объявления @ManyToOne(optional = true)
Ответ 3
Это очень странно.
В JPA параметр nullable по умолчанию равен true. Я использую такую конфигурацию все время, и она работает нормально. Если вы пытаетесь сохранить объект, он должен быть успешным.
Вы пытались удалить таблицу, созданную для этих отношений? Может быть, у вас есть таблица устаревших с этим столбцом?
Или, может быть, вы должны попытаться найти решение на других фрагментах кода, потому что это правильная настройка.
Примечание. Я пробовал эту конфигурацию в PostgreSQL с JPA2 и Hibernate.
ИЗМЕНИТЬ
В этом случае, возможно, вы можете попробовать немного другое определение первичного ключа.
Например, вы можете использовать определение следующим образом:
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column()
private Long id;
и postgresql сгенерирует
id bigint NOT NULL
-- with constraint
CONSTRAINT some_table_pkey PRIMARY KEY (id)
Если это достаточно хорошо, вы можете попробовать это решение.
Ответ 4
в транзакции, но перед операцией сохранения, явно установите значение столбца внешнего ключа как нулевое. Благодаря этому режиму гибернации никогда не выполняйте запросы выбора для этой таблицы, связанной с внешним ключом, и не создавайте исключение "сохранить временный экземпляр перед сбросом". если вы хотите установить "нулевое значение" условно, то выполните 1. выборку и установку значения с помощью вызова репозитория get/find 2. затем проверьте извлеченное значение для условия и установите его равным нулю соответственно. вставил приведенный ниже код, который был протестирован и нашел работу
// Transaction Start
Optional<Customer> customerObject = customerRepository.findByCustomerId(customer.getCustomerId())
if(customerObject.isPresent())yourEnclosingEntityObject.setCustomer(customerObject)}
else {yourEnclosingEntityObject.setCustomer(null)}
yourEnclosingEntityObjectRepository.save(yourEnclosingEntityObject)
// Transaction End