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

Что такое one-to-one?

2.0 Middle🔥 201 комментариев
#ORM и Hibernate

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

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

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

Что такое one-to-one отношение?

One-to-one (один-к-одному) — это тип отношения в базах данных и объектно-ориентированном программировании, при котором одна запись/объект связана ровно с одной записью/объектом другой сущности, и наоборот.

Основной концепт

Если у вас есть две таблицы, то отношение one-to-one означает:

  • Один User имеет ровно один Profile
  • Один Profile принадлежит ровно одному User
User (ID) ↔ Profile (ID)
   1      ↔      1
   2      ↔      2
   3      ↔      3

Примеры one-to-one отношений

1. Пользователь и его профиль

User: id, username, email
Profile: id, user_id, bio, avatar, phone

2. Человек и паспорт

Person: id, name, birth_date
Passport: id, person_id, passport_number, issue_date

3. Компания и её регистрация

Company: id, name
CompanyRegistration: id, company_id, registration_number, date

Реализация в SQL

Вариант 1: Foreign Key в дочерней таблице

CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(100),
    email VARCHAR(100)
);

CREATE TABLE profiles (
    id INT PRIMARY KEY,
    user_id INT UNIQUE,
    bio TEXT,
    avatar_url VARCHAR(255),
    FOREIGN KEY (user_id) REFERENCES users(id)
);

Ключ UNIQUE на user_id гарантирует, что каждый пользователь имеет только один профиль.

Вариант 2: Foreign Key в главной таблице

CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(100),
    profile_id INT UNIQUE,
    FOREIGN KEY (profile_id) REFERENCES profiles(id)
);

CREATE TABLE profiles (
    id INT PRIMARY KEY,
    bio TEXT,
    avatar_url VARCHAR(255)
);

Вариант 3: Shared Primary Key

CREATE TABLE users (
    id INT PRIMARY KEY,
    username VARCHAR(100)
);

CREATE TABLE profiles (
    user_id INT PRIMARY KEY,
    bio TEXT,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

Здесь user_id в profiles одновременно и primary key, и foreign key.

Реализация в Java (JPA/Hibernate)

Вариант 1: Foreign Key в дочерней таблице

@Entity
@Table(name = "users")
public class User {
    @Id
    private Long id;
    
    private String username;
    private String email;
    
    @OneToOne(mappedBy = "user")
    private Profile profile;
    
    // getters/setters
}

@Entity
@Table(name = "profiles")
public class Profile {
    @Id
    private Long id;
    
    @OneToOne
    @JoinColumn(name = "user_id", unique = true)
    private User user;
    
    private String bio;
    private String avatarUrl;
    
    // getters/setters
}

Вариант 2: Shared Primary Key

@Entity
@Table(name = "users")
public class User {
    @Id
    private Long id;
    
    private String username;
    
    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private Profile profile;
    
    // getters/setters
}

@Entity
@Table(name = "profiles")
public class Profile {
    @Id
    private Long userId;  // primary key + foreign key
    
    private String bio;
    
    @OneToOne(mappedBy = "profile")
    private User user;
    
    // getters/setters
}

Использование One-to-One в коде

// Создание
User user = new User();
user.setId(1L);
user.setUsername("john_doe");

Profile profile = new Profile();
profile.setId(1L);
profile.setBio("Software Engineer");
profile.setUser(user);
user.setProfile(profile);

userRepository.save(user);

// Получение
User retrievedUser = userRepository.findById(1L).orElse(null);
if (retrievedUser != null) {
    String bio = retrievedUser.getProfile().getBio();
    System.out.println(bio);
}

// Обновление
User user = userRepository.findById(1L).orElse(null);
if (user != null) {
    user.getProfile().setBio("Senior Software Engineer");
    userRepository.save(user);
}

// Удаление
User user = userRepository.findById(1L).orElse(null);
if (user != null) {
    userRepository.delete(user);
}

Cascade операции

При one-to-one важно правильно настроить cascade:

public class User {
    @OneToOne(cascade = CascadeType.ALL)
    private Profile profile;
}

// cascade = CascadeType.ALL означает:
// - Сохранить profile при сохранении user
// - Обновить profile при обновлении user
// - УДАЛИТЬ profile при удалении user ← осторожно!

Это может быть опасно! Альтернатива:

@OneToOne(cascade = CascadeType.PERSIST)
private Profile profile;
// Только PERSIST (сохранение), без DELETE

Различия One-to-One от других отношений

ТипОтношениеПример
One-to-One1:1User ↔ Profile
One-to-Many1:NUser ↔ Posts (один пользователь много постов)
Many-to-OneN:1Posts ↔ User (много постов одного пользователя)
Many-to-ManyN:MStudents ↔ Courses (много студентов, много курсов)

Когда использовать One-to-One

Хорошо использовать, когда:

  • Нужно разделить большую таблицу на две более узкие таблицы
  • Секретные данные нужны только иногда (отдельно от основных)
  • Опциональные данные (профиль может не существовать)
  • Улучшение производительности через нормализацию

Плохо использовать, когда:

  • Можно просто добавить столбцы в одну таблицу
  • Усложняется запросы и логика
  • Теряется простота и производительность

Оптимизация запросов

Ленивая загрузка (Lazy Loading)

@OneToOne(fetch = FetchType.LAZY)
private Profile profile;

User user = userRepository.findById(1L).orElse(null);
// profile НЕ загружена
String bio = user.getProfile().getBio();  // Второй SQL запрос!

Жадная загрузка (Eager Loading)

@OneToOne(fetch = FetchType.EAGER)
private Profile profile;

User user = userRepository.findById(1L).orElse(null);
// profile ЗАГРУЖЕНА в первом запросе
String bio = user.getProfile().getBio();  // Нет доп. запросов

JPQL с JOIN

Query query = em.createQuery(
    "SELECT u FROM User u JOIN FETCH u.profile WHERE u.id = :id"
);
query.setParameter("id", 1L);
User user = (User) query.getSingleResult();

Заключение

One-to-one отношение — это фундаментальный концепт в проектировании БД. Правильное использование помогает нормализовать данные, но нужно быть осторожным с cascade операциями и оптимизацией запросов, чтобы не потерять производительность.

Что такое one-to-one? | PrepBro