EJBs and JPA design question
I'm using EJB 3.1 and JPA 2.0.
I'll give you an example to explain my doubts and ask for hints as what is a good practice and what's not.
Let's say I have an EJB that's a user Facade. So you have something like this:
@Stateless
public class UserRepository {
public User find(Long id) {
...do user lookup using entitymanager.
}
}
Alright, now let's say I r开发者_运维百科eturn the User entity from this and that this entity has a collection of comments (Comment being also an entity).
I could have a Comment Repository with a findCommentsByUser(Long userId)
method, or I could fetch the user and call the getComments()
method. May be this case is simple, but I've faced this decision many times sometimes not knowing what is best.
Also, let's say I want to add a comment, should I add it to the comment collection the entity has and the have the entity merged, or should I have a addComment(Long userId, Comment newComment)
method?
I'm looking for advice or best practices regarding this. If you need further clarifications please do not hesitate to ask.
EDIT:
I've found the comments so far helpful, however notice this, it isn't really about users and comments, I just made that up to explain my situation. It's about whether it is convenient to mix both approaches (which I think is not) or is one better over the other. I liked the "always persist through the repositories" suggestion. But the fact that I have a fetchComments()
repository method and the getComments()
in the user entity creates to entry points for the same functionality, so how do I deal with that?.
Also the performance (1 query vs 2 queries) isn't really important because I will be fetching the user entity too so it's not like I'm actually saving anything.
We typically only work with detached entities in our applications, so we have a data access manager to fetch and update entities. Then we know that anything we do in our business logic will not be persisted unless specifically called to. I would also fetch the comments with the user entity, but make sure it is not persisted until explicitly called.
I could have a Comment Repository with a findCommentsByUser(Long userId) method, or I could fetch the user and call the getComments()
I would say that from a performance point of view, the first alternative is slightly better, because you don't fech the user (1 query) and then the comments (another query). The first does it in a single shot.
In the other side, i find the second more readable, abstract, and object oriented approach. I would use this one.
You would generally add the getComments() method to your user object. When you want to add one, you would add it to the user set and then call update on the user object.
I think this highly depends on the requirements, on how fine-grained control of the process do you want to have (this often depends on the performance requirements and expected load, etc.).
If the comments are never to be retrieved independently, I would only keep them as a reference inside User
.
If you, however, want to get the comments regardless of the user, or you want to perform some other comments-related queries (like all the comments for users in group A), then I would create separate CommentsRepository
.
If you want to be able to add the comment to a user that's not loaded from the DB, but you have the foreign key, you may simply want to add the comment through CommentsRepository
like you suggested (also adding a comment to a list of user's comments in parallel and persisting such two lists into DBs may result in 'weird behavior').
There are several considerations that needs to make I hope I will document them here for you.
- Domain model is important consideration in EJB3. In your example if you see your domain model allows you to fetch comments lazily because in any flow you show user details first and then his comments.
- In some cases your collection (I am referring to comments here) may contain lots of data, In this case its hardly question of string data so not a major concern, but if it would have been real application data then always opt for transient relationship and provide appropriate methods to fetch them independently.
- Its never a good practice to expose your collection inside entity bean to outside world so if you OneToMany or ManyToMany relationship then you should provide three basic methods over collections (add, remove, get).
- Collections.unmodifiableCollection method should be used while returning collections from get method.
- Always use Set when you are using collections inside entities remember they do not allow duplicate.
- In your case comments collection has direct dependency on user so you should use cascade type in user to save comments.
- I am not convinced why you need UserRepository because em.find method will do job for you.
- By default for OneToMany relation fetchtype is lazy so if you want to do eager load you will need to specify it.
I hope these guidelines hopes to solve your problem.
Regards, Amit
精彩评论