How to get the JPA generated SQL query? - jpa

I use JPA specification and Hibernate as my vendor. I need somehow to take the generated SQL Query which is sent to the the DB (printed to the sysout) and save it as a simple string.
Is there a way to do this?
EDIT
Let me make it a beat clearer: I don't need hibernate log. I need to be able to execute the same query on a different DB. Therefore, I need to get the SQL query as is, and hold it in a normal String variable.
Edit 2
Is there a util which I can provide it a bean and it will automatically generate an Insert query? can I somehow use Hibernate beans here? I know it's a beat complex.
Thanks,
Idob

Create a bean like this.
#Bean
public JpaVendorAdapter jpaVendorAdapter(){
HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter();
jpaVendorAdapter.setGenerateDdl(true);
jpaVendorAdapter.setShowSql(true);
return jpaVendorAdapter;
}
If you're using Spring Boot add it somewhere to your #Configuration.
The logs created from this are executable in MySQL workbench.
You stated that you are using JPA and Hibernate. There's no other way except if the database you support are supported by JPA. In that case there is an AbstractJpaVendorAdapter that you can implement.

The simple answer to your question is No. What you want to do is something that many developers would also like to do however it was not part of the JPA specification and thus the ability to get the generated SQL will depend upon what the vendor decided to do. Using Hibernate the only way to obtain the SQL is via the log.

You have to enable the log4j logging and add an appender for Hibernate to show the queries.
This has already been described here: How to print a query string with parameter values when using Hibernate

If I understand you correctly, you want to get the insert query which Hibernate is executed on one database, and via code, run it on a different database via entityManager#executeUpdate or similar.
Hibernate does not expose the generated query as it is specific for the dialect of target database. So even if were to get the insert query, it could be pointless.
However in your case, you can create two database connections (via two DataSource or EntityManagerFactory whatever in your case) and call dao.persist(entity) two times for both databases, and let Hibernate handle the query construction part.
Edit: By query I mean native query here, HQL query would be same for both databases.
Hope it helps.

I don't know what you mean by a generated query but if you use Hibernate and you have javax.persistence.Query query you can get HQL string very easy (for EclipseLink it is similar). And if you have HQL you can translete it to SQL with QueryTranslator.
// Get HQL
String hqlQueryString = query.unwrap(org.hibernate.query.Query.class).getQueryString();
// Translate HQL to SQL
ASTQueryTranslatorFactory queryTranslatorFactory = new ASTQueryTranslatorFactory();
SessionImplementor hibernateSession = em.unwrap(SessionImplementor.class);
QueryTranslator queryTranslator = queryTranslatorFactory.createQueryTranslator("", hqlQueryString, Collections.emptyMap(), hibernateSession.getFactory(), null);
queryTranslator.compile(Collections.emptyMap(), false);
String sqlQueryString = queryTranslator.getSQLString();

Try to add properties in instance of LocalContainerEntityManagerFactoryBean ,Its working for me :-
#EnableJpaRepositories(basePackages = "org.common.persistence.dao")
public class PersistenceJPAConfig {
#Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource());
em.setPackagesToScan(new String[] { "org.common.persistence.model" });
final HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
em.setJpaProperties(additionalProperties());
return em;
}
final Properties additionalProperties() {
final Properties hibernateProperties = new Properties();
hibernateProperties.setProperty("showSql", "true");
hibernateProperties.setProperty("hibernate.show_sql", "true");
hibernateProperties.setProperty("hibernate.format_sql", "true");
hibernateProperties.setProperty("hibernate.query.substitutions", "false");
return hibernateProperties;
}
}

Related

How to retrieve/set SQL query for an ItemReader from database?

I have spring batch program which reads the data from DB and process it and inserts(using ItemWriter) in to other table in the database. Here i am using a bunch of SQL queries for ItemReader,ItemProcessor and ItemWriter.
my requirement is store all these queries in a table with parameter and value format and retrieve it with a single DB call and pass it to ItemReader or ItemProcessor or ItemrWriter. So that if there is any change in the queries in future, we will end up in doing only DB updates and the code will be untouched.
I tried to do in beforeJob section but i am facing error saying "java.lang.IllegalArgumentException: The SQL query must be provided". But i can do this successfully by making an DB call inside the ItemReader method. Iam trying to avoid this way of approach because i need to make db call for each ItemReader,ItemProcessor and ItemWriter. Please let me know how to achieve this ?
You can create a step with a tasklet that reads the query from the database and adds it to the execution context under some key, then configure the reader of your chunk-oriented step with the query from the execution context. Here is a quick example:
1. Retrieve the query and put it in the execution context:
#Bean
public Tasklet queryRetrievalTasklet() {
return (contribution, chunkContext) -> {
String query = ""; // retrieve query from db (using a JdbcTemplate for example)
chunkContext.getStepContext().getJobExecutionContext().put("query", query);
return RepeatStatus.FINISHED;
};
}
2. Configure the reader with the query from the execution context
#Bean
#StepScope
public ItemReader<Integer> itemReader(#Value("#{jobExecutionContext['query']}") String query) {
// return your reader configured with the query
return null;
}
Hope this helps.
In my opinion, such configuration is usually done storing queries in properties not in database. Like :
batch.query.unload=SELECT ...

JPA CriteriaDelete with huge parameters

I am using JPA 2.1, Oracle DB and have a list of ids for entities to be removed (about 430000 ids). At first, it was implemented as splitting that id list into each smaller one with 1000 ids, pass them as parameters for a JPQL and executing.
delete from SOPFilter f where f.id in (?1)
Then, I want to change to use JPA CriteriaDelete.
CriteriaDelete<SOPFilter> criteriaDelete = cb.createCriteriaDelete(SOPFilter.class);
Root<SOPFilter> from = criteriaDelete.from(SOPFilter.class);
criteriaDelete.where(from.get(SOPFilter_.id).in(sopFilterIds));
It runs fine until it reach the 90000th one and there is a runtime exception cause it to stop here
org.hibernate.SessionException: Session is closed
and make entity manager factory to close.
INFO : bernate.impl.StmpContainerEntityManagerFactoryBean: Closing JPA EntityManagerFactory for persistence unit 'IMOFFERINGMANAGEMENT'
For whom was mislead by my first post with this exception
java.lang.IllegalStateException: EntityManagerFactory is closed
There was a catch clause to handle runtime exception by adding a record to database before throwing it. And to add a event record, it attempts to create another entity manger from the factory which is closed now.
public static void logEvent(EntityManager em) {
EntityManager em2 = null;
EntityManagerFactory emFactory = em.getEntityManagerFactory();
em2 = emFactory.createEntityManager();
// ...
}
Could anyone shed some light on it?
Im not clear on your code but you are likely hitting a transaction timeout. You can set a hint -
query.setHint("javax.persistence.query.timeout", 8000);
There may also be timeouts on the database side

Spring LDAP JPA - Limit number of result

I'm using spring-ldap 2.0.4 and spring-data-jpa 1.9.0.
I built a JPA repository like this :
public interface PersonRepo extends LdapRepository<Person> {
Person findByUid (String uid);
#Query("(&(attribute=*{0}*)(attribute2=X)(attribute3=Y))")
List<Person> findByAttributeContains(String attribute);
}
So far everything is fine. I could write methods that fill my needs thanks to query methods.
For some queries i had to use #Query annotation because they were many and operator.
But i would like to limit the number of result to return from my second query method.
I know there is there is the Top and First keywords to define query methods in spring JPA. But I didn't manage to get it work. Plus I want to use multiple and operator in the method.
Thanks
I managed to limit the number of result using XML LDAP configuration.
<ldap:ldap-template context-source-ref="contextSource" count-limit="100"/>

Use custom query for built-in entities in liferay 6.1?

I have read many posts about creating service layer in liferay. I was able to build a service layer and custom query sample project in eclipse. However, all of the examples I got so far is about adding new entities and performing CRUD actions on those new entities.
I want to ask is it possible to use custom query with built-in entities?. For example, I want to use custom query with JounalArticle, AssetEntry, and so on.
If that possible, how can I do that. Please give me some hints or examples.
(I have tried dynamic queries with sub queries but they do not meet my requirements)
If someone is interested:
In the XXXLocalServiceImpl you can add:
public List<JournalArticle> getArticles(String username) {
Session session = xxxPersistence.openSession();
try {
String sql = "SELECT * FROM journalarticle ja WHERE ja.userName like '%"+username+"%'";
SQLQuery query = session.createSQLQuery(sql);
return query.list();
}finally {
session.close();
}
}
rerun your service builder and you can use form XXXLocalServiceUtil.getArticles("Test Test")
Using service builder: create database portlets

Spring Data JPA finder for dynamic fields as Map

My requirement is to have few custom fields in the domain objects. These fields may vary as per the clients.
We are using Spring Data JPA to execute finders. Spring data implicitly provides finders for static fields of the domain and can also handle finders for the fields in the object graph.
I want to know if there is a way to find data on the custom fields? Can someone suggest me a strategy to achieve the same. Below is the sample of my domain class.
public class Employee{
private String name;
private String age;
private Map customeFields; (May vary as per client)
}
I was thinking of overriding QueryLookupStrategy and create my CustomJpaQuery on lines of PartTreeJpaQuery to achieve it. Is there any better approach? Does spring data jpa provides an easy mechanism to override query creation mechanism?
If you are using hibernate (not sure about other JPA implementations) you may add methods with #Query annotations like this:
#Query("select e from Employee as e where e.customeFields[:key] = :value")
List<Employee> findSomeHow(#Param("key") String key, #Param("value") String value)