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.
Related
Experienced a need of creating full text index for several columns without using query builder in Laravel 9.0 to make decision clearer, however all answers or tips were either for one/two columns or with query builder.
Tips and tutorials didn't work for me as I had 6 columns to create this index and that led to an error :
SQLSTATE[42000]: Syntax error or access violation: 1059 Identifier name 'table_column1,column2,column3,col...' is too long
So my code was:
class AddFulltextIndexes extends Migration
{
private array $fields = [
'column1',
'column2',
'column3',
'column4',
'column5',
'column6'
];
public function up()
{
Schema::table('invoice_cds', function (Blueprint $table) {
$table->fullText($this->fields);
});
}
}
For those who found themselves in similar situation there is an exit:
Laravel allows to create indexes passing the index name as a second parameter, so I changed one line and migration started to work:
$table->fullText($this->fields, 'table_full_test_indexes');
currently, in our project, we already have a composite of Specification to query in the table. And now, we add a new column which is Geography type.
then my problem is, are there any ways to use JPA specification way to composite the existing specification to query the geography column?
or whether the JPA specification query can be composited with SQL statement?
such like we have a JPA specification query as below:
public static <T> Specification<T> materialDateValuationBetween(String fromDate, String toDate) {
return (root, query, cb) -> {
if (StringUtils.isEmpty(fromDate) || StringUtils.isEmpty(toDate)) {
return cb.conjunction();
}
log.info("Search criteria for fromMaterialDate {}, toMaterialDate {}", fromDate, toDate);
List<Predicate> predicates = new ArrayList<>();
predicates.add(cb.between(root.get(CommonConstants.SpecificationType.MATERIAL_DATE_VALUATION).as(Date.class), DateUtil.parseDate(fromDate),
DateUtil.parseDate(toDate)));
return cb.or(predicates.toArray(new Predicate[predicates.size()]));
};
}
as you can see we have a "Specification" feature query, can we also involve SQL statement with this kind of Specification query?
really appreciate your opinion. thanks a lot!
I use yii2 to build one app which need to connect some tables. I can join them simply and search for data in related fields. I am doing it by adding the connection like this>
public function getNextTab()
{
return $this->hasOne(NextTab::className(),['id' =>'id_nexttab']);
}
and ask for the data in search model using like this ->
->where ('id'='ok') ->
->joinWith('nextTab')
->joinWith('nextTab.nextTab1')
->joinWith('nextTab.nextTab1.nextTab2');
My problem is when I try to do this with tables from different database. The query is give me error like
SQLSTATE[42S02]: Base table or view not found:
any tips how to pass it? or how to do other way of connection to have the data.
Joining tables from different databases may not be supported by your RDBMS (PostgreSQL for example). But if supported (MSSQL, MySQL) then table names should be prefixed with database name (and schema if needed). You can achieve this in Yii2 using {{%TableName}} syntax in tableName() function.
public static function tableName()
{
return '{{%table_name}}';
}
But be careful with joining tables from different databases if they are located on different servers -- this can be very slow.
If you just want to get related data (joined tables are not used in WHERE) then use with() instead of joinWith(). This will be executed as separate query with IN statement. In most cases this way has a better performance and no problems with different sources (and even different DBMS).
->with('nextTab', 'nextTab.nextTab1', 'nextTab.nextTab1.nextTab2')
Configure your second database component in the application's config.
Override the getDB() function in your ActiveRecord Model to return the second DB component.
This will attach your Model to the secondary DB and allow you to query from the table in secondary DB.
Config sample:
'components' => [
'db2' => [
'class' => 'yii\db\Connection',
'dsn' => 'mysql:host=localhost;dbname=db2',
'username' => 'hello',
'password' => 'world',
],
],
getDB() function override sample:
class Tab extends ActiveRecord
{
public static function getDb()
{
// use the "db2" component
return \Yii::$app->db2;
}
}
Good Luck!
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"));
i have 2 schema on postgresql egg: public,items. in public schema all CRUD ok but when i'm try to insert on items schema there is a problem.
the problem is, data inserted but the yii framework return error 'items.table1_id_seq doesn't exist'.
when i'm check to the database the squence is exist.
how to resolve this problem?
check the tableName function into protected/models/table.php
The table must be nameSchema.table
public function tableName()
{
return 'items.nameOfTable';
}