Automatic generation of java hibernate code from sql code - java

Is there a tool able to generate java hibernate code starting from the sql query?
(like reverse of what hibernate does, generating selects from java code) It will help me move all my queries to hibernate!
I mean if i have a select with parameters like this:
select ta.id label, ta.nume value
from ar
left outer join ta ta on idp = ta.ID
where ta.status = 1
and (dp = 0 OR ps = idps_)
and status = 1
order by ta.nume;
to obtain in the end something like this:
DetachedCriteria criteria = DetachedCriteria.forEntityName("ar");
criteria.createAlias("ta", "ta", Criteria.LEFT_JOIN);
criteria.add(Restrictions.eq("ta.status", 1));
Criterion eq = Restrictions.eq("ps.id", idps_);
Criterion isZero = Restrictions.eq("dp.id", 0);
Criterion or = Restrictions.or(eq, isZero);
criteria.add(or);
criteria.add(Restrictions.eq("status", 1));
ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.property("ta.id"), "value");
projectionList.add(Projections.property("ta.nume"), "label");
criteria.setProjection(Projections.distinct(projectionList));
criteria.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
criteria.addOrder(Order.asc("ta.nume"));
OR something similar using maps as output...
providing to the tool the path where i store the mappings of the entities/beans with the tables (or the path to the beans, if the beans are annotated)

You have the HQL that is an SQL-like dialect to work with Hibernate. You use field names from entities instead of those from tables. It supports joins etc.
In fact, Criteria API has very limited support of joins (at least it was so last time I've tried to used) and I've a few times finished rewriting everything from Criteria API to HQL, so I now simply tread Criteria API as no option.
In HQL you can also use SQL functions, both in SELECT and in WHERE part, those embedded and those you've written yourself.

Related

QueryDsl - How create inner join with sorted and grouped table

I have to find Salesmen that have sold some itemType. I created method (see below).
But client told me that he wants to find Salesmen by LAST sold itemType.
DB schema:
My attempts: we have in table ORDERS date column, so in normal SQL query I can do double subquery and it should work.
Double, because first I'm sorting by date, then group by salesman - that returns list with only last sold items.
SELECT *
FROM SALESMEN
JOIN
(SELECT *
FROM
(SELECT *
FROM ORDERS
ORDER BY ORDERS.date)
GROUP BY ORDERS.salesman_id) ON SALESMEN.id = ORDERS.salesman_id
WHERE ORDERS.item_type = "CAR"
Unfortunately, queryDSL can do subquery only in IN clause not in FROM.
I spend many hours to find a solution, and in my opinion, it's simply impossible using queryDSL to get sorted and grouped list and join it with another table in one query.
But maybe someone with grater experience has any idea, maybe a solution is simpler than I think :D
public List<SalesmanEntity> findSalesman(SalesmanSearchCriteriaTo criteria) {
SalesmanEntity salesmanEntity = Alias.alias(SalesmanEntity.class);
EntityPathBase<SalesmanEntity> alias = Alias.$(salesman);
JPAQuery<SalesmanEntity> query = new JPAQuery<SalesmanEntity>(getEntityManager()).from(alias);
... a lot of wird IF's....
if (criteria.getLastSoldItemTyp() != null) {
OrderEntity order = Alias.alias(OrderEntity.class);
EntityPathBase<OrderEntity> aliasOrder = Alias.$(order);
query.join(aliasOrder)
.on(Alias.$(salesman.getId()).eq(Alias.$(order.getSalesmanId())))
.where(Alias.$(order.getItemTyp()).eq(criteria.getLastSoldItemTyp()));
}
return query.fetch();
}
Environment:
Java 1.8
SpringBoot 2.0.9
QueryDSL 4.1.4
This is not a limitation of QueryDSL, rather it is a limitation of JPQL - the query language of JPA. For example, SQL does allow subqueries in the FROM clause, and as such querydsl-sql also allows it. With plain plain JPA, or even Hibernate's proprietary HQL it cannot be done. You would have to write a native SQL query then. For this you can have a look at #NamedNativeQuery.
It is possible to add subqueries on top of JPA using Common Table Expressions (CTE) using Blaze-Persistence. Blaze-Persistence ships with an optional QueryDSL integration as well.
Using that extension library, you can just write the following:
QRecursiveEntity recursiveEntity = new QRecursiveEntity("t");
List<RecursiveEntity> fetch = new BlazeJPAQuery<>(entityManager, cbf)
.select(recursiveEntity)
.from(select(recursiveEntity)
.from(recursiveEntity)
.where(recursiveEntity.parent.name.eq("root1"))
.orderBy(recursiveEntity.name.asc())
.limit(1L), recursiveEntity)
.fetch();
Alternatively, when using Hibernate, you can map a Subquery as an Entity, and then correlate that in your query. Using this you can achieve the same result, but you won't be able to reference any outer variables in the subquery, nor will you be able to parameterize the subquery. Both of these features will however be available with the above approach!
#Entity
#Subselect("SELECT salesman_id, sum(amount) FROM ( SELECT * FROM ORDERS ORDER BY ORDERS.date ) GROUP BY ORDERS.salesman_id")
class OrdersBySalesMan {
#Id #Column(name = "salesman_id") Long salesmanId;
#Basic BigDecimal amount; // or something similarly
}
And then in your query:
.from(QSalesman.salesman)
.innerJoin(QOrdersBySalesMan.ordersBySalesMan)
.on(ordersBySalesMan.salesmanId.eq(salesman.id))

How to change order with TypedQuery in JPA

I wish to implement pagingation with FIQL support.
I am using apache cxf with JPA(Hibernate).
Here is sample example given for it http://cxf.apache.org/docs/jax-rs-search.html#JAX-RSSearch-JPA2.0
SearchConditionVisitor<Order, TypedQuery<Order>> visitor
= new JPATypedQueryVisitor<>(em, Order.class);
// connect FIQL cxf SearchCondition with our JPA visitor
searchCondition.accept(visitor);
// creeate JPA specific TypedQuery by our visitor
TypedQuery<Order> typedQuery = visitor.getQuery();
typedQuery.setFirstResult((page * perPage) - perPage);
typedQuery.setMaxResults(perPage);
// Run the query and return matching a complex FIQL criteria
return typedQuery.getResultList();
Every thing looks working including searching and pagination.
It looks have no order by clause being use from generate sql log and seems follow database insertion order.
Now I wish to change the default sorting order. For example I wish to sort by Order id field in descending order. How can I achieve that?
I can get it working with use of CriteriaQuery.
JPACriteriaQueryVisitor<Order, Order> jpa
= new JPACriteriaQueryVisitor<>(em, Order.class, Order.class);
searchCondition.accept(jpa);
CriteriaQuery<Order> cq = jpa.getCriteriaQuery();
CriteriaBuilder cb = em.getCriteriaBuilder();
Root<Order> root = (Root<Order>) cq.getRoots().iterator().next();
cq.orderBy(cb.desc(root.get("id")));
TypedQuery<Order> query = jpa.getTypedQuery();
query.setFirstResult((page * perPage) - perPage);
query.setMaxResults(perPage);
return query.getResultList();
You cannot change the order after creating the query.
If you use MySQL, then maybe you can use an integer parameter in ORDER BY :colnum DESC to specify the column number by which to sort (starting at 1 from the selected columns only), but you cannot change direction.
PostgreSQL does not allow you to do this. I do not know how it is on other databases, though. With MySQL, the parameters are replaced by the SQL Driver, so it always receives the full query with escaped sequences. With PG, the query is first parsed by the server, the execution plan is created (including if and how to order the results) and then the parameters are sent.

Querydsl ignores common table expression alias for JPA Entities

I need to build SQL query with common table expression using QueryDSL:
WITH cte AS (
SELECT DISTINCT BUSINESS_ID FROM BUSINESS WHERE MERCHANT_CODE like ?
)
SELECT t0.*
FROM PAYMENT t0
LEFT JOIN cte t1 ON t0.PAYER = t1.BUSINESS_ID
LEFT JOIN cte t2 ON t0.PAYEE = t2.BUSINESS_ID
WHERE (t1.BUSINESS_ID IS NOT NULL OR t2.BUSINESS_ID IS NOT NULL)
I have two JPA entities (Payment, Business).
This is how I implemented that:
String merchantCode = "abcd%";
QPayment payment = QPayment.payment;
QBusiness business = QBusiness.business;
QBusiness cte = new QBusiness("cte");
QBusiness merchant1 = new QBusiness("t1");
QBusiness merchant2 = new QBusiness("t2");
Configuration configuration = new Configuration(new OracleTemplates());
new JPASQLQuery<>(entityManager, configuration)
.with(cte,
JPAExpressions.selectDistinct(business.businessId).from(business)
.where(business.merchantCode.like(merchantCode)))
.select(payment)
.from(payment)
.leftJoin(cte, merchant1).on(payment.payer.eq(merchant1.businessId))
.leftJoin(cte, merchant2).on(payment.payee.eq(merchant2.businessId))
.where(merchant1.businessId.isNotNull()
.or(merchant2.businessId.isNotNull()));
And the problem is that during leftJoin it doesn't treat cte as a link, instead it inserts table name and two aliases: LEFT JOIN BUSINESS cte t1 ON .... I tried different templates – didn't help.
Am I doing something wrong or it's a QueryDSL bug?
JPQL doen't support CTEs, as we can see in grammar. And querydsl works over JPQL. CTEs are pretty vendor-specific, so you'll have to do one of following:
Rewrite query to be JPA-compatible
Use JPA native query
Query sql with querydsl (actually I don't remember if it supports CTEs)
From all above I would chose the 2nd option. Making native queries doen't harm your code. It makes your code more performant.
Take a good look at the tutorial
QCat cat = QCat.cat;
QCat mate = new QCat("mate");
QCat kitten = new QCat("kitten");
query.from(cat)
.innerJoin(cat.mate, mate)
.leftJoin(cat.kittens, kitten)
.list(cat);
You will want to .leftjoin(cte.merchant1, merchant1).on(...) or whatever the corresponding field is called in the parent "cte".
Basically you need to name the field which you want to join. Just stating the meta model does not suffice as there is no way of telling what you actually want. You can see it in your code (as well in the tutorial's kitten example): you have two Merchant you want to join to the cte, so which one is which.
The .on()-clause just states the conditions under which a join is valid, like you could place filters there.
.

Subquery example in hibernate 5.2 Criteria API

I need to fire below query using hibernate 5.2+, but there is no such way given in hibernate official docs
SELECT * from candidate WHERE candidate.id NOT IN (SELECT candidate_id FROM interview)
N.B. I don't want to use named query or native query stuffs which
makes my code more database specific.
In the image below, Black circle is Candidate, White one is Interview.
Any idea how to run this.
Try something like this:
DetachedCriteria subquery = DetachedCriteria.forClass(Interview.class, "interview")
.setProjection(Projections.property("interview.candidate_id"));
DetachedCriteria criteria = DetachedCriteria.forClass(Candidate.class, "candidate")
.add(Subqueries.propertyNotIn("candidate.id", subquery));
Criteria executableCriteria = criteria.getExecutableCriteria(session);
List list = executableCriteria.list();
You could use an exists command.
from Candidate as candidate
where exists (
from Interview as interview where interview.candidate = candidate
)

Hibernate Criteria filter Entity where ManyToMany relation contains multiple objects

I need help with Hibernate Criteria API.
I have a class Job that contain a list of Skill in ManyToMany relationship.
I need to select jobs based on a skill list given as parameter.
I've tried with Restriction.in("skill.id",ids) but this gives me wrong list.If i've selected 2 skills in search form i want the jobs that contain BOTH of them,so i need somehow to implement AND clause.
I've tried with Conjunction:
Conjunction and = Restrictions.conjunction();
for(Skill skill:job.getSkills()){
and.add(Restrictions.eq("skill.id",skill.getId()));
}
And this:
Criteria crit = criteria.createCriteria("skills",JoinType.LEFT_OUTER_JOIN);
for(Skill skill:job.getSkills()){
crit.add(Restrictions.eq("id", skill.getId()));
}
but it creates same alias for skill and it gives me no result.
sql is and (skill1_.ID=? and skill1_.ID=?)
Can anyone help me with this ?thanks
UPDATE:
HQL Query will be like:
select a from Job a where exists (select skill1.id from Skill skill1 join skill1.jobs r where r.id=a.id and skill1.id=1) and exists (select skill2.id from Skill skill2 join skill2.jobs r where r.id=a.id and skill2.id=4)
I need Criteria based on this.
for(Skill skill:job.getSkills()){
DetachedCriteria subquery = DetachedCriteria.forClass(Skill.class,"skill");
subquery.add(Restrictions.eq("id",skill.getId()));
subquery.setProjection(Projections.property("id"));
subquery.createAlias("jobs", "job");
subquery.add(Restrictions.eqProperty("job.id", "Job.id"));
criteria.add(Subqueries.exists(subquery));
}
I managed to solve it.now it works perfect.

Categories

Resources