Дополнительные запросы Hibernate, проблема с @GeneratedValue - PullRequest
0 голосов
/ 21 июня 2019

Проблема с Hibernate и связями (отображением) для существующих дочерних значений и стратегий @GeneratedValue.Требуются дополнительные запросы?

Я изучаю Hibernate и практикуюсь на своем собственном примере, используя связи один-ко-многим, многие-к-одному и многие-ко-многим.Упрощение ради запроса:

  • У меня есть классы Java Пользователь, Страна и Языки.Один пользователь может жить только в одной стране (так как отношение много-к-одному) и может говорить на нескольких языках (поэтому отношение много-ко-многим)

  • У меня есть таблицы базы данных 'пользователи »,« страны »,« языки »и« пользователи_языки ».'Country_id' хранится в таблице 'users' (поскольку существует только один на пользователя), а несколько 'language_id' и 'user_id' хранятся в таблице 'users_languages'.

  • Я предварительно заполнил таблицы «страны» и «языки», поскольку они являются фиксированными значениями, поэтому я не хочу добавлять / удалять любые из этих значений из этих таблиц.Они будут связаны с разными пользователями.

Схема таблиц (для краткости я убрал столбцы):

CREATE TABLE `users` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `username` varchar(20) NOT NULL,
   `country_id` int(11) DEFAULT NULL,
   PRIMARY KEY (`id`),
   UNIQUE KEY `username_UNIQUE` (`username`),
   CONSTRAINT `u_country_fk` FOREIGN KEY (`country_id`) REFERENCES `countries` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
 )

CREATE TABLE `countries` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `name` varchar(45) NOT NULL,
   PRIMARY KEY (`id`),
   UNIQUE KEY `name_UNIQUE` (`name`)
 )

CREATE TABLE `languages` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `language` varchar(15) NOT NULL,
   PRIMARY KEY (`id`),
   UNIQUE KEY `language_UNIQUE` (`language`)
 )

CREATE TABLE `users_languages` (
   `user_id` int(11) NOT NULL,
   `language_id` int(11) NOT NULL,
   PRIMARY KEY (`user_id`,`language_id`),
   CONSTRAINT `ul_language_fk` FOREIGN KEY (`language_id`) REFERENCES `languages` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
   CONSTRAINT `ul_user_fk` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
 )

Как уже отмечалось, я уже вставилнеобходимые строки в таблицах 'country' и 'languages', чтобы они были доступны только пользователям.

Соответствующие объекты Java:

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    @NaturalId
    @Column(name = "username", nullable = false, unique = true)
    private String username;
    @ManyToOne
    @JoinColumn(name="country_id",
    foreignKey = @ForeignKey(name = "u_country_fk"))
    private Country country;
    @ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
    @LazyCollection(LazyCollectionOption.FALSE)
    @JoinTable(
    name = "users_languages",
    joinColumns = @JoinColumn(name = "user_id", nullable = false),
    inverseJoinColumns = @JoinColumn(name = "language_id", nullable = false)
    )
    private List<Language> languages;

    public User() {
        this.languages = new ArrayList<>();
    }
    public User(String username, Country country) {
        this.username = username;
        this.country = country;
        this.languages = new ArrayList<>();
    }

    public void addLanguage(Language language) {
        this.languages.add(language);
        language.getUsers().add(this);
    }
    public void removeLanguage(Language language) {
        this.languages.remove(language);
        language.getUsers().remove(this);
    }
    public void removeAllLanguages() {
        this.languages.clear();
    }

    // getters and setters omitted
}
@Entity
@Table(name = "countries")
public class Country {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    @NaturalId
    @Column(name = "name", nullable = false, unique = true)
    private String name;
//  @OneToMany(cascade = CascadeType.ALL)
//  @JoinColumn(name="id")
//  private Set<User> users;

    public Country() {
//      users = new HashSet<>();
    }
    public Country(String name) {
        this.name = name;
//      users = new HashSet<>();
    }

    // getters and setters omitted
}
@Entity
@Table(name = "languages")
public class Language {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    @NaturalId
    @Column(name = "language", nullable = false, unique = true)
    private String language;
    @ManyToMany(mappedBy = "languages")
    Set<User> users;

    public Language() {
        users = new HashSet<>();
    }
    public Language(String language) {
        this.language = language;
        users = new HashSet<>();
    }

    // getters and setters omitted
}

КогдаПри создании нового пользователя у меня возникли проблемы с @GeneratedValue, потому что в пакете мне нужно хранить несколько языков.Наконец, тот, который работал для меня, это GenerationType.IDENTITY, но я прочитал о дополнительном запросе, который требуется Hibernate, чтобы узнать следующий идентификатор для использования.Кроме того, поскольку в пакете, если я создаю новый объект Language (который уже существует в базе данных), возникает проблема (я ожидал, что Hibernate сможет управлять этим автоматически).То же самое для нового объекта Country.Поэтому вместо создания новых объектов, поскольку соответствующие строки уже существуют в базе данных, я получаю их из базы данных, чтобы связать их с создаваемым пользователем.Но я думаю, что Hibernate снова выполнит те же запросы (проверит наличие в базе данных объектов Language и Country, связанных с пользователем), так что, вероятно, я дублирую количество необходимых запросов, снижая производительность.См. Фрагмент кода ниже:

public int addUser(SessionFactory factory, String username, String countryName, String[] languages) {
    User user = null;
    try (Session session = factory.openSession()) {
        Transaction transaction = session.beginTransaction();
        Country country = (Country) session
            .createQuery("FROM Country WHERE name = :country_name")
            .setParameter("country_name", countryName).getSingleResult();
        user = new User(username, password, name, email, gender, country);
        for (String language : languages) {
            Language lang = (Language) session
                .createQuery("FROM Language WHERE language = :language")
                .setParameter("language", language).getSingleResult();
            user.addLanguage(lang);
        }
        session.persist(user);
        transaction.commit();
    }
    return user==null ? -1 : user.getId();
}

Может кто-нибудь помочь мне с нижеследующими пунктами:

  • Использую ли я правильную стратегию для генерации идентификаторов?

  • Использую ли я правильную каскадную методологию (как в базе данных, так и в Hibernate)?

  • Есть ли лучший способ для вышеуказанной процедуры для добавления пользователя,сокращение общего количества запросов к базе данных / повышение производительности?

  • Должен ли я сделать двустороннюю ассоциацию страны также?Рекомендации по однонаправленному / двунаправленному классу для страны и языка.

  • Любые другие предложения / улучшения, которые мне следует применить с Hibernate?

Большое спасибо взаранее, Хавьер

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...