Why are my entities only partially populated when loading them using Spring Data JPA? - jpa

I'm using Spring Data JPA and DataNucleus as JPA persistence provider and have something like
interface BookRepository extends CrudRepository<Book, Long> {
Book findByAuthorId(Long id);
}
If I call bookRepository.findByAuthorId() and then access book.publishingHouse.manager.name is null. As opposed to calling bookRepository.findAll() when the fields are populated correctly all the way. I set datanucleus.DetachAllOnCommit=true and datanucleus.maxFetchDepth=-1 (I also tried with 10).
Any idea why?

If you don't have any additional transaction boundaries defined, the EntityManager closed when leaving the query method. That means what you get back is detached entities and what kind of load state you get back is determined by the defaults the persistence provider uses.
You basically have two options:
Have a client (service or controller class) using #Transactional to keep the EntityManager open and thus the loaded instances eligible to lazy-loading to pull data out of the store while you use the instance. If a controller or service is not enough, you might wanna look into the OpenEntityManagerInViewFilter/-Interceptor which basically keeps the EntityManager open until the view is rendered.
Define what should be fetched explicitly either using JPA 2.1 entity graphs (see the reference docs for details) or explicitly adding fetch-joins to the query by defining it manually.

Related

Is it ok to reference a JPA entity that has been deleted?

I have a JPA entity that links to others -- something like this:
#Entity
class LinkRec implements Serializable {
...
#OneToOne
private OtherEntity otherTable;
...
}
So my logic eventually can delete this entity (calling the EntityManger.remove method), then I want to write to a log file what was done, including reference members of the otherTable object. Is this a permitted operation in JPA?
Is this a permitted operation in JPA?
Yes.
What JPA (underlying JPA provider) does when you invoke remove is just "mark" that the instance is expected to be deleted/removed. But even if the transaction is committed (and the instance deleted from the database) or not, the instance object remains the same. Any changes on its attributes depend on what you do.
Due to you mark the entity as removed you won't can refresh the instance's state from the database (call EntityManager.refersh method). You will get an IllegalArgumentException.
Be aware that, in other cases, you could screw up if you refresh the entity before loggin what you want.
I quote a text from the JPA specification (see Synchronization to the Database section) that could help you to understand the "JPA" behaivor
Synchronization to the database does not involve a refresh of any managed entities unless the refresh operation is explicitly invoked on those entities or cascaded to them as a result of the specification of the cascade=REFRESH or cascade=ALL annotation element value
The relevant line in the spec is:
After an entity has been removed, its state (except for generated state) will be that of the entity at the point at which the remove operation was called.
Since this is all I can find on the subject in the spec, I would say that it could vary from implementation to implementation. In my opinion, this makes what you are tying to do dangerous. It may work in one JPA implementation and not another, or work in one version and not in an upgrade.
If I had to guess on implementations, I would say that #OneToOne objects will probably work okay. Where I would worry is with things like #OneToMany. In the case of Hibernate for example: this collection may be hydrated and in memory, but it may also point to a proxy. If it is a proxy and you call the getter it will check with the database for the collection and fail to load it because the object is gone.

Why JPA entities are treated like this outside a session?

Hy,
I am having a "Solve failed to lazily initialize a collection of role.... exception" in jpa. I understand that outside a session, when you want to retrieve a lazy collection if there is not session bound you will get this error, fine. But what I dont understand is if I have this spring controller(the code is not 100% correct, is just to explain my case):
#Controller
#Autowired
EnterpriseService enterpriseService ;
public List<Enterprise> getAll(){
List<Enterprise> enterprises = enterpriseService.getAll();
for(Enterprise enterprise:enterprises){
enterprise.getEmployees();
}
return enterprises;
}
When I call "enterprise.getEmployees()", I know there is not anymore a session but why when I try to do "enterprise.getEmployees()", why enterprise is treated like a jpa entity and not just like a normal bean?, I mean; for what I understand a jpa entity is treated like this inside a session but outside like in this case it should be treated like a normal java bean so the calling to enterprise.getEmployees() should be treated like the calling of a get method of a java bean and should not throw any lazy exception....
Maybe is because spring controller treats the enterprise objects like jpa entities and not just only like java beans? this behaviour is specific to spring controllers?
Thanks
An entity returned from an EntityManager is not necessarily an instance of your entity class, but a proxy class which extends your class. In many cases the same is true for the persistent properties of such entities (especially for those annotated with (One/Many)To(One/Many)).
For example if you are using field based access:
#Entity
public class Enterprise {
#OneToMany
private List<Employee> employees = new ArrayList<>();
public List<Employee> getEmployees() {
return employees;
}
}
Here the JPA provider will create a proxy class that extends Enterprise and remembers the previous state from the DB somehow. In addition it will change the employees list to its own implementation of List (which does not need to extend ArrayList).
Because the proxy class can "overwrite" your methods, it could know when you are calling getEmployees() and check for the session. But I don't think that this would happen here, as the method is not annotated with any JPA specific annotation.
In addition some frameworks like Hibernate do support byte code enhancement or byte code instrumentation. That changes the implementation of your class (in byte code) and replaces every access to employees with some provider specific code. I don't know if Spring JPA provides this, but this could lead to check for a session.
Otherwise any call to enterprise.getEmployees() should just return the current value of employees - without any check for the session and without the LazyInitializationException.
But calling enterprise.getEmployees().size() will try to initialize the list and check for the session - this could lead to the mentioned exception.
If you are using property based access things are a little bit different:
#Entity
public class Enterprise {
private List<Employee> employees = new ArrayList<>();
#OneToMany
public List<Employee> getEmployees() {
return employees;
}
}
Here the proxy class will not delegate to your implementation, but overwrite the getEmployees() method and return its own List implementation, without changing employees. Thus here it is possible to get a LazyInitalizationException for enterprise.getEmployees().
Remark: This describes how most JPA implementations are working - but as this is implementation specific some unusual frameworks could do things differently.
It can't do anything else. The only alternative would be to return an empty collection of employees, which would be much much worse: you would incorrectly assume that the enterprise has 0 employee, which is a valid, but completely incorrect result.
To realize how much worse it would be to do that, let's imagine a HospitalAnalysis entity, having a collection of DetectedDisease entities. And let's say you try to display the result of the analysis but forget to initialize the collection. The page would tell you that you're perfectly healthy and that you can safely go home, whereas in reality, you have a cancer, and the program has a bug. I'd much prefer the program to crash with an exception and be fixed rather than not starting my treatment.
Trying to access employees without having initialized the collection, and thus without knowing the actual collection of employees, is just a bug. This bug is signalled by throwing a runtime exception.

Spring Data JPA Repository CRUD Testing

I'm playing around with the Spring Data Repository and have a question on writing CRUD tests. I have written many CRUD tests against Hibernate DAOs and EJB 3 entity beans where I create and entity, flush it to the database, clear the entity manager, and read it back by ID. The entity manager is cleared so the first level cache is not hit on the read.
Using the Spring Data repository I can't find a way to clear the underlying entity manager used by my test so my read is not going back to the actual database, making my test invalid.
Is there any way to clear the entity manager in the test? Is there a way I can inject one into my test so that it is used by the repository?
Thanks!
Cory.
Try it by injecting the entitymanager like this:
#PersistenceContext
EntityManager entityManager
and make your test transactional by setting the #Transactional attribute on your test method. Then inside the method you can call the entityManager.flush() method.
Regards
If you wish the EntityManager to be cleared automatically you can set #Modifying annotation’s clearAutomatically attribute to true.
Please see here

How to get a detached object from JPA

In my application I need most objects fetched in detached mode (fetched with the find API).
I'm wondering if there is a way to ask a detached object from the JPA provider and save the extra call to detach() API.
In additional I would expect the object created in such mode to be less expensive since the JPA provider doesn't need to add it to the entity manager context.
Is there a way to achieve this with JPA APIs?
Is there a way to achieve such functionality with query results?
Specifically I'm using Eclipse Link so if there is a specific way to do it with this implementation it will be helpful as well.
You can fetch a detached entity without an extra call to detach() if you fetch it outside a transaction. If you are not using container-managed transactions, it's trivial, simply do not start a transaction.
If you are using CMT, you have to make sure the requesting object is not a transaction-enabled EJB:
if in an EJB, suspend the transaction by annotating the appropriate method with:#TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED),
or
call the EntityManager from a POJO. You dont have to call it directly, it only impotrant that the query result will end in a non-EJB object.
AFAIK, there is no performance gain to be expected, since the query result will always be put in the current persistence context, however shortlived it may be.
EDIT: There is another possibility to get detached objects which does not depend on transaction demarcations: JPA constructor expressions:
List<DTO> dtos = em.createQuery("SELECT NEW com.example.DTO( o.title, o.version) FROM Entity o").getResultList();
The constructed type must have a constructor with all the relevant attributes. The objects in the list, entities or not, will always be created detached. However there is a small overhead of instantiating a new object.

Dynamicly select datasource for entities runtime

I have an entity bean that will represent an expected result over multiple databases/datasources and can also be different queries executed, but same result always comming back. So the bean is re-used over different datasources that should be able to be dynamicly selected.
Is it possible with JPA to select during runtime the data source to be used to execute a query, and return the same type of entity bean?
Also, does my ejb/application need to define the datasources that will be used? Or can I always specify via jndi what datasource to use? Modifying the descriptor's and re-deploying an application everytime a new datasource is created is not an option.
Sorry if the question does not make 100% sense, rather difficult to get the idea through.
Is it possible with JPA to select during runtime the data source to be used to execute a query, and return the same type of entity bean?
You can't change the datasource of a persistence unit at runtime. However, you can configure several persistence unit and use one or another EntityManagerFactory. Maybe JPA is not the right tool for your use case.
Modifying the descriptor's and re-deploying an application everytime a new datasource is created is not an option.
And how will the application be aware of the "available datasources"?
You can change the JPA datasource at runtime, but the approach is tricky (introspection, JPA implementation specific, ...).
I've implemented my own implementation of javax.persistence.spi.PersistenceProviderwhich override the org.hibernate.ejb.HibernatePersistence and sets the datasource in both the Map and PersistenceUnitInfo of the PersistenceProvider just before creating the EntityManagerFactory. This way, my EntityManagerFactory has a datasource which has been configured at runtime. I keep my EntityManagerFactory until the application is undeployed.
You could use the same be approach and create N different EntityManagerFactory, each with its specific datasource. However keep in mind that each ÈntityManagerFactory uses a lot of memory.