Using native queries but maintaining database independence - java

With Spring JPA is there an easy way to use native queries but maintaining database independence, for example by using the query which fits best?
At the moment I do this by checking the currently set Dialect from the Environment and call the proper method of my Repository:
public Foo fetchFoo() {
if (POSTGRES_DIALECT.equals(env.getRequiredProperty("hibernate.dialect"))) {
return repo.postgresOptimizedGetFoo();
}
return repo.getFoo();
}
This works but I have the feeling that there is a better way or that I am missing something. Especially because (Spring) JPA allows it to use native queries quite easily but that breaks one of its big advantages: database independence.

As per my understanding, this can be achieved simply by using #Transactional(readOnly=false) and then instead of calling session.createQuery, one can use session.createSQLQuery, as provided in this example.
Your sql can be any of your native query.
Hope this works for you. :)
#Override
#Transactional(readOnly = false)
public Long getSeqVal() {
Session session = entityManager.unwrap(Session.class);
String sql = "SELECT nextval('seqName')";
Query query = session.createSQLQuery(sql);
BigInteger big = (BigInteger) query.list().get(0);
return big.longValue();
}

This is just an idea: I do not know whether it works or not:
My idea would be having subinterfaces, one normal Spring-Data-JPA-interface with all methods for one entiy (without native query hints). Than I would crate a subinterface for every database, that "override" the database specific native statements. (This intrface would be empty if there are no DB specific statements). Then I would try configure Spring-JPA with some profiles to load the right specific interface (for example by a class-name or package-name-pattern)

This seems like a way to complicated way to get queries to work.
If you really want to use optimized queries make it at least transparant for your code. I suggest using named queries and create an orm.xml per database (much like Spring Boot uses to load the schema.xml for a different database).
In your code you can simply do
public interface YourRepository extends JpaRepository<YourEntity, Long> {
List<YourEntity> yourQueryMethod();
}
This will look for a named query with the name YourEntity.yourQueryMethod. Now in your orm.xml add the named query (the default one and in another one the optimized one).
Then you need to configure your LocalContainerEntityManagerFactory to load the specific one needed. Assuming you have a property defining which database you use, lets name it database.type you could do something like the following
<bean class="LocalContainerEntityManagerFactoryBean">
<property name="mappingResources" value="classpath:META-INF/orm-${database.type}.xml" />
... other config ...
</bean>
This way you can keep your code clean of the if/then/else construct and apply where needed. Cleans your code nicely imho.

Related

Capture generated SQL from Hibernate

I have a requirement to capture changes to certain data. I am looking to capture the following details:
Table Name the change occurred in
Column Changed
Previous Value
Updated Value
I suggested to our Technical lead that this can be accomplished easily with DB Triggers. I was told we do not control the DB and do not have a way of adding Triggers.
I am currently using Spring AspectJ with a custom annotation and wrapping my services to try and capture the generated SQL (I figure parsing SQL is much easier that trying to capture with Objects) that's executed after the 'save' method is called, however I have not found a way to trap the generated SQL.
I tried p6Spy and was able to view the SQL and print it to the console, but was told we cannot wrap our db drivers in our PROD environment.
Is there a Spring class I am missing to make this easier?
EDIT : We're using Spring Repositories to save the data.
EDIT 2: I'm looking into EventListeners, however I cannot seem to get them to listen to my events.
#Component
public class EventListner implements PreInserEventListener, PreUpdateEventListener {
#Override
#EventListener
public boolean onPreUpdate(PreUpdateEvent event){
// do something
return false;
}
#Override
#EventListener
public boolean onPreInsert(PreUpdateEvent event){
// do something
}
return false
}
I have break points around my Listener, however they're never reached.
This question looks like it might address my issue
If you are absolutely sure that every insert/update/delete is done through JPA without any bulk SQL then you should take a look at envers
With that you can use #Audited on the entities(all column) OR columns of entities you wish to keep history.
This will create 1 revision table with timestamp change (and other data you want like uid of the user that made the change), and 1 table per entity with the old value of the modified data.
For every data you can then retrieve the history and previous value.
one other way is to add instrument on your DB like a Change Data Capture (CDC) tools and push those event to another data repository.
On the + side everything will be detected (even native SQL run directly on the DB). Drawback, your DB need to support this kind of tools correctly. For instance tools like kafka-connect can work like you want, but some implementations are too simple (like with SAP hana for instance where the process is to do a select * from xxx).
I finally settled on using Hibernate interceptors to do the trick. Works perfectly!
public class TableInterceptor extends EmptyInterceptor {
#Override
public boolean onFlushDirty(Object entity, Serializable id,
Object[] currentState, Object[] previousState,
String[] propertyNames, Type[] types){
// do comparison logic on fields and save to DB
}
}
I bootstrapped the interceptor injecting it into my LocalContainerEntityManagerFactoryBean

How to specify spring data jpa request to load collection of entity properties?

I use spring data JPA. I need in my repository request to load only collection of concrete properties colors:
#Query(value = "SELECT cd.color FROM CalendarDetails cd where cd.userCalendar.userId = :userId")
List<String> findCalendarColorsByUserWithDuplicates(#Param("userId") Long userId);
Provided solution works correctly.
I want simplify it using spring approach to load collection of the repository objects I'd use (repository public interface CalendarDetailsRepository extends JpaRepository<CalendarDetails, Long>):
List<CalendarDetails> findByUserCalendarUserId(#Param("userId") Long userId);
But I need collection of colors! Trying
List<String> findColorByUserCalendarUserId(Long userId);
I get collection of CalendarDetails
Is it possible to improve my last request following spring data approaches to load list of colors?
You can try special Projection mechanisms that Spring Data provides. It will allow you not only to optimize your queries but also to make it with pure java without using #Query.
There are a lot of ways to
make it, but I would recommend the following.
You add an interface that contains getters for the properties that you need to take from entity:
public interface ColorOnly {
String getColor();
}
Then you return the list of this interface' objects:
List<ColorOnly> findColorByUserCalendarUserId(Long userId);
To use the colours from the interface, you just invoke getColor method. You may consider simplifying it with Java 8 streams and map conversions. BTW, this one will only query colour. No other fields will be included into the query Hibernate produces.
Try to add All
findAllByUserCalendarUserId(Long userId);
BTW, IntelliJ IDEA provide very deep support of JPA repositories, so it's prevent a lot of possible issues when you create queries like this one

Use Spring Data JPA, QueryDSL to update a bunch of records

I'm refactoring a code base to get rid of SQL statements and primitive access and modernize with Spring Data JPA (backed by hibernate). I do use QueryDSL in the project for other uses.
I have a scenario where the user can "mass update" a ton of records, and select some values that they want to update. In the old way, the code manually built the update statement with an IN statement for the where for the PK (which items to update), and also manually built the SET clauses (where the options in SET clauses can vary depending on what the user wants to update).
In looking at QueryDSL documentation, it shows that it supports what I want to do. http://www.querydsl.com/static/querydsl/4.1.2/reference/html_single/#d0e399
I tried looking for a way to do this with Spring Data JPA, and haven't had any luck. Is there a repostitory interface I'm missing, or another library that is required....or would I need to autowire a queryFactory into a custom repository implementation and very literally implement the code in the QueryDSL example?
You can either write a custom method or use #Query annotation.
For custom method;
public interface RecordRepository extends RecordRepositoryCustom,
CrudRepository<Record, Long>
{
}
public interface RecordRepositoryCustom {
// Custom method
void massUpdateRecords(long... ids);
}
public class RecordRepositoryImpl implements RecordRepositoryCustom {
#Override
public void massUpdateRecords(long... ids) {
//implement using em or querydsl
}
}
For #Query annotation;
public interface RecordRepository extends CrudRepository<Record, Long>
{
#Query("update records set someColumn=someValue where id in :ids")
void massUpdateRecords(#Param("ids") long... ids);
}
There is also #NamedQuery option if you want your model class to be reusable with custom methods;
#Entity
#NamedQuery(name = "Record.massUpdateRecords", query = "update records set someColumn=someValue where id in :ids")
#Table(name = "records")
public class Record {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
//rest of the entity...
}
public interface RecordRepository extends CrudRepository<Record, Long>
{
//this will use the namedquery
void massUpdateRecords(#Param("ids") long... ids);
}
Check repositories.custom-implementations, jpa.query-methods.at-query and jpa.query-methods.named-queries at spring data reference document for more info.
This question is quite interesting for me because I was solving this very problem in my current project with the same technology stack mentioned in your question. Particularly we were interested in the second part of your question:
where the options in SET clauses can vary depending on what the user
wants to update
I do understand this is the answer you probably do not want to get but we did not find anything out there :( Spring data is quite cumbersome for update operations especially when it comes to their flexibility.
After I saw your question I tried to look up something new for spring and QueryDSL integration (you know, maybe something was released during past months) but nothing was released.
The only thing that brought me quite close is .flush in entity manager meaning you could follow the following scenario:
Get ids of entities you want to update
Retrieve all entities by these ids (first actual query to db)
Modify them in any way you want
Call entityManager.flush resulting N separate updates to database.
This approach results N+1 actual queries to database where N = number of ids needed to be updated. Moreover you are moving the data back and forth which is actually not good too.
I would advise to
autowire a queryFactory into a custom repository
implementation
Also, have a look into spring data and querydsl example. However you will find only lookup examples.
Hope my pessimistic answer helps :)

Extend spring data's default syntax

In my current project almost every entity has a field recordStatus which can have 2 values:
A for Active
D for Deleted
In spring data one can normally use:
repository.findByLastName(lastName)
but with the current data model we have to remember about the active part in every repository call, eg.
repository.findByLastNameAndRecordStatus(lastName, A)
The question is: is there any way to extend spring data in such a way it would be able to recognize the following method:
repository.findActiveByLastName(lastName)
and append the
recordStatus = 'A'
automatically?
Spring Data JPA provides 2 additional options for you dealing with circumstances that their DSL can't handle by default.
The first solution is custom queries with an #Query annotation
#Query("select s from MyTable s where s.recordStatus like 'A%'")
public MyObect findActiveByLastName(String lastName);
The second solution is to add a completely custom method the "Old Fashion Way" You can create a new class setup like: MyRepositoryImpl The Impl is important as it is How spring knows to find your new method (Note: you can avoid this, but you will have to manually link things the docs can help you with that)
//Implementation
public class MyRepositoryImpl implements MyCustomMethodInterface {
#PersistenceContext
EntityManager em;
public Object myCustomJPAMethod() {
//TODO custom JPA work similar to this
String myQuery = "TODO";
return em.createQuery(myQuery).execute();
}
}
//Interface
public interface MyCustomMethodInterface {
public Object myCustomJPAMethod();
}
//For clarity update your JPA repository as well so people see your custom work
public interface MySuperEpicRepository extends JPARepository<Object, String>, MyCustomMethodInterface {
}
These are just some quick samples so feel free to go read their Spring Data JPA docs if you would like to get a bit more custom with it.
http://docs.spring.io/spring-data/jpa/docs/current/reference/html/
Finally just a quick note. Technically this isn't a built in feature from Spring Data JPA, but you can also use Predicates. I will link you to a blog on this one since I am not overly familiar on this approach.
https://spring.io/blog/2011/04/26/advanced-spring-data-jpa-specifications-and-querydsl/
You can use Spring Data's Specifications. Take a look at this article.
If you create a 'Base'-specification with the recordStatus filter, and deriving all other specifications form this one.
Of course, everybody in your team should use the specifactions api, and not the default spring data api.
I am not sure you can extend the syntax unless you override the base class (SimpleReactiveMongoRepository; this is for reactive mongo but you can find the class for your DB type), what I can suggest you is to extend the base methods and then make your method be aware of what condition you want to execute. If you check this post you get the idea that I did for the patch operation for all entities.
https://medium.com/#ghahremani/extending-default-spring-data-repository-methods-patch-example-a23c07c35bf9

SQL\HQL queries repository

While working on some java projects i've saw some sort of SQL repository.
The idea was to place all queries in one(or few) xml files and retrieve them when needed by name. Something like this:
String sql = getSQLRepository().getSQL("SELECT_ALL_ROWS", params)
String sql2 = getSQLRepository().getSQL("SELECT_ROWS_WITH_COND", params)
In my current Grails project i have a lot of HQL queries in dozens of classes and it's hard to track them all. It seems that HQL repository would be very nice solution.
So could anyone tell if some sort of SQL\HQL repository implementation allready present or there are better solutions present ?
Have a look at Mapping Queries in Hibernate reference.
After we started use the http://source.mysema.com/display/querydsl there is no need to think about text queries and how to manage them.
I'd recommend you to use the good old properties files. You can put them into your classpath and then use as following:
Properties sql = new Properties();
properties.load(getClass().getResourceAsStream("sql.properties"));
////////
String query = sql.get("SELECT_ALL_ROWS");
I'm sorry, and it doesn't relate to Hibernate, but when I worked with iBatis, - there are the situation as you are writing about exactly. A few xml (partially generated itself) files, containing SQL queries, which was easily to use in DAO

Categories

Resources