hibernate - 部分主键列上的 JPA OneToOne 映射
问题描述
我有两个表,如下所述:
+-------------------+ +------------------------+
| TB_CONFIG | | TB_CONFIG_LANG |
+-------------------+ +------------------------+
| Customer_Id (PK) | | Product_Id (PK)|
| Company_Id (PK) | | Config_Id (PK)|
| Product_Id (PK) | | Language_Id (PK)|
| Config_Id (PK) | | Config_Description |
| Config_Value | | Config_Hint |
+-------------------+ | Config_Help |
+------------------------+
TB_CONFIG
数据是关于由指定的“客户”和“公司”在我们拥有的“产品(解决方案)”上设置的“配置”。
TB_CONFIG_LANG
存储有关我们在各种语言上拥有的所有配置的描述、提示和帮助的数据,例如“EN_US”、“PT_BR”等。
我们总是将语言作为参数传递给JPQL
将用于加载数据的,但我无法实现映射此连接的“正确方法”......
实际上,这些表映射如下:
TB_CONFIG_LANG 主键
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Embeddable
@MappedSuperclass
public class PkConfigurationLang implements Serializable {
@Column(name = "PRODUCT_ID")
@EqualsAndHashCode.Include
private int productId;
@Column(name = "CONFIG_ID")
@EqualsAndHashCode.Include
private String configId;
@Column(name = "LANGUAGE_ID")
@EqualsAndHashCode.Include
private String languageId;
}
TB_CONFIG_LANG 表
@Getter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Entity
@Table(schema = "CLOUD_CATALOG", name = "TB_CONFIG_LANG")
@Cache(region = "oConfigurationsCache", usage = CacheConcurrencyStrategy.READ_WRITE)
public class ConfigurationLanguage {
@EmbeddedId
private PkConfigurationLang id;
@Column(name = "CONFIG_DESCRIPTION")
private String name;
@Column(name = "CONFIG_HINT")
private String description;
@Column(name = "CONFIG_HELP")
private String help;
}
TB_CONFIG 主键
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Embeddable
@MappedSuperclass
public class PkConfiguration implements Serializable {
@Column(name = "CUSTOMER_ID")
@EqualsAndHashCode.Include
private int customerId;
@Column(name = "COMPANY_ID")
@EqualsAndHashCode.Include
private int companyId;
@Column(name = "PRODUCT_ID")
@EqualsAndHashCode.Include
private int productId;
@Column(name = "CONFIG_ID")
@EqualsAndHashCode.Include
private String configId;
}
TB_CONFIG 表
@Getter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Entity
@Table(schema = "CLOUD_CATALOG", name = "TB_CONFIG")
@Cache(region = "oConfigurationsCache", usage = CacheConcurrencyStrategy.READ_WRITE)
public class Configuration {
@EmbeddedId
private PkConfiguration id;
@Column(name = "CONFIG_VALUE")
private String configValue;
}
我的第一次尝试是映射@OneToOne
一个类的关系Configuration
,如下所示:
TB_CONFIG 具有 OneToOne 关系的表
@Getter
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Entity
@Table(schema = "CLOUD_CATALOG", name = "TB_CONFIG")
@Cache(region = "oConfigurationsCache", usage = CacheConcurrencyStrategy.READ_WRITE)
public class Configuration {
@EmbeddedId
private PkConfiguration id;
@Column(name = "CONFIG_VALUE")
private String configValue;
@OneToOne
@JoinColumn(name = "PRODUCT_ID", referencedColumnName = "PRODUCT_ID", insertable = false, updatable = false)
@JoinColumn(name = "CONFIG_ID", referencedColumnName = "CONFIG_ID", insertable = false, updatable = false)
private ConfigurationLanguage language;
}
像这样写一个JPQL:
TypedQuery<ConfigurationCompany> query = manager.createQuery("select c from ConfigurationCompany c " +
"join c.language l " +
"where c.id.customerId = :customerId " +
"and c.id.companyId = :companyId " +
"and c.id.productId = :productId " +
"and c.id.configId = :configId " +
"and l.id.languageId = :languageId", ConfigurationCompany.class);
但我在尝试运行应用程序时收到以下异常:
org.springframework.orm.jpa.JpaSystemException: referencedColumnNames(PRODUCT_ID, CONFIG_ID) of com.mycompany.api.configuration.domain.model.ConfigurationCompany.language referencing com.mycompany.api.configuration.domain.model.ConfigurationLanguage not mapped to a single property; nested exception is org.hibernate.AnnotationException: referencedColumnNames(PRODUCT_ID, CONFIG_NAME) of com.mycompany.api.configuration.domain.model.ConfigurationCompany.language referencing com.mycompany.api.configuration.domain.model.ConfigurationLanguage not mapped to a single property
我认为这是因为我在创建@OneToOne
映射时没有引用 Language_Id 列......
所以,问题是:这是实现这种关系的正确方式吗?如果是,我错过了什么?如果没有,我该如何正确配置此映射?
提示:我无法更改数据库架构。
非常感谢!
解决方案
像这样的一对一映射是 Hibernate 不支持的 AFAIK。除此之外,这样做是不安全的,除非您为TB_CONFIG(PRODUCT_ID, CONFIG_ID)
and定义了唯一键TB_CONFIG_LANG(PRODUCT_ID, CONFIG_ID)
。像这样使用@OneToMany
关联有什么问题:
@OneToMany
@JoinColumn(name = "PRODUCT_ID", referencedColumnName = "PRODUCT_ID", insertable = false, updatable = false)
@JoinColumn(name = "CONFIG_ID", referencedColumnName = "CONFIG_ID", insertable = false, updatable = false)
private Set<ConfigurationLanguage> languages;
这样,您只需要一个唯一的密钥TB_CONFIG_LANG(PRODUCT_ID, CONFIG_ID)
。为了避免这种情况,您需要使用地图:
@OneToMany
@MapKeyColumn(name = "LANGUAGE_ID")
@JoinColumn(name = "PRODUCT_ID", referencedColumnName = "PRODUCT_ID", insertable = false, updatable = false)
@JoinColumn(name = "CONFIG_ID", referencedColumnName = "CONFIG_ID", insertable = false, updatable = false)
private Map<String, ConfigurationLanguage> languages;
但请注意,从数据库的角度来看,这仍然不安全。外键必须始终引用唯一键。因此,要么将Company_Id
and也Customer_Id
放入,要么TB_CONFIG_LANG
从主键中删除这两个TB_CONFIG
推荐阅读
- javascript - 如何在同一页面中显示多个文本区域的字符计数器?
- ada - 如何使用递归删除元素?
- jupyter-notebook - 有没有办法让 SAS 只打印频率表的前 10 行?
- sql-server - SQL Server在while中双重录制
- php - 手动安装 laravel/ui 会报错
- airflow-scheduler - 如何在 Airflow 的 on_dag_failure 函数中获取标签?
- python - 使用 DataFrame 等效的 R 和 Python
- c++ - 隐式转换为基础类型
- python - 如何在 Python 中使用 Openpyxl 在现有 excel 文件中保留图表格式和文本框?
- node.js - Nodejs无法读取docker镜像中的文件夹“没有这样的文件或目录”