I have a many to many relationship and am trying to order by the one side. So in SQL this would be:
select * from
patient join patientuserrelation on patient.id=patientuserrelation.p_id
join user on patientuserrelation.u_id=user.id
order by user.name
Which I have implemented in Ormlite as:
QueryBuilder<Visit, String> qbVisit = setupAccess(Visit.class)
.queryBuilder();
QueryBuilder<UserVisitRelation, String> qbUserVisitRelation = setupAccess(
UserVisitRelation.class).queryBuilder();
QueryBuilder<User, String> qbUser = setupAccess(User.class)
.queryBuilder();
qbUser.orderBy(sortByThisColumn, true);
qbUserVisitRelation.join(qbUser);
qbVisit.join(qbUserVisitRelation);
return qbVisit.distinct().query();
However, this does not work. The results are not ordered at all. I could try to use rawSQL and rawRowMapper but that bloat up my code.
There is a similar question here: ORMLITE order by a column from another table. Unfortunately with no answer. Is there a helpful expert around?
Ok, to answer my own question for posterity: It seems like join and order across multiple tables is not supported in ormlite 4.48. If you think about it for a while you figure out why this is probably the case. Anyway, the solution is to write a raw sql statement, only select the necessary columns WITHOUT foreign collections and cast it to your object using RawRowMapper and GenericRawResults. Not what you like to do when using an ORM, but OK.
Related
I have tricki problem:
When I translate a HQL to SQL (using org.hibernate.hql.spi.QueryTranslator) i got a valid SQL.
Hibernate: Parse/Translate HQL FROM part to get pairs class alias, class name
It works as expected!
But, my problem is the transalation of the column aliases!
HQL to SQL
*) HQL for entity:Base
SELECT Base FROM Base Base
leads into:
*) SQL for entity:Base
select base0_.iD as id1_0_, base0_.comment as comment2_0_, base0_.creationDate as creation3_0_ from ...
You can see my problem:
The Alias of the columns are not intutive names:
base0_.creationDate --> creation3_0_
Expected:
base0_.creationDate --> creationDate
UseCases:
Creating Views for each entity, automatically
Better readabillity for our Database adminitrators
I have debugged hours and hours to find a solution to influence the mechnanism.
I hope some one has an idea how to solve this problem (whithout hacking)!
I know this is a not conventional question, so i would be glad, someone has an idea ;-)
Thanks, in advance
Andy
The problem you faced is known as Hibernate naming stragety.
Here https://www.baeldung.com/hibernate-naming-strategy you can find a deeper explanation on how to customize the generated column names by implementing the interface PhysicalNamingStrategy.
Thanks for your help, but it was not the solution, because now I can rename every column, table or schema, but not the alias in a select query.
I still get queries like this:
select base0_.i_d as i_d1_0_, ....
from base base0_
left outer join campaign base0_1_ on base0_.i_d = base0_1_.i_d
but I would like to have a query without all this "1_0" etc.
select base.i_d as i_d, ....
from base base
left outer join campaign base on base.i_d = campaign.i_d ...
I want to use the translated queries to create views for all entities:
resulting view and columns
You can see, column names are not very useful ;-)
Does anyone have any idea, without string substitution or modifying the SQL AST?
Sorry for the bad question title, couldnt think of anything better.
Anyway, my tables are Tags - Poststags - Posts. Poststags is a junction table for many to many relation. I need to select all posts with given tags, I dont know how many tags the user will choose to search for. One way I found to do this is in the code below, however i would need to loop all the tags given by the user and construct the query string from there since the number of tags is unknown. Seems like a pretty bad solution to me.
Another solution would be to store all tags in one column in the Posts table as a pure string, but I dont want to do that because of other application requirements.
I have a working sql query, since I was trying pure sql before trying to implement it in hibernate, but I dont like doing a select of all posts containing each tag and then joining each query, is there a way to specify the same column multiple times in the WHERE clause? Something along the lines WHERE pt.tag_id = x AND pt.tag_id = y? (I know this won't work). IN operator won't work either since it will give me Posts that contain any of the supplied tags and not just the posts containing ALL of the supplied tags.
Also how would I implement such a query in HQL(if subqueries like this are even supported?). Or can I somehow manage this via criteria? Or do I have to resort to using createSQLQuery method of a hibernate session?
SELECT * FROM
( SELECT * FROM posts p
inner join poststags pt on pt.post_id = p.id
WHERE pt.tag_id = 1 ) AS A
INNER JOIN
( SELECT * FROM posts p
inner join poststags pt on pt.post_id = p.id
WHERE pt.tag_id = 2 ) AS B ON A.id = B.id
And yes, I know this query is not returning the Post entity itself, but I can handle that later.
Don't use hibernate or ORM for this kind of complex select, it may work, but in a bad way.
Your use case should be solved by full text search, which means each Post will need have its own tags.
I don't see much value to make Tag an entity. It's just a string.
Full text search could be heavy for database , A better way is using elasticsearch to help. Spring has integration with spring-data-elasticsearch and it's not difficult to use. Elasyicsearch is very powerful for free text search.
Here is a solution that 'should' work using Criteria queries in Hibernate.
Assuming that you have an entity for Post and an entity for PostTag and PostTag has reference to Post (which I think it should given the example query that you provided), I believe that something like this should do what you want:
static DetachedCriteria getPostTagCriteria(String tagString)
{
DetachedCriteria criteria = DetachedCriteria.forClass(PostTag.class, "uniqueName_" + postTagId);
criteria.createAlias("tag", "tag");
criteria.add(Restrictions.eq("tag.tagString", tagString));
criteria.setProjection(Projections.property("postId"));
return criteria;
}
static List<Post> getPosts(List<String> tagStrings)
{
Criteria criteria = getCurrentSession().createCriteria(Post.class, "post");
for(String tagString : tagStrings)
{
criteria.add(Property.forName("post.id").in(getPostTagCriteria(tagString)));
}
List<Post> ret = criteria.list();
return ret;
}
This assumes that you have reasonable entities to represent Post, PostTag and Tag and that they all reference each other in obvious parent/child sort of ways that I have completely made up here.
But, the general idea of creating multiple detached criteria objects based on your input should solve your problem. This solution also comes with the same caveats regarding SQL complexity mentioned above. You will be creating a sub-query for each tag passed in. So, depending on your indexes and table sizes, you may need to consider a different approach.
I'd like to know the best way to implement this query in SQL-style QueryDSL which joins to a subquery. I struggled a bit, but got it to generate the necessary SQL. I'm wondering if there are any simplifications/improvements, however, particularly related to the three "paths" I had to create? For example, would be great to define latestCaseId in terms of latestSubQuery.
In the simplified form of my actual query below, I am finding the set of records (fields spread across ucm and pcm) which have the latest timestamp per case group. The subquery identifies the latest timestamp per group so that we can filter the outer query by it.
final SimplePath<ListSubQuery> latestSubQueryPath = Expressions.path(ListSubQuery.class, "latest");
final SimplePath<Timestamp> caseLatestMentioned = Expressions.path(Timestamp.class, "caseLatestMentioned");
final SimplePath<Integer> latestCaseId = Expressions.path(Integer.class, "latest.caseId");
final ListSubQuery<Tuple> latest = new SQLSubQuery()
.from(ucm2)
.innerJoin(pcm2).on(ucm2.id.eq(pcm2.id))
.groupBy(pcm2.caseId)
.list(pcm2.caseId.as(latestCaseId), ucm2.lastExtracted.max().as(caseLatestMentioned));
q.from(ucm)
.join(pcm).on(ucm.id.eq(pcm.id))
.innerJoin(latest, latestSubQueryPath).on(pcm.caseId.eq(latestCaseId))
.where(ucm.lastExtracted.eq(caseLatestMentioned));
I believe you could use the .get(<various Path impls>) method of PathBuilder. The way I like to think of it is that creating final PathBuilder<Tuple> latestSubQueryPath = new PathBuilder<>(Tuple.class, "latest") and joining to it .innerJoin(latest, latestSubQueryPath) is creating an alias for the subquery. Then you can use .get(<various Path impls>) to access the fields as follows:
q.from(ucm)
.join(pcm).on(ucm.id.eq(pcm.id))
.innerJoin(latest, latestSubQueryPath).on(pcm.caseId.eq(latestSubQueryPath.get(pcm2.caseId)))
.where(ucm.lastExtracted.eq(latestSubQueryPath.get(maxLastExtractedDate)));
I've not run the code but hopefully this is in the right direction. If not, I'll have a look tomorrow when I have the relevant codebase to hand.
Update: As mentioned in the comments, ucm2.lastExtracted.max() requires an alias. I've called it maxLastExtractedDate and assume it's used to alias ucm2.lastExtracted.max() when creating the subquery.
this topic is rather cryptic for me cause I am no SAP/ABAP developer (SAP tables are for me just a datasource)
I have 2 tables COVP and GLPCA. I try to join them but I do not know on which key I can do that.
Any idea ? Thanks
COVP is a View (Two already 'joined' tables, being COEP and COBK).
To join them my guess without having any data to look at is join using the following rules;
GLPCA-RCLNT = COVP-MANDT
GLPCA-DOCNR = COVP-BELNR
GLPCA-REFDOCLN = COVP-BUZEI
GLPCA-KOKRS = COVP-KOKRS
EDIT: Looking in our system we don't actually use GLPCA. Exactly what data is it you are wanting to display against each other? I will edit my answer accordingly as I do not understand your requirements at the moment reading your comments above. Cheers
I want the equivalent of SO search by tag, so I need an exists query but I also still need to left join on all tags. I've tried a couple of approaches and I'm out of ideas.
The Qustion - Tag relationship is through has_and_belongs_to_many both ways (i.e. I have a QuestionTags joiner table)
e.g.
Question.join(:tags).where('tag.name = ?', tag_name).includes(:tags)
I would expect this to do what I need but actually it just mashes up the includes with the join and I just end up with basically an inner join.
Question.includes(:tags)
.where("exists (
select 1 from questions_tags
where question_id = questions.id
and tag_id = (select id
from tags
where tags.name = ?))", tag_name)
This fetches the correct results but a) is really ugly and b) gives a deprecation warning as again it seems to confuse the includes with the join:
DEPRECATION WARNING: It looks like you are eager loading table(s) (one
of: questions, tags) that are referenced in a string SQL sn ippet. For
example:
Post.includes(:comments).where("comments.title = 'foo'")
Note I'm trying to write these as named scopes.
Let me know if the question isn't clear. Thanks in advance.
OK, got it. I know no built in syntax to do it. I have used an alternative before, You can do like this:
Question.include(:tags).where("questions.id IN (
#{ Question.joins(:tags).where('tags.name = ?', tag_name).select('questions.id').to_sql})")
You can also join this subquery to your questions table instead of using IN. Alternatively if You are not against adding gems and You are using Postgres, use this gem.
It provides really neat syntax for advanced queries.
Use preload instead of includes:
Question.preload(:tags).where("exists ....