При записи объекта в PostgreSQL не отрабатывает генератор последовательности: "null value in column [] of relation [] violates not-null constraint"

Рейтинг: 3Ответов: 1Опубликовано: 04.03.2023

Всем привет! Прошу помощи. Пересмотрела массу тем с подобной ошибкой, но решение для себя не нашла.

При записи объекта в PostgreSQL не отрабатывает генератор последовательности.
Выдает ошибку:

ERROR: null value in column "doc_number" of relation "receiving_acts" violates not-null constraint

Более детально:

"org.springframework.dao.DataIntegrityViolationException: could not
execute statement; SQL [n/a]; constraint [doc_number\" of relation
\"receiving_acts].
org.hibernate.exception.ConstraintViolationException: could not
execute statement. org.postgresql.util.PSQLException: ERROR: null
value in column \"doc_number\" of relation \"receiving_acts\" violates
not-null constraint\n  Подробности: Failing row contains
(feba8451-de26-47c2-9fe2-5381318d8deb, null, 2023-03-01 14:42:03+02,
f, 47a9bb21-fa67-4ff7-bf33-e276d23a19eb)."

Есть класс Entity:

@Entity
@Table(name = "receiving_acts")
@Getter
@Setter
public class ReceivingAct extends AbstractDocumentEntity {

    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ra_dn_gen")
    @SequenceGenerator(name = "ra_dn_gen", sequenceName = "receiving_acts_doc_number_seq", allocationSize = 1)
    @Column(name = "doc_number")
    private Integer docNumber;

    @ManyToOne
    @JoinColumn(name = "contractor_id", nullable = false)
    private Contractor contractor;

    @OneToMany(mappedBy = "receivingAct", cascade = CascadeType.ALL)
    private List<ReceivingActTSVehiclesLine> tableVehicles;
}

Поле @Id типа UUID находится в классе AbstractDocumentEntity и с ним проблем нет, генератор отрабатывает.
DDL создание таблицы:

CREATE SEQUENCE IF NOT EXISTS receiving_acts_doc_number_seq AS INTEGER INCREMENT BY 1;

CREATE TABLE receiving_acts
(
    id            UUID                     NOT NULL DEFAULT gen_random_uuid(),
    doc_number    INTEGER                  NOT NULL DEFAULT nextval('receiving_acts_doc_number_seq'),
    date_time     TIMESTAMP WITH TIME ZONE NOT NULL,
    marked        BOOLEAN                  NOT NULL,
    contractor_id UUID                     NOT NULL,
    CONSTRAINT pk_receiving_acts PRIMARY KEY (id),
    FOREIGN KEY (contractor_id) REFERENCES contractors (id)
);

Метод в классе-контроллере:

@PostMapping("/")
@ResponseStatus(HttpStatus.CREATED)
public ReceivingActResponse createReceivingAct(@RequestBody ReceivingActRequest docRequest) {
    ReceivingAct docEntity = receivingActMapper.toReceivingActEntity(docRequest);
    ReceivingAct savedDocEntity = receivingActService.add(docEntity);
    return receivingActMapper.toReceivingActResponse(savedDocEntity);
}

Метод add в классе ReceivingActServiceImpl:

@Override
public ReceivingAct add(ReceivingAct receivingAct) {
    return receivingActRepository.save(receivingAct);
}

Метод save стандартный из репозитория ReceivingActRepository:

public interface ReceivingActRepository extends JpaRepository<ReceivingAct, UUID> {
}

JSON post-запроса:

{
    "docDateTime": "2023-03-03 11:53:56",
    "contractorId": "47a9bb21-fa67-4ff7-bf33-e276d23a19eb",
    "tableVehicles": [
        {
            "grossWeight": 1564.910,
            "vinCodeId": "d7094b75-fefc-4745-a4b6-4c5d4a2f97c3",
            "cellId": "bba1f527-a815-41ac-bbf9-aa5433d2d888"
        }
    ]
}

В итоге при создании объекта docNumber инициализируется null, как и id. Но id возвращается сгенерированным, а docNumber нет.

Меняла тип Integer на int, генерация не происходит, инициализируется 0 и 0 записывается.

Последовательность в БД создана, к полю привязана. Если руками добавлять строку, то docNumber генерируется.

Пробовала в @Column добавлять columnDefinition = "integer default nextval('receiving_acts_doc_number_seq'), не помогло.

Уже не знаю куда смотреть, куда копать. Может самому как-то надо генерировать, до сохранения, но это ж нелогично. Может когда есть еще одна последовательность, то нужен другой подход.

Ответы

▲ 1Принят

Для того, чтобы hibernate не использовал поле при вставке вы можете указать это поле как игнорируемое (insertable = false)

@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ra_dn_gen")
@SequenceGenerator(name = "ra_dn_gen", sequenceName = "receiving_acts_doc_number_seq", allocationSize = 1)
@Column(name = "doc_number", insertable=false) // <--
private Integer docNumber;

Второй вариант: добавляем аннотацию @Generated(GenerationTime.INSERT), чтобы объяснить хиберу, что это поле будет генерироваться базой данных и его не нужно вставлять.

@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ra_dn_gen")
@SequenceGenerator(name = "ra_dn_gen", sequenceName = "receiving_acts_doc_number_seq", allocationSize = 1)
@Column(name = "doc_number")
@Generated(value = GenerationTime.INSERT) // <--
private Integer docNumber;

Второй вариант мне нравится больше, т.к сразу после сохранения в сущности окажется сгенерируемое поле. В первом варианте придется писать логику получения сущности.