I am having an entity, which has collections field (params). There can be many params. I want to make unique join for each unique param. i.e. p1.id = ? AND p2.id = ? AND p3.id = ? etc.
In Hibernate, when I try to create alias for the same field, it throws exception with message about duplicate alias.
How can I achieve multiple joins for the field?
I am using spring framework, hibernate and postgresql.
Thanks in advance.
I assume that you are using the Criteria API.
If you check the source code of Hibernate you can find the problem here:
CriteriaQueryTranslator#createAssociationPathCriteriaMap()
private void createAssociationPathCriteriaMap() {
Iterator iter = rootCriteria.iterateSubcriteria();
while ( iter.hasNext() ) {
CriteriaImpl.Subcriteria crit = ( CriteriaImpl.Subcriteria ) iter.next();
String wholeAssociationPath = getWholeAssociationPath( crit );
Object old = associationPathCriteriaMap.put( wholeAssociationPath, crit );
if ( old != null ) {
throw new QueryException( "duplicate association path: " + wholeAssociationPath );
}
int joinType = crit.getJoinType();
old = associationPathJoinTypesMap.put( wholeAssociationPath, new Integer( joinType ) );
if ( old != null ) {
// TODO : not so sure this is needed...
throw new QueryException( "duplicate association path: " + wholeAssociationPath );
}
}
}
I'm using Hibernate 3.3.2.
What happens internally is that When you are adding Criterias to your root Criteria Hibernate creates SubCriterias and later fills a Map (check the code below) with all the paths you are trying to use and if it finds a duplication it will throw an exception.
So for example if you are trying to join: entity0.entity1 and later you try to join that again you will get an Exception. It won't work even with an alias because Hibernate doesn't care about them here.
You can work around this if you don't join something twice OR you can use HQL/JPQL. You may check out newer versions of Hibernate but I'm not sure if they fixed this quasi-bug.
Related
I have tried the below code for getting the single NotificationSubType Object. But finding exception like " org.apache.openjpa.persistence.ArgumentException: Encountered "from" at character 1, but expected: ["DELETE", "SELECT", "UPDATE"]."
Please help me on given problem.
Code is given below :
...
...
try {
Query query = this.em.createQuery("from NotificationSubType conc WHERE conc.id.alertTyC = ?1 and conc.id.alertSubTyC = ?2");
query.setParameter(1, "MPPRINT");
query.setParameter(2, "REIN");
NotificationSubType trackIdResLst = (NotificationSubType)query.getSingleResult();
if (null != trackIdResLst) {
System.out.println("TRUE");
} else{
System.out.println("FALSE");
}
} catch (Exception e) {
NotificationLogger.errorWithThrowable(e);
throw new NotificationServiceException(e.getMessage());
}
...
Why not read the exception? and read the JPA spec ?
JPQL statements have to start with SELECT, UPDATE or DELETE. Anything other than those is illegal (and yours starts with FROM for some reason). Put a SELECT and the candidate alias in front of it for more chance of success
Yes Hibernate bastardises the JPQL standard with HQL (which allows you to omit SELECT plus the alias) but you are using OpenJPA so you need to use JPA compliant queries, and besides it would be good practice even when using Hibernate to obey the standard!
I have this setup
#Table(name ="A")
EntityA {
Long ID;
List<EntityB> children;
}
#Table(name ="B")
EntityB {
Long ID;
EntityA parent;
EntityC grandchild;
}
#Table(name ="C")
EntityC {
Long ID;
}
The SQL query is this (I omitted irrelevant details):
select top 300 from A where ... and ID in (select parent from B where ... and grandchild in (select ID from C where ...)) order by ...
The sql query in direct database or through Hibernate (3.5) SQL runs 1000 faster than using Criteria or HQL to express this.
The SQL generated is identical from HQL and Criteria and the SQL I posted there.
[EDIT]: Correction - the sql was not identical. I didn't try the Hibernate style parameter setting on the management studio side because I did not realize this until later - see my answer.
If I separate out the subqueries into separate queries, then it is fast again.
I tried
removing all mappings of child, parent, ect.. and just use Long Id references - same thing, so its not a fetching, lazy,eager related.
using joins instead of subqueries, and got the same slow behaviour with all combinations of fetching and loading.
setting a projection on ID instead of retrieving entities, so there is no object conversion - still slow
I looked at Hibernate code and it is doing something astounding. It has a loop through all 300 results that end up hitting the database.
private List doQuery(
final SessionImplementor session,
final QueryParameters queryParameters,
final boolean returnProxies) throws SQLException, HibernateException {
final RowSelection selection = queryParameters.getRowSelection();
final int maxRows = hasMaxRows( selection ) ?
selection.getMaxRows().intValue() :
Integer.MAX_VALUE;
final int entitySpan = getEntityPersisters().length;
final ArrayList hydratedObjects = entitySpan == 0 ? null : new ArrayList( entitySpan * 10 );
final PreparedStatement st = prepareQueryStatement( queryParameters, false, session );
final ResultSet rs = getResultSet( st, queryParameters.hasAutoDiscoverScalarTypes(), queryParameters.isCallable(), selection, session );
// would be great to move all this below here into another method that could also be used
// from the new scrolling stuff.
//
// Would need to change the way the max-row stuff is handled (i.e. behind an interface) so
// that I could do the control breaking at the means to know when to stop
final EntityKey optionalObjectKey = getOptionalObjectKey( queryParameters, session );
final LockMode[] lockModesArray = getLockModes( queryParameters.getLockOptions() );
final boolean createSubselects = isSubselectLoadingEnabled();
final List subselectResultKeys = createSubselects ? new ArrayList() : null;
final List results = new ArrayList();
try {
handleEmptyCollections( queryParameters.getCollectionKeys(), rs, session );
EntityKey[] keys = new EntityKey[entitySpan]; //we can reuse it for each row
if ( log.isTraceEnabled() ) log.trace( "processing result set" );
int count;
for ( count = 0; count < maxRows && rs.next(); count++ ) {
if ( log.isTraceEnabled() ) log.debug("result set row: " + count);
Object result = getRowFromResultSet(
rs,
session,
queryParameters,
lockModesArray,
optionalObjectKey,
hydratedObjects,
keys,
returnProxies
);
results.add( result );
if ( createSubselects ) {
subselectResultKeys.add(keys);
keys = new EntityKey[entitySpan]; //can't reuse in this case
}
}
if ( log.isTraceEnabled() ) {
log.trace( "done processing result set (" + count + " rows)" );
}
}
finally {
session.getBatcher().closeQueryStatement( st, rs );
}
initializeEntitiesAndCollections( hydratedObjects, rs, session, queryParameters.isReadOnly( session ) );
if ( createSubselects ) createSubselects( subselectResultKeys, queryParameters, session );
return results; //getResultList(results);
}
In this code
final ResultSet rs = getResultSet( st, queryParameters.hasAutoDiscoverScalarTypes(), queryParameters.isCallable(), selection, session );
it hits the database with the full SQL, but there are no results collected anywhere.
Then it proceeds to go through this loop
for ( count = 0; count < maxRows && rs.next(); count++ ) {
Where for every one of the expected 300 results, it ends up hitting the database to get the actual result.
This seems insane, since it should already have all the results after 1 query. Hibernate logs do not show any additional SQL being issued during all that time.
Anyone have any insight? The only option I have is to go to native SQL query through Hibernate.
I finally managed to get to the bottom of this. The problem was being caused by Hibernate setting the parameters separately from the actual SQL query that involved subqueries. So native SQL or not, the performance will be slow if this is done. For example this will be slow:
String sql = some sql that has named parameter = :value
SQLQuery sqlQuery = session.createSQLQuery(sql);
sqlQuery.setParameter ("value", someValue);
List<Object[]> list = (List<Object[]>)sqlQuery.list();
And this will be fast
String sql = some native sql where parameter = 'actualValue'
SQLQuery sqlQuery = session.createSQLQuery(sql);
List<Object[]> list = (List<Object[]>)sqlQuery.list();
It seems that for some reason with letting Hibernate take care of the parameters it ends up getting stuck in the resultSet fetching. This is probably because the underlying query on the database is taking much longer being parameterized. I ended up writing the equivalent of Hibernate Criteria and Restrictions code that sets the parameters directly as above.
We noticed a similar behaviour in our system.
And also encountered that writing the query with hardcoded parameters instead of using setParameter() would fixed the issue.
We are using MS SQL Server and after further investigation we noticed the the root cause of our issue is a default configuration of the sql server driver that transmits the query parameters as unicode. This lead to our indices being ignored since they were based on the ascii values on the queried columns.
The solution was to setup this property in the jdbc url : sendStringParametersAsUnicode=false
More details can be found here.
https://stackoverflow.com/a/32867579
I'm using an ebean query in the play! framework to find a list of records based on a distinct column. It seems like a pretty simple query but the problem is the ebean method setDistinct(true) isn't actually setting the query to distinct.
My query is:
List<Song> allSongs = Song.find.select("artistName").setDistinct(true).findList();
In my results I get duplicate artist names.
From what I've seen I believe this is the correct syntax but I could be wrong. I'd appreciate any help. Thank you.
I just faced the same issue out of the blue and can not figure it out. As hfs said its been fixed in a later version but if you are stuck for a while you can use
findSet()
So in your example use
List<Song> allSongs = Song.find.select("artistName").setDistinct(true).findSet();
According to issue #158: Add support for using setDistinct (by excluding id property from generated sql) on the Ebean bug tracker, the problem is that an ID column is added to the beginning of the select query implicitly. That makes the distinct keyword act on the ID column, which will always be distinct.
This is supposed to be fixed in Ebean 4.1.2.
As an alternative you can use a native SQL query (SqlQuery).
The mechanism is described here:
https://ebean-orm.github.io/apidocs/com/avaje/ebean/SqlQuery.html
This is from the documentation:
public interface SqlQuery
extends Serializable
Query object for performing native SQL queries that return SqlRow's.
Firstly note that you can use your own sql queries with entity beans by using the SqlSelect annotation. This should be your first approach when wanting to use your own SQL queries.
If ORM Mapping is too tight and constraining for your problem then SqlQuery could be a good approach.
The returned SqlRow objects are similar to a LinkedHashMap with some type conversion support added.
// its typically a good idea to use a named query
// and put the sql in the orm.xml instead of in your code
String sql = "select id, name from customer where name like :name and status_code = :status";
SqlQuery sqlQuery = Ebean.createSqlQuery(sql);
sqlQuery.setParameter("name", "Acme%");
sqlQuery.setParameter("status", "ACTIVE");
// execute the query returning a List of MapBean objects
List<SqlRow> list = sqlQuery.findList();
i have a solution for it:-
RawSql rawSql = RawSqlBuilder
.parse("SELECT distinct CASE WHEN PARENT_EQUIPMENT_NUMBER IS NULL THEN EQUIPMENT_NUMBER ELSE PARENT_EQUIPMENT_NUMBER END AS PARENT_EQUIPMENT_NUMBER " +
"FROM TOOLS_DETAILS").create();
Query<ToolsDetail> query = Ebean.find(ToolsDetail.class);
ExpressionList<ToolsDetail> expressionList = query.setRawSql(rawSql).where();//ToolsDetail.find.where();
if (StringUtils.isNotBlank(sortBy)) {
if (StringUtils.isNotBlank(sortMode) && sortMode.equals("descending")) {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"desc");
//expressionList.orderBy().asc(sortBy);
}else if (StringUtils.isNotBlank(sortMode) && sortMode.equals("ascending")) {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"asc");
// expressionList.orderBy().asc(sortBy);
} else {
expressionList.setOrderBy("LPAD("+sortBy+", 20) "+"desc");
}
}
if (StringUtils.isNotBlank(fullTextSearch)) {
fullTextSearch = fullTextSearch.replaceAll("\\*","%");
expressionList.disjunction()
.ilike("customerSerialNumber", fullTextSearch)
.ilike("organizationalReference", fullTextSearch)
.ilike("costCentre", fullTextSearch)
.ilike("inventoryKey", fullTextSearch)
.ilike("toolType", fullTextSearch);
}
//add filters for date range
String fromContractStartdate = Controller.request().getQueryString("fm_contract_start_date_from");
String toContractStartdate = Controller.request().getQueryString("fm_contract_start_date_to");
String fromContractEndtdate = Controller.request().getQueryString("fm_contract_end_date_from");
String toContractEnddate = Controller.request().getQueryString("fm_contract_end_date_to");
if(StringUtils.isNotBlank(fromContractStartdate) && StringUtils.isNotBlank(toContractStartdate))
{
Date fromSqlStartDate=new Date(AppUtils.convertStringToDate(fromContractStartdate).getTime());
Date toSqlStartDate=new Date(AppUtils.convertStringToDate(toContractStartdate).getTime());
expressionList.between("fmContractStartDate",fromSqlStartDate,toSqlStartDate);
}if(StringUtils.isNotBlank(fromContractEndtdate) && StringUtils.isNotBlank(toContractEnddate))
{
Date fromSqlEndDate=new Date(AppUtils.convertStringToDate(fromContractEndtdate).getTime());
Date toSqlEndDate=new Date(AppUtils.convertStringToDate(toContractEnddate).getTime());
expressionList.between("fmContractEndDate",fromSqlEndDate,toSqlEndDate);
}
PagedList pagedList = ToolsQueryFilter.getFilter().applyFilters(expressionList).findPagedList(pageNo-1, pageSize);
ToolsListCount toolsListCount = new ToolsListCount();
toolsListCount.setList(pagedList.getList());
toolsListCount.setCount(pagedList.getTotalRowCount());
return toolsListCount;
I am trying to query a table based on criteria from a join table using ORMLite.
Here is how I would express the query I am trying to write in tsql:
select * from media m inner join file f on m.fileId = f.fileId
where m.isNew = 1 OR f.isNew = 1
The result should be a list of media records where either the media record or the corresponding file record has isNew = 1.
I have read through the documentation on using OR in ORMLite, but all of the examples use a single table, not joining. Likewise I have read the documentation on joins, but none of the examples include a where clause that spans both tables. Is there any way besides a raw query to do this?
I had a look at this question: https://stackoverflow.com/a/12629645/874782 and it seems to ask the same thing, but the accepted answer produces an AND query, not an OR. Here is my code that I used to test that theory:
public List<Media> getNewMedia() {
Session session = getSession();
Account account = session.getSelectedAccount();
ContentGroup contentGroup = account.getSelectedContentGroup();
List<Media> results = null;
try {
QueryBuilder<Category, Integer> categoryQueryBuilder = getHelper().getCategoryDao().queryBuilder();
categoryQueryBuilder.where().eq("group_id", contentGroup.getId());
QueryBuilder<MediaCategory, Integer> mediaCatQb = getHelper().getMediaCategoryDao().queryBuilder();
mediaCatQb = mediaCatQb.join(categoryQueryBuilder);
QueryBuilder<FileRecord, Integer> fileQueryBuilder = getHelper().getFileDao().queryBuilder();
fileQueryBuilder.where().ge("lastUpdated", contentGroup.getLastDownload());
QueryBuilder<Media, Integer> mediaQb = getHelper().getMediaDao().queryBuilder();
mediaQb.where().eq("seen", false);
// join with the media query
results = mediaQb.join(fileQueryBuilder).join(mediaCatQb).query();
} catch (SQLException e) {
Log.e(TAG, "Sql Exception", e);
}
return results;
}
For the sake of completion, this is querying for a slightly more complex example than the one I gave above, this one expressed in tsql would be
select * from Media m join FileRecord f on m.fileRecordId = f.fileRecordId
where m.seen = false OR f.lastUpdated >= lastUpdateDate
When I run it, it is actually doing an AND query, which is what I would expect based on two joins with independent where clauses.
I think the key issue is that a where clause is inherently tied to a table, because it is performed on a QueryBuilder object which comes from a Dao that is specific to that table. How can I get around this?
I think what you are looking for is joinOr search for it in the ORMLite docs.
Like join(QueryBuilder) but this combines the WHERE statements of two
query builders with a SQL "OR".
How can I build a query in ORMLite so that I can use the orderBy function (using either the one with the raw string or the parametrized one) referencing an attribute of a different entity than the one of the dao I'm building the query from? My query is built like that:
// Inner query for performances
QueryBuilder<Performance, String> performancesQB = performanceDao.queryBuilder();
performancesQB.selectColumns("performance_id");
SelectArg performanceSelectArg = new SelectArg();
performancesQB.where().lt("date", performanceSelectArg);
// Outer query for Order objects, where the id matches in the performance_id
// from the inner query
QueryBuilder<Order, String> ordersQB = orderDao.queryBuilder();
ordersQB.where().isNull("user_id").and().in("performance_id", performancesQB);
ordersQB.orderByRaw("performances.date DESC");
pastOrdersQuery = ordersQB.prepare();
And the exception I'm getting whenever I try to execute this query is:
android.database.sqlite.SQLiteException: no such column: performances.date:,
while compiling: SELECT * FROM `orders` WHERE
(`user_id` IS NULL AND `performance_id` IN
(SELECT `performance_id` FROM `performances` WHERE `date` < ? ) )
ORDER BY performances.date DESC
The only solution I see here is writing a raw query myself using a JOIN instead of a nested select. May this be a good solution?
ORMLite now supports simple JOIN queries. Here the docs on the subject:
http://ormlite.com/docs/join-queries
So your query would now look something like:
QueryBuilder<Performance, String> performancesQB = performanceDao.queryBuilder();
SelectArg performanceSelectArg = new SelectArg();
performancesQB.where().lt("date", performanceSelectArg);
performancesQB.orderBy("date", false);
// query for Order objects, where the id matches
QueryBuilder<Order, String> ordersQB = orderDao.queryBuilder();
ordersQB.join(performancesQB).where().isNull("user_id");
pastOrdersQuery = ordersQB.prepare();