Hibernate usage in a Servlet environment
I'm trying to create a domain object that contains objects in a read-only fashion but be able to modify which objects it points to.
So in the example below I would like Hibernate to handle populating the Account and Category objects but I don't want to ever update those objects. However I may wish to change which Account or which Category I want Expense to point at.
In other words I may wish to update开发者_如何学Python the account_id or category_id on the expense table but I never want to update the account or category table with changes I've made on the Account or Category objects contained in the Expense object.
@Entity
@Table(name = "expense")
public class Expense {
@Id
@GeneratedValue
private long id;
private String name;
private BigDecimal amount;
@OneToOne
@JoinColumn(name = "id", referencedColumnName = "category_id")
//From table called category
private Category category;
@OneToOne
@JoinColumn(name = "id", referencedColumnName = "account_id")
//From table called account
private Account account;
Am I totally misusing Hibernate or is it just not the right tool? Should I define my own sql with @SQLUpdate and @SQLInsert?
The general flow of the application is standard web app stuff.
- Load data from database
- Display on form
- Gather changes made by user
- Persist those changes
Thanks
It is a bit of a departure from the intent of hibernate. Changes to persistent objects, are supposed to be persistent. Don't change them if you don't want their state to be saved! Explaining and thus better understanding for yourself why you want that kind of behavior might help you arrive at a solution (or help us help you.)
One option you have is to detach/evict the mapped objects that you don't want to be changed inside your data access layer before returning the Expense object. Then as long as no cascading is on, changes made to them won't be saved. IMO this leads to "surprising" behavior for other programmers.
Another option is to use hibernate in a Sessionless manner. Each call is its own atomic database operation and all objects are detached after returning. Then only entities that you explicitly call saveOrUpdate on will get saved to the database. You can learn how to configure Hibernate to work this way in the Hibernate reference docs, although again, I don't know why you'd want to if you're not trying to emulate the behavior of some legacy system you're uplifting.
One final point to understand, withing a Session, the Category that you get by calling expense.getCategory() will usually be the same actual object that you get by calling session.get(Category.class, 123). So if both ways of accessing it are available inside the same Session, it will be easy to lose track of its entity state if you're trying to manually manage it.
edit:
Ah-ha! Now it makes more sense.
If you're doing purely CRUDy web form screens, you don't have much to worry about. As long as you don't have any Cascading on, when you merge back the detached Expense
object, changes on the Category
and Account
aren't going to end up in the database. You could null out every other field, and as long as the id is still there, you're ok. (Assuming you don't have other things like cascading validation that would cry about it.)
There are two basic patterns for handling this a little bit better.
One is to put the Expense
object on the user's web session. This way you have the entire thing, and your web data binding framework only needs to bind back onto it the fields that are actually changed by the form. The original Category
and Account
are still there (or proxies of them) and the binder doesn't need to munge them. Downside is of course adding server side state, as well as needing to clean it up.
Two is to register data binders that actually go into the database and get the mapped entity during web binding. So all that would actually appear in the form is the ID of the mapped field, and the binder will go fetch and put the full object there for you. Downside is possibly unneeded round trips to the database, but aggressive L2 caching can fix that (assuming Categories almost never change and Accounts rarely change.)
You may also want to look into the OpenSessionInView/OpenEntityManagerInView patterns if you're presently using hibernate in an essentially sessionless manner by creating and disposing sessions within the DAO.
精彩评论