1

I am trying to do something like this, but using @One-To-Many instead of @ElementCollection.

public class Book {
    ...
    @ElementCollection
    Set<String> tags;
    ...
}

This creates two tables, one for Book and one for the Tags with (BookID, Tag) which works fine, except for the fact that I can't use Criteria with @ElementCollection.

So I changed it and made a wrapper class for the tags:

public class Book {
    ...
    @OneToMany(...)
    Set<BookTag> tags;
    ...
}


public class BookTag {
    ...
    String tag;
    ...
}

The problem is that using @OneToMany annotation hibernate creates 3 tables: one for the books, one for the tags, and one to join books and tags. This solutions works perfectly but I would like to have 2 tables instead of 3, since the Tag class contains only a String.

Is there any way to use @OneToMany and make hibernate create 2 tables like it does with @ElementCollection ?

Nicktar
  • 5,548
  • 1
  • 28
  • 43
LorenzoR
  • 421
  • 2
  • 14
  • Are you sure you can't use element collections in criteria queries? We have at least [one answer](http://stackoverflow.com/questions/3892851/using-elementcollection-in-criteriaquery) which says otherwise. – Tom Anderson Jun 27 '12 at 23:15
  • In my case with ElementCollection I got an org.hibernate.MappingException: collection was not an association. Then I read [this](https://community.jboss.org/wiki/HibernateFAQ-AdvancedProblems#Im_getting_orghibernateMappingException_collection_was_not_an_association_when_I_try_to_join_a_collection_of_components_with_Criteria_queries) and thought I couldn't use ElementCollection and Criteria. – LorenzoR Jul 01 '12 at 02:55
  • Interesting. There are probably some kinds of queries you can do with element collections, and some you can't. What query are you trying to do? – Tom Anderson Jul 01 '12 at 11:17
  • I need to list all boks that contains a certain tag. – LorenzoR Jul 01 '12 at 22:05

2 Answers2

2

Yes, by specifying that the OneToMany must use a join column instead of the default (which uses a join table):

@OneToMany(...)
@JoinColumn(name = "BOOK_ID")
private Set<BookTag> tags;
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
0

You can do that query on an @ElementCollection with JPA criteria. Assuming you're using the metamodel, it looks like this:

CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<Book> cq = cb.createQuery(Book.class);
Root<Book> book = cq.from(Book.class);

cq.select(book).where(cb.equal(book.join(Book_.tags), "fanfic"));

TypedQuery<Book> query = em.createQuery(cq);

If you're not using the metamodel, it's substantially the same, but you will probably need to use joinSet.

That query makes a reasonable amount of sense. The only surprising bit is the need to do a join rather than a get, which is because tags is a plural property, rather than a singular one, but you're looking at individual elements of it.

You might also expect that the more naturally-reading

cq.select(book).where(cb.isMember("fanfic", book.get(Book_.tags)));

would work. Perhaps with some JPA implementations it does. But with Hibernate (4.1.4), it doesn't; it blows up with a syntax error. This appears to be Hibernate bug HHH-5209.

Tom Anderson
  • 46,189
  • 17
  • 92
  • 133