QueryDsl - filtering Map based on key - java

I have folowing property in my hibenrate entity class:
#MapKeyJoinColumn(name = "language_code")
#LazyCollection(LazyCollectionOption.EXTRA)
#ElementCollection(fetch = FetchType.LAZY)
#CollectionTable(name = "text_translations", joinColumns = #JoinColumn(name = "text_id"))
private Map<Language, String> translations = new HashMap<>();
Now I want to query this entity and filter content of map by user's language (i.e. by map's key). I have folowing join in my query:
StringPath titleTran = new StringPath("title_tran");
from(entity).
.leftJoin(entity.translations, titleTran).fetch().where(?mapKey?.eq(userLanguage));
What I need is ?mapKey? to by path to language of titleTran path. Is this somehow possible in QueryDsl?

If you want to search for a given Map key, you can use the following HQL query:
select me
from MyEntity me
join me.translations tr
where
index(tr) = :lang
or with JPQL:
select me
from MyEntity me
join me.translations tr
where
key(tr) = :lang

In Querydsl JPA the following works
query.from(me).where(me.translations.containsKey(lang)).list(me);

Related

How to create complex join between three tables using Hibernate?

I have three tables (I'll list only important fields):
Format
id
template_id
parameterisation_id
Param
name
value
parameterisation_id
ParamDict
name
template_id
I need to map params into formats into one-to-many association, but they do not have direct connection and needed to be joined through ParamDict table.
I currently did it with #JoinTable like so:
#Entity
#Table(schema = "123", name = "format")
public class Format implements Serializable {
#Id
private Long id;
private String template_id;
private String parameterisation_id;
#OneToMany(fetch = FetchType.EAGER)
#JoinTable(name = "paramDict", schema = "123",
joinColumns = {#JoinColumn(name = "template_id", referencedColumnName =
"template_id",insertable = false,updatable = false)},
inverseJoinColumns = {#JoinColumn(name = "param_name", referencedColumnName =
"param_name",insertable = false,updatable = false)})
private Set<Param> params = new HashSet<>()
}
And in sql it looks like this
Select * from
Format f
left join ParamDict pd on f.template_id = pd.template_id
left join Param p on p.name = pd.name;
But now i need to add another condition in Param join:
Select * from
Format f
left join ParamDict pd on f.template_id = pd.template_id
left join Param p on (p.name = pd.name and p.parameterisation_id = f.parameterisation_id);
I tried to add where clause into #Query annotation in my Repository:
public interface FormatRepository extends Repository<Format, String> {
#Query("select n from format f "
+ "left join fetch f.params p "
+ "where f.parameterisation_id in (?1) "
+ "and f.parameterisation_id = p.parameterisation_id"
)
Set<Format> findBypParameterisationIdIn(List<String> ids);
}
And it created right sql query, but Hibernate does not count that condition (f.parameterisation_id = p.parameterisation_id) when mapping query results into class objects, and i get wrong results in Param set (there are items in the params set, that does not have the same parameterisation_id as Format).
How can i do it via Hibernate mapping?

JPA CriteriaBuilder left outer join for table with no relations?

Initially I had requirement to write code using JPA CriteraiBuilder for following SQL:
SELECT ve.col_1,
(SELECT vm.col_4
FROM table2 vm
WHERE vm.col_2 = ve.col_2
AND vm.col_3 = ve.col_3
) as col_a
FROM table1 ve;
But I learnt that, it is not possible to add subquery in select clause. So I changed my query to use left outer join like this.
SELECT ve.col_1,
vm.col_4 as col_a
FROM table1 ve,
table2 vm
WHERE
vm.col_2 (+) = ve.col_2
AND vm.col_3 (+) = ve.col_3;
Now table1 and table2 do not have direct relations using foreign keys. Corresponding JPA entities look like:
Table1.java ->
#Column(name = "COL_1")
private String col_1;
#Column(name = "COL_2")
private String col_2;
#Column(name = "COL_3")
private String col_3;
#OneToOne(fetch = FetchType.LAZY)
#JoinColumns({
#JoinColumn(name="COL_2"),
#JoinColumn(name="COL_3")
})
private Table2 table2;
Table2.java ->
#Column(name = "COL_4")
private String col_4;
#Column(name = "COL_2")
private String col_2;
#Column(name = "COL_3")
private String col_3;
My code looks like:
final CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
final CriteriaQuery<SearchTO> query = criteriaBuilder.createQuery(
SearchTO.class);
Root<Table1> root = query.from(Table1.class);
final Join<Table1, Table2> joinTable2 = root.join(Table1_.table2,
JoinType.LEFT);
Then I am trying to fetch value using:
joinTable2.get(Table2_.col_4)
Then now I am getting error as:
A Foreign key refering com.Table2 from com.Table1 has the wrong number of column
Table2 has around 6 columns with annotation #Id and I can not change change it to have only two columns with #Id annotation.
Please let me know:
If it is possible to write code using CriteriaBuilder for my approach 1 (subquery in select clause).
If thats not possible, how can I implement this left outer join as mentioned for approach 2. Please note that Table2 does not have any references of Table1.
Please note that I am using plain JPA APIs. DB is Oracle11g. JDK version is 1.7.
One solution is to create view and query against it using criteria builder.
You can see my answer in Joining tables without relation using JPA criteria
Only change you need to do is use left join in your view definition.
Hope it solves your use case.
For Approach 1: You can write subquery for criteria query
Subquery<Entity1> subquery = cq.subquery( Entity1.class );
Root fromSubQuery = subquery.from( Entity1.class );
subquery.select( cb.max( fromSubQuery.get( "startDate" ) ) );
subquery.where( cb.equal( fromSubQuery.get( "xyzId" ), fromRootOfParentQuery.get( "xyzId" ) ) );
Use it as :
Root<Entity2> entity2 = cq.from( Entity2.class );
Predicate maxDatePredicate = cb.and( cb.equal( entyty2.get( "startDate" ), subquery ) );
For Approach 2:
There is no other way than having relationship between two entities for left join. You can define private variable for relationship without getter & setter and use that variable for setting left join.
Then add the predicate to criteriaBuilder

NamedNativeQuery in Hibernate generates many select statements? How get referenced entities in a batch-way?

I thought I understood hibernate's fetching strategies, but it seems I was wrong.
So, I have an namedNativeQuery:
#NamedNativeQueries({
#NamedNativeQuery(
name = "getTest",
resultClass = ArticleOnDate.class,
query = "SELECT `a`.`id` AS `article_id`, `a`.`name` AS `name`, `b`.`price` AS `price` FROM article a LEFT JOIN price b ON (a.id = b.article_id) WHERE a.date <= :date"
)
})
#Entity()
#Immutable
public class ArtikelOnDate implements Serializable {
#Id
#OneToOne
#JoinColumn(name = "article_id")
private Article article;
...
}
Then I call it:
Query query = session.getNamedQuery("getTest").setDate("date", date);
List<ArticleOnDate> list = (List<ArticleOnDate>) query.list();
The query returns thousand of entities... Well, ok, but after that query hibernate queries thousand other queries:
Hibernate:
select
article0_.id as id1_0_0_,
article0_.bereich as name2_0_0_,
price1_.price as price1_14_1_
from
article artikel0_
where
artikel0_.id=?
Ok, that's logic, because the #OneToOne relation is fetched eagerly. I don't want to fetch it lazy, so I want a batch fetching strategy.
I tried to annotate the Article property but it didn't work:
#Id
#OneToOne
#JoinColumn(name = "article_id")
#BatchSize(size=100)
private Article article;
So what can I do to fetch the relation in a batch?

JPQL querying a collection of non-entites

I want to make a JPQL query with a collection of non entities. This is my Table entity :
#Entity
#Table(name = "ct_table")
public class Table {
...
#CollectionOfElements(fetch = FetchType.EAGER)
#JoinTable(name = "ct_table_result", joinColumns = #JoinColumn(name = "tableId"))
#MapKey(columns = { #Column(name = "label") })
#Column(name = "value")
private Map<String, String> tableResults;
...
then I try to make a query like this
select count(*) from table where table.tableResults['somekey'].value='somevalue'
but I get the following exception:
Cannot create element join for a collection of non-entities!
Any suggestion??
thanks for your time
EDIT:
I use JPA 1 and hibernate 3.3. Default libraries in JBoss 5
The JPA 2 spec (page 139) defines the KEY() and VALUE() functions to access the key and the value of a map-valued element collection:
select count(t.id) from Table t
where KEY(t.tableResults) = 'somekey' and VALUE(t.tableResults) = 'somevalue'

Hibernate Criteria Api Subqueries

I am currently working on a project to transfer some legacy jdbc select statements over to using Hibernate and it's criteria api.
The two relevant table columns and the SQL query looks like:
-QUERIES-
primaryId
-QUERYDETAILS-
primaryId
linkedQueryId -> Foreign key references queries.primaryId
value1
value2
select *
from queries q
where q.primaryId not in (SELECT qd.linkedQueryId
FROM querydetails qd
WHERE (qd.value1 LIKE 'PROMPT%'
OR qd.value2 LIKE 'PROMPT%'));
My entity relationships look like:
#Table("queries")
public class QueryEntity{
#Id
#Column
private Long primaryId;
#OneToMany(targetEntity = QueryDetailEntity.class, mappedBy = "query", fetch = FetchType.EAGER)
private Set<QueryDetailEntities> queryDetails;
//..getters/setters..
}
#Entity
#Table(name = "queryDetails")
public class QueryDetailEntity {
#Id
#Column
private Long primaryId;
#ManyToOne(targetEntity = QueryEntity.class)
private QueryEntity query;
#Column(name="value1")
private String value1;
#Column(name="value2")
private String value2;
//..getters/setters..
}
I am attempting to utilize the criteria api in this way:
Criteria crit = sessionFactory.getCurrentSession().createCriteria(QueryEntity.class);
DetachedCriteria subQuery = DetachedCriteria.forClass(QueryDetailEntity.class);
LogicalExpression hasPrompt = Restrictions.or(Restrictions.ilike("value1", "PROMPT%"),
Restrictions.ilike("value2", "PROMPT%"));
subQuery.add(hasPrompt);
Criterion subQueryCrit = Subqueries.notIn("queryDetails", subQuery);
crit.add(subQueryCrit);
List<QueryMainEntity> entities = (List<QueryMainEntity>) crit.list();
System.out.println("# of results = " + entities.size());
I am getting a NullPointerException on the crit.list() line that looks like
Exception in thread "main" java.lang.NullPointerException
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getProjectedTypes(CriteriaQueryTranslator.java:362)
at org.hibernate.criterion.SubqueryExpression.createAndSetInnerQuery(SubqueryExpression.java:153)
at org.hibernate.criterion.SubqueryExpression.toSqlString(SubqueryExpression.java:69)
at org.hibernate.loader.criteria.CriteriaQueryTranslator.getWhereCondition(CriteriaQueryTranslator.java:380)
at org.hibernate.loader.criteria.CriteriaJoinWalker.<init>(CriteriaJoinWalker.java:114)
at org.hibernate.loader.criteria.CriteriaJoinWalker.<init>(CriteriaJoinWalker.java:83)
at org.hibernate.loader.criteria.CriteriaLoader.<init>(CriteriaLoader.java:92)
at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1687)
at org.hibernate.impl.CriteriaImpl.list(CriteriaImpl.java:347)
Now, I think its pretty safe to say I'm using the Criteria Api/Detached Query Api incorrectly, but I'm not sure what the 'correct' way to do it is since the Hibernate Docs only briefly cover criteria api subqueries.
I realize this is a pretty long question, but I figure its appear to put it all the relevant aspects of the question (query I'm attempting to represent via Criteria API, tables, entities).
Give this a shot:
DetachedCriteria d = DetachedCriteria.forClass(QueryDetailEntity.class, "qd");
d.setProjection(Projections.projectionList().add(Projections.property("qd.query")));
d.add(Restrictions.or(Restrictions.like("qd.value1", "PROMPT%"), Restrictions.like("qd.value2", "PROMPT%")));
criteria = session.createCriteria(QueryEntity.class, "q");
criteria.add(Subqueries.propertyNotIn("q.primaryId", d));
criteria.list();
The use of the following are property names, not column names:
qd.query
qd.value1
qd.value2
q.primaryId
As a side note, if this is not a dynamically generated query, have you given thought to using HQL instead?

Categories

Resources