Проблема с 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?
Большое спасибо взаранее, Хавьер