hi there,
we are after a little advice if possible. we are trying to build a generic, extensible, community-ish framework, and part of that is the desire to make certain elements commentable - ie people can leave comments on pictures, images, forum posts, etc etc. however, as it is a framework, we will need to extend this functionality to make the actual system (which might, for example, have 'books' that people can comment on). we're using annotations, as they rock.
this is proving quite difficult to understand so i hope someone here might be able to enlighten us as to the best approach.
basically, we have a class Comment, which has a 'text' field, a 'user' field, and a 'parent' field (that points to the object that has been commented on). the object itself (eg Video) has a List<Comment> that holds the comments for the video/whatever. All commentable objects implement an interface ICommentable.
we have tried the following approaches with the following problems (using Video as an example of a commentable object) :
a) Comment is a single class, which has a many-to-any association to parent. here is the code that implements this :
(in Comment.java)
Code:
@Any(fetch = FetchType.LAZY, metaColumn = @Column(name = "item_type"))
@AnyMetaDef( idType = "long", metaType = "int", metaValues = { @MetaValue(targetEntity = Video.class, value = Video.LINKAGEIDENTIFIER) } )
@JoinColumn(name = "item_id")
public ICommentable getParent() {
return parent;
}
(in Video.java)
Code:
public static final String LINKAGEIDENTIFIER = "1";
@OneToMany
@Fetch(FetchMode.SUBSELECT)
@LazyCollection(LazyCollectionOption.EXTRA)
@Where(clause = "item_type=" + LINKAGEIDENTIFIER)
@JoinColumn(name = "item_id")
@Cascade( { org.hibernate.annotations.CascadeType.DELETE_ORPHAN, org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.PERSIST, org.hibernate.annotations.CascadeType.MERGE })
public List<Comment> getComments() {
return comments;
}
as you can see, this is far from ideal, as we need to have these LINKAGEIDENTIFIERs (or some other way to do that, such as an enum), and also there is a requirement for the @Where clause, which is messy.
aside from that, this does work, but for the problem that we'd like to extend the system to handle new objects (eg Book) - how to add these new metaValues to the @AnyMetaDef?
b) creating a table-per-class-heirarchy for Comment, making simple subclasses such as VideoComment :
(Comment)
Code:
@Entity
@Table(name = "comment")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="type", discriminatorType=DiscriminatorType.STRING)
public abstract class Comment extends BaseEntity {
...
@Transient
public ICommentable getParent() {
return parent;
}
(VideoComment)
Code:
@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class VideoComment extends Comment {
...
@Override
@ManyToOne(fetch = FetchType.LAZY, targetEntity = Video.class)
@JoinColumn(name = "parent")
public ICommentable getParent() {
return super.getParent();
}
(Video)
Code:
@OneToMany(mappedBy = "parent", fetch = FetchType.LAZY)
@Cascade( { org.hibernate.annotations.CascadeType.DELETE_ORPHAN, org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.PERSIST, org.hibernate.annotations.CascadeType.MERGE })
public List<VideoComment> getComments() {
return comments;
}
this works really nicely, but as soon as there is more than one type of comment (eg ImageComment) then hibernate tries to generate a foreign key constraint on the 'parent' column to BOTH image.id and video.id which means that inserting any row is likely to fail with foreign key constraint violation.
c) attempting to make the entire object heirarchy use inheritance (not just the comment section) - so Video, Image, Book, etc all inherit from something. this is undesirable because all of the strategies are inefficient for 'normal' access (eg having a BaseEntity table that simply has the id and discriminator value - this would double the time it takes to create a normal object)
so, have we been barking up the wrong tree? are any of these strategies changable so they work as we would like? or is there a better idea out there? :)
thanks in advance
chris