GenericJDBCException: could not extract ResultSet - java

I found similar questions about this error but I can't make it work
I'm working on a java 8, spring 2.6.4 and a MySQL database
I'm trying to do a DELETE native query using JPA and I'm getting this error:
org.springframework.orm.jpa.JpaSystemException: could not extract ResultSet; nested exception is org.hibernate.exception.GenericJDBCException: could not extract ResultSet
this is my query:
#Query(value = "DELETE FROM reservation a WHERE a.slotid =:slotid", nativeQuery = true)
void deleteWhereSlotid(Integer slotid);
and this is the service:
repo.deleteWhereSlotid(reservationSlot.getId());
I've also tried:
#Query(value = "DELETE FROM reservation a WHERE a.slotid =:slotid", nativeQuery = true)
Object deleteWhereSlotid(Integer slotid);
//service
Object object= userCourseResRepo.deleteWhereSlotid(reservationSlot.getId());
but it failed
Usually I delete rows with deleteById(id) which comes with spring
The query works though, I tried it on phpMyadmin console and it worked
Someone know what I can try?

The way you have it set up, Spring Data assume you want to perform a query (typically a SELECT). For DELETE and similar statements that don't return a ResultSet you need to provide an additional #Modifying annotation.
#Modifying
#Query(value = "DELETE FROM reservation a WHERE a.slotid =:slotid", nativeQuery = true)
void deleteWhereSlotid(Integer slotid);

I know is not the best solution, but you can try to use a query SELECT to find your reservation object and then do this repo.deleteById(reservation.getId())
This should allow you to go ahead while you find a better way to do it

If you are using it that way, I believe the query should be:
DELETE a FROM reservation a WHERE a.slotid =:slotid
I am not particularly sure about the code, however, with Mysql, the case seems to be so when giving an alias to the table.

You need to add #Param
#Query(value = "DELETE FROM reservation a WHERE a.slotid =:slotid", nativeQuery = true)
Object deleteWhereSlotid(#Param("slotid")Integer slotid);
As mentioned above, we use the #Param annotation in the method declaration to match parameters defined by name in JPQL/Native query with parameters from the method declaration.

Related

Spring Boot unknown column 'INNER. ' in order clause

I am using Spring Data JPA to get data from a MySQL database. I have this function adnotated with #Query in a repository
#Query(value = "SELECT * FROM Treatments INNER JOIN Animals ON Treatments.animal_id = Animals.animal_id WHERE Animals.owner_id = ?1 AND Treatments.enddate > curdate()", nativeQuery = true)
Page<Treatments> findAll(#Param("ownerId") Optional<Owner> owner, Pageable pageable);
Spring generates the SQL as it is, then adds order by INNER.startdate asc limit ?
And then it throws this error
Unknown column 'INNER.startdate' in 'order clause'
Why is this happening? I tried adding an orderby clause myself but it will just add its own orderby clause anyway and throw the error. The query works inside MySql.
I suppose Spring modifies your query because you use Page as the return type. When you use a native query together with Page, you should add a countQuery, see Spring Documentation.

How to run a native SQL query in Spring without an entity and a JPA Repository?

I am trying to run some native SQL queries in my Spring application. I donĀ“t have an entity or JpaRepository class. I know it's strange, but this is a microservice just to collect two count queries and send it to Kafka.
Well trust me, all I need is these two integers from the queries. I run these code and always returns 0. I can see in the logs that Hikari is connecting to the database, so I don't know what to do. Searched a lot, but all answers involved the #Query solution, which does not work for me.
#Repository
#AllArgsConstructor
public class ReportRepository {
private final EntityManager em;
public int numberOfAccounts() {
String sql = "SELECT count(*) FROM account";
Query query = em.createNativeQuery(sql);
System.out.println(query.getFirstResult());
return query.getFirstResult();
}
public int numberOfSubscriptions() {
String sql = "SELECT count(*) FROM subscriptions";
Query query = em.createNativeQuery(sql);
System.out.println(query.getFirstResult());
return query.getFirstResult();
}
}
If you have EntityManager, and from what you are saying it can connect to DB, try this way:
public int numberOfSubscriptions() {
// >> "subscriptions" has to be the exact name of your table
// if does not work, consider trying SUBSCRIPTIONS or Subscriptions
String sql = "SELECT count(*) FROM subscriptions";
Query query = em.createNativeQuery(sql);
// getSingleResult() instead :)
return ((Number) query.getSingleResult()).intValue();
}
There is this (a bit old) JavaDoc for Query.getFirstResult() :
The position of the first result the query object was set to retrieve. Returns 0 if setFirstResult was not applied to the query object
So, I'd say that is not the right method for your case.
Happy Hacking :)
You should be using JDBC instead of an Entity Manager. Under the JPA uses JDBC but it requires defined entites to work. JDBC allows you to manage the connection and run the raw SQL queries.
Here's a link for how to do it in Spring:
https://spring.io/guides/gs/relational-data-access/#_store_and_retrieve_data

Hibernate - Select all rows in a table

Im trying to get my query to load all rows in a table, im working with hibernate.
#Override
public List<Teacher> getTeachersBySubject(Subject subject) {
List<Teacher> teachersBySubject = entityManager.createQuery("SELECT * FROM teacher t INNER JOIN teacher_subject ts on t.email = ts.email")
.getResultList();
return teachersBySubject;
}
The * (All) Gives the error im dealing with, it won't load because of hibernate
the error that im getting is this : org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected token: * near line 1, column 8 [SELECT * FROM com.scalda.vos.models.Teacher t INNER JOIN teacher_subject ts on t.email = ts.email]
its not native and your writing hql so replace * with t.
You have written a native query, which must be compiled using the createNativeQuery method.
The createQuery expects HQL and not native SQL, and therefore to do it in HQL use just "teacher FROM Teacher". Also, if you are just retrieving a Teacher and not a Teacher-Subject hybrid then, Hibernate can also help you with retrieving associations check this
HIbernate expects you to use JPQL, it looks like SQL, but should be like this:
SELECT t FROM teacher t JOIN t.subject
Of course, it depends on how you Teacher class is annotated (ManyToMany, OneToMany mappings).
as my recommendation, use hql for clear code in hibernate or using criteria
but, still running well in native.
For native SQL, use createSqlQuery, like :
List<Teacher> teachersBySubject = entityManager.createSqlQuery("SELECT * FROM teacher t INNER JOIN teacher_subject ts on t.email = ts.email")
.getResultList();

Datanucleus creates subquery instead of join

I have these annotations:
public class Account {
#Persistent(defaultFetchGroup = "true", dependent = "false")
#Column(name = "user_owner_id")
private User user;
}
public class User {
#Persistent(defaultFetchGroup = "true", mappedBy = "user")
#Element(column = "user_owner_id", dependent = "true")
private Set<Account> accounts;
}
When initiating the Account class, the query on the database use a SELECT * FROM accounts where exists (SELECT id from users where id=23)
I am trying to give datanucleus an annotation that tells it to run on the database SELECT a.* FROM accounts a JOIN users u on a.id = u.user_id where u.id = 23 as this is more optimal.
So which annotation should I use to make data nucleus change its query formation?
--- addition ----
This is a stripped down version of how we're retrieving the data:
PersistenceManager persistenceManager = persistenceManagerFactory.getPersistenceManager();
persistenceManager.getFetchPlan().setMaxFetchDepth(FetchPlan.FETCH_SIZE_GREEDY);
Query query = persistenceManager.newQuery("javax.jdo.query.JDOQL", null);
query.setClass(User.class);
query.setFilter("this.uuid==p1");
query.declareParameters("java.lang.String p1");
final List<E> entities = (List<E>) query.execute(uuid);
E entity = entities.iterator().next();
return persistenceManager.detachCopy(entity);
You are performing a Query just to get one object, which is very inefficient. Instead you could easily do
User u = pm.getObjectById(User.class, 1);
and this would likely issues 2 SQLs in total; 1 to get the basic User object, and 1 to get the Accounts connected to that User. There would be no EXISTS clause.
With regards to what you are actually doing. A Query is issued. A Query is general and in most use-cases will return multiple objects. The filter clause of the query can be complex. The Query is converted into an SQL to get the basic User fields. It can't get the related objects in a single call, so your log will likely say something about BULK FETCH (or something similar, whatever DataNucleus calls it). This will have an EXISTS clause with the EXISTS subquery restricting the second query to the objects the Query applies to). They do this to avoid the N+1 problem. Using EXISTS is, in general, the most appropriate for the general case of a query. In your specific situation it would have been nice to have an INNER JOIN, but I don't think that is supported as a BULK FETCH option currently. Their code is open source and I know they have asked people to contribute things in the past where they want alternative handling ... so you could contribute something if you want to use a query in this precise situation.

How To Configure Query Cacheing in EclipseLink

I have a collection of states, that I want to cache for the life of the application, preferably after it is called for the first time. I'm using EclipseLink as my persistence provider. In my EJB3 entity I have the following code:
#Cache
#NamedQueries({
#NamedQuery(
name = "State.findAll",
query = "SELECT s FROM State s",
hints = {
#QueryHint(name=QueryHints.CACHE_USAGE, value=CacheUsage.CheckCacheThenDatabase),
#QueryHint(name=QueryHints.READ_ONLY, value=HintValues.TRUE)
}
)
})
This doesn't seem to do anything though, if I monitor the SQL queries going to MySQL it still does a select each time my Session Bean uses this NamedQuery.
What is the correct way to configure this query so that it is only ever read once from the database, preferably across all sessions?
Edit: I am calling the query like this:
Query query = em.createNamedQuery("State.findAll");
List<State> states = query.getResultList();
The solutions posted here not worked for me. But i've made it work with:
#Cache
#NamedQueries({#NamedQuery(
name = "State.findAll",
query = "SELECT s FROM State s",
hints = {
#QueryHint(name = QueryHints.QUERY_RESULTS_CACHE, value = HintValues.TRUE)
}
)})
Just a guess here, but you might try
query.cacheQueryResults();
after you create it but before you getResultList.
-- MarkusQ
I got EclipseLink 1.0.1 cache to work by adding just the query hint:
Query query = em.createNamedQuery("Person.find");
query.setParameter("NAME", name);
query.setHint("eclipselink.cache-usage", "CheckCacheThenDatabase");
return (Person)query.getSingleResult();
I didn't change the entity at all, and haven't yet tried to configure cache using annotations.

Categories