Got some troubles with JPQL (JPA) query SELECT DISTINCT ORDERED BY - java

as in title i got some problems with query in JPQL
that's the query I'm using:
#Query(value = "SELECT DISTINCT o.idWhom FROM Message o WHERE o.idWho = ?1 ORDER BY o.date DESC")
List<Users> allCorrespondents(Users user);
Class message:
public class Message {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#NotBlank
private String content;
#Temporal(TemporalType.TIMESTAMP)
private Date date = new Date();
boolean read = false;
#ManyToOne
#JoinColumn(name = "id_who")
private Users idWho;
#ManyToOne
#JoinColumn(name = "id_whom")
private Users idWhom;
}
Error I've got:
2022-06-17 13:02:22.435 ERROR 2304 --- [nio-8080-exec-2] c.e.h.S.CustomAuthorizationFilter :
Error logging in: Request processing failed; nested exception is org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet

Although I am not able to prove it through issue tickets, I have done enough investigation to believe that this issue occurs to hibernate version previous from 5. (Example Hibernate 4!) with database being used Oracle.
What the issue seems to be is the following:
When you have written the JPQL query SELECT DISTINCT o.idWhom FROM Message o you would expect that the entities would be filtered to be distinct after the execution of the SQL query, where the mapper would filter out the distinct entities.
For some reason however in those versions I mention above what actually happens is that the distinct is passed in the SQL query and what actually executes is the following:
SELECT DISTINCT idh.id_column FROM O_TABLE o JOIN IDWHOM_TABLE idh on (o.id_foreign_key_column = idh.id_column
As you can see this select query does not return in result set all columns that are needed from mapper to build the entity but only returns the id column. Therefore the mapper is not able to extract from ResultSet all columns needed to build the entity and you get the error that you get could not extract ResultSet.
A workaround I have found, is that it is able to work when distinct is placed on some primitive field and not on an entity, so you can use this to your advantage to bypass this issue with the following approach:
This supposes that the entity Users has as id a field named id.
#Query(value = "SELECT us FROM Users us where us.id in (SELECT DISTINCT o.idWhom.id FROM Message o WHERE o.idWho = ?1)")
List<Users> allCorrespondents(Users user);
I have excluded the ordering which you have in your original query since even from logical point of view seems wrong. A User entity can have multiple messages. How could you order the returned users just from created date of messages? This field is not able to order Users themselves. It could be used to order messages only.

Related

GenericJDBCException: could not extract ResultSet

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.

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.

JPA #ElementCollection how can I query?

I am using Spring JPA and in order to ad a List of String to my Entity I am using #ElementCollection as below.
#ElementCollection
private Map<Integer, String> categories;
When I use this it generates a table called subscription_categories this contains the following columns subscription(varchar), catergories(varchar) and caterogies_key (int)
If I use my SQL tool on my desktop I can query this table fine with the following
select `subscription_categories`.`subscription` from `subscription_categories` where `subscription_categories`.`categories`='TESTING';
However, when I attempt to use this in Spring Data it fails with a "... not mapped" error
Here are a few attempts below:
#Query("select s.subscription from subscription_categories s where s.categories = ?1")
List<Subscription> findUsernameByCategory(String category);
#Query("select s.subscription from categories s where s.categories = ?1")
List<Subscription> findUsernameByCategory(String category);
Both return the same error.
Caused by: org.hibernate.hql.internal.ast.QuerySyntaxException:
categories is not mapped
My question is this:
How can I query the table created by the #ElementCollection?
You can't directly query from #ElementCollection. You should query base entity (I assume its name is Subscription).
#Query("select s from Subscription s where s.categories = ?1")
List<Subscription> findUsernameByCategory(String category);
If you want query by key, then use
#Query("select s from Subscription s where index(s.categories) = ?1")
List<Subscription> findUsernameByCategoryKey(Integer key);
I'd also side with #talex on this and argue that you need to base your query on the parent/container/base object of the #ElementCollection.
In my experience following query should suffice:
#Query("select category from Subscription subscription inner join subscription.categories category")
Side-note: Querying subscription_categories seems to be the wrong path, since this table is part of a different layer (the database layer in Sql/Jpql), while the query should be formed on the Hibernate layer (hql), which uses your entity/class-names as references.
I have used Upper-case class names, instead of lower-case table names.

insert into select with jpa error

I'm trying to do an insert into select with Jpa.
The Entity on which I try to do it is like this:
#Entity
public class A {
private String fieldOne;
private String fieldTwo;
private String fieldThree;
private B fieldFour;
#Id
public String getFieldOne(){...}
#Id
public String getFieldTwo(){...}
#Id
#OneToOne
public B getFieldThree(){...}
public String getFieldFour(){...}
....
#Entity
public class B {
private CompositeId id;
....
#EmbeddedId
public CompositeId getId(){
return MyUUIDGenerator.generateCompositeId();
}
....
The insert I'm trying to is very simple:
insert into A (fieldOne, fieldTwo, fieldThree, fieldFour)
select 'staticValueOne', 'staticValueTwo', B.id, 'staticValueFour' from B
where ....
The 'staticValueX' are values calculated by the application that I need to be all equals for a given set of B elements.
During execution the application return the exception:
java.lang.IllegalArgumentException: org.hibernate.QueryException: can
only generate ids as part of bulk insert with either sequence or
post-insert style generators [insert into ...
I don't understand why, because I don't have any generated value in A, I give to the insert all the values it need.
Does anyone has a suggestion to understand this behaviour?
Thanks!
EDIT: a little update...
I changed the class A with only a field of String type marked as #Id, but hibernate makes errors in building correctly the query: the association of tables alias with fields name miss some fields.
From JPA 2.0 specification, chapter 4.2 Statement Types:
A Java Persistence query language statement may be either a select
statement, an update statement, or a delete statement. (...)
In BNF syntax, a query language statement is defined as:
QL_statement :: = select_statement | update_statement | delete_statement
Instead of SELECT statement which is not supported in JPA (either in JPQL or Criteria API) use ElementManager.persist on an entity within a transaction. When transaction commits the entity is written to the database (SQL INSERT will be done implicitly by Hibernate which acts as the persistence provider).
EDIT: In case of a large number of insertions you may take a closer look at Hibernate's batch inserts. Another option is to give up with JPA and use JDBC's batch insertion (PreparedStatement) directly.

EJB - How to search by non-indexed fields?

I'm trying to create "search engine" on my DB.
I have a table with Id, Name and Description.
When I have an Id I can get the record with this Id by find().
But if I want to get records by Name or Description how can I do that? Did I've to set the Name as index?
Thanks.
As a general rule, if you have to frequently query a table by one of its fields and the table contains many records, it might be a good idea to create an index for that field in the database.
About the other question: if you need to get records by name, by description or by some other field and you're using JPA, then use the JPQL query language. For example, assuming that the entity is of type MyEntity (with fields id, name, description) the following query will return a list of entities with name aName:
EntityManager em = ... // get the entity manager
Query q = em.createQuery("SELECT me FROM MyEntity me WHERE me.name = :name");
q.setParameter("name", aName); // aName is the name you're looking for
List<MyEntity> results = (List<MyEntity>) q.getResultList();
Read more about the Java Persistence API in the tutorial.

Categories