← Назад к вопросам

Есть ли в PostgreSQL enum?

1.2 Junior🔥 71 комментариев
#Базы данных и SQL

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Enum в PostgreSQL

Короткий ответ: ДА, PostgreSQL полностью поддерживает ENUM типы данных

PostgreSQL имеет встроенную поддержку enum (перечислений) как полноценный тип данных. Это один из основных типов, наряду с INTEGER, VARCHAR, BOOLEAN и т.д.

Что такое ENUM в PostgreSQL

ENUM — это пользовательский тип данных, который ограничивает значения столбца фиксированным набором строк. Это отличная альтернатива "magic strings" (волшебным строкам).

Создание ENUM типа

Синтаксис

CREATE TYPE type_name AS ENUM ('value1', 'value2', 'value3');

Пример 1: Статусы заказа

-- Создаём enum для статусов заказов
CREATE TYPE order_status AS ENUM (
    'pending',
    'processing',
    'shipped',
    'delivered',
    'cancelled'
);

-- Используем в таблице
CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    order_number VARCHAR(50),
    status order_status NOT NULL DEFAULT 'pending',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Пример 2: Роли пользователей

CREATE TYPE user_role AS ENUM (
    'admin',
    'moderator',
    'user',
    'guest'
);

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(100) UNIQUE NOT NULL,
    role user_role NOT NULL DEFAULT 'user'
);

Использование ENUM в SQL запросах

Вставка данных

-- Вставляем значение enum
INSERT INTO orders (order_number, status)
VALUES ('ORD-001', 'pending');

INSERT INTO orders (order_number, status)
VALUES ('ORD-002', 'processing');

-- Это работает, но значение должно быть в списке enum
INSERT INTO orders (order_number, status)
VALUES ('ORD-003', 'invalid_status');  -- ОШИБКА!

Запросы с WHERE

-- Найти все обработанные заказы
SELECT * FROM orders WHERE status = 'processing';

-- Найти заказы со статусом pending или processing
SELECT * FROM orders WHERE status IN ('pending', 'processing');

-- Исключить отменённые заказы
SELECT * FROM orders WHERE status != 'cancelled';

Обновление enum значений

-- Обновить статус заказа
UPDATE orders SET status = 'shipped' WHERE id = 1;

-- Обновить несколько заказов
UPDATE orders SET status = 'delivered' WHERE id IN (1, 2, 3);

ENUM в Java с Hibernate/JPA

1. Определение enum в Java

public enum OrderStatus {
    PENDING("pending"),
    PROCESSING("processing"),
    SHIPPED("shipped"),
    DELIVERED("delivered"),
    CANCELLED("cancelled");
    
    private final String value;
    
    OrderStatus(String value) {
        this.value = value;
    }
    
    public String getValue() {
        return value;
    }
}

2. Использование в Entity с Hibernate

import javax.persistence.*;
import java.time.LocalDateTime;

@Entity
@Table(name = "orders")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "order_number", nullable = false, length = 50)
    private String orderNumber;
    
    // Способ 1: ORDINAL (рискованный, не рекомендуется)
    // @Column(name = "status")
    // @Enumerated(EnumType.ORDINAL)
    // private OrderStatus status;
    
    // Способ 2: STRING (рекомендуется для PostgreSQL)
    @Column(name = "status", nullable = false, columnDefinition = "order_status")
    @Enumerated(EnumType.STRING)
    private OrderStatus status;
    
    @Column(name = "created_at", nullable = false)
    private LocalDateTime createdAt;
    
    public Order() {}
    
    public Order(String orderNumber, OrderStatus status) {
        this.orderNumber = orderNumber;
        this.status = status;
        this.createdAt = LocalDateTime.now();
    }
    
    // Getters and setters
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    
    public String getOrderNumber() { return orderNumber; }
    public void setOrderNumber(String orderNumber) { this.orderNumber = orderNumber; }
    
    public OrderStatus getStatus() { return status; }
    public void setStatus(OrderStatus status) { this.status = status; }
    
    public LocalDateTime getCreatedAt() { return createdAt; }
    public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}

3. Использование в коде

import org.hibernate.Session;
import org.hibernate.SessionFactory;

public class OrderService {
    private SessionFactory sessionFactory;
    
    public void createOrder(String orderNumber) {
        Session session = sessionFactory.openSession();
        try {
            Order order = new Order(orderNumber, OrderStatus.PENDING);
            session.save(order);
            session.flush();
        } finally {
            session.close();
        }
    }
    
    public void updateOrderStatus(Long orderId, OrderStatus newStatus) {
        Session session = sessionFactory.openSession();
        try {
            Order order = session.get(Order.class, orderId);
            if (order != null) {
                order.setStatus(newStatus);
                session.update(order);
                session.flush();
            }
        } finally {
            session.close();
        }
    }
    
    public List<Order> getOrdersByStatus(OrderStatus status) {
        Session session = sessionFactory.openSession();
        try {
            return session.createQuery(
                "FROM Order o WHERE o.status = :status",
                Order.class
            )
            .setParameter("status", status)
            .list();
        } finally {
            session.close();
        }
    }
}

Способы хранения enum в PostgreSQL

Способ 1: Native ENUM (встроенный тип)

CREATE TYPE order_status AS ENUM ('pending', 'processing', 'shipped');

CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    status order_status NOT NULL DEFAULT 'pending'
);

Плюсы:

  • Строгая типизация на уровне БД
  • Компактное хранилище
  • Быстрые сравнения

Минусы:

  • Нужно изменять тип при добавлении новых значений
  • Сложнее с миграциями

Способ 2: VARCHAR с CHECK constraint

CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    status VARCHAR(50) NOT NULL DEFAULT 'pending',
    CHECK (status IN ('pending', 'processing', 'shipped', 'cancelled'))
);

Плюсы:

  • Легче менять значения
  • Более гибкая миграция

Минусы:

  • Меньше типизации
  • Чуть больше памяти

Способ 3: SMALLINT с таблицей-справочником (Reference Table)

CREATE TABLE order_statuses (
    id SMALLINT PRIMARY KEY,
    name VARCHAR(50) NOT NULL UNIQUE
);

INSERT INTO order_statuses VALUES (1, 'pending'), (2, 'processing'), (3, 'shipped');

CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    status_id SMALLINT NOT NULL REFERENCES order_statuses(id)
);

Проблемы с ENUM и как их решить

Проблема 1: Добавление нового значения в enum

-- НЕВОЗМОЖНО просто обновить существующий enum
-- CREATE TYPE order_status AS ENUM ('...', 'new_value');  -- ОШИБКА

-- Решение: создать новый тип и переконвертировать
ALTER TYPE order_status ADD VALUE 'refunded' AFTER 'cancelled';

Проблема 2: LazyInitializationException в Hibernate

// Если enum неправильно настроен в @Enumerated
@Column(name = "status")
@Enumerated(EnumType.ORDINAL)  // ОПАСНО! Зависит от порядка
private OrderStatus status;

// Правильно:
@Column(name = "status")
@Enumerated(EnumType.STRING)  // Безопасно
private OrderStatus status;

Проблема 3: Миграция данных

-- Заменить enum значения при миграции
UPDATE orders SET status = 'new_value' WHERE status = 'old_value';

-- Или переименовать значение
ALTER TYPE order_status RENAME VALUE 'old_value' TO 'new_value';

Best Practices для ENUM

  1. Используйте ENUM для типизирования данных:

    @Enumerated(EnumType.STRING)  // Не ORDINAL!
    private OrderStatus status;
    
  2. Документируйте возможные значения:

    /**
     * Статус заказа:
     * - PENDING: ожидание обработки
     * - PROCESSING: обрабатывается
     * - SHIPPED: отправлен
     * - DELIVERED: доставлен
     * - CANCELLED: отменён
     */
    @Enumerated(EnumType.STRING)
    private OrderStatus status;
    
  3. Избегайте ORDINAL (порядок зависимых значений):

    // ПЛОХО
    @Enumerated(EnumType.ORDINAL)
    private OrderStatus status;  // Хранится как 0, 1, 2...
    
    // ХОРОШО
    @Enumerated(EnumType.STRING)
    private OrderStatus status;  // Хранится как "pending", "processing"...
    
  4. Рассмотрите VARCHAR+CHECK для часто меняющихся значений:

    -- Если часто добавляете новые статусы, используйте VARCHAR
    status VARCHAR(50) NOT NULL CHECK (status IN (...))
    
  5. Синхронизируйте Java enum с DB enum:

    // Java enum должен полностью совпадать с PostgreSQL enum
    // ORDER_STATUS в БД → OrderStatus в Java
    public enum OrderStatus {
        PENDING,      // → 'pending'
        PROCESSING,   // → 'processing'
        SHIPPED,      // → 'shipped'
        DELIVERED,    // → 'delivered'
        CANCELLED     // → 'cancelled'
    }
    

Сравнение подходов

ПодходПлюсыМинусы
Native ENUMСтрогая типизация, компактноСложные миграции
VARCHAR+CHECKГибкий, простые измененияМеньше типизации
Reference TableЛегко расширяется, нормализованоБольше запросов (JOIN)

Вывод

Да, PostgreSQL полностью поддерживает ENUM типы данных. Это удобный и безопасный способ хранения фиксированного набора значений. В Hibernate используйте @Enumerated(EnumType.STRING) для работы с PostgreSQL enum.

Однако учитывайте сложность миграций при добавлении новых значений в enum. Для часто меняющихся значений рассмотрите VARCHAR+CHECK как альтернативу.

Есть ли в PostgreSQL enum? | PrepBro