Spring JPA repository and QueryDsl how to force left join - spring-data-jpa

Let's say I have two entities User and Task, each user can have one task.
The issue that I'm facing is if I have one record in the user table whose email starts with a and there are no records at all in the task table.
This snippet below will return no records although I would expect users that have mail starting with a.
UserRepository in example extends QuerydslPredicateExecutor.
userRepository.findAll(
QUser.user.email.startsWith("a")
.or(QUser.user.task.text.contains("something"))
)
If I check logs, Hibernate is creating cross join with user.task_id=task.id as a part of where clauses. This type of join automatically discards users whose mails are starting with a if they don't have a task assigned.
Is there a way to force usage of left join instead of a cross join in findAll method of the repository?
I know I can do it by using JPAQuery but then I would have to reimplement paging functionality...
JPAQuery query = new JPAQuery(entityManager);
query
.from(QUser.user)
.leftJoin(QTask.task)
// ...

I am not sure if we can do that since the findAll implementation is generated for us. However we can pass a predicate in the findAll method which will help deal with issue you are encountering.
You can try to do something like this:
QUser qUser = QUser.user;
QTask qTask = QTask.task;
JPQL<UserEntity> userJpqlQuery = JPAExpressions.selectFrom(qUser)
.leftjoin(qUser.task, qTask)
.where(qUser.email...., qTask.text...);
userRepository.findAll(qUser.in(userJpqlQuery));
In the code above I have used Querydsl, which is an alternative to CriteriaBuilder and is type safe. Then I have created a subquery to make the selection I want and return the all users matching the subquery.
In the end , hibernate should generate something like this:
select * from User qUser0 where qUser0.id.in(
select qUser1.id from User qUser1
left join Task qTask0 on
qUser1.taskId = qTask0.id
where ...
);

Related

Laravel 4.2 order by another collections field or result of a function

I have a mongo database and I'm trying to write an Eloquent code to change some fields before using them in WHERE or ORDER BY clauses. something like this SQL query:
Select ag.*, ht.*
from agency as ag inner join hotel as ht on ag.hotel_id = ht.id
Where ht.title = 'OrangeHotel'
-- or --
Select ag.*, ht.*
from agency as ag inner join hotel as ht on ag.hotel_id = ht.id
Order by ht.title
sometimes there is no other table and I just need to use calculated field in Where or Order By clause:
Select *
from agency
Where func(agency_admin) = 'testAdmin'
Select *
from agency
Order by func(agency_admin)
where func() is my custom function.
any suggestion?
and I have read Laravel 4/5, order by a foreign column for half of my problem, but I don't know how can I use it.
For the first query: mongodb only support "join" partially with the aggregation pipeline, which limits your aggregation in one collection. For "join"s between different collections/tables, just select from collections one by one, first the one containing the "where" field, then the one who should "join" with the former, and so on.
The second question just puzzled me for some minutes until I see this question and realized it's the same as your first question: sort the collection containing your sort field and retrive some data, then go to another.
For the 3rd question, this question should serve you well.

Faster/efficient alternative to IN clause in custom/native queries in spring data jpa

I have a custom query along these lines. I get the list of orderIds from outside. I have the entire order object list with me, so I can change the query in any way, if needed.
#Query("SELECT p FROM Person p INNER JOIN p.orders o WHERE o.orderId in :orderIds)")
public List<Person> findByOrderIds(#Param("orderIds") List<String> orderIds);
This query works fine, but sometimes it may have anywhere between 50-1000 entries in the orderIds list sent from outside function. So it becomes very slow, taking as much as 5-6 seconds which is not fast enough. My question is, is there a better, faster way to do this? When I googled, and on this site, I see we can use ANY, EXISTS: Postgresql: alternative to WHERE IN respective WHERE NOT IN or create a temporary table: https://dba.stackexchange.com/questions/12607/ways-to-speed-up-in-queries-under-postgresql or join this to VALUES clause: Alternative when IN clause is inputed A LOT of values (postgreSQL). All these answers are tailored towards direct SQL calls, nothing based on JPA. ANY keyword is not supported by spring-data. Not sure about creating temporary tables in custom queries. I think I can do it with native queries, but have not tried it. I am using spring-data + OpenJPA + PostgresSQL.
Can you please suggest a solution or give pointers? I apologize if I missed anything.
thanks,
Alice
You can use WHERE EXISTS instead of IN Clause in a native SQL Query as well as in HQL in JPA which results in a lot of performance benefits. Please see sample below
Sample JPA Query:
SELECT emp FROM Employee emp JOIN emp.projects p where NOT EXISTS (SELECT project from Project project where p = project AND project.status <> 'Active')

Recreating a query using OpenJPA

THere is a oracle query that I am trying to recreate using OpenJPA. I am writing a service in Websphere integration developer, and I am using OpenJPA as my ORM tool of choice. Previously this query was performed using a stored proc, a ref cursor was used and the information was retrieved like that. Now we are trying to use OpenJPA as our tool of choice. So I am thinking that I should then reconstruct the stored proc using OpenJPA...
SELECT DISTINCT
P.col1 as ID,
P.col2,
P.col3,
P.col4,
P.col5,
S.col6,
PC.col7,
P.col8,
A.ADDRESS_1,
A.ADDRESS_2,
A.ADDRESS_3,
A.CITY,
A.COUNTY,
A.STATE,
A.ZIP_CODE,
P.CONFIRMED_BY,
P.CONFIRMED_DATE,
P.MOD_USERID,
P.MOD_DATE
FROM EPCD13.PROVIDER P, EPCD13.provider_channel PC, EPCD13.provider_channel_link pcl,
EPCD13.provider_specialty ps, EPCD13.SPECIALTY S, EPCD13.Address A, EPCD13.ADDRESS_LINK AL
WHERE P.RESOURCE_ID = personID
AND P.RESOURCE_ID = PS.RESOURCE_ID (+)
AND 1 = PS.PRIMARY_SPECIALTY_ID (+)
AND PS.SPECIALTY_ID = S.SPECIALTY_ID (+)
AND P.RESOURCE_ID = PCL.RESOURCE_ID (+)
AND PCL.PROVIDER_CHANNEL_ID = PC.PROVIDER_CHANNEL_ID
AND 1 = PCL.PREFERENCE (+)
AND 9 = pc.channel_type_id (+)
AND PC.CHANNEL_ADDRESS NOT LIKE '%#%'
AND P.RESOURCE_ID = AL.RESOURCE_ID (+)
AND AL.ADDRESS_ID = A.ADDRESS_ID (+)
AND 1 = A.ADDRESS_TYPE_ID (+)
AND 1 = AL.PREFERENCE (+);
Notice all those inner Joins and so forth. I am thinking right now of putting a named query in one my methods that will return the same results as above. As you may note, there are multiple tables that are being called there and joined at various points... I am thinking I can just put this query into the createNamedQuery() function with minor changes... But I am thinking there has to be a simpler way to do this? Maybe not. Can I just call a stored proc using JPA?
Because your SQL is very complex it's not easy to convert to JPQL, I suggest preserve it.
You can use OpenJPA's NativeQuery, which can do SQL query. If your SQL is not started with SELECT, it will be treated as stored proc.
You can create a JPA entity for each table and then join the entities by doing something similar to this:
http://openjpa.apache.org/builds/1.1.1-SNAPSHOT/docs/jpa_overview_mapping_full.html
Look at the #OnetoMany and #ManytoOne and #ManytoMany annotations in the example for some ideas.

QueryDSL: querying relations and properties

I'm using QueryDSL with JPA.
I want to query some properties of an entity, it's like this:
QPost post = QPost.post;
JPAQuery q = new JPAQuery(em);
List<Object[]> rows = q.from(post).where(...).list(post.id, post.name);
It works fine.
If i want to query a relation property, e.g. comments of a post:
List<Set<Comment>> rows = q.from(post).where(...).list(post.comments);
It's also fine.
But when I want to query relation and simple properties together, e.g.
List<Object[]> rows = q.from(post).where(...).list(post.id, post.name, post.comments);
Then something went wrong, generiting a bad SQL syntax.
Then I realized that it's not possible to query them together in one SQL statement.
Is it possible that QueryDSL would somehow deal with relations and generate additional queries (just like what hibernate does with lazy relations), and load the results in?
Or should I just query twice, and then merge both result lists?
P.S. what i actually want is each post with its comments' ids. So a function to concat each post's comment ids is better, is this kind of expressin possible?
q.list(post.id, post.name, post.comments.all().id.join())
and generate a subquery sql like (select group_concat(c.id) from comments as c inner join post where c.id = post.id)
Querydsl JPA is restricted to the expressivity of JPQL, so what you are asking for is not possible with Querydsl JPA. You can though try to express it with Querydsl SQL. It should be possible. Also as you don't project entities, but literals and collections it might work just fine.
Alternatively you can load the Posts with only the Comment ids loaded and then project the id, name and comment ids to something else. This should work when accessors are annotated.
The simplest thing would be to query for Posts and use fetchJoin for comments, but I'm assuming that's too slow for you use case.
I think you ought to simply project required properties of posts and comments and group the results by hand (if required). E.g.
QPost post=...;
QComment comment=..;
List<Tuple> rows = q.from(post)
// Or leftJoin if you want also posts without comments
.innerJoin(comment).on(comment.postId.eq(post.id))
.orderBy(post.id) // Could be used to optimize grouping
.list(new QTuple(post.id, post.name, comment.id));
Map<Long, PostWithComments> results=...;
for (Tuple row : rows) {
PostWithComments res = results.get(row.get(post.id));
if (res == null) {
res = new PostWithComments(row.get(post.id), row.get(post.name));
results.put(res.getPostId(), res);
}
res.addCommentId(row.get(comment.id));
}
NOTE: You cannot use limit nor offset with this kind of queries.
As an alternative, it might be possible to tune your mappings so that 1) Comments are always lazy proxies so that (with property access) Comment.getId() is possible without initializing the actual object and 2) using batch fetch* on Post.comments to optimize collection fetching. This way you could just query for Posts and then access id's of their comments with little performance hit. In most cases you shouldn't even need those lazy proxies unless your Comment is very fat. That kind of code would certainly look nicer without low level row handling and you could also use limit and offset in your queries. Just keep an eye on your query log to make sure everything works as intended.
*) Batch fetching isn't directly supported by JPA, but Hibernate supports it through mapping and Eclipselink through query hints.
Maybe some day Querydsl will support this kind of results grouping post processing out-of-box...

How to do a joined query in the ZF tables interface?

I have the db and my tables look like this:
alt text http://img15.imageshack.us/img15/2568/stackdijag.png
What I want to do is to get all models where manufacturers name column starts with A.
Which means that that simple part of query should be like $manufacturers->fetchAll("name LIKE '$letter%'");
I am trying to accomplish this with ZF relations but it ain't going, so any kind of help is welcome...
$models = new Models();
$select = $models->select(Zend_Db_Table::SELECT_WITH_FROM_PART);
$select->setIntegrityCheck(false)
->join(array("a"=>"manufacturers"), 'models.manufacturer_id = a.id',
array("man_name"=>"name", "man_description"=>"description"))
->where("a.name LIKE 'A%'");
$rowset = $models->fetchAll($select);
Unfortunately the Zend_Db_Table relationships interface doesn't have much intelligence in it related to creating joined queries from its declared reference map. The community-contributed solution for complex queries is the Zend_Db_Table_Select query factory.
Note you have to give column aliases for manufacturer's name and description, or else these columns will suppress the model's name and description in the associative array for the row data. You should name columns distinctly to avoid this.
But in your case, I'd skip the table interface and the select interface, and simply execute an SQL query directly using the Db adapter:
$data = $db->fetchAll("
SELECT m.*, a.name AS man_name, a.description AS man_description
FROM Models m JOIN Manufacturers a ON m.manufacturer_id = a.id
WHERE a.name LIKE 'A%'");
You'll get the data back as a simple array of associative arrays, not as a Zend_Db_Table_Rowset. But since a joined rowset isn't writeable anyway, you haven't sacrificed much.