How to run Custom query on audit table hibernate envers - spring-data-jpa

Trying to retreive records through custom query on audit table. I have Hibernate entity structure like below..,
#Audited
#Entity
public class TableA {
#EmbeddedId
private PrimaryKeyID primaryKeyID;
private Double price;
private Date modifiedOn;
private String status;
}
#Embeddable
public class PrimaryKeyID implements Serializable {
private Integer Id;
}
The query I am trying to replicate through AuditFactory is
SELECT * FROM table_A_aud where id = ? and status=? order by modifiedOn desc limit 3;
Tried Hibernate document on the Envers but I didnt find much info on that. Can someone kindly help me with this?

In order to issue a query against the audit tables, you would do the following:
List results = AuditReaderFactory.get( session )
.createQuery()
.forRevisionsOfEntity( TableA.class, true, false )
.add( AuditEntity.id().eq( entityId ) )
.add( AuditEntity.property( "status" ).eq( entityStatus ) )
.addOrder( AuditEntity.property( "modifiedOn" ).desc() )
.setMaxResults( 3 )
.getResultList();
In this query, we instruct the reader to get only entity instances (excluding delete markers) where the entity id is equal to entityId and the status is equal to entityStatus and ordering the results based on the modifiedOn column in descending order.
You'll notice that a lot of this Query API mimics that of the legacy Hibernate Criteria API.

Related

spring boot JPA returns null object when it finds a null attribute eg date is null

I wanted to load an object from the database that contains more
attributes, one of which is null (e.g : date), using spring boot JPA
that returns a null object because it found the date is null despite
the object exists in the database. Please who can who can tell me how I solve this problem
#Entity #IdClass(Exemple.class) #Table(name= "table") public class
Exemple implements Serializable{
#Id #Column(columnDefinition = "id_activite") private Integer idActivite;
//the Problem is here
#Column(columnDefinition = "date_debut_prevue") #Nullale private Calendar dateDebutPrevue;
//Other attribute and Id
#Id ...
// Getters && setters
}
//Query Repository
#Query("SELECT ar FROM table ar where ar.idActivity = ?1 ")
List<Activitie> findAllPositionByIdActivitie(Integer idActivitie);
Your query is a native query and not a JPQL query.
The query must use the Entity name in the FROM clause not the table name.
#Query("SELECT ar FROM Exemple ar where ar.idActivity = ?1 ")
List<Activitie> findAllPositionByIdActivitie(Integer idActivitie);

Rewrite SQL query with JOINS in JPA

I have this SQL query for MariaDB.
select #ref:=id as id, unique_id, reference_id
from mytable
join (select #ref:=id from mytable WHERE unique_id = 55544)tmp
where reference_id=#ref
https://www.db-fiddle.com/f/jKJodfVfvw65aMaVDyFySd/0
How this query can be implemented in HQL query? I would like to use it in JPA?
(Answer largely re-written after comments below)
JPA doesn't have built-in support for hierarchical queries. The main option is a native query.
E.g. with this entity class:
#Entity
public class MyTable {
#Id
#GeneratedValue
private int id;
private int uniqueId;
#ManyToOne
private MyTable reference;
// ... getters and setters ...
}
The following is an example of a native hierachical SQL query (actually against MySQL, just in case):
Query query = entityManager.createNativeQuery(
"select #ref\\:=id as id, unique_id, reference_id\r\n" +
"from my_table\r\n" +
"join (select #ref\\:=?)tmp\r\n" +
"where reference_id=#ref",
MyTable.class);
query.setParameter(1, 1);
query.getResultList();
This was chasing down a chain of references successfully.
(Other alternatives)
There probably aren't too many other options that can do this as a single query. If scalability is less of a concern, adding a back reference would be a simple way to navigate the model:
#OneToMany(mappedBy = "reference")
private Set<MyTable> backReferences;
Those would then be straightforward to recursively navigate. Clearly the relation defaults to lazy loading, so would add little overhead until used.
With #df778899's MyTable in spring-data it could look like:
#Repository
public interface MyRepository extends ...
#Query("select #ref:=id as id, unique_id, reference_id "+
"from mytable join (select #ref:=id from mytable WHERE unique_id = :pUid) tmp "+
"where reference_id=#ref", //just copy paste the query, use :pUid instead of constant...
nativeQuery = true) // and this!
List<MyTable> myCustomHirachicalQuery(#Param("pUid") Integer uid/*String/Long/...*/);
...

How can I write an EXISTS predicate on a collection attribute in Criteria API?

I have these classes:
#Entity
public class Customer {
#Id long id;
String name;
#OneToMany List<Customer> related;
}
and I'm using this JPQL query:
select c from Customer c where c.name = 'ACME'
or exists( select 1 from c.related r where r.name = 'ACME' )
How can I write the same query with the Criteria API? I need to use exists with a subquery, like the JPQL, but I don't know how to create a subquery from a collection attribute in the Criteria API.
Something like this would give EXISTS (subquery)
Subquery<Long> sq = cq.subquery(Long.class);
Root<Customer> customerSub = sq.correlate(customer);
Join<Customer,Customer> related = customerSub.join(Customer_.related);
... extra config of subquery
Predicate existsCustomer = cb.exists(sq);
where cq is the CriteriaQuery, and cb is CriteriaBuilder. This comes from an example in the JPA 2.1 spec p323 Example 4

Query value of JoinColumn with JPQL

I have two JPA entities
public class Job {
#ManyToOne
#JoinColumn(name = "service")
public Service service;
#Column(name = "queue_time")
public Long queueTime;
#Column(name = "run_time")
public Long runTime;
}
public class Service {
#Id
#Column(name = "id")
public Long id;
#Column(name = "name")
public String name;
#Column(name = "host")
public String host;
}
Now I want to do some aggregation queries with JPQL:
SELECT job.service.id, AVG(job.queueTime), AVG(job.runTime) FROM Job job GROUP BY job.service.id
The resulting SQL query (I'm using a MySQL database) looks like this:
SELECT t0.id, AVG(t1.queueTime), AVG(t1.runTime) FROM Service t0, Job t1 WHERE (t0.service = t1.id) GROUP BY t0.id
As you can see, JPA translates my JPQL query to a SQL query with a join. This however slows down the query dramatically. The following SQL query executes ~6 time faster and returns the exact same result set:
SELECT t1.service, AVG(t1.queueTime), AVG(t1.runTime) FROM Job t1 GROUP BY t1.service
If I change the JPQL query to
SELECT job.service, AVG(job.queueTime), AVG(job.runTime) FROM Job job GROUP BY job.service
the resulting SQL query looks like this:
SELECT t0.id, t0.name, t0.host AVG(t1.queueTime), AVG(t1.runTime) FROM Service t0, Job t1 WHERE (t0.service = t1.id) GROUP BY t0.id, t0.name, t0.host
Is there a way to write the JPQL which only queries the job table without making a join to the service table?
This question solved the issue for me: How can I retrieve the foreign key from a JPA ManyToOne mapping without hitting the target table?
I took the second solution (b) Use read-only fields for the FKs)

Select highest/lowest value for x in Data Object

my
#Entity
#Table(name = "Creditcard")
#AdditionalCriteria( ..... )
public class Customer implements Serializable {
#Id
#Column(name ="CustomerId")
private long customerId;
#Column(name = "cardNumber");
private String cardNumber;
#Column(name = "apply_date")
private java.sql.Date date;
}
Example Table Data for CustomerID 1234:
CustomerId|cardNumber|apply_date|....other fields
----------|----------|----------|----------------
0000000123|0000000001|2013-01-01|----------------
0000000123|0000000002|2013-09-10|----------------
Yes, I know, the Primary Key has to be a Composite Key (EmbeddedID), but I still have to figure it out.
Due to the #AdditionalCriteria I only get 1 entry (because the other card is "banned")
but I need to get the 'apply_date' from cardNumber '1'.
Is something like that possible?
Like:
#Column(name = "apply_date")
#GetMinValue(field_name = "CustomerId")
private java.sql.Date date;
Thanks in advance!
First, your entity should represent a row in the database, not all rows. So your entity probably should be a "CreditCard" entity, using "cardNumber" as the primary key, or what ever uniquely identifies the database row.
Then, since CustomerId seems to be a foreign key probably to a table that has customer data, you would have a Customer Entity that has a 1:M relationship to CreditCards. This customer entity could then have a transient date attribute that you set in a JPA postLoad event, getting the value from a JPQL query : "select cc.date from CreditCard cc where cc.customerId = :customerId";
Setting up an Customer entity that only uses a single card/row from a CreditCard table seems like a bad idea, as what will you do when the customer gets another creditCard assigned - it is the same customer, but a new card. If you use separate entity objects, you just keep the same Customer entity and assign a new creditcard object.