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 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.
Related
I searched around but I only get people asking the opposite of my question. So let's say we have:
#Entity
class Book {
...
#ManyToOne(fetch = FetchType.LAZY)
private Author author;
}
Is there a (preferably global) property/way in JPA/Hibernate to prevent from ever lazily loading the author (or any entity)?
Don't get me wrong, I don't want to use EAGER fetch. I want to prevent juniors from ever accidentally calling book.getAuthor().getName() and making another DB call. A lot of people are looking to fix their LazyInitializationException, but I basically want to force such an exception to be thrown even if there is an active session (which when using #Transactional is quite an easy mistake to make). However I also still want Author to be fetched if you properly use "JOIN FETCH Author" in your JPQL query.
My particular use case is with Spring and GraphQL.
#Transactional quite easily hides when a session is open and avoids the LazyInitializationException.
And with GraphQL you can specify which fields to get so I don't want unnecessarily joins when such fields aren't requested (here we use a Field Resolver with a DataLoader).
Would a sufficient workaround be to instead use a projection (https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#projections) of the Book entity without the reference to the author? And by working with different projections guarantee that related entities are not unintentionally loaded?
Suppose I have a User class:
#Entity
public class User {
#Id
private int id;
private String name;
//Other fields and methods
}
At one point of time, the name is say 'XYZ' and at other point of time its 'ABC'. I want to keep track of both the states just as we track files under version control system. These objects are stored in relational database. A crude approach which I could think of is to put the data of database itself under version control, but that's just not feasible as database table will have data for many such users and change in even any one of them will call for committing an update.
How can I achieve this? How do applications usually store edit history when they have to?
Thanks!
if you are using hibernate you could use envers
In my mapper class, I always have to retrieve a list of departments from my company entity :
#OneToMany(mappedBy = "company", orphanRemoval = true, cascade = CascadeType.ALL)
public List<CompanyDepartments> getDepartments()
{
return departments; //java.util.List variable.
}
There are a couple thousand departments(about 2000+). I am trying to reduce the time taken to fetch company data and want to begin with the departments which are going to be fairly static in nature.
Option 1 : I can always have a private method in the mapper which populates a cache on the first load and return from the cache all the time.
But is there anything more simpler? I am probably not the first one to face this and wanted to know how I could approach this. Is there a known annotation that can be used. Like a #Singleton annotation on the variable in the entity ? (there is no such annotation obviously). What is the simplest way to make this list a singleton.
Just a typical spring mvc 3 application using spring data jpa for db interaction.
I suggest this link that solves the n +1 query problem ...
How can i resolve the N+1 Selects problem?
Moreover you could put a cache (lvl 2) on your search service,
if the data does not change during the life cycle of the application.
http://docs.spring.io/spring/docs/3.1.0.M1/spring-framework-reference/html/cache.html
Another approach is to add to indexes on the db.
I hope I've given you all the answers about your question.
What you're experiencing is part of the "object-relational impedance mismatch". One solution would be to extend your object model so that you can use a left join fetch to load multiple companies including their departments using only one SQL statement.
The problem using this technique is that you can populate only one list at a time. Although it's nice & easy to define many sub-lists in the world of objects, it's hard to load all these lists at the same time (and therefor efficiently) from a relational database. However it can be done on Oracle using object- or XMLType queries.
The straight-forward way to get such data out of a RDBMS is by writing custom queries that match exactly the task at hand and provide only the data that is actually needed. Therefor you'd need not only one company class, but many - one for each task. - you'd actually do inheritance instead of attribution
BTW, that is why ORM is still considered the Vietnam of Computer science - easy to get started, but hard to succeed with.
I've been using JPA 2.0 for a while but, sad to admit, I haven't had enough time to learn it properly. It seems like I lack the basics of how to work with Entity Manager.
Moving one step at a time, I'd like to first ask you about maintaining relationships between mapped entities. Of course I know how to create mappings between entities, different types of available associations (OneToOne, etc.) and how databases work in general. I'm purely focused on maintaining it via Entity Manager, so please do not send me to any kind of general knowledge tutorial :-).
The questions are:
Am I right that as a programmer I'm responsible for maintaining (creating/updating/removing) relationships between instances of entities?
Do I have to always update (set to null, remove from collection, etc.) instances by hand?
Plain SQL can set entities to NULL on deleting, but it seems like JPA can't do such a simple thing. It also seems like a burden to do it manually. Is there a way to achieve that with JPA?
If I have OneToMany relationship and set to NULL the entity on the Many side of the relationship. Then I persist the changes in a Set by saving the entity on the One side. Do I then have to update the entities in the Many side and set association to NULL in each instance? Seems pure silliness for one-directional bindings!
Thanks in advance!
The main thing you need to investigate is the different options you have when mapping on entity. For example in the next piece of code the cascade all option will instruct jpa to delete the child list when the parent is deleted.
#OneToMany(fetch = FetchType.LAZY, cascade = { CascadeType.ALL }, mappedBy = "parent")
private Set<Child> events = new HashSet<Child>();
Yes. You maintain the object tree and modify it to look like what
you want.
Yes and no. If you want the entity to reference null, then yes.
For instance, if you are removing one Entity, then you should clean
up any references to it held by other entities that you are not
removing. A practical example: its good practice to let an Employee
know his/her Manager has been let go. If the Employee is going to
stay, it should either have its manager reference nulled out or set
to a different manager, before the current manager can be removed.
If the employee is going to be removed as well, then cascade remove
can cascade to all the Manager's subordinates, in which case you do
not need to clean up their references to the manager - as they are
going away too.
I don't quite understand what SQL is setting to null. Deleting
removes the row in the database, so there isn't anything to set to
null. Cleaning up a reference shouldn't be that difficult in the
object model, as JPA has a number of events to help such as
preremove preupdate etc. In the end though, the problem is with
your java objects. They are just java objects, so if you want
something done, your application will need to do it for the most
part. JPA handles building them and pushing them to the database,
not changing the state for you.
Yes and no. If you set up a bidirectional relationship, you must
maintain both sides as mentioned above. If you set the child's
parent reference to null, you should let the parent know it no
longer has a child, wouldn't you? Your parent will continue to
reference its child for as long as that Parent instance exists. So
even though the database is updated/controlled through the side that
owns a relationship, the object model will be out of synch with the
database until it is refreshed or somehow reloaded. JPA allows for
multiple levels of caching, so it all depends on your provider setup
how long that Parent instance will exist referencing a Child that no
longer exists in the database.
I am trying to create a web service using JAX-WS (SOAP) allowing CRUD operations on some entities. I am also writing a desktop client with an UI which provides easy means of doing CRUD on those entities via the web service.
However, I am running into some design issues. I would like the client to only be able to view and manipulate certain fields of an entity, and I'm not sure what's the best way to impose this restriction.
For instance, given:
#Entity
public class Customer {
#Id
#GeneratedValue
public Long id;
public String name;
// neither read nor write from the web service
public String password;
// read-only from the web service
#Temporal(TemporalType.DATE)
public Date joinedAt;
#ManyToOne
#LazyCollection(LazyCollectionOption.FALSE)
private List<Order> orders;
// .. boilerplate getters and setters
}
#Entity
public class Order {
#Id
#GeneratedValue
public Long id;
public String name;
}
I would like to provide the client with these basic operations:
get the list of all customers with their orders
he can see all the fields EXCEPT for password
create a new customer with some orders
allow control to all fields EXCEPT FOR joinedAt and password
modify a customer
same as above, you're not allowed to modify joinedAt or password
.
My current solution for (1) is to add #XmlTransient to the password field. This is problematic if you want to send the password to certain clients but not to others. Another solution would be to do customer.setPassword(null); before marshalling that entity via
the webservice. But is this really the way to do it? A third solution would be to create a base class BaseCustomer which contains all the fields except for password and then Customer would be a BaseCustomer with the added password field. The user would receive a BaseCustomer object instead of a Customer one. But this has problems with create/update as well.
Same as for (1), one solution is to do customer.setJoinedAt(my_value); customer.setPassword(my_value); customer.setId(null); when the client wants to create a new customer. Is manually nulling the id really best practice? I find that hard to believe. Should the id be XmlTransient as well? But then how would the user of the web service be able to modify/delete entities?
When the client wants to change a Customer, he retrieves the list of customers, makes changes to one of the Customer objects, then marshals that object and passes it back to the web service.
There are a few problems with this: if the id field is XmlTransient, then the EntityManager's persist won't know which row to modify and would likely create a new one. A similar issue raises if the user is evil and simply refuses to pass an id, so I have to manually check that the id is non-null. Moreover, the user has not received the password field, so now the server has received an object with a null password field which it will attempt to persist. I believe this will cause the EntityManager to completely remove the password of that existing Customer. Having the user specify exactly which fields he wants modified and to which values seems impractical.
Note that this is just a proof-of-concept of what I need to do in a nutshell, I have far more entities, relations and operations to provide.
I am new to using these technologies and I was hoping that being so high level and having so many abstractions would make my life easier, but so far it has been mostly headaches. It appears very difficult to achieve this common, basic task. Am I doing something wrong? Please don't suggest creating a web application instead :)