How can you call custom database functions with Hibernate? - java

If I were to define some function in the database (perhaps Postgres, or any other database):
create or replace function isValidCookie(ckie);
I would call it from SQL as:
select * from cookietable c where isValidCookie(c.cookie);
How can I call a custom function such as this from Hibernate?

If you want to use your custom function in HQL, you'll need to define it in appropriate Dialect
Take a look at PostgreSQLDialect (or any other, really) source, and you'll see a bunch of registerFunction() calls. You'll need to add one more :-) - for your own custom function.
You'll then have to specify your own dialect in Hibernate configuration.

As of Hibernate 5, if you don't want to depend on or customize the dialect, you can define a MetadataBuilderInitializer. For example, to use MySQL DATE_ADD with an INTERVAL from HQL, you can define a custom function called date_add_interval:
public class DateAddIntervalMetadataBuilderInitializer
implements MetadataBuilderInitializer {
#Override
public void contribute(MetadataBuilder metadataBuilder,
StandardServiceRegistry serviceRegistry) {
metadataBuilder.applySqlFunction("date_add_interval",
new SQLFunctionTemplate(DateType.INSTANCE,
"DATE_ADD(?1, INTERVAL ?2 ?3)"));
}
}
You would also need to put the name of the class in a JAR resource file called META-INF/services/org.hibernate.boot.spi.MetadataBuilderInitializer.
This approach is particularly useful when using Hibernate via a framework such as JPA and/or Spring, where the configuration is performed implicitly by the framework.

Related

How to get the JOOQ query text using AspectJ without changing the query method itself?

There is a project using JOOQ.
Example of the method:
public List<A> getA(List<String> C) {
return context
.select(...)
.from(...)
.join(...).on(...)
.where(...)
.fetch().map(...);
}
You need to use AspectJ to get the request text.
Everything is OK with HPQL and JDBC, according to the first - we work with the annotation, according to the second - we intercept the parameter with the query from the public JDBC class.
Example point for JDBC:
#Pointcut("target(org.springframework.jdbc.core.JdbcTemplate) && " +
"args(String, ..) && (call(* query(..)) || call(* update(..)) || call(* batchUpdate(..)))")
public void executeMetricsRepository() { }
But what can you do here? It is with the application of the aspect, because rewriting each request is not considered.
There are two parts here - fetch() and map(). But interested in - fetch.
fetch ---> interface org.jooq.ResultQuery
Inherited classes:
AbstractResultQuery and SelectImpl. But it is package-private.
Alternative approach without AspectJ
I'm not sure if you have to use this annotation based approach, but it is very easy to extract the SQL query for every jOOQ query in your application by adding an ExecuteListener to your jOOQ Configuration, e.g.
configuration.set(ExecuteListener.onRenderEnd(ctx -> {
// Or, do whatever.
System.out.println(ctx.sql());
}));
Possibility to use AspectJ
The jOOQ Query type has a Query.getSQL() method which can be used to extract the generated SQL for any Query. That's a bit less efficient than the above approach, because it will re-generate the SQL string a second time, but that might be irrelevant to your application?

Custom SQL for Order in JPA Criteria API

I'm switching from deprecated (unfortunately) Hibernate Criteria API to JPA Criteria API. We have a custom Order (from Hibernate) interface implementation to redefine SQL generated for it. The case is quite sophisticated as we need to use a giant SELECT with subqueries. We implemented toSqlString method of the interface to return this huge SQL and we need a way to migrate it to JPA Criteria API.
The question is: is there a way in JPA Criteria API to redefine the SQL generated? Or is there a weird way to use Hibernate Order with JPA Criteria API?
Thank you!
UPDATE Although #Tobias Liefke suggestion is quite interesting, my SQL varies too much to create a function class per SQL. I tried implementing a single function class and passing the SQL there as an argument but that didn't work (the rendered SQL was enclosed in single quotes thus it was sent to the database as parameter and not as part of the generated query)
You can't use SQL fragments in JPQL or criteria queries...
... except when ...
1. Calling a function
JPA and Hibernate allow to use functions in their expressions, for example:
... ORDER BY trim(entity.label) ASC
Resp.
query.orderBy(criteriaBuilder.asc(
criteriaBuilder.function("trim", String.class, root.get(ExampleEntity_.label))));
The problem is, that this is not really the call to the SQL function trim, but the call to a JPA function, which must be registered (Hibernate does this already for the most common SQL functions).
Fortunately you can define your own JPA functions in a DialectResolver:
public class MyDialectResolver implements DialectResolver {
public Dialect resolveDialect(final DialectResolutionInfo info) {
Dialect dialect = StandardDialectResolver.INSTANCE.resolve(info);
dialect.registerFunction("myOrderFunction", ...);
return dialect;
}
}
registerFunction takes two parameters, the first is the name of the function in JPA, the other is the mapping to SQL.
Don't forget to declare your dialect resolver in your persistence.xml:
<persistence-unit name="database">
<properties>
<property name="hibernate.dialect_resolvers"
value="my.package.MyDialectResolver" />
</properties>
</persistence-unit>
You could now create your own function in your SQL server which contains your huge SQL and register that as function:
dialect.registerFunction("myOrderFunction",
new StandardSQLFunction("myOrderFunctionInSQL", StringType.INSTANCE));
Or you could write your own mapping, which includes your huge SQL:
public class MyOrderFunction implements SQLFunction {
public String render((Type firstArgumentType, List arguments,
SessionFactoryImplementor factory) throws QueryException) {
return my_huge_SQL;
}
// ...
}
And register that one:
dialect.registerFunction("myOrderFunction", new MyOrderFunction());
Another advantage of this solution: you could define different SQLs depending on the actual database dialect.
2. Using a formula
You could use an additional attribute for your entity:
#Formula("my huge SQL")
private String orderAttribute;
You could now sort by this attribute:
... ORDER BY entity.orderAttribute ASC
Resp.
query.orderBy(criteriaBuilder.asc(root.get(ExampleEntity_.orderAttribute))));
I only recommend this solution, if you need the result of the huge SQL in your model anyway. Otherwise it will only pollute your entity model and add the SQL to every query of your entity (except you mark it with #Basic(fetch = FetchType.lazy) and use byte code instrumentation).
A similar solution would be to define a #Subselect entity with the huge SQL - with the same drawbacks.

How to make dynamic queries at run-time in Spring Boot and Data?

I am new to Java and started with Spring Boot and Spring Data JPA, so I know 2 ways on how to fetch data:
by Repository layer, with Literal method naming: FindOneByCity(String city);
by custom repo, with #Query annotation: #Query('select * from table where city like ?');
Both ways are statical designed.
How should I do to get data of a query that I have to build at run time?
What I am trying to achieve is the possibility to create dynamic reports without touching the code. A table would have records of reports with names and SQl queries with default parameters like begin_date, end_date etc, but with a variety of bodies. Example:
"Sales report by payment method" | select * from sales where met_pay = %pay_method% and date is between %begin_date% and %end_date%;
The Criteria API is mainly designed for that.
It provides an alternative way to define JPA queries.
With it you could build dynamic queries according to data provided at runtime.
To use it, you will need to create a custom repository implementation ant not only an interface.
You will indeed need to inject an EntityManager to create needed objects to create and execute the CriteriaQuery.
You will of course have to write boiler plate code to build the query and execute it.
This section explains how to create a custom repository with Spring Boot.
About your edit :
What I am trying to achieve is the possibility to create dynamic
reports without touching the code. A table would have records of
reports with names and SQl queries with default parameters like
begin_date, end_date etc, but with a variety of bodies.
If the queries are written at the hand in a plain text file, Criteria will not be the best choice as JPQL/SQL query and Criteria query are really not written in the same way.
In the Java code, mapping the JPQL/SQL queries defined in a plain text file to a Map<String, String> structure would be more adapted.
But I have some doubts on the feasibility of what you want to do.
Queries may have specific parameters, for some cases, you would not other choice than modifying the code. Specificities in parameters will do query maintainability very hard and error prone. Personally, I would implement the need by allowing the client to select for each field if a condition should be applied.
Then from the implementation side, I would use this user information to build my CriteriaQuery.
And there Criteria will do an excellent job : less code duplication, more adaptability for the query building and in addition more type-checks at compile type.
Spring-data repositories use EntityManager beneath. Repository classes are just another layer for the user not to worry about the details. But if a user wants to get his hands dirty, then of course spring wouldn't mind.
That is when you can use EntityManager directly.
Let us assume you have a Repository Class like AbcRepository
interface AbcRepository extends JpaRepository<Abc, String> {
}
You can create a custom repository like
interface CustomizedAbcRepository {
void someCustomMethod(User user);
}
The implementation class looks like
class CustomizedAbcRepositoryImpl implements CustomizedAbcRepository {
#Autowired
EntityManager entityManager;
public void someCustomMethod(User user) {
// You can build your custom query using Criteria or Criteria Builder
// and then use that in entityManager methods
}
}
Just a word of caution, the naming of the Customized interface and Customized implementating class is very important
In last versions of Spring Data was added ability to use JPA Criteria API. For more information see blog post https://jverhoelen.github.io/spring-data-queries-jpa-criteria-api/ .

How do I configure JPA table name at runtime?

I have an issue where I have only one database to use but I have multiple servers where I want them to use a different table name for each server.
Right now my class is configured as:
#Entity
#Table(name="loader_queue")
class LoaderQueue
I want to be able to have dev1 server point to loader_queue_dev1 table, and dev2 server point to loader_queue_dev2 table for instance.
Is there a way i can do this with or without using annotations?
I want to be able to have one single build and then at runtime use something like a system property to change that table name.
For Hibernate 4.x, you can use a custom naming strategy that generates the table name dynamically at runtime. The server name could be provided by a system property and so your strategy could look like this:
public class ServerAwareNamingStrategy extends ImprovedNamingStrategy {
#Override
public String classToTableName(String className) {
String tableName = super.classToTableName(className);
return resolveServer(tableName);
}
private String resolveServer(String tableName) {
StringBuilder tableNameBuilder = new StringBuilder();
tableNameBuilder.append(tableName);
tableNameBuilder.append("_");
tableNameBuilder.append(System.getProperty("SERVER_NAME"));
return tableNameBuilder.toString();
}
}
And supply the naming strategy as a Hibernate configuration property:
<property
name="hibernate.ejb.naming_strategy"
value="my.package.ServerAwareNamingStrategy"
/>
I would not do this. It is very much against the grain of JPA and very likely to cause problems down the road. I'd rather add a layer of views to the tables providing unified names to be used by your application.
But you asked, so have some ideas how it might work:
You might be able to create the mapping for your classes, completely by code. This is likely to be tedious, but gives you full flexibility.
You can implement a NamingStrategy which translates your class name to table names, and depends on the instance it is running on.
You can change your code during the build process to build two (or more) artefacts from one source.

Finding associated mybatis query

To initiate a query I use :
org.apache.ibatis.session.SqlSession.seleteList("myquery");
myquery itself is defined within an XML configuration file.
With Spring JDBC as the query statements is defined with the class itself it is very easy find the query associated with a given method call. But with mybatis once I reach the query call I have to search for the argument (in this case "myquery") to discover where the actual query is defined.
Is there an easier method of finding a query associated with a method call instead of manually searching for references ? I'm thinking there isn't as since the query itself is within an XML file not a .java file and IDE maintains a "linking" of methods, constants etc between the files.
There are two ways to do that but you will need to use mapper interfaces.
The first approach is to use IDE plugin. There are several plugins for IntelliJ and some for eclipse. Plugin for IntelliJ says it has
Proxy interfaces support, "Go to Implementaion" jumps right into
mapper xml
Another way is to define queries using annotations. In this case queries will be directly in java file.
Define query in mapper interface
interface MyMapper {
#Select("Select * from myentity where id = #id")
MyEntity selectMyEntity(#Param("id") Long id);
}
And then use mapper like usually:
MyMapper mapper = session.getMapper(MyMapper.class);
MyEntity myEntity = mapper.selectMyEntity(101);

Categories