现有子值和@GeneratedValue策略的Hibernate和关联(映射)问题。需要其他查询吗?
我正在学习Hibernate,并使用一对多,多对一和多对多关联以自己的示例进行练习。 为了查询而简化:
我有Java类User,Country和Languages。一个用户只能住在一个国家(关系是多对一的),并且会说几种语言(因此关系是多对多的)
我有数据库表“ users”,“ countries”,“ languages”和“ 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
)
如前所述,我已经在表“国家/地区”和“语言”中插入了所需的行,这样它们将对用户唯一可用。
相应的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知道要使用的下一个ID所需的额外查询。此外,因为如果我创建了一个新的对象Language(该批处理已经存在于数据库中),则该批处理存在一个问题(我希望Hibernate可以自动管理它)。新对象国家/地区也是如此。因此,不是创建新对象,而是在数据库中已经存在相应的行,而是从数据库中获取它们,以将它们与正在创建的用户相关联。但是我认为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();
}
有人可以在以下几点上帮助我吗:
我是否使用正确的策略生成ID?
我是否使用正确的级联方法(在数据库和Hibernate中都使用)?
上述方法是否有更好的方法来添加用户,从而减少对数据库的查询总数/提高性能?
我也应该使乡村协会成为竞标者吗?有关国家和语言类单向/双向的建议。
我应该与Hibernate一起使用的其他建议/改进吗?
非常感谢, 哈维尔