How to avoid slow request to use envers at Spring boot - spring-boot

I am using envers at Spring boot project.
#Repository
public interface DataAudRepository extends RevisionRepository<Data, String, Integer>, JpaRepository<Data, String> {
}
If there is numerous data on the data_aud table (data number : 3500), request of findRevisions(key, pageable) is very slow.
Sure if there is not numerous data, request is argent.
When Spring call this function, some of hiberate sql occur.
select *
from data_aud publish_au0_
where
data_au0_.rev=(
select max(data_au1_.rev) from data_aud data_au1_
where
data_au1_.rev<=999999 and
data_au0_.data_id=data_au1_.data_id) AND
data_au0_.data_id='my-data-01'
Perhaps i think select max sequence is reason for slow request.
How to avoid this slow condition?
+)
Primary key of data_aud table is (data_id, varchar(32), rev, int(11))
and PK of data table is data_id.

Related

How to populate H2 DB UUID primary key using sql file in Spring Boot

I am developing a Spring Boot-2.4 application which use Spring Data JPA and I am writing test for my CURD implementation. I have few scenarios needs to load table data with specific primary key values before my test execution. I thought of using import.sql or data.sql in which i can write native SQL queries and use those supplied primary key values. As my primary key is defined as UUID eventhough I gave valid UUID data in SQL while fetching using findById its not working.
data.sql
insert into TEST_TABLE(id,name,pass) value ('2B12245566587878779679','test','xxxxx');
Assume given UUID value is valid and I can able to insert this data in H2 and able to view.
But when in JPA try to access this same ID using findById('2B12245566587878779679') will not returning this record. Is there any correct way to load the UUID and access it using .sql file.
EDIT: Included code sample
#Entity(name="TEST_TABLE")
public class TestTable{
#Id
#GeneratedValue
private UUID id;
}
#Repository
public interface TestTableRepository extends JpaRepository<TestTable,UUID>{}
#Service
public class TestTableService {
#Autowired
TestTableRepository testTableRepository;
public UUID getTableData(UUID id){
testTableRepository.findById(id); // here passing 2B12245566587878779679
}
}
Expecting one row returned but its not returning any

Spring Data JPA passing dynamic column names as parameter to query

I have table emp with columns emp_name, emp_desc, emp_age, emp_country, emp_pincode.
I am using Spring data Jpa for database operations.
#Repository
public interface EmpRepository extends JpaRepository<Emp, String> {}
and empRepository.findAll(); fires a select * from emp table.
But I have a requirement like as follows
The client app would pass column names to select in the method as parameter and I want to fetch only those in Jpa not Findall().
How to acheive this in Jpa?
Try this one
To get the employee details based on emp_name
empRepository.findByEmpName(String empName);

How to query more than one columns but not all columns with #Query but still use the Domain Data Model to map with Spring Data JDBC?

My Data model is
#Getter
#Setter
public class Customer {
#Id private ID id;
#CreatedDate protected Instant createdAt;
#LastModifiedDate protected Instant updatedAt;
#CreatedBy protected String createdBy;
#LastModifiedBy protected String updatedBy;
#Version protected Long version;
private UUID orderId;
private String offer;
}
My Repository is
public interface CustomerRepository extends CrudRepository<Customer, UUID> {
#Query(
"SELECT ID, Offer FROM Customer WHERE orderId = :orderId ")
List<Customer> findCustomerByOrderId(
#Param("orderId") UUID orderId);
}
This will result in an exception saying 'orderId column not found [42122-190]'. So Spring expects you to always query all the columns. I understand that with JPA we have a strong mapping between the Entities and the Data Schema. But the whole point of spring data JDBC is avoiding the tight coupling between POJO's data model and database schema. Why not the EntityRowMapper is just mapping NULL to the properties which are not part of the query?
Is there a way to tell the RowMapper used, to ignore properties which are not part of the query? Creating separate RowMapper for these simple queries seems a lot of unnecessary work.
I still can work around this by changing the query like
#Query(
"SELECT ID, Offer, OrderId, null as CreatedAt, null as CreatedBy, null as UpdatedAt, null as UpdatedBy, null as Version FROM Customer WHERE orderId = :orderId ")
But this will still serialize the entire object with null values. Am I missing something obvious here?
Note This is not Spring Data JPA. Its Spring Data JDBC.
Edit
Looking more into it, the exception is from h2 database lib.
Caused by: org.h2.jdbc.JdbcSQLException: Column "orderid" not found [42122-190]
at org.h2.message.DbException.getJdbcSQLException(DbException.java:345)
at org.h2.message.DbException.get(DbException.java:179)
at org.h2.message.DbException.get(DbException.java:155)
at org.h2.jdbc.JdbcResultSet.getColumnIndex(JdbcResultSet.java:3129)
at org.h2.jdbc.JdbcResultSet.get(JdbcResultSet.java:3217)
at org.h2.jdbc.JdbcResultSet.getObject(JdbcResultSet.java:522)
at com.zaxxer.hikari.pool.HikariProxyResultSet.getObject(HikariProxyResultSet.java)
at org.springframework.data.jdbc.core.EntityRowMapper.readFrom(EntityRowMapper.java:127)
You can't at least right now.
There are three solutions to this, two of which you already pointed out:
extend your select statement with , NULL as <column-name> for all the missing columns.
I'm not sure if
But this will still serialize the entire object with null values.
means that this isn't working for you in some way.
specify a RowMapper.
You could use a class containing exactly the fields returned by the query. It could even have getters for the other columns if you want an interface implemented by both your normal entity and the partial entity.
You write:
But the whole point of spring data JDBC is to avoid the tight coupling between pojo's data model and database schema.
This is not quite right.
An important goal of Spring Data JDBC is to not have a run time connection between entities and table rows.
This would require proxies or similar and brings a lot of complexity.
But the structural mapping between entities and table is probably going to be stronger (and certainly is right now) since all the variants of mappings available in JPA bring complexity.
And the main goal in Spring Data JDBC is to be conceptually simpler than JPA.
You also ask
Why not the EntityRowMapper is just mapping NULL to the properties which are not part of the query?
I'm not sure if I actively thought about it when I coded it but I don't like the idea of defaulting to NULL because this would make it easy to accidentally not load a column because you have a typo in an alias.
But I'm not against alternative solutions.
If you have an idea please create a feature request.

Spring Data Cassandra Pagination

Can anybody know how to achieve the pagination in Spring Data Cassandra ?
I have tried all the possible alternatives to implement pagination for my table.
One of the stackoverflow answers says it is not provided directly.
Paging SELECT query results from Cassandra in Spring Boot application
As per documentation (https://docs.spring.io/spring-data/cassandra/docs/current-SNAPSHOT/reference/html/#cassandra.repositories.queries) says CrudRepository provides method with Pageable.
org.springframework.data.repository.CrudRepository does not provide.
I am using Cassandra 3.11.3 and Spring Boot 1.5.1.RELEASE.
Can one provide me simple pagination demo with Spring Data Cassandra ?
The pagination in Spring Data Cassandra work in 'forward only' basis like the way iterable interface works.
#Autowired
PersonRepository personrepo;
#GetMapping("/all/{page}")
public List<Person> getPaginated(#PathVariable Integer page) {
int currpage = 0, size = 2;
Slice<Person> slice = personrepo.findAll(CassandraPageRequest.first(size));
while(slice.hasNext() && currpage < page) {
slice = personrepo.findAll(slice.nextPageable());
currpage++;
}
return slice.getContent();
}
Where your repository contains:
public interface PersonRepo extends CrudRepository<Person, Integer> {
Slice<Person> findAll(Pageable pr);
}
Hence, you keep querying the result until the desired location of the data is reached.
This is the simplest way to do pagination in reactive Cassandra or regular Cassandra with spring data
In your repository class create custom methods with Query annotation (key point here is for second method there should be a offset that needs to sent as a parameter) Assuming date created is a clustering key
user table primary key(user_id, date_created)
Ex:-
#Query("select * from user order by date_created asc Limit :count)
Flux<UserEntity> findFirstSetOfUsers(#Param(count) int noOfRecords);
Next set of records
#Query("select * from user where date_created > :dateCreated order by date_created asc Limit :count)
Flux<UserEntity> findNextSetOfUsers(#Param(count) int noOfRecords, #Param(dateCreated) Date dateCreated);

How do I migrate my JPA DAO to Spring Data with second level cache?

I have bunch of JPA DAOs im looking to migrate to Spring Data JPA. Some of my DAOS have second-level / query caching set up.
I have a process where I only retrieve the ID in my queries, and then look up the entity using findByID(). This way, only the id's are multiplied in the different query caches, and the entire entities are in the second level cache.
Example:
#NamedQuery(name = "SystemUser.findByEmail",
query = "SELECT u.id FROM SystemUser u WHERE email=:email"),
…
public SystemUser findByEmail(String email) {
TypedQuery<Long> q = getEntityManager().createNamedQuery("SystemUser.findByEmail", Long.class);
q.setParameter("email", email);
q.setHint("org.hibernate.cacheable", true);
q.setHint("org.hibernate.cacheRegion", "query.systemUser");
List<Long> res = q.getResultList();
if (res != null && res.size() > 0) {
return findById(res.get(0));
}
return null;
}
I have several more findBy…-methods, all doing it like this. It feels like a good way to keep cache memory consumption down.
I'm kind of new to the Spring Data JPA business, but I can't see how I would go about realizing this here? The #Cacheable annotations seems only to deal with query caches, which to me would duplicate the entities in each query cache?
Is there any way to do this with Spring Data? Pointers would be much appreciated.
In Spring Data JPA just create a findByEmail method and either Spring Data JPA will found your named query or create one itself.
public class SystemUserRepository extends CrudRepository<SystemUser, Long> {
SystemUser findByEmail(String email);
}
Should be all you need to get the query executed and the desired result. Now with the #QueryHints you can add the hints you are setting now.
public class SystemUserRepository extends CrudRepository<SystemUser, Long> {
#QueryHints(
#QueryHint(name="org.hibernate.cacheable", value="true"),
#QueryHint(name="org.hibernate.cacheRegion", value="query.systemUser") )
SystemUser findByEmail(String email);
}
The result will be cached and still the user will come from the 2nd level cache (if available, else created). Assuming of course your entity is #Cacheable.
A nice read on how the 2 different caches work (together) can be found here. A small snippet on how the query cache works.
The query cache looks conceptually like an hash map where the key is composed by the query text and the parameter values, and the value is a list of entity Id's that match the query:
If you want more complex logic (and really implement the optimization you did) you can always implement your own repository.

Resources