Hibernate 查询:通过映射键连接
Hibernate query: join by map key
我有以下 classes:
public abstract class Entity<T> implements Serializable {
protected long id;
protected Map<Language,EntityMetaData> metaData;
...
public class Service extends Entity<Long> implements Serializable {
protected String name;
XML 映射:
<class name="Service" table="SERVICES" schema="EDRIVE" dynamic-update="false" dynamic-insert="true" batch-size="30">
<meta attribute="class-description">
This class contains definitions of relations between entities
</meta>
<id name="id" type="long" column="id">
<generator class="identity" />
</id>
<map name="metaData" table="ServiceMetaData">
<key column="serviceid"/>
<map-key-many-to-many column="languageId" class="com.core.domain.Language"/>
<one-to-many entity-name="serviceMetaData"/>
</map>
</class>
<class name="EntityMetaData" entity-name="serviceMetaData" table="SERVICESMETADATA" lazy="false">
<id name="id" type="long" column="id" access="field">
<generator class="identity" />
</id>
<property name="name" column="name" type="string" length="256" />
<property name="description" column="description" type="string" length="512"/>
</class>
我只想加载特定语言的 serviceMetadata,即 ru、en、uk 等:
Service serviceEntity = (Service) databaseUtilities
.getSession()
.createQuery("FROM Service s"
+ " JOIN FETCH s.metaData m"
+ " WHERE s.id = :id AND m.language = :language")
.setParameter("id", serviceId)
.setParameter("language", language)
.uniqueResult();
其中serviceId很长,language是Language的一个实例class。
当我尝试上述方法时,出现以下异常:
org.hibernate.QueryException: could not resolve property: language of:
serviceMetaData [FROM com.core.domain.service.Service s JOIN FETCH
s.metaData m WHERE s.id = :id AND m.language = :language]
其他情况:
Service serviceEntity = (Service) databaseUtilities
.getSession()
.createQuery("FROM Service s"
+ " WHERE s.id = :id AND s.metaData['language'] = :language")
.setParameter("id", serviceId)
.setParameter("language", language)
.uniqueResult();
例外是
org.postgresql.util.PSQLException: ERROR: invalid input syntax for
type integer: "language" Position: 235
和交叉连接!
好吧,去掉任何条件,只有服务+所有元数据属于它,看看它是如何构建的。这是一个序列化为 JSON 结果集:
"service": {
"id": 5,
"entityType": null,
"entityKey": null,
"metaData": {
"com.core.domain.Language@57d41bfc": {
"id": 2145,
"entityType": null,
"entityKey": null,
"metaData": null,
"miscMetaData": null,
"name": "Заправка топливом",
"description": null
},
"com.core.domain.Language@99ca0b20": {
"id": 2144,
"entityType": null,
"entityKey": null,
"metaData": null,
"miscMetaData": null,
"name": "Fueling",
"description": null
}
},
"miscMetaData": null,
"name": "FUELING",
"serviceType": {
"id": 23,
"entityType": null,
"entityKey": null,
"metaData": null,
"miscMetaData": null,
"value": "RECURRENT"
}
}
正如你在这里看到的,metaData 由两个对象组成(实际上是 PersistentMap with key = Language)。
所以,问题是:如何使用 Hibernate 按语言检索特定的映射值?
JPA 要求托管实体的状态和数据库必须在事务结束时同步,因此仅获取实体的部分集合实际上会删除 non-fetched 元素,这就是为什么JPA 不允许这样做。但是,您可以使用 Hibernate 执行此操作,但我不推荐它,因为它可能会导致删除。
所以要得到你想要的,你需要编写一个 HQL 查询并将数据提取到 DTO 中。在查询中,您 select 所有您需要的字段,并可以根据需要定义您的连接条件。类似于以下内容:
SELECT s.id, s.name, m.id, m.name
FROM Service s
LEFT JOIN s.metadata m ON KEY(m) = 'en'
我认为这是 Blaze-Persistence Entity Views 的完美用例。
我创建了库以允许在 JPA 模型和自定义接口或抽象 class 定义的模型之间轻松映射,类似于 Spring 类固醇数据投影。这个想法是您按照自己喜欢的方式定义目标结构(领域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。
您的用例的 DTO 模型可能如下所示 Blaze-Persistence Entity-Views:
@EntityView(Service.class)
public interface ServiceDto {
@IdMapping
Long getId();
String getName();
@Mapping("metadata['en']")
LanguageDto getLanguage();
@EntityView(EntityMetaData.class)
interface LanguageDto {
@IdMapping
Long getId();
String getName();
}
// Other mappings
}
查询就是将实体视图应用于查询,最简单的就是通过 id 进行查询。
ServiceDto a = entityViewManager.find(entityManager, ServiceDto.class, id);
Spring 数据集成让您几乎可以像 Spring 数据投影一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
我有以下 classes:
public abstract class Entity<T> implements Serializable {
protected long id;
protected Map<Language,EntityMetaData> metaData;
...
public class Service extends Entity<Long> implements Serializable {
protected String name;
XML 映射:
<class name="Service" table="SERVICES" schema="EDRIVE" dynamic-update="false" dynamic-insert="true" batch-size="30">
<meta attribute="class-description">
This class contains definitions of relations between entities
</meta>
<id name="id" type="long" column="id">
<generator class="identity" />
</id>
<map name="metaData" table="ServiceMetaData">
<key column="serviceid"/>
<map-key-many-to-many column="languageId" class="com.core.domain.Language"/>
<one-to-many entity-name="serviceMetaData"/>
</map>
</class>
<class name="EntityMetaData" entity-name="serviceMetaData" table="SERVICESMETADATA" lazy="false">
<id name="id" type="long" column="id" access="field">
<generator class="identity" />
</id>
<property name="name" column="name" type="string" length="256" />
<property name="description" column="description" type="string" length="512"/>
</class>
我只想加载特定语言的 serviceMetadata,即 ru、en、uk 等:
Service serviceEntity = (Service) databaseUtilities
.getSession()
.createQuery("FROM Service s"
+ " JOIN FETCH s.metaData m"
+ " WHERE s.id = :id AND m.language = :language")
.setParameter("id", serviceId)
.setParameter("language", language)
.uniqueResult();
其中serviceId很长,language是Language的一个实例class。
当我尝试上述方法时,出现以下异常:
org.hibernate.QueryException: could not resolve property: language of: serviceMetaData [FROM com.core.domain.service.Service s JOIN FETCH s.metaData m WHERE s.id = :id AND m.language = :language]
其他情况:
Service serviceEntity = (Service) databaseUtilities
.getSession()
.createQuery("FROM Service s"
+ " WHERE s.id = :id AND s.metaData['language'] = :language")
.setParameter("id", serviceId)
.setParameter("language", language)
.uniqueResult();
例外是
org.postgresql.util.PSQLException: ERROR: invalid input syntax for type integer: "language" Position: 235
和交叉连接!
好吧,去掉任何条件,只有服务+所有元数据属于它,看看它是如何构建的。这是一个序列化为 JSON 结果集:
"service": {
"id": 5,
"entityType": null,
"entityKey": null,
"metaData": {
"com.core.domain.Language@57d41bfc": {
"id": 2145,
"entityType": null,
"entityKey": null,
"metaData": null,
"miscMetaData": null,
"name": "Заправка топливом",
"description": null
},
"com.core.domain.Language@99ca0b20": {
"id": 2144,
"entityType": null,
"entityKey": null,
"metaData": null,
"miscMetaData": null,
"name": "Fueling",
"description": null
}
},
"miscMetaData": null,
"name": "FUELING",
"serviceType": {
"id": 23,
"entityType": null,
"entityKey": null,
"metaData": null,
"miscMetaData": null,
"value": "RECURRENT"
}
}
正如你在这里看到的,metaData 由两个对象组成(实际上是 PersistentMap with key = Language)。
所以,问题是:如何使用 Hibernate 按语言检索特定的映射值?
JPA 要求托管实体的状态和数据库必须在事务结束时同步,因此仅获取实体的部分集合实际上会删除 non-fetched 元素,这就是为什么JPA 不允许这样做。但是,您可以使用 Hibernate 执行此操作,但我不推荐它,因为它可能会导致删除。
所以要得到你想要的,你需要编写一个 HQL 查询并将数据提取到 DTO 中。在查询中,您 select 所有您需要的字段,并可以根据需要定义您的连接条件。类似于以下内容:
SELECT s.id, s.name, m.id, m.name
FROM Service s
LEFT JOIN s.metadata m ON KEY(m) = 'en'
我认为这是 Blaze-Persistence Entity Views 的完美用例。
我创建了库以允许在 JPA 模型和自定义接口或抽象 class 定义的模型之间轻松映射,类似于 Spring 类固醇数据投影。这个想法是您按照自己喜欢的方式定义目标结构(领域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。
您的用例的 DTO 模型可能如下所示 Blaze-Persistence Entity-Views:
@EntityView(Service.class)
public interface ServiceDto {
@IdMapping
Long getId();
String getName();
@Mapping("metadata['en']")
LanguageDto getLanguage();
@EntityView(EntityMetaData.class)
interface LanguageDto {
@IdMapping
Long getId();
String getName();
}
// Other mappings
}
查询就是将实体视图应用于查询,最简单的就是通过 id 进行查询。
ServiceDto a = entityViewManager.find(entityManager, ServiceDto.class, id);
Spring 数据集成让您几乎可以像 Spring 数据投影一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features