Java generate queryDSL - java

So I have a table named BLOG_REPORTERS like:
blog_id | reporter_id
1 1
2 3
And REPORTER:
reporter_id | name | etc
1 asd etc
I need to perform JOIN operation between them. I generated a QueryDSL class for them, and tried something like:
new JPAQuery<>(entityManager)
.select(QReporter.reporter)
.from(QReporter.reporter)
.join(QBlogReporters.blogreporters)
but this is wrong because join() method accepts EntityPath<P> and QBlogReporters extends BeanPath<T>.
Is any way to do this?

Related

How to query Oracle via JDBC for an intersection

I need to check from Java if a certain user has at least one group membership. In Oracle (12 by the way) there is a big able that looks like this:
DocId | Group
-----------------
1 | Group-A
1 | Group-E
1 | Group-Z
2 | Group-A
3 | Group-B
3 | Group-W
In Java I have this information:
docId = 1
listOfUsersGroups = { "Group-G", "Group-A", "Group-Of-Something-Totally-Different" }
I have seen solutions like this, but this is not the approach I want to go for. I would like to do something like this (I know this is incorrect syntax) ...
SELECT * FROM PERMSTABLE WHERE DOCID = 1 AND ('Group-G', 'Group-A', 'Group-Of-Something-Totally-Different' ) HASATLEASTONE OF Group
... and not use any temporary SQL INSERTs. The outcome should be that after executing this query I know that my user has a match because he is member of Group-A.
You can do this (using IN condition):
SELECT * FROM PERMSTABLE WHERE DocId = 1 AND Group IN
('Group-G', 'Group-A', 'Group-Of-Something-Totally-Different')

Postgresql Array Functions with QueryDSL

I use the Vlad Mihalcea's library in order to map SQL arrays (Postgresql in my case) to JPA. Then let's imagine I have an Entity, ex.
#TypeDefs(
{#TypeDef(name = "string-array", typeClass =
StringArrayType.class)}
)
#Entity
public class Entity {
#Type(type = "string-array")
#Column(columnDefinition = "text[]")
private String[] tags;
}
The appropriate SQL is:
CREATE TABLE entity (
tags text[]
);
Using QueryDSL I'd like to fetch rows which tags contains all the given ones. The raw SQL could be:
SELECT * FROM entity WHERE tags #> '{"someTag","anotherTag"}'::text[];
(taken from: https://www.postgresql.org/docs/9.1/static/functions-array.html)
Is it possible to do it with QueryDSL? Something like the code bellow ?
predicate.and(entity.tags.eqAll(<whatever>));
1st step is to generate proper sql: WHERE tags #> '{"someTag","anotherTag"}'::text[];
2nd step is described by coladict (thanks a lot!): figure out the functions which are called: #> is arraycontains and ::text[] is string_to_array
3rd step is to call them properly. After hours of debug I figured out that HQL doesn't treat functions as functions unless I added an expression sign (in my case: ...=true), so the final solution looks like this:
predicate.and(
Expressions.booleanTemplate("arraycontains({0}, string_to_array({1}, ',')) = true",
entity.tags,
tagsStr)
);
where tagsStr - is a String with values separated by ,
Since you can't use custom operators, you will have to use their functional equivalents. You can look them up in the psql console with \doS+. For \doS+ #> we get several results, but this is the one you want:
List of operators
Schema | Name | Left arg type | Right arg type | Result type | Function | Description
------------+------+---------------+----------------+-------------+---------------------+-------------
pg_catalog | #> | anyarray | anyarray | boolean | arraycontains | contains
It tells us the function used is called arraycontains, so now we look-up that function to see it's parameters using \df arraycontains
List of functions
Schema | Name | Result data type | Argument data types | Type
------------+---------------+------------------+---------------------+--------
pg_catalog | arraycontains | boolean | anyarray, anyarray | normal
From here, we transform the target query you're aiming for into:
SELECT * FROM entity WHERE arraycontains(tags, '{"someTag","anotherTag"}'::text[]);
You should then be able to use the builder's function call to create this condition.
ParameterExpression<String[]> tags = cb.parameter(String[].class);
Expression<Boolean> tagcheck = cb.function("Flight_.id", Boolean.class, Entity_.tags, tags);
Though I use a different array solution (might publish soon), I believe it should work, unless there are bugs in the underlying implementation.
An alternative to method would be to compile the escaped string format of the array and pass it on as the second parameter. It's easier to print if you don't treat the double-quotes as optional. In that event, you have to replace String[] with String in the ParameterExpression row above
For EclipseLink I created a function
CREATE OR REPLACE FUNCTION check_array(array_val text[], string_comma character varying ) RETURNS bool AS $$
BEGIN
RETURN arraycontains(array_val, string_to_array(string_comma, ','));
END;
$$ LANGUAGE plpgsql;
As pointed out by Serhii, then you can useExpressions.booleanTemplate("FUNCTION('check_array', {0}, {1}) = true", entity.tags, tagsStr)

Stored procedures in jooq with dynamic names

I want to call stored procedures from PostgreSQL in JOOQ by name dynamically:
final Field function = function("report_" + name, Object.class, (Field[])params.toArray(new Field[params.size()]));
dsl().select(function).fetchArrays();
For example it generates:
select report_total_requests('83.84.85.3184');
Which returns:
report_total_requests
-----------------------
(3683,2111,0)
(29303,10644,1)
And in java it is array of "(3683,2111,0)" objects.
I want to generate:
select * from report_total_requests('83.84.85.3184')
To produce:
total | users | priority
------+-------+----------
3683 | 2111 | 0
29303 | 10644 | 1
That is in java array of arrays of objects
Any ideas?
The way forward is to use plain SQL as follows:
Name name = DSL.name("report_" + name);
QueryPart arguments = DSL.list(params);
dsl().select().from("{0}({1})", name, arguments).fetch();
Note, I've wrapped the function name in a DSL.name() object, to prevent SQL injection.

JPQL/SQL get the latest of records grouped by columns

I'm currently using JPQL (latest version I think) with a postgresql database and I'm trying to get the latests objects grouped by some (not all columns).
Let's say I have a table MyTable like this:
id | name | color | shape | date_of_creation | other_stuff|
---+------+-------+-------+------------------+------------+
01 | apple| red| circle| 30/12/2015| stuff|
02 | apple| red| circle| 15/12/2015| somestuff|
03 | apple| green| circle| 01/12/2015|anotherstuff|
04 | box| orange| cubic| 13/12/2015| blahblah1|
05 | box| orange| cubic| 25/12/2015| blahblah2|
And I only want the latest grouped by name, color and shape like this:
id | name | color | shape | date_of_creation | other_stuff|
---+------+-------+-------+------------------+------------+
01 | apple| red| circle| 30/12/2015| stuff|
05 | box| orange| cubic| 25/12/2015| blahblah2|
Let's assume that the java equivalent is MyClass with the same elements.
My query in JPQL would look like something like:
SELECT m FROM MyClass m GROUP BY m.name, m.color, m.shape ORDER BY m.date_of_creation;
But this does not work because "date_of_creation" must be in the "GROUP BY" clause, which does not make sense because I don't want to group by date_of_creation. I tried using nested select but I have the same problem with the id now..
I found out about the greatest-n-per-group problem ( SQL Select only rows with Max Value on a Column ) on SO's title suggestions after orginaly typing the question, so I created a named query like this:
SELECT m FROM Myclass LEFT JOIN m.id mleft WHERE
m.name = mleft.name AND
m.color = mleft.color AND
m.shape = mleft.shape AND
m.date_of_creation < mleft.date_of_creation AND
mleft.id = null
Which makes a java.lang.NullPointerException at org.hibernate.hql.internal.ast.HqlSqlWalker.createFromJoinElement(HqlSqlWalker.java:395) or does not even work (no error, nothing). I think it's because the field "id" does not have a relationship with itself. And using an inner join with a select inside does not seem to work in JPQL:
SELECT m FROM MyClass INNER JOIN (SELECT mInner from Myclass GROUP BY m.name etc.)
Maybe I'm missing something and I'll have to build a much more complicated query, but JPQL seems not to be powerfull enough, or I don't know enough of it.
Also, I don't really feel like using the query.getResultList() and doing the work in java...
Any tips are welcome, thanks in advance.
You can't use subqueries in FROM block in JPQL, only SELECT and WHERE blocks allowed. Try someting like this:
SELECT m
FROM Myclass m
WHERE m.date_of_creation IN ( SELECT MAX(mm.date_of_creation)
FROM Myclass mm
WHERE mm.id = m.id)
P.S. Code not tested

QueryDSL and SubQuery with Tuple condition

I am trying to write a query in QueryDSL to fetch the oldest elements of a table grouped by their parentId.
The SQL equivalent should be:
SELECT a.* FROM child a
INNER JOIN
(
SELECT parentId, MAX(revision) FROM child GROUP BY parentId
) b
ON (
a.parentId = b.parentId AND a.revision = b.revision
)
Now in QueryDSL I'm stuck with the syntax.
JPQLQuery<Tuple> subquery = JPAExpressions
.select(child.parent, child.revision.max())
.from(child)
.groupBy(child.parent);
HibernateQuery<Child> query = new HibernateQuery<>(session);
query.from(child)
.where(child.parent.eq(subquery.???).and(child.revision.eq(subquery.???))));
How do you write this query using a subquery ?
The tables are looking like this :
___parent___ (not used in this query, but exists)
parentId
P1 | *
P2 | *
P3 | *
___child___
parentId | revision
P1 | 1 | *
P1 | 2 | *
P1 | 3 | *
P2 | 2 | *
P2 | 3 | *
P3 | 1 | *
___result from child, the highest revision for each parentId___
P1 | 3 | *
P2 | 3 | *
P3 | 1 | *
What I've tried so far :
.where(JPAExpressions.select(child.parent,child.revision).eq(subquery));
-> org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected end of subtree
and many syntax errors ...
I use a dirty loop, for now, since I haven't found a solution yet.
You can use Expressions.list() to specify more than one column for the in clause:
query.from(child).where(Expressions.list(child.parent, child.revision).in(subquery));
The alternative is to use innerJoin(), as in your original SQL.
Expressions.list(ENTITY.year, ENTITY.week).in(//
Expressions.list(Expressions.constant(1029), Expressions.constant(1)),
Expressions.list(Expressions.constant(1030), Expressions.constant(1)),
Expressions.list(Expressions.constant(1031), Expressions.constant(1))
would be what you are looking for, but QueryDSL generates wrong SQL from it:
((p0_.year , p0_.week) in (1029 , 1 , (1030 , 1) , (1031 , 1)))
In JPA subqueries can appear only in the where part.
Here is my take on your query
select(child).from(child).where(child.revision.eq(
select(child2.revision.max())
.from(child2)
.where(child2.parent.eq(child.parent))
.groupBy(child2.parent))).fetch()
Building off of JRA_TLL's answer - nested use of Expressions.list() is, per the maintainers of QueryDSL, not supported. Choice quote:
This is not really a bug, is just improper use of QueryDSL's internal list expressions for tuples.
[...]
The solution is quite simply: don't use the list expression for this. This use a Template expression.
Here's a version of JRA_TLL's answer with the Template paradigm recommended by the maintainers, which does the right thing:
public static SimpleExpression<Object> tuple(Expression<?>... args) {
return Expressions.template(Object.class, "({0}, {1})", args);
}
// ...
Expressions.list(ENTITY.year, ENTITY.week).in(
tuple(Expressions.constant(1029), Expressions.constant(1)),
tuple(Expressions.constant(1030), Expressions.constant(1)),
tuple(Expressions.constant(1031), Expressions.constant(1)));
This should generate the right SQL:
(p0_.year , p0_.week) in ((1029 , 1) , (1030 , 1) , (1031 , 1))
Note that this kind of query construction doesn't work for all kinds of databases; for instance, this is valid in PostgresSQL, but isn't valid in Microsoft SQL Server.

Categories

Resources