Jpa custom query with date nearest to present filter - sql

I have SpringBoot webapp using JPA and I have a model class like this:
#Entity
class Server {
.....
private Date updateDate;
}
now I would like to create a custom query inside my repository to get the Server entity with the attribute updateDate nearest to present Date in Oracle 11g database.
Right now I found just a few example, like this for SQLServer:
SELECT TOP 1 *
FROM x
WHERE x.date < #CurrentDate
ORDER BY x.date DESC
I would like something similar to build a custom query with Jpa in Oracle 11g DB.
Thank you all

As you are using Spring Data JPA you can create a repository method:
Server findFirstByUpdateDateLessThan(Date currentDate);
You have to pass the currentDate as parameter because there is no way to use current_date in a repository method.
If you want to use a query that would be possible.
You could also use plain JPA:
List<Server> list = entityManager
.createQuery("select s from Server s where s.updateDate < current_date", Server.class)
.setMaxResults(1)
.getResultList();
If you are sure that you will get one result you could also call getSingleResult()

no, maybe I expressed myself badly. In any case, I want a method, using Jpa, that gives me back the entity whose date is more recent.
The solution is:
public interface ZabbixInstanceRepository extends JpaRepository<ZabbixInstance,String> {
#Query("select z from ZabbixInstance z where z.refreshDate = (select max(z.refreshDate) from ZabbixInstance z)")
ZabbixInstance findByClosestDate();
}
It works perfectly. I hope it helps others.
Thanks anyway

Related

Pageable usage with Query Annotation

Can I use Pageable attribute in Spring Data R2dbc repositories with #Query annotation? For example;
public interface PartyRepository extends ReactiveCrudRepository<Party,String> {
#Query("select * from party order by id")
Flux<Party> getParties(Pageable pageable);
}
It gives "org.springframework.data.repository.query.ParameterOutOfBoundsException : Invalid parameter index! You seem to have declared too little query method parameteres!"
Is there any way to use pagination in spring Data R2dbc repositories?
Thanks.
This is not supported by R2DBC and probably never will.
But you can adjust the query string with SpEL expressions manually to include paging. For MySQL, in your example, this could look like this:
#Query("SELECT * FROM party ORDER BY id LIMIT :#{[0].offset},:#{[0].pageSize}")
Flux<Party> getParties(Pageable pageable);
The [0] part indicates which argument in your argument list is the Pageable.

Compare two database fields in extbase repository

I am using TYPO3 8. In my extension I have a database table "company" in which I store for each company the total number of places (number_places) and the number of occupied places (occupied_places).
Now I want to limit the search to companies which have available places left.
In MySQL it would be like this:
SELECT * FROM company WHERE number_places > occupied_places;
How can I create this query in the extbase repository?
I tried to introduce the virtual property placesLeft in my model but it did not work.
I don't want to use a raw SQL statement as mentioned below, because I already have implemented a filter which uses plenty of different constraints.
Extbase query to compare two fields in same table
You can do it like this in your repository class, please note the comments inside the code:
class CompanyRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
{
public function findWithAvailablePlaces(bool $returnRawQueryResult = false)
{
// Create a QueryBuilder instance
$queryBuilder = $this->objectManager->get(\TYPO3\CMS\Core\Database\ConnectionPool::class)
->getConnectionForTable('company')->createQueryBuilder();
// Create the query
$queryBuilder
->select('*')
->from('company')
->where(
// Note: this string concatenation is needed, because TYPO3's
// QueryBuilder always escapes the value in the ExpressionBuilder's
// methods (eq(), lt(), gt(), ...) and thus render it impossible to
// compare against an identifier.
$queryBuilder->quoteIdentifier('number_places')
. \TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder::GT
. $queryBuilder->quoteIdentifier('occupied_places')
);
// Execute the query
$result = $queryBuilder->execute()->fetchAll();
// Note: this switch is not needed in fact. I just put it here, if you
// like to get the Company model objects instead of an array.
if ($returnRawQueryResult) {
$dataMapper = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\Mapper\DataMapper::class);
return $dataMapper->map($this->objectType, $result);
}
return $result;
}
}
Notes:
If you have lots of records to deal with, I would - for performance reasons - not use the data mapping feature and work with arrays.
If you want to use the fluid pagination widget, be sure you don't and build your own pagination. Because of the way this works (extbase-internally), you'd get a huge system load overhead when the table grows. Better add the support for limited db queries to the repository method, for example:
class CompanyRepository extends \TYPO3\CMS\Extbase\Persistence\Repository
{
public function findWithAvailablePlaces(
int $limit = 10,
int $offset = 0,
bool $returnRawQueryResult = false
) {
// ...
$queryBuilder
->setMaxResults($limit)
->setFirstResult($offset);
$result = $queryBuilder->execute()->fetchAll();
// ...
}
}
I think you cant do this using the default Extbase Query methods like equals() and so on. You may use the function $query->statement() for your specific queries like this.
You also can use the QueryBuilder since TYPO3 8 which has functions to compare fields to each other:
https://docs.typo3.org/typo3cms/CoreApiReference/latest/ApiOverview/Database/QueryBuilder/Index.html#quoteidentifier-and-quoteidentifiers
It's fine to use this QueryBuilder inside Extbase repositories. After this you can use the DataMapper to map the query results to Extbase models.
In case of using "statement()" be aware of escaping every value which may cause any kind of SQL injections.
Based on the current architecture of TYPO3, the data structure is such that comparing of two tables or, mixing results from two tables ought to be done from within the controller, by injecting the two repositories. Optionally, you can construct a Domain Service that can work on the data from the two repositories from within the action itself, in the case of a routine. The service will also have to be injected.
Note:
If you have a foreign relation defined in your table configuration, the results of that foreign relation will show in your defined table repository. So, there's that too.

How to use group_concat in hibernate criteria?

I wrote a query in mysql using group_concat like
SELECT c1,group_concat(c2) FROM table1 where sno in(1,4,8,10) group by c1;
and gives my expected result.
Now the same query I want to write using hibernate criteria.
You have two options (depending on your hibernate version).
Override the dialect class
any hibernate version
You will need to subclass your dialect to add group_concat()
Introduce the dialect override class
Create the following class somewhere in your app (e.g. util package)
package com.myapp.util;
import org.hibernate.dialect.MySQL5Dialect;
import org.hibernate.dialect.function.StandardSQLFunction;
import org.hibernate.type.StandardBasicTypes;
public class MySQLCustomDialect extends MySQL5Dialect {
public MySQLCustomDialect() {
super();
registerFunction("group_concat",
new StandardSQLFunction("group_concat",
StandardBasicTypes.STRING));
}
}
Map the dialect override class to boot properties
Add the following property to your application.properities
spring.jpa.properties.hibernate.dialect = com.myapp.util.MySQLCustomDialect
Use JPA Metadata Builder Contributor
hibernate 5.2.18 or newer only
Introduce metadata builder class
Create the following class, remember to add package & resolve imports.
public class SqlFunctions implements MetadataBuilderContributor {
#Override
public void contribute(MetadataBuilder metadataBuilder) {
metadataBuilder.applySqlFunction( "group_concat",
new StandardSQLFunction( "group_concat",
StandardBasicTypes.STRING ) ); }
}
Map new class in application boot properties
Leave the dialect properties as is
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
spring.jpa.properties.hibernate.metadata_builder_contributor = com.myapp.util.SqlFunctions
Simple answer is No
Why?
Hibernate support only common function/syntax used in multiple database. There ain't any group_concat function in Microsoft SQL Server and may be in other database as well.
Solution:
You have to execute it as Simple SQL Query.
Finally i go through like below code and got expected result
String query="select c1,group_concat(c2) from table1 where sno in (:pageIds) group by c1";
SQLQuery sqlQuery= session.createSQLQuery(query);
sqlQuery.setParameterList("pageIds", myList);
List list= sqlQuery.list();
c1 group_concat(c2)
aaa valu1,value2
bbb value3
ccc value4,value5,value6
Please refer following code snippets
Criteria cr = session.createCriteria(table1.class);
cr.add(Restrictions.in("sno",snoarray));
criteria.setProjection("c1");
criteria.setProjection(Projections.groupProperty("c1"));

Grails query LEFT JOIN on table with no domain relations

I'm new to Grails and I'm stuck with this problem for hours. Thanks in advance for you help!
Here is my question:
I have a database with two tables
PROJECT
LIKES
As you can guess a user has the right to Like a Project.
In the Domain Class Project there is NO relations (belongsTO, hasOne, etc..)
Same in the Domain Class Likes
In the database the table LIKES has a field project_id but it is not set as a foreign_key. It is this made this way for right purpose.
Now I need to execute a native SQL query with grails which is really simple and returns the result expected.
The result is all projects that have Likes or not
Here is the query :
SELECT project.name, Likes.likes
FROM project
LEFT JOIN Likes
ON project.id = likes.project_id;
I cant find a way to convert this SQL query to HQL.
It seems that HQL works on Domain instance and the fact that there is no relation between the domains return an error like "the Domain Project has no Likes attribute" which is correct.
Is there a way to get the right result with one query or do I need to do two query and build an Array with the result programmatically ?
Thanks for you help
If your domain classes don't have relationships you cannot do it using HQL, as you noticed.
But in Grails you can access the database directly, using groovy.sql.Sql. Example of service:
class MyService {
def dataSource
void addNewRecord(String data) {
groovy.sql.Sql sql = new Sql(dataSource)
sql.execute("insert into my_table(my_anydata_clomn) values(sys.anyData.convertVarchar2(?))",[data])
}
}
Ok it works like this :)
def dataSource
def getProjectList() {
groovy.sql.Sql sql = new groovy.sql.Sql(dataSource)
log.info(sql)
log.info("datasource " + dataSource)
def t = sql.rows("SELECT * \n" +
"FROM project\n" +
"LEFT JOIN Likes\n" +
"ON project.id=likes.project_id\n" +
"ORDER BY likes.likes")
sql.close()
return t;
}

Is it possible to make `#SQLDelete` take the `hibernate.default_schema` parameter into account?

In a webapp, I use Hibernate's #SQLDelete annotation in order to "soft-delete" entities (i.e. set a status column to a value that denotes their "deleted" status instead of actually deleting them from the table).
The entity code looks like this :
#Entity
#SQLDelete(sql="update pizza set status = 2 where id = ?")
public class Pizza { ... }
Now, my problem is that the web application doesn't use the owner of the schema to which the tables belong to connect to the DB. E.g. the schema (in Oracle) is called pizza, and the db user the webapp uses to connect is pizza_webapp. This is for security reasons. The pizza_webapp user only has select/update/delete rights, it can't modify the structure of the DB itself. I don't have any choice here, it is a policy that I can't change.
I specify the name of the schema where the tables actually are with the hibernate-default_schema parameter in hibernate config :
<property name="hibernate.default_schema">pizza</property>
This works fine for everything that goes through mapped entities, Hibernate knows how to add the schema name in front of the table name in the SQL it generates. But not for raw SQL, and the #SQLDelete contains raw SQL. This is executed 'as is' and results in a "table or view not found error".
So far we worked around the issue by adding synonyms to the pizza_webapp schema, pointing to the pizza schema. It works, but it is not fun to maintain across multiple DBs when entities are added.
So, is it possible to make #SQLDelete take the hibernate.default_schema parameter into account ?
(NB: Obviously I don't want to hard-code the schema name in the SQL either...)
Yes, it is possible:
#SQLDelete(sql="update {h-schema}pizza set status = 2 where id = ?")
I could not find any Hibernate solution to this problem. However I found a work-around based on an Oracle feature. I do this in to my session before using it :
//set the default schema at DB session level for raw SQL queries (see #SQLDelete)
HibernateUtil.currentSession().doWork(new Work() {
#Override
public void execute(Connection connection) throws SQLException {
connection.createStatement().execute("ALTER SESSION SET CURRENT_SCHEMA="+HibernateUtil.getDefaultSchema());
}
});
I works fine, but unfortunately only on Oracle (which is fine for us for now at least). Maybe there are different ways to achieve the same thing on other RDBMS as well ?
Edit: the the getDefaultSchema() method in my HibernateUtil class does this to get the default schema from Hibernate's config :
defaultSchema = config.getProperty("hibernate.default_schema");
where config is my org.hibernate.cfg.Configuration object.