Hi this is my repository
#Repository
interface GuestRepository : JpaRepository<Guest, Long> {
#Query("from Guests g where g.date>=:fromDate and g.date<=:toDate ",nativeQuery = true)
fun findInPeriod(
#Param("fromDate") fromDate: Date
, #Param("toDate") toDate: Date
, pageRequest: Pageable
): Page<Guest>
}
and this is my guest object
#Entity
#Table(name = "Guests")
data class Guest(
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "guest_seq")
#SequenceGenerator(name = "guest_seq", sequenceName = "guest_seq", allocationSize = 1)
val id: Long=0
,
#Column(length = 50, unique = true)
#NotNull
#Size(min = 4, max = 50)
val userName: String="username"
, val firstName: String?=null
, val lastName: String?=null
,
#Temporal(TemporalType.TIMESTAMP)
#NotNull
val date: Date=Date()
)
I use spring and h2 but when I run my application it show me this error message
org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "FROM GUESTS G WHERE[*] G.DATE>=? AND G.DATE<=? LIMIT ? "; expected "(, USE, RIGHT, LEFT, FULL, INNER, JOIN, CROSS, NATURAL, ,, SELECT"; SQL statement:
With nativeQuery = true you tell Spring Data JDBC to treat your query as a native, i.e a SQL, query.
But the query you provide is not legal SQL.
It looks very much like HQL which is a Hibernate specific dialect of JPQL.
To fix the problem remove the nativeQuery = true part or use SQL
Related
I am working in a Spring JPA/Hibernate application with Kotlin and I want to find all elements in an entity.
That entity has a foreign key with a #ManyToOne relationship. I want to get all elements with their associated values with a JOIN query avoiding the N+1 problem.
One thing is that the foreign keys are not related to the primary keys, but to another unique field in the entities (UUID).
I was able to make that query with a JOIN creating a custom Query with a JOIN FETCH, but my point is to avoid creating those queries and make those JOINS in all findAlls by default.
Is that possible or do I have to make a query in JPQL manually to force the JOIN FETCH?
Here is the example code:
#Entity
data class A {
#Id
val id: Long,
#Column
val uuid: UUID,
#Column
val name: String
}
#Entity
data class B {
#Id
val id: Long,
...
#Fetch(FetchMode.JOIN)
#ManyToOne
#JoinColumn(name = "a_uuid", referencedColumnName = "uuid", insertable = false, updatable = false)
val a: A
}
#Repository
interface Repo<B> : CrudRepository<B, Long>
...
repo.findAll() // <-- This triggers N+1 queries instead of making a JOIN
...
Another option for you is using EntityGraph. It allows defining a template by grouping the related persistence fields which we want to retrieve and lets us choose the graph type at runtime.
This is an example code that is made by modifying your code.
#Entity
data class A (
#Id
val id: Long,
#Column
val uuid: UUID,
#Column
val name: String
) : Serializable
#NamedEntityGraph(
name = "b_with_all_associations",
includeAllAttributes = true
)
#Entity
data class B (
#Id
val id: Long,
#ManyToOne
#JoinColumn(name = "a_uuid", referencedColumnName = "uuid")
val a: A
)
#Repository
interface ARepo: CrudRepository<A, Long>
#Repository
interface BRepo: CrudRepository<B, Long> {
#EntityGraph(value = "b_with_all_associations", type = EntityGraph.EntityGraphType.FETCH)
override fun findAll(): List<B>
}
#Service
class Main(
private val aRepo: ARepo,
private val bRepo: BRepo
) : CommandLineRunner {
override fun run(vararg args: String?) {
(1..3L).forEach {
val a = aRepo.save(A(id = it, uuid = UUID.randomUUID(), name = "Name-$it"))
bRepo.save(B(id = it + 100, a = a))
}
println("===============================================")
println("===============================================")
println("===============================================")
println("===============================================")
bRepo.findAll()
}
}
On B entity, an entity graph named "b_with_all_associations" is defined, and it is applied to the findAll method of the repository of B entity with LOAD type.
These things will prevent your N+1 problem by fetching with join.
Here is the SQL log for the bRepo.findAll().
select
b0_.id as id1_1_0_,
a1_.id as id1_0_1_,
b0_.a_uuid as a_uuid2_1_0_,
a1_.name as name2_0_1_,
a1_.uuid as uuid3_0_1_
from
b b0_
left outer join
a a1_
on b0_.a_uuid=a1_.uuid
ps1. due to this issue, I don't recommend using many to one relationship with non-pk. It forces us to use java.io.Serializable to 'One' entity.
ps2. EntityGraph can be a good answer to your question when you want to solve the N+1 problem with Join. But I would recommend the better solution: try to solve it with Lazy loading.
ps3. It's not a good idea that using non-pk associations for Hibernate. I truly agree on this comment. I think it's a bug that is not solved yet. It breaks the lazy loading mechanism of hibernate.
As far as I know, the fetch mode only applies to EntityManager.find related queries or when doing lazy loading but never when executing HQL queries, which is what is happening behind the scenes. If you want this to be join fetched, you will have to use an entity graph, which is IMO also better as you can define it per use-site, rather than globally.
I don't know how to configure exactly what you are asking, but the following suggestion might be worth considering...
Change
#Fetch(FetchMode.JOIN)
#ManyToOne
#JoinColumn(name = "a_uuid", referencedColumnName = "uuid", insertable = false, updatable = false)
val a: A
to
#ManyToOne(fetch = javax.persistence.FetchType.LAZY)
#JoinColumn(name = "a_uuid", referencedColumnName = "uuid", insertable = false, updatable = false)
val a: A
And then on your entity A, add the annotation to the class
#BatchSize(size = 1000)
Or whatever batch-size you feel to be appropriate.
This will generally give you the results in 2 queries if you have less than 1000 results. It will load a proxy for A rather than joining to A, but then the first time that A is accessed, it will populate the proxies for BATCH_SIZE number of entities.
It reduces the number of queries from
N + 1
to
1 + round_up(N / BATCH_SIZE)
The findAll implementation will always load b first and then resolve it's dependencies checking the annotations. If you want to avoid the N+1 problem you can add the #Query annotation with JPQL query:
...
#Query("select b from TableB b left join fetch b.a")
repo.findAll()
...
This HQL query give me error:
String q4 = "SELECT i, COUNT(ie) FROM CorsoStudi cs \n"
+ "\t JOIN cs.inserogati ie JOIN ie.insegn i \n"
+ "\t WHERE cs.nome = 'Laurea in Informatica' \n"
+ "\t GROUP BY i";
The error is:
Exception in thread "main" java.lang.IllegalArgumentException: org.hibernate.QueryException: could not resolve property: insegn. of: component[_id,annierogazione,annoaccademico,crediti,discriminante,discriminantemodulo,hamoduli,id_facolta,insegn,inserogato_padre,modulo,nomemodulo,nomeunita,programma]
This is InsErogato:
#Embeddable
public class InsErogato {
private Integer _id;
private String annoaccademico;
#Embedded
private Insegn insegn;
#Embedded
private Discriminante discriminante;
private Integer modulo;
private String discriminantemodulo;
private String nomemodulo;
private Double crediti;
private String programma;
private Integer id_facolta;
private String hamoduli;
#Embedded
private InsErogatoPadre inserogato_padre;
private String nomeunita;
private Integer annierogazione;
// constructors, getters and setters and toString
}
and this is Insegn:
#Embeddable
public class Insegn {
private Integer _id;
private String nomeins;
private String codiceins;
// constructors, getters and setters and toString
}
Main:
// begin transaction
entityManager.getTransaction().begin();
List<Object[]> insegn = entityManager
.createQuery(q4, Object[].class)
.getResultList();
for(Object[] i : insegn) {
Insegn ins = (Insegn)i[0];
Long count = (Long)i[1];
System.out.println("nomeins: " + ins.getNomeins() + ", numero inserogati: " + count);
}
// commit transaction
entityManager.getTransaction().commit();
The MongoDB structure:
https://i.stack.imgur.com/qFusC.jpg
https://i.stack.imgur.com/k04HK.png
https://i.stack.imgur.com/H8nhS.png
https://i.stack.imgur.com/eYl2M.png
I tried to change the query but Hibernate doesn't find "insegn" (and also "discriminante") property in "inserogato", but he can find other simple attributes from it (like "annoaccademico" etc.).
The same query works on Hibernate ORM with PostgreSQL.
Maybe I have to add something in the annotations, or change the mongoDB structure (?).
I'm using Hibernate OGM 5.3.1.Final and MongoDB 3.6.3 JDBC Driver.
The same query works on Hibernate ORM with PostgreSQL
But PostgreSQL is a SQL database, MongoDB is a NoSQL database. Hibernate OGM is a means to add Hibernate ORM to NoSQL databases. The '(H)SQL' for OGM is limited (see below).
You don't say what your application is deployed on. I use WildFly 12.0.0.Final. I've had OGM with MongoDB working on version 11 & 12.
entityManager.getTransaction().begin();
I use the (WildFly) container to handle transactions. Annotate my EJB.
#TransactionManagement(TransactionManagementType.CONTAINER)
I don't believe you can use (H)SQL per se with Hiberate OGM but:
Use JPQL - only for simple queries for now
Use the NoSQL native query mapping the result as managed entities
Use Hibernate Search queries - primarily full-text queries
It says in the documentation:
In particular and of notice, what is not supported is:
cross entity joins
JPQL functions in particular aggregation functions like count
JPQL update and delete queries
One of my queries:
Query query = mongoDBEntityManager.createQuery("FROM FoodsCosmeticsMedicines f WHERE f.ean = :ean")
.setParameter("ean", ean);
The entity (the #Expose are for the JSON)
#Entity(name = "FoodsCosmeticsMedicines")
#Indexed
#Table(name = "foodsCosmeticsMedicines")
public class FoodsCosmeticsMedicines implements Serializable {
// Arrays of Objects
#Expose(deserialize = true, serialize = true)
#Embedded
ProductCharacteristics productCharacteristics;
#Expose(deserialize = true, serialize = true)
#Embedded
CalcNutrition calcNutrition;
#Expose(deserialize = true, serialize = true)
#Embedded
Nutrients nutrients;
#Expose(deserialize = true, serialize = true)
#Embedded
Enumbers enumbers;
#Expose(deserialize = true, serialize = true)
#Embedded
ChemicalsMineralsVitamins chemicalsMineralsVitamins;
#Expose(deserialize = true, serialize = true)
#Embedded
Lifestyle lifestyle;
.....
}
Why are you using JOIN?
With MySQL I use Hibernate as my ORM I would use annotations to map relationships with entities such as:
#OneToOne(cascade = CascadeType.ALL, mappedBy = "product", fetch = FetchType.LAZY)
private UriEntity uri;
entityManager.getTransaction().commit();
This is irrelevant as all you've done is read. Maybe you excluded the persist?
I'm sure you've read the documentation. I found all the answers to the problems I encountered here:
Hibernate OGM 5.3.1.Final: Reference Guide
Currently, queries with group by are not supported. You will need to run a native MongoDB query for this particular use case. See the reference documentation for more details about it.
I think the join on an embedded entity instead of a collection is confusing the parser even if it's a valid query. You should see a clearer exception with the following one (equivalent to the one you are trying):
SELECT ie.i, COUNT(ie)
FROM CorsoStudi cs
JOIN cs.inserogati ie
WHERE cs.nome = 'Laurea in Informatica'
GROUP BY ie.i;
It should throw:
java.lang.UnsupportedOperationException: The GROUP BY clause is not supported
Aggregate functions are not supported as well.
I would like to sort a Map of by the value. For example I have Person class which has a map of details that are stored in a map with key-value Map<String, String>.
I am using springboot with hibernate5. This is the mapping.
public class Person implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#DocumentId
#Column(name = "personid")
private Integer id;
#Column(name = "name")
private String name;
// this is a collection of person details
#ElementCollection()
#MapKeyColumn(name = "detailkey")
#Column(name = "detailvalue")
#CollectionTable(name = "details", joinColumns = #JoinColumn(name = "personid"))
Map<String, String> details = new HashMap<>();
//getter and setters omitted
So far I am able to retrieve a person with some specific detailskey and specific detail value. So for example a person table in the DB has eyecolor as detail attribute and as value can have "green", "blue", "brown". Note this is not a real example, just for clarity purposes.
So for example I can get the list of persons and sort them by their name, in the controller I can do
Sort sort = new Sort(Sort.Direction.ASC, "name");
and the opposite direction
Sort sort = new Sort(Sort.Direction.DESC, "name");
Pageable pageable = new PageRequest(1, 10, sort);
pageResult = personRepository.findAll(
"eyecolor", "green", pageable
);
and this one will return the list of persons that have "eyecolor" as green. So far so good and this is working as expected. Now I would like to define a sorting on the detailvalue.
For example I would like to get a list of person sorted by their eyecolor. So first I should have the persons that have "blue", "brown", "green".
how can the Sort be specified in this case ?
In standard SQL it would be something like this:
SELECT p.* from persons p LEFT JOIN details d ON
p.personid = d.personid AND p.detailkey='eyercolor' ORDER BY
p.detailvalue ASC;
The following query worked for me:
SELECT p FROM Person p JOIN p.details d WHERE KEY(d) = 'eyecolor' ORDER BY d
(note that ORDER BY VALUE(d) would fail since VALUE(d) still seems to behave as described here: JPA's Map<KEY, VALUE> query by JPQL failed)
Now, I'm not particularly well versed with Spring Data, but I suppose you should be able to use the above query (without the ORDER BY part) with the #Query annotation on your PersonRepository.findAll method (I'm assuming that's a custom method) and provide the sorting using JpaSort.unsafe("d").
I have a mysql database table 'Faculties' like below,
My entity class (FacultyEntity) is as below
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "Faculty_Id")
private Integer facultyId;
#Column(name = "Faculty_Name", length = 100, nullable = false, unique = true)
private String facultyName;
#ManyToOne(cascade = { CascadeType.REFRESH })
#JoinColumn(name = "HOD_ID", nullable = true)
private FacultyEntity hodId;
I want to display the result in my application sorted by HOD's and and then faculties. This is what I've tried
#Query("from FacultyEntity f ORDER BY f.hodId.facultyName, f.facultyName")
But I'm getting total 4 rows sorted like this. I think it is because of the null value in the HOD_ID column, But I'm not sure. I'm newbie here.
The expected result will be
Please help me to fix this issue.
When you do f.hodId in the query, it implicitly translates to inner join, which eliminates the two rows with null in HOD_Id column.
Try this instead
#Query("from FacultyEntity f left join f.hodId hod ORDER BY hod.facultyName, f.facultyName")
I have two Entities related by a ManyToMany and I want to select them via a named Query. This works in my test (with a H2 DB set up) and throws exceptions at runtime (with postgresql set up). Other than the H2 and PG I am hard pressed to find differences between test and production.
The Entities and the Query look like so (abbreviated):
#Entity(name = "Enrichment")
#Table(name = "mh_Enrichment")
NamedQueries({
#NamedQuery(name = "findByLink",
query = "SELECT e FROM Enrichment e INNER JOIN e.links l WHERE l.link in (:links)") })
public class EnrichmentImpl {
#Id
#Column(name = "enrichmentId")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#ManyToMany
#JoinTable(name = "mh_EnrichmentLinks", joinColumns = { #JoinColumn(name = "EnrichmentId",
referencedColumnName = "enrichmentId") }, inverseJoinColumns = { #JoinColumn(name = "Link",
referencedColumnName = "link") })
private List<Link> links;
}
#Entity(name = "Link")
#Table(name = "mh_enrichment_link")
public class LinksImpl {
#Id
#Column(name = "link", length = 1024)
private String link;
}
Upon running the query with a String value in production I get:
Internal Exception: org.postgresql.util.PSQLException: ERROR: operator does not exist: character varying = bigint
Hinweis: No operator matches the given name and argument type(s). You might need to add explicit type casts.
Position: 215
Error Code: 0
Call: SELECT t1.enrichmentId FROM mh_enrichment_link t0, mh_EnrichmentLinks t2, mh_Enrichment t1 WHERE ((t0.link IN (?)) AND ((t2.EnrichmentId = t1.enrichmentId) AND (t0.link = t2.Link)))
Any ideas what's wrong? It is the query, isn't it?
The query is supposed to retrieve a list of Enrichments that are related to the given link.
Update #1
As requested: the tables in the DB look as follows:
For entity Link
CREATE TABLE mh_enrichment_link
(
link character varying(1024) NOT NULL,
CONSTRAINT mh_enrichment_link_pkey PRIMARY KEY (link)
)
For entity Enrichment
CREATE TABLE mh_enrichment
(
enrichmentid bigint NOT NULL,
CONSTRAINT mh_enrichment_pkey PRIMARY KEY (enrichmentid)
)
For the relation (See answer, this was where it went wrong)
CREATE TABLE mh_enrichmentlinks
(
link character varying(1024) NOT NULL,
CONSTRAINT mh_enrichment_link_pkey PRIMARY KEY (link)
)
The issue was fixed by dropping all related tables and having JPA regenerate them. Table definitions didn't match Entity definitions.
Thats also the quite obviously the reason why the test worked and the production didn't. In testing the tables are generated on runtime, in production they existed already (with an outdated definition).
Side note: The query is correct and does what it should.