We're using JPA, and when a collection of objects returns from a query, a separate query is executed for each "child" object related through a foreign key.
For example, in our Authorization entity class, we have the following Client object mapped:
#JoinColumn(name = "clientId", referencedColumnName = "clientId")
#ManyToOne (fetch = FetchType.LAZY)
#NotNull(groups = Default.class)
private Client client;
When 10 Authorizations are returned, 10 Client queries are executed. In TopLink, I was able to bring this number to one with the ReadAllQuery class's addBatchReadAttribute() method. According to the TopLink docs, "when any of the batched parts is accessed, the parts will all be read in a single query, this allows all of the data required for the parts to be read in a single query instead of (n) queries."
This worked perfectly, giving us a single query using an IN clause with 10 ids.
What I read about re: JPA pointed me toward a batch join or something like:
hints = {#QueryHint(name = "eclipselink.batch", value = "p.client"), ...
This strategy helps reduce the number of queries,
but it also gave me more joins, possibly slowing things down (?) on
some queries
and it didn't seem to help as drastically as the TopLink call.
Is there a way to get the strategy that uses a single query with IN in the WHERE clause?
Thanks in advance.
Dave
Internally the QueryHint "eclipselink.batch" is translated to addBatchAttribute() so the behaviour you see should be identical. Does the JPQL you have created produce the same query as the native TopLink API? It is possible you have Fetch's or additional joins in the JPQL?
EclipseLink supports several types of batch fetching.
See,
http://java-persistence-performance.blogspot.com/2010/08/batch-fetching-optimizing-object-graph.html
Related
I'm trying to use JPA (#CrudRepository), but I want to create also my custom controller with Mybatis.
I have that working, but the problem is that for example in procedures, they don't work together.
Is it possible to implement JPA with Mybatis to work together?
I've been reading a lot, I understand that Mybatis is not ORM. Some blogs indicate that it's possible, but not how.
It's possible to manage the both JPA and mybatis together under Spring Transaction. Both of them, in fact, can be rollback together within the same transaction. However, do take note of side effects such as:
e.g.
Within the same transaction:
// Perform insert and expect id to be returned
TableA tableA = new TableA();
jpaRepositoryForTableA.save( tableA );
// Use the tableA in the next mybatis mapper
TableB tableB = new TableB();
tableB.setTableAId( tableA.getId() );
this.mapper.saveTableB( tableB )
In the scenario above, TableB will not be able to get the TableA's ID.
I don't think it's good idea at all. Use one or the other.
I can imagine that you could make it working, but your can't use them on top of same tables or use transaction management for both persistence frameworks.
So if you have such use case (you didn't explain any), I would argue that your application should be split into two separate services. Optionally consider to separate your storage into two separate DB instances.
If I have an entity A with one-to-many relationship with entity B and I have to fetch A along with associated Bs, I can either use EntityManager's find method OR I can write JPQL query using JOIN.
Which approach is better in terms of efficiency and minimum DB calls?
I'm assuming that the EntityManager.find(class, primary key, ...) invocation will load all associated B's thanks to proper usage of #JoinTable and #OneToMany annotations thus the find and the JPQL JOIN produce the same result in a single invocation.
If that is true then my experience with Hibernate and JPA (2.0/2.1) is that there is no difference especially if you use a secondary cache which I do. Do whatever is convenient.
Having said that the only way to know for sure would be to perform the timing yourself. There are differences not only between different JPA implementations such as Hibernate and EclipseLnk but between different versions of the same system.
Additionally your JPQL performance will vary depending upon if you have the query precompiled (I always do this) or it is a dynamic Criteria JPQL. Additionally various Secondary Caches effect performance greatly.
We are using DTOs server side, and have configured a dbcontext using fluent api in order to give breeze the metadata it needs. We map 1:1 to our real database entities, and each DTO contains a simple subset of the real database entity properties.
This works great, but now I need to figure out a way to make queries efficient - i.e. if the Breeze client queries for a single item I don't want to have to create a whole set of DTO objects before I can filter. i.e. I want to figure out a way to execute the filter/sort on the actual entities, but still return DTO objects.
I guess I need to figure out a way to intercept the query execution in order to query my real database entities and return a DTO instead of the real database entity.
Any ideas for how to best approach this?
Turns out that if you use projection in a link statement, e.g.
From PossibleCustomer As Customer
In Customers
Select New CustomerDto With {.Id = PossibleCustomer.Id,
.Name = PossibleCustomer.Name,
.Email = PossibleCustomer.Email}
.. then linq is smart enough to still optimize any queries to the database - i.e. if I query on the linq statement above to filter for a single item by Id, the database is hit with that query for just a single item and a single DTO is created. Pretty clever stuff. This only works if you do a direct projection in the linq statement - if you call off to a function to create your DTO then this won't work.
Just in case others are facing the same scenario, you might want to look at AutoMapper - it can create these projections for you using a model you create just once - avoids all those huge linq statements that are hard to read and validate. The automapper projections (assuming you stick to the simple stuff) still allow the linq to entities magic that ensures you don't have to do table scans when you create your DTOs.
I have a Question object which has List of Comment objects with #OneToMany mapping. The Question object has a fetchComments(int offset, int pageSize) method to fetch comments for a given question.
I want to paginate the comments by fetching a limited amount of them at a time.
If I write a Query object then I can set record offset and maximum records to fetch with Query.setFirstResult(int offset) and Query.setMaxResults(int numberOfResults). But my question is how(if possible) can I achieve the same result without having to write a Query i.e. with simple annotation or property. More clearly, I need to know if there is something like
#OneToMany(cascade = CascadeType.ALL)
#Paginate(offset = x,maxresult = y)//is this kind of annotation available?
private List<Comment> comments;
I have read that #Basic(fetch = FetchType.LAZY) only loads the records needed at runtime, but I won't have control to the number of records fetched there.
I'm new to JPA. So please consider if I've missed something really simple.
No, there is no such a functionality in JPA. Also concept itself is bit confusing. With your example offset (and maxresult as well) is compile time constant and that does not serve pagination purpose too well. Also in general JPA annotations in entities define structure, not the context dependent result (for that need there is queries).
If fetching entities when they are accessed in list is enough and if you are using Hibernate, then closest you can get is extra #LazyCollection:
#org.hibernate.annotations.LazyCollection(LazyCollectionOption.EXTRA)
I have a JPA object which has a many-to-many relationship like this:
#Entity
public class Role {
//...
#ManyToMany(fetch=FetchType.EAGER)
#JoinTable(
name="RolePrivilege",
joinColumns=
#JoinColumn(name="role", referencedColumnName="ID"),
inverseJoinColumns=
#JoinColumn(name="privilege", referencedColumnName="ID")
)
private Set<Privilege> privs;
}
There isn't a JPA object for RolePrivilege, so I'm not sure how to write a JPQL query to delete entries from the privs field of a role object. For instance, I've tried this, but it doesn't work. It complains that Role.privs is not mapped.
DELETE FROM Role.privs p WHERE p.id=:privId
I'm not sure what else to try. I could of course just write a native query which deletes from the join table RolePrivilege. But, I'm worried that doing so would interact badly with locally cached objects which wouldn't be updated by the native query.
Is it even possible to write JPQL to remove entries from a join table like this? If not I can just load all the Role objects and remove entries from the privs collection of each one and then persist each role. But it seems silly to do that if a simple JPQL query will do it all at once.
The JPQL update and delete statements need to refer to an Entity name, not a table name, so I think you're out of luck with the approach you've suggested.
Depending on your JPA provider, you could delete the entries from the JoinTable using a simple raw SQL statement (should be 100% portable) and then programmatically interact with your cache provider API to evict the data. For instance in Hibernate you can call evict() to expire all "Role.privs" collections from the 2nd level cache:
sessionFactory.evictCollection("Role.privs", roleId); //evict a particular collection of privs
sessionFactory.evictCollection("Role.privs"); //evict all privs collections
Unfortunately I don't work with the JPA APIs enough to know for sure what exactly is supported.
I also was looking for a JPQL approach to delete a many-to-many relation (contained in a joining table). Obviously, there's no concept of tables in an ORM, so we can't use DELETE operations... But I wish there were a special operator for these cases. Something like LINK and UNLINK. Maybe a future feature?
Anyway, what I've been doing to achieve this need has been to work with the collections implemented in the entity beans, the ones which map the many-to-many relations.
For example, if I got a class Student which has a many-to-many relation with Courses:
#ManyToMany
private Collection <Courses> collCourses;
I build in the entity a getter and setter for that collection. Then, for example from an EJB, I retrieve the collection using the getter, add or remove the desired Course, and finally, use the setter to assign the new collection. And it's done. It works perfectly.
However my main worry is the performance. An ORM is supposed to keep a huge cache of all objects (if I'm not mistaken), but even using it to work faster, I wonder if retrieving all and every element from a collection is really effective...
Because for me it's as much inefficient as retrieving the registries from a table and post-filter them using pure Java instead of a query language which works direct or undirectly with the internal DB engine (SQL, JPQL...).
Java is an object-oriented language and the whole point of ORM is to hide details of join tables and such from you. Consequently even contemplating deletion from a join table without considering the consequences would be strange.
No you can't do it with JPQL, that is for entities.
Retrieve the entities at either end and clear out their collections. This will remove the join tables entries. Object-oriented.