我正在使用 MySQL 数据库并使用 Hibernate (6.6.0.Final),而不使用 Spring。我的架构涉及两个表,
film
和 film_text
,它们以一对一关系隐式相关。 film
表有一个生成的主键 (film_id
),我希望将此键与 film_text
表共享作为其主键。
逻辑关系是这样的:
Film
实体知道其对应的Text
实体,但Text
不保留对Film
的引用。这是我的表格的结构:
DDL
film
:
CREATE TABLE `film` (
`film_id` smallint unsigned NOT NULL AUTO_INCREMENT,
`title` varchar(128) NOT NULL,
`description` text,
`release_year` year DEFAULT NULL,
`language_id` tinyint unsigned NOT NULL,
`original_language_id` tinyint unsigned DEFAULT NULL,
`rental_duration` tinyint unsigned NOT NULL DEFAULT '3',
`rental_rate` decimal(4,2) NOT NULL DEFAULT '4.99',
`length` smallint unsigned DEFAULT NULL,
`replacement_cost` decimal(5,2) NOT NULL DEFAULT '19.99',
`rating` enum('G','PG','PG-13','R','NC-17') DEFAULT 'G',
`special_features` set('Trailers','Commentaries','Deleted Scenes','Behind the Scenes') DEFAULT NULL,
`last_update` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`film_id`),
KEY `idx_fk_language_id` (`language_id`),
KEY `idx_fk_original_language_id` (`original_language_id`),
CONSTRAINT `fk_film_language` FOREIGN KEY (`language_id`) REFERENCES `language` (`language_id`) ON DELETE RESTRICT ON UPDATE CASCADE,
CONSTRAINT `fk_film_language_original` FOREIGN KEY (`original_language_id`) REFERENCES `language` (`language_id`) ON DELETE RESTRICT ON UPDATE CASCADE
) ENGINE=InnoDB;
DDL
film_text
:
CREATE TABLE `film_text` (
`film_id` smallint NOT NULL,
`title` varchar(255) NOT NULL,
`description` text,
PRIMARY KEY (`film_id`),
FULLTEXT KEY `idx_title_description` (`title`, `description`)
) ENGINE=InnoDB;
在我的 Hibernate 模型中,我使用
@MapsId
注释来确保 Text
实体重用关联的 Film
的主键。这是我的实体的代码:
Film
实体:
@Entity
@Table(schema = "movie", name = "film")
@Getter
@Setter
@NoArgsConstructor
@RequiredArgsConstructor
public class Film {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "film_id")
private Integer id;
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
@MapsId
@JoinColumn(name = "film_id", nullable = false)
@NonNull
private Text text;
// other fields and relationships
}
Text
实体:
@Entity
@Table(schema = "movie", name = "film_text")
@Getter
@Setter
@NoArgsConstructor
@RequiredArgsConstructor
public class Text {
@Id
@Column(name = "film_id")
@Setter(AccessLevel.NONE)
private Integer id;
@Column(nullable = false)
@NonNull
private String title;
@Column(length = 65_535)
private String description;
}
当我尝试持久化
Film
实体(它应该级联 Text
的持久化)时,我遇到以下错误:
Identifier of entity 'Text' must be manually assigned before calling 'persist()'
org.hibernate.id.IdentifierGenerationException: Identifier of entity 'Text' must be manually assigned before calling 'persist()'
我希望当我持久化一个
Film
实体时,Hibernate 应该为 film
实体生成一个 ID,并自动将该 ID 分配给关联的 Text
实体(因为它们共享相同的主键)。
@MapsId
实体中设置了 Film
,它应该将 Film
的主键映射到 Text
。CascadeType.ALL
。@JoinColumn
来确保两个实体中 film_id
列之间的正确映射。为什么 Hibernate 在
film_id
操作期间不自动将 Film
从 Text
分配给 persist()
,我该如何解决这个 IdentifierGenerationException
?我的实体映射中是否缺少某些内容,可以让 ID 在两个实体之间正确共享?
任何见解或建议将不胜感激!
您的关系倒退,并且滥用了 MapsId 注释。您希望该注释位于“另一”侧,即 Text->Film 关系上,因为它告诉 JPA 此关系(及其 FK 列)控制注释中指定的 ID 列/属性。更多这样的:
@Entity
public class Film {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "film_id")
private Integer id;
@OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
@NonNull
private Text text;
// other fields and relationships
}
@Entity
public class Text {
@Id
@Column(name = "film_id")
private Integer id;
@MapsId
@JoinColumn(name = "film_id")
Film film;
// other fields and relationships
}
您也可以在文本中不使用 Integer id 属性,因为 JPA 允许将关系标记为 ID:
@Entity
public class Text {
@Id
@JoinColumn(name = "film_id")
Film film;
// other fields and relationships
}