Hibernate combine AND and OR query - spring

I have the following hibernate query
findByUserOrReaderAndIdInAndStatusNotIn
the actual query generated is
WHERE user=? OR reader=? AND (id in (?, ?)) AND (status not in (?, ?))
but what I actually need is
WHERE (user=? OR reader=?) AND (id in (?, ?)) AND (status not in (?, ?))
How to properly create the findBy method? I cannot use #Query cause this method is generic.

My problem is the generics, so I did the following and it works.
#Query("from #{#entityName} t where (t.user = :user or t.reader = :reader) and (t.id in (:ids)) and (t.status not in (:statuses))")

Related

Two wherehas in a Laravel Eloquent query

I have the following query, simplified below.
$property = Property::whereHas('features', function ($q1) use ($features) {
$q1->where(function ($sub) use ($features) {
$sub->whereIn('features.FeatureID', $features);
});
$q1->groupBy('PropertyID');
$q1->having('count(*)', '=', count($features));
})
->whereHas('Week', function ($q) use ($nights) {
$q->where(function ($sub) use ($nights) {
$sub->whereIn('priceAvailDate', $nights);
});
$q->groupBy('PropertyID');
$q->having('count(*)', '=', count($nights));
})
->get();
If I take one whereHas out, I get the expected collection returned. If I take the other out I get the expected result.It is when they are together I have nothing returned. I assume it is down to having two count(*). I know I have data where both whereHas is true.
SQL output is
select * from `tblproperties` where (select count(*) from `features` inner
join `propertyfeatures` on `features`.`featureid` =
`propertyfeatures`.`FeatureID` where `propertyfeatures`.`PropertyID` =
`tblproperties`.`PropertyID` and (`features`.`FeatureID` in (?)) group by
`PropertyID` having `count(*)` = ?) >= 1 and (select count(*) from
`tblpriceavail` where `tblpriceavail`.`propertyID` = `tblproperties`.`PropertyID`
and (`priceAvailDate` in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)) group by
`PropertyID` having `count(*)` = ?) >= 1
Fixed, although not 100% sure why. Using havingRaw instead of having worked.
i.e. changing the $q->having('count(*)', '=', count($nights));
to $q->havingRaw('count(*) = ' . count($nights));

Insert timestamp with JdbcTemplate in Oracle database ( ORA-01858 )

I've read a lot of stuff about this error, and still not found the mistake.
I'm using JdbcTemplate to insert a row in some table with some timestamp column
I'm pretty sure the timestamp is the problem, as if delete from the insert it works fine)
My code:
private static final String INSERT_CITAS = "INSERT INTO CITAS ("
+ "idCita, idServicio, " + "fechaCita, "
+ "idEstado, idUsuarioInicial) " + "VALUES (?, ?, ?, ?, ?)";
Object[] params = {
idCita,
citaQuenda.getIdServicio(),
getDateToDBFormat(citaQuenda.getFechaCita()),
ESTADO_INICIAL,
USUARIO_INICIAL };
String queryCitas = INSERT_CITAS;
super.getJdbcTemplate().update(queryCitas, params);
protected String getDateToDBFormat(Date fechaCreacion){
return "TO_TIMESTAMP('" +
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(fechaCreacion)
+ "', 'yyyy-mm-dd hh24:mi:ss')" ;
}
And having the next error:
org.springframework.dao.DataIntegrityViolationException: PreparedStatementCallback; SQL [INSERT INTO citas_55 (idCita, idServicio, fechaCita, idEstado, idUsuarioInicial) VALUES (?, ?, ?, ?, ?)];
ORA-01858: a non-numeric character was found where a numeric was expected
I've tried to execute the sql in some SQL editor having success, so I can't be more confused.
Being my params: [461, 100, TO_TIMESTAMP('2015-01-28 00:00:01', 'yyyy-mm-dd hh24:mi:ss'), 1, 8888] This actually works.
INSERT INTO citas (idCita, idServicio, fechaCita, idEstado, idUsuarioInicial) VALUES (457, 100, TO_TIMESTAMP('2015-01-28 00:00:01', 'yyyy-mm-dd hh24:mi:ss') , 1, 8888);
Any kind of help would be appreciated. Thanks in advance!
Don't convert back and forth between dates/timestamps and Strings.
Just pass a java.sql.Timestamp instance as a parameter:
Object[] params = {
idCita,
citaQuenda.getIdServicio(),
new java.sql.Timestamp(citaQuenda.getFechaCita()),
ESTADO_INICIAL,
USUARIO_INICIAL };
String queryCitas = INSERT_CITAS;
super.getJdbcTemplate().update(queryCitas, params);
I will go out on a limb here, and think I may see the problem. getDateToDBFormat() method is returning a String type, which contains the text, "TO_TIMESTAMP(...)". That is not a date or timestamp parameter. It is a string parameter. You need to do this instead:
Remove the TO_TIMESTAMP stuff from getDateToDBFormat() and have it just return the formatted DATE/TIME value (the format you show is not an oracle timestamp, but a DATE type).
change your insert to:
"INSERT INTO CITAS ... VALUES (?, ?, TO_DATE(?,?) , ?, ?)"
Where the parameters to the TO_DATE call are the return from getDateToDBFormat() and the second parameter is the date format mask. However, can't you just get rid of that mess and bind a Java Date type (or jdbc sql equivalent) directly?
That should work.

Converting SQL statement to Active Record syntax

I'm having some trouble figuring out the Active Record syntax for the following Postgres query:
SELECT *
FROM tracks
JOIN displays on displays.track_id = tracks.id
JOIN users on displays.user_id = users.id
WHERE user_id = 1
AND displays.display is true
AND tracks.likes_count > 200
The above query above works, and now I'm trying to convert it to activerecord's syntax but getting the error:
PG::SyntaxError: ERROR: syntax error at or near "'t'" LINE 1:
...ck_id" = "tracks"."id" WHERE (displays.display is 't', track...
Track.joins(:users, :displays).where('displays.display is ?, tracks.likes_count > ?, users.id = ?', true, 200, 1)
It seems like I'm doing something wrong with the displays.display is True statement, but I have tried a few variations to no avail.
I'm using Activerecord-4.2.0.
I would appreciate some insight.
By default ActiveRecord doesn't know how to join tracks and users, so indicate that you want to join through the displays:
Track.joins(:displays => :users).where('displays.display is true, tracks.likes_count > ?, users.id = ?', 200, 1)
Hope I remember syntax well :)
I think the problem is in the joins, you can try this:
Track.joins(displays: :users).where('displays.display = ?, tracks.likes_count > ?, users.id = ?', true, 200, 1)
You can make a join with a third table using the syntax:
Table1.joins(table2: :table3)
Result:
SELECT "table1".*
FROM "table1"
INNER JOIN "table2" ON "table2"."table_id" = "table1"."table1_id"
INNER JOIN "table3" ON "table3"."table2_id" = "table2"."table2_id"
able3" ON "table3"."table2_id" = "table2"."table2_id"
You can read more for ActiveRecord's joins method here: http://guides.rubyonrails.org/active_record_querying.html#joining-tables
Looks like I was actually just missing the ANDs....derp:
Track.joins(:users, :displays).where('displays.display = ? AND tracks.likes_count > ? AND users.id = ? ', true, 200, 1)
UPDATE Also as #Andrius pointed out Track.joins(:users, :displays) and Track.joins(:displays => :users) are two different joins and I want to use the former.

Laravel Eager Loading, filtering and limit column fields does not work

I want to filter my selection of columns in my channels hasmany relation as well filtering it using where method but to no avail. Any idea?
$data['users'] = Users::with(array('Channels' => function($q){
$q->where('is_default','=', 1)->select(array('channel_name', 'channel_id'));
}))->get(array('id'));
But this works perfectly fine.
$data['users'] = Users::with(array('Channels' => function($q){
$q->where('is_default','=', 1)
}))->get(array('id'));
I tried putting the select method first before the where method still does not work
EDIT:
These are SQL queries executed which is really what I wanted however it does not fill up the relations property.
select created_at, id, name, referrer from users where status = ? limit 10 offset 0
select channel_name, channel_id from channels where channels.user_id in (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) and is_default = ?
You may try this
$data['users'] = Users::with(array('Channels' => function($q){
$q->select('id, channel_name', 'user_id')->where('is_default','=', 1);
}))->get();
If you want to select columns in get() then make sure you select the associated key for channels table, for example, assuming that user_id is the foreign key in chennels table so you can do it using this:
$data['users'] = Users::with(array('Channels' => function($q){
$q->select('id, channel_name', 'user_id')->where('is_default','=', 1);
}))->get(array('id'));
If you don't select the primary key in the main query and foreign key in the sub query then with will not work. In both querys/select the related keys should be available.

How to write Order by expression in JPQL

PostgreSQL and MySQL offers to write expression into ORDER BY clause in SQL query. It allows to sort items by some column but the special values are on the top. The SQL looks like this one. ( works in Postgres )
select * from article order by id = 4, id desc;
Now I want to write it in the JPQL but it doesn't work. My attempt is:
#NamedQuery(name = "Article.special", query = "SELECT a FROM Article a ORDER BY ( a.id = :id ) DESC, a.id DESC")
This is JPA 1.0 with Hibernate driver. Application server throws this exception on deploy.
ERROR [SessionFactoryImpl] Error in named query: Article.special
org.hibernate.hql.ast.QuerySyntaxException: unexpected AST node: = near line 1, column 73 [SELECT a FROM cz.cvut.fel.sk.model.department.Article a ORDER BY ( a.id = :id ) DESC, a.id DESC]
at org.hibernate.hql.ast.QuerySyntaxException.convert(QuerySyntaxException.java:54)
Thanks a lot.
For a named query, (ORDER BY ( a.id = :id ) or ORDER BY (:id )) won't work as DSC/ASC can't be parametrized at run-time.
1) Dynamic way if ordering element varies at runtime.
String query = "SELECT a FROM Article a ORDER BY "+orderElement+" DESC, a.id DESC";
entityManager.createQuery(query).getResultList();
2) Static way in entity bean if ordering element is fixed.
Field level:
#OrderBy("id ASC")
List<Article> articles;
Method level:
#OrderBy("id DESC")
public List<Article> getArticles() {...};

Resources