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 mo
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
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...]
}
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.
.?
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").