私はここしばらくの間、これを理解しようとしてきました。JPAクエリから1つの結果のみを取得する方法を尋ねるこれらすべての質問とは完全に対照的に、複数の結果が必要です。すべての結果が必要です。エンティティマネージャーのfind()
メソッドを呼び出すことになりますが、ネストされた結果のすべてを取得することはできません。
「親」クラス
@Entity
@Table(name = "LANGUAGE")
public class LanguageData extends PersistedAuditedData<Long> {
@Id
@Column
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(name = "ISO6391ALPHA2CODE")
private String iso6391Alpha2Code;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "languageData")
@MapKeyColumn(name = "LANGUAGE_ID")
private Map<Long, LanguageDataLocalization> localizations;
@OneToMany(fetch = FetchType.LAZY, mappedBy = "localizationLanguage")
@MapKeyColumn(name = "LANGUAGE_LOCALIZATION_ID")
private Map<Long, LanguageDataLocalization> languagesLocalized;
/**
* Private no-arg constructor for reflection-based
* construction inside JPA providers.
*/
@SuppressWarnings("unused")
private LanguageData() {
// Fields initialized by JPA.
}
// Plus getters for the fields.
}
「子供」クラス
@Entity
@Table(name = "LANGUAGE_LOCALIZATION")
public class LanguageDataLocalization extends PersistedAuditedData<LanguageLocalizationKey>{
@EmbeddedId
private LanguageLocalizationKey id;
@Column(name = "REFERENCE_NAME")
private String name;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "LANGUAGE_ID", insertable = false, updatable = false)
private LanguageData languageData;
@ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "LANGUAGE_LOCALIZATION_ID", insertable = false, updatable = false)
private LanguageData localizationLanguage;
/**
* Private no-arg constructor for reflection-based JPA provider instantiation.
*/
@SuppressWarnings("unused")
private LanguageDataLocalization() {
// Fields initialized by JPA.
}
// Plus getters for fields.
}
「子」クラスのキー。
@Embeddable
public class LanguageLocalizationKey {
@Column(name = "LANGUAGE_ID")
private Long languageId;
@Column(name = "LANGUAGE_LOCALIZATION_ID")
private Long languageLocalizationId;
/**
* No-arg constructor, for use by JPA provider implementation.
* <h1>DO NOT USE!</h1>
* <p>Ideally, this should be <code>private</code>,
* however OpenJPA doesn't appear to be allowing that at the moment
* (unsure of cause). This constructor does not initialize any data.</p>
*/
@SuppressWarnings("unused")
public LanguageLocalizationKey() {
// Field initialization performed by JPA provider.
}
// Plus getters
}
そしてそのように使用されます:
@Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
private Collection<LanguageDataLocalization> getLocalizationsById(final Long id, final Collection<String> localeCodes) {
try {
if (localeCodes == null || localeCodes.isEmpty()) {
final LanguageData data = entityManager.find(LanguageData.class, id);
if (data == null) {
return Collections.emptyList();
} else {
return data.getlocalizations().values();
}
} else {
List<LanguageDataLocalization> results = entityManager.createNamedQuery("FIND_LANGUAGE_BY_ID", LanguageDataLocalization.class)
.setParameter("iso6391Alpha2Codes", localeCodes)
.setParameter("languageId", id)
.getResultList();
return results;
}
} catch (NoResultException e) {
// TODO: add logging
return Collections.emptyList();
}
}
そのnamed-queries.xml
ように定義された場合:
<?xml version="1.0" encoding="UTF-8"?>
<entity-mappings version="1.0"
xmlns="http://java.sun.com/xml/ns/persistence/orm"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
http://java.sun.com/xml/ns/persistence/orm_1_0.xsd ">
<named-query name="FIND_LANGUAGE_BY_ID">
<query>
SELECT localized
FROM LanguageDataLocalization localized
JOIN localized.localizationLanguage local
WHERE localized.id.languageId = :languageId
AND local.iso6391Alpha2Code IN :iso6391Alpha2Codes
</query>
</named-query>
</entity-mappings>
LANGUAGE
データ(Derbyインメモリデータベース、それは重要ではないことは確かです):
ID, ISO6391ALPHA2CODE
123, en
137, fr
LANGUAGE_LOCALIZATION
データ
LANGUAGE_ID, LANGUAGE_LOCALIZATION_ID, REFERENCE_NAME
123, 123, English
123, 137, anglais
実際の問題は、ロケールデータなしlocaleCodes
( nullまたは空)でクエリを実行すると、ローカライズされた言語名データの1つ(フランス語のデータですが、SQL'ランダム')のリストが返されることdata.getLocalizations().values()
ですentityManager.find()
。明示的に(単独または一緒に)クエリを実行すると、両方のローカリゼーションを取得できるため、データがそこにあり、 JPQLクエリが機能することがわかります。
特定のローカリゼーションを照会されていないときに、両方のローカリゼーション(2つ以上の言語のローカリゼーションデータがある場合)を返すようにするにはどうすればよいですか?
実際のクエリは次のようです(一部の結果列が削除されています):
SELECT t0.LANGUAGE_ID, t0.LANGUAGE_LOCALIZATION_ID,
t1.ISO6391ALPHA2CODE,
t0.REFERENCE_NAME
FROM LANGUAGE_LOCALIZATION t0
LEFT OUTER JOIN LANGUAGE t1
ON t0.LANGUAGE_LOCALIZATION_ID = t1.id
WHERE t0.LANGUAGE_ID = ? [params=?]
これは、行数を制限しているようには見えません。(to.LANGUAGE_ID
複数の行に一致します)。特に、同じデータセットに対して、以下は両方の結果行を取得するためです(一部の結果列は再び削除されます)。
SELECT t0.LANGUAGE_ID, t0.LANGUAGE_LOCALIZATION_ID,
t2.id, t2.ISO6391ALPHA2CODE,
t3.id, t3.ISO6391ALPHA2CODE,
t0.REFERENCE_NAME
FROM LANGUAGE_LOCALIZATION t0
INNER JOIN LANGUAGE t1
ON t0.LANGUAGE_LOCALIZATION_ID = t1.id
LEFT OUTER JOIN LANGUAGE t2
ON t0.LANGUAGE_ID = t2.id
LEFT OUTER JOIN LANGUAGE t3
ON t0.LANGUAGE_LOCALIZATION_ID = t3.id
WHERE (t0.LANGUAGE_ID = ? AND t1.ISO6391ALPHA2CODE IN (?, ?)) [params=?, ?, ?]
(これは、なぜとの両方があるのかという問題を提起しますt1
が t3
、これは現時点ではそれほど懸念されていません。)