Подтвердить что ты не робот

Невозможно изменить отношение @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? Есть ли обходной путь для включения обнуляемых внешних ключей в этом случае?

Спасибо заранее.

4b9b3361

Ответ 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