How to use group_concat in hibernate criteria? - sql

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"));

Related

Jpa createSQLQuery returns List<Object> instead of List<Employee>

Trying to make an sql query to get as a result a list of Class "EmployeeCardOrderLink". But this code always returns me an list of Object. Casts doesn't working. I got the right data in this list, but it's just object. In debug i can call methods(Idea suggest according interface of my class), but then i got "class Object doesn't have a such method". And i can't use TypedQuery cause i have old JPA version, it doesn't support this.
#Repository
public class EmployeeCardOrderLinkDAOImpl extends AbstractBasicDAO<EmployeeCardOrderLink> implements EmployeeCardOrderLinkDAO {
//....
#Override
public List<EmployeeCardOrderLink> getLinksByOrderNumber(Integer num) {
List<EmployeeCardOrderLink> result = (ArrayList<EmployeeCardOrderLink>) getSessionFactory().getCurrentSession().createSQLQuery("select * from employee_card_order_links " +
"where trip_order_id = " + num).list();
return result;
}}
You use Hibernate (not JPA), if you are using Session. Hibernate is JPA provider of course. You have to use EntityManager and other related things to use JPA.
You don't need SQL here. SQL always returns list of objects (if you don't use transformers to DTO objects).
Just use HQL (JPQL in JPA)
To get all EmployeeCardOrderLink
getSessionFactory().getCurrentSession()
.createQuery("select link from EmployeeCardOrderLink link").list();
Query "from EmployeeCardOrderLink" will work for Hibernate too (for JPA will not work).

Stored procedures in HQL

How can I write stored procedure or function in HQL? Is it possible? I haven't found any information about it.
The problem is that my app works with several Databases(Oracle, MSSQL, PostgreSQL) and I need to count Levenshtein distance in my query. Can I do it without writing 3 native SQL functions and queries for each database?
You can try to encapsulate discrepancy between Levenshtein function names in different databases in hibernate dialect. Below I will provide an example for Oracle and PostgreSQL. (I did not work with MSSQL)
The extended Oracle dialect:
public class MyOracleDialect extends Oracle12cDialect
{
public MyOracleDialect()
{
super();
registerFunction( "levenshtein", new SQLFunctionTemplate( StandardBasicTypes.INTEGER, "UTL_MATCH.EDIT_DISTANCE(?1,?2)" ) );
}
}
The extended PostgreSQL dialect:
public class MyPostgreSQLDialect extends PostgreSQL95Dialect
{
public MyPostgreSQLDialect()
{
super();
registerFunction( "levenshtein", new SQLFunctionTemplate(StandardBasicTypes.INTEGER, "levenshtein(?1, ?2)"));
}
}
And now you can use the levenshtein function in your HQL.
List<Integer> result = session.createQuery(
"select levenshtein(word1, word2) from TestEntity",
Integer.class
).getResultList();
P.S. I have faced with the following problem for PostgreSQL:
If the extension fuzzystrmatch was installed for the particular schema TEST_SCHEMA:
SQL> create extension fuzzystrmatch;
then you should specify this schema in the connection url:
<property name="hibernate.connection.url">jdbc:postgresql://localhost:5432/postgres?currentSchema=TEST_SCHEMA</property>
otherwise you will get an exception:
org.postgresql.util.PSQLException: ERROR: function levenshtein(character varying, character varying) does not exist. No function matches the given name and argument types. You might need to add explicit type casts.

Jpa custom query with date nearest to present filter

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

Spring data rest - expose default methods

I have a Person Repository as follows
#RepositoryRestResource
public interface PersonRepository extends Repository<Person, String> {
List<Person> findAll();
default List<Person> findNewPersons() {
return findByStartDateAfter(LocalDate.now().minusMonths(3));
}
List<Person> findByStartDateAfter(LocalDate date);
}
I am not able to expose the default method through rest.. is there a way to do it without creating an implementation of the repo ?
I faced a similar problem, and was able to solve it using a SpEL expression inside an HQL query in a #Query annotation.
While nowhere near as clean as using a default method, this was the tidiest way I could find without writing a custom controller or introducing a custom implementation with a new DSL library or something for just this one query.
#Query("select p from Person p where p.startDate > :#{#T(java.time.LocalDate).now().minusMonths(3)}")
List<Person> findNewPersons();
My actual query was different so I might have typoed the syntax here, but the idea is the same and it worked for my case (I was using a LocalDate parameter and finding timestamps on that day by using a findByTimestampBetween style query).

Grails sql queries

Imagine I have something like this:
def example = {
def temp = ConferenceUser.findAllByUser(User.get(session.user))
[temp: temp]
}
Explaining my problem:
Although dynamic finders are very easy to use and fast to learn, I must replace dynamic finders of my website for sql queries because it is a requirement. As I don't understand SQL that much, my main questions are:
a) I am using an SQLS database, with the drivers and datasource good configured and my website works as it is right now. If I want to replace the "findAllByUser" for an sql statement, should i do something like this:
def dataSource
...
def db = new Sql(dataSource)
def temp = db.rows("SELECT ... ")
b) And that will work? I mean, the temp object will be a list as it is if I use "findAllByUser", and do I need to open a connection to the database =?
With Grails you can use Dynamic Finders, Criteria Builders, Hibernate Query Language (HQL), or Groovy SQL.
To use Groovy SQL:
import groovy.sql.Sql
Request a reference to the datasource with def dataSource or def sessionFactory for transactions
Create an Sql object using def sql = new Sql(dataSource) or def sql = new Sql(sessionFactory.currentSession.connection())
Use Groovy SQL as required
Grails will manage the connection to the datasource automatically.
Sql.rows returns a list that can be passed to your view.
For example:
import groovy.sql.Sql
class MyController {
def dataSource
def example = {
def sql = new Sql(dataSource)
[ temp: sql.rows("SELECT . . .") ]
}
}
And within a transaction:
import groovy.sql.Sql
class MyController {
def sessionFactory
def example = {
def sql = new Sql(sessionFactory.currentSession.connection())
[ temp: sql.rows("SELECT . . .") ]
}
}
I recommend the book Grails Persistence with GORM and GSQL for a lot of great tips and techniques.
yes, with grails you can do both plain sql and hql queries. HQL is 'hibernate query language' and allows you to write sql-like statements, but use your domain classes and properties instead of the table names and column names. To do an hql query, do something like
def UserList = ConferenceUser.executeQuery('from ConferenceUser cu where cu.user = ?', [user]),
what you have here is a parameterized query -- executeQuery sees the ? in the hql string and substitutes the arguments in the array that is the second parameter to the method([user] in this case) for you.
See
http://grails.org/doc/latest/ref/Domain%20Classes/executeQuery.html
and you can see this on how to do sql queries with Grails
Sql query for insert in grails
Going Further / Tips
Use Spring beans
You can make the groovy.sql.Sql instance a Spring bean in your Grails application. In grails-app/conf/spring/resources.groovy define the Sql bean:
// File: grails-app/conf/spring/resources.groovy
beans = {
// Create Spring bean for Groovy SQL.
// groovySql is the name of the bean and can be used
// for injection.
sql(groovy.sql.Sql, ref('dataSource'))
}
Next inject the Sql instance in your your class.
package com.example
import groovy.sql.GroovyRowResult
class CarService {
// Reference to sql defined in resources.groovy.
def sql
List<GroovyRowResult> allCars(final String searchQuery) {
final String searchString = "%${searchQuery.toUpperCase()}%"
final String query = '''\
select id, make, model
from car
where ...
'''
// Use groovySql bean to execute the query.
final results = sql.rows(query, search: searchString)
results
}
}
Multiple Datasources
adminSql(groovy.sql.Sql, ref("dataSource_admin"))
userSql(groovy.sql.Sql, ref("dataSource_user"))
and inject the beans
def userSql
def adminSql
Into the services that need them.
or without injection
import groovy.sql.Sql
// ...
// inject the datasource bean
def dataSource_admin
// ...
// in a method
Sql sql = new Sql(dataSource_admin)
Early Grails Version
Looping through GORM result sets in early grails versions can cause needless queries in the middle of template loops. Using groovy SQL can help with this.