Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion documentation/src/main/asciidoc/querylanguage/From.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ where pub.name like :pubName
[[collection-valued-associations]]
==== Joining collections and many-valued associations

When a join involves a collection or many-valued association, the declared identification variable refers to the _elements_ of the collection, that is:
When a join involves a collection or many-valued association, by default, the declared identification variable refers to the _elements_ of the collection, that is:

- to the elements of a `Set`,
- to the elements of a `List`, not to their indices in the list, or
Expand All @@ -507,6 +507,17 @@ where author.name like :namePattern
In this example, the identification variable `author` is of type `Author`, the element type of the list `Book.authors`.
But if we need to refer to the index of an `Author` in the list, we need some extra syntax.

If the key of a map is an entity, we may join it using the `key()` function:

[source, hql]
----
select book.isbn, lang.language, lang.country, lang.variant
from Book book
join key(book.translations) as lang
----

It's also legal, but redundant, to use the `value()` function when joining the value of a map.

You might recall that we mentioned <<list-functions>> and <<map-functions>> a bit earlier.
These functions may be applied to the identification variable declared in a collection join or many-valued association join.

Expand Down Expand Up @@ -544,6 +555,13 @@ where leadAuthor.name like :namePattern
and index(leadAuthor) == 0
----

[source, hql]
----
select book.isbn, key(translation).language, key(translation).country
from Book book
join book.translations as translation
----


[[implicit-collection-join]]
==== Implicit joins involving collections
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.hql;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.Jpa;
import org.junit.jupiter.api.Test;

import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;

@Jpa(annotatedClasses = {MapJoinKayValueTests.Book.class,
MapJoinKayValueTests.Language.class})
class MapJoinKayValueTests {
@Test void test(EntityManagerFactoryScope scope) {
scope.getEntityManagerFactory().getSchemaManager().truncate();
scope.inTransaction( s -> {
Language language = new Language();
language.id = "en_AU";
language.language = "en";
language.country = "AU";
s.persist( language );
Book book = new Book();
book.isbn = "978-1932394153";
book.title = "Hibernate in Action";
book.translations = Map.of( language, book );
s.persist( book );
var t =
s.createQuery( "select t from Book b join b.translations t", Book.class )
.getSingleResult();
assertEquals(book, t);
var title =
s.createQuery( "select t.title from Book b join b.translations t", String.class )
.getSingleResult();
assertEquals("Hibernate in Action", title);
} );
}
@Test void testValue(EntityManagerFactoryScope scope) {
scope.getEntityManagerFactory().getSchemaManager().truncate();
scope.inTransaction( s -> {
Language language = new Language();
language.id = "en_AU";
language.language = "en";
language.country = "AU";
s.persist( language );
Book book = new Book();
book.isbn = "978-1932394153";
book.title = "Hibernate in Action";
book.translations = Map.of( language, book );
s.persist( book );
var b =
s.createQuery( "select t from Book b join value(b.translations) t", Book.class )
.getSingleResult();
assertEquals(book, b);
var title =
s.createQuery( "select t.title from Book b join value(b.translations) t", String.class )
.getSingleResult();
assertEquals("Hibernate in Action", title);
} );
}
@Test void testKey(EntityManagerFactoryScope scope) {
scope.getEntityManagerFactory().getSchemaManager().truncate();
scope.inTransaction( s -> {
Language language = new Language();
language.id = "en_AU";
language.language = "en";
language.country = "AU";
s.persist( language );
Book book = new Book();
book.isbn = "978-1932394153";
book.title = "Hibernate in Action";
book.translations = Map.of( language, book );
s.persist( book );
var l =
s.createQuery( "select l from Book b join key(b.translations) l", Language.class )
.getSingleResult();
assertEquals(language, l);
var c1 =
s.createQuery( "select l.country from Book b join key(b.translations) l", String.class )
.getSingleResult();
assertEquals("AU", c1);
var c2 =
s.createQuery( "select key(t).country from Book b join b.translations t", String.class )
.getSingleResult();
assertEquals("AU", c2);
} );
}

@Entity(name="Book")
static class Book {
@Id String isbn;
String title;
@ManyToMany
Map<Language, Book> translations;
}

@Entity
static class Language {
@Id
String id;
String language;
String country;
String variant;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.hql;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToMany;
import org.hibernate.testing.orm.junit.EntityManagerFactoryScope;
import org.hibernate.testing.orm.junit.FailureExpected;
import org.hibernate.testing.orm.junit.Jpa;
import org.junit.jupiter.api.Test;

import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;

@Jpa(annotatedClasses = MapJoinTests.Book.class)
class MapJoinTests {
@Test void test(EntityManagerFactoryScope scope) {
scope.getEntityManagerFactory().getSchemaManager().truncate();
scope.inTransaction( s -> {
Book book = new Book();
book.isbn = "978-1932394153";
book.title = "Hibernate in Action";
book.translations = Map.of( "en", book );
s.persist( book );
var t =
s.createQuery( "select t from Book b join b.translations t", Book.class )
.getSingleResult();
assertEquals(book, t);
var title =
s.createQuery( "select t.title from Book b join b.translations t", String.class )
.getSingleResult();
assertEquals("Hibernate in Action", title);
} );
}
@Test void testValue(EntityManagerFactoryScope scope) {
scope.getEntityManagerFactory().getSchemaManager().truncate();
scope.inTransaction( s -> {
Book book = new Book();
book.isbn = "978-1932394153";
book.title = "Hibernate in Action";
book.translations = Map.of( "en", book );
s.persist( book );
var b =
s.createQuery( "select t from Book b join value(b.translations) t", Book.class )
.getSingleResult();
assertEquals(book, b);
var title =
s.createQuery( "select t.title from Book b join value(b.translations) t", String.class )
.getSingleResult();
assertEquals("Hibernate in Action", title);
} );
}
@FailureExpected(jiraKey = "HHH-19759")
@Test void testKey(EntityManagerFactoryScope scope) {
scope.getEntityManagerFactory().getSchemaManager().truncate();
scope.inTransaction( s -> {
Book book = new Book();
book.isbn = "978-1932394153";
book.title = "Hibernate in Action";
book.translations = Map.of( "en", book );
s.persist( book );
var lang =
s.createQuery( "select l from Book b join key(b.translations) l", String.class )
.getSingleResult();
assertEquals("en", lang);
} );
}

@Entity(name="Book")
static class Book {
@Id String isbn;
String title;
@ManyToMany
Map<String, Book> translations;
}
}
Loading