views:

218

answers:

4

I couldn't figure out how to cleanly do a tag cloud with JPA where each db entity can have many tags.

E.g

Post can have 0 or more Tags User can have 0 or more Tags

Is there a better way in JPA than having to make all the entities subclass something like Taggable abstract class? Where a a Tag entity would reference many Taggables.

Edit: the tag cloud is just a sample to simplify the problem I am having. In my scenario, the relation should be OneToMany where a Tag cannot be reused.

thank you

A: 

why dont you just map a Collection of Tags or even Strings?

sudocode:

@Entity
@Table(name="entities")
class MyEntity{
    long id;
    String someField;
    @ManyToMany(targetEntity=Tag.class)
    @JoinTable(name="entities_to_tags",
         joinColumns={
             @JoinColumn(name="id",  
             referencedColumnName="entity_id",
             inverseJoinColumns={
                 @JoinColumn(name="id", referencedColumnName="tag_id")})
    List<Tag> tags;
    [...getter&setter...]
}

@Entity
@Table(name="tags")
class Tag{
    @Id
    @GeneratedValue
    long id;
    String title;
    [....getter & setter...]
}
smeg4brains
Pseudocode! :-)
Steven Schlansker
thx for the response steve.in your sample, Tag should hold a "join column" to MyEntity right? how is that going to work if if I have two taggable entities e.g MyEntity1 and MyEntity2.
bob
sorry, i meant to say: thanks smeg4brains
bob
like Affe elaborated you could just use @ManyToMany instead so youll get a relation table from MyEntity's PKs to Tag's PK.
smeg4brains
i edited this this answer and used @ManyToMany instead
smeg4brains
+1  A: 

This seems like ManyToMany, not one to Many. Users can have multiple tags, and a tag can be associated with more than one user?

You would only need such a superclass if you want to be able to have a relationship on your Tag to a single collection that contains every object marked with that tag. Do you have a requirement for a tag.getOneGiantCollectionOfEveryTaggedEntity() method?

Since the marked objects don't seem to otherwise have anything in common, does such a collection really have any value in your application domain? It could also ostensibly be quite large and not something you'd really want to work with via object relationships anyway. From a practical standpoint, without knowing about your use case, it seems like tag.getTaggedUsers(), tag.getTaggedPosts() etc are more useful.

Sorry, guess I'm asking more questions than giving answers, but it's not clear what you want your finished object domain to look like :)

edit:

Maybe the actual answer then to the question asked is just "No, Hibernate will not map for you you a Raw Collection of types with no common ancestor that just happen to all have foreign keys to your entity." You don't neccessarily have to impose a 'fake' superclass on your entities, but if you don't then you'll have to make a join table.

.?

Affe
thx for the reply affe. I gave tag cloud as a sample to simply the problem. sorry, maybe it was not the best example. but ideally I would prefer OneToMany as in my scenario the "tag" is not reused and I want to avoid having additional join tables.
bob
true, ill change my answer....
smeg4brains
heh i should have read your comment before editing my answer to use @manyToMany then ;)
smeg4brains
A: 

If you don't need polymorphic queries such as "get everything tagged tagged Foo", then you can also introduce a new entity (say TaggingTarget), and create a unidirectional one-to-one relation from User (Post, etc) to TaggingTarget and many-to-many relation between TaggingTarget and Tag:

@Entity
public class User {
    @OneToOne
    private TaggingTarget target;
    ...
}

@Entity
public class TaggingTarget {
    @ManyToMany(...)
    private Set<Tag> tags;
    ...
}

@Entity
public class Tag {
    @ManyToMany(...)
    private Set<TagTarget> targets;
    ...
}

The difference from Affe's solution is that you don't need a tag.getTaggedUsers(), tag.getTaggedPosts(), etc and can add new tagged entities without changing Tag. Tagged entities can be queried using JPQL:

select u from User u where :tag member of u.target.tags

or

select u from User u join u.target.tags t where t.name = :name 
axtavt
A: 

Is there a better way in JPA than having to make all the entities subclass something like Taggable abstract class?

Let's forget the example :) JPA does support polymorphic associations but the target classes have to be part of an inheritance hierarchy. And here are some more rules of thumb about inheritance strategies:

  • SINGLE_TABLE:
    • All the classes in a hierarchy are mapped to a single table
    • This strategy provides good support polymorphic relationships between entities and queries that cover the entire class hierarchy.
    • May contain null fields for some subclass data
  • TABLE_PER_CLASS:
    • Each class in a hierarchy mapped to a separate table and hence, provides poor support for polymorphic relationships
    • requires SQL union or separate SQL queries for each subclass
  • JOINED
    • no null fields => compact data
    • This provides good support for polymorphic relationships, but requires one or more join operations – may result in poor performance

In short, if your subclasses declare relatively few properties, prefer the SINGLE_TABLE strategy. If not, use a JOINED strategy unless you have a deep hierarchy (in which case the cost of joins may become more expensive than unions and then TABLE_PER_CLASS would be "less worse").

References

  • JPA 1.0 Specification
    • Section 2.1.9 "Inheritance"
    • Section 2.1.10 "2.1.10 Inheritance Mapping Strategies"
Pascal Thivent