Query update using hibernate runs into QueryException : Unable to resolve path - java

I have following entities, and need to update a field that is in a specific field and its removedDate is null. But the following code returns exception.
#Entity
public class Cart implements Serializable {
#Id
#GeneratedValue
private Long id;
#OneToMany(cascade = CascadeType.ALL)
#LazyCollection(LazyCollectionOption.FALSE)
private List<CartItem> items;
public Cart() {
}
getters and setters
}
#Entity
public class CartItem {
#Id
#GeneratedValue
private long id;
#ManyToOne
private Product pro;
#Temporal(javax.persistence.TemporalType.TIMESTAMP)
private Date addedDate;
#Temporal(javax.persistence.TemporalType.TIMESTAMP)
private Date removedDate;
getters and setters
}
Hibernate Code 1
Query query = session.createQuery("UPDATE CartItem SET removedDate = :currentDateTime "
+ " WHERE CartItem.id IN (Select Cart.items.id From Cart"
+ " WHERE Cart.id = :cartId"
+ " AND Cart.items.pro.id = :pro"
+ " AND Cart.items.removedDate is null)");
query.setParameter("currentDateTime", dt.getCurrentDateTime());
query.setParameter("cartId", cartId);
query.setParameter("pro", proId);
int result = query.executeUpdate();
Exception of Code 1
SEVERE: org.hibernate.QueryException: Unable to resolve path [CartItem.id], unexpected
token [CartItem] [UPDATE com.myproject.CartItem SET removedDate =
:currentDateTime WHERE CartItem.id IN (Select Cart.items.id From
com.myproject.Cart WHERE Cart.id = :cartId AND cart.items.pro.id = :proId
AND Cart.items.removedDate is null))]
at org.hibernate.hql.internal.ast.tree.IdentNode.resolveAsNakedComponentPropertyRefLHS(IdentNode.java:245)
at org.hibernate.hql.internal.ast.tree.IdentNode.resolve(IdentNode.java:110)
at org.hibernate.hql.internal.ast.tree.DotNode.resolveFirstChild(DotNode.java:177)
at org.hibernate.hql.internal.ast.HqlSqlWalker.lookupProperty(HqlSqlWalker.java:577)
at org.hibernate.hql.internal.antlr.HqlSqlBaseWalker.addrExpr(HqlSqlBaseWalker.java:4719)
Hibernate Code 2
Query query = session.createQuery("UPDATE CartItem SET removedDate = :currentDateTime "
+ " WHERE id IN (Select items.id From Cart"
+ " WHERE id = :CartId"
+ " AND items.pro.id = :pro"
+ " AND items.removedDate is null)");
Exception of Code 2
SEVERE: org.hibernate.QueryException: illegal attempt to dereference collection
[{synthetic-alias}{non-qualified-property-ref}items] with element property
reference [id] [UPDATE com.myproject.CartItem SET removedDate =
:currentDateTime WHERE id IN (Select items.id From com.myproject.Cart WHERE
id = :cartId AND items.pro.id = :pro AND items.removedDate is null)]
at org.hibernate.hql.internal.ast.tree.DotNode$1.buildIllegalCollectionDereferenceException(DotNode.java:68)
at org.hibernate.hql.internal.ast.tree.DotNode.checkLhsIsNotCollection(DotNode.java:550)
at org.hibernate.hql.internal.ast.tree.DotNode.resolve(DotNode.java:246)
at org.hibernate.hql.internal.ast.tree.FromReferenceNode.resolve(FromReferenceNode.java:118)
at org.hibernate.hql.internal.ast.tree.FromReferenceNode.resolve(FromReferenceNode.java:114)

Why don't you make your association bidirectional?
Add this to your CartItem entity:
#ManyToOne
private Cart cart;
Set the mappedBy on your cartItem fied in Cart:
#OneToMany(cascade = CascadeType.ALL, mappedBy="cart")
#LazyCollection(LazyCollectionOption.FALSE)
private List<CartItem> items;
The resulting HQL would be much simpler (and should work):
"UPDATE CartItem c SET c.removedDate = :currentDateTime "
+ " WHERE c.cart.id = :cartId"
+ " AND c.pro.id = :pro"
+ " AND c.removedDate is null"

Try this by adding an alias in the inner select query.
Query query = session.createQuery("UPDATE CartItem SET removedDate = :currentDateTime "
+ " WHERE id IN (Select cart.items.id From Cart cart"
+ " WHERE cart.id = :CartId"
+ " AND cart.items.pro.id = :pro"
+ " AND cart.items.removedDate is null)");
EDIT 2
I did a bit of reading and found out that the object.collection.id works only for a 1:1 or an N:1 relation, not for a 1:N relation, which is what you have. Try this.
SELECT items.id
FROM Cart cart
LEFT JOIN cart.items items
WHERE cart.id = :CartId AND items.pro.id = :pro AND items.removedDate is null
Here is more info, info, info

Thanks to Hrishikesh's comment, I found the answer by providing the exact SQLQuery.
UPDATE cartItem SET removedDate = :currentDateTime"
+ " WHERE pro = :pro"
+ " AND removedDate IS NULL"
+ " AND id IN
( SELECT items_id from Cart_CartItem WHERE Cart_id = :CartId)

Try that:
Query query = session.createQuery("UPDATE CartItem SET removedDate = :currentDateTime "
+ " WHERE id IN (Select ci.id From Cart c inner join c.items ci"
+ " WHERE c.id = :cartId"
+ " AND ci.pro.id = :pro"
+ " AND ci.removedDate is null)");

Related

Problem with querying entities with a set of entities in Spring Boot #Query

I want to have a Spring Boot #Query in JpaRepository which returns me entities with a set of other entities. I know that I can use findAll() method, and then take only those rows that I'm interested in, but I think that this is much slower. The problem is that my entity Booster contains a Set of other entities Boost, and when i try to query it, I get an error. So, this is my Query:
#Query( "select new app.model.Booster(" +
" B.id, " +
" B.active, " +
" B.games, " +
" B.ownerAccount, " +
" B.boosts, " +
" B.boosterNickname, " +
" B.additionalInformation) " +
"from " +
" Booster B " +
" left join Boost Bo on B.id = Bo.boostingAccount.id " +
"where " +
" B.games like %?1% " +
" and Bo.finished = true " +
"group by " +
" B.id")
List<Booster> findBoostersForOverview(String game);
These are my entity classes:
#Data
#Entity(name = "Booster")
public class Booster {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
private boolean active;
private String games;
#OneToOne(mappedBy = "boosterAccount", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Account ownerAccount;
#OneToMany(mappedBy = "boostingAccount", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Boost> boosts;
private String boosterNickname;
private String additionalInformation;
#Data
#Entity(name = "Boost")
public class Boost {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id",nullable=false,unique=true)
private long id;
private Date dateCreated;
private Date dateFinished;
private Date dateLastModification;
private boolean finished;
private boolean paused;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "boosted_account_id", referencedColumnName = "id")
private Account boostedAccount;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "boosting_account_id", referencedColumnName = "id")
private Booster boostingAccount;
#OneToMany(mappedBy = "boost", cascade = CascadeType.ALL, fetch = FetchType.EAGER)
private Set<Game> games;
private String game;
private String additionalInformation;
private String server;
This is what console shows:
Hibernate:
select
booster0_.id as col_0_0_,
booster0_.active as col_1_0_,
booster0_.games as col_2_0_,
booster0_.id as col_3_0_,
. as col_4_0_,
booster0_.booster_nickname as col_5_0_,
booster0_.additional_information as col_6_0_
from
booster booster0_
//HERE IS SOME MORE LOGS THAT ARE UNNECESSARY HERE
And this is error i get:
java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '. as col_4_0_, booster0_.booster_nickname as col_5_0_, booster0_.additional_info' at line 1
I know that I can query without Set of Boost, and then in another query get specific Boosts, but in my app there will be a lot of SQL and my code will turn to spaghetti. Is there a simple way to solve my problem?
Can you add the following to your repository without any #Query annotation and see?
List<Booster> findByGamesLikeAndBoostsFinishedIsTrue(String game);
The problem here, you are using GROUP BY. When you are using Group By you have use aggregate functions (COUNT, MAX, MIN, SUM, AVG) to group the result-set by one or more columns in the select column. Aggregate functions are mandatory when using GROUP BY function.
In your case, I hope Group By is not required. Modify your code like below.
#Query( "select new app.model.Booster(" +
" B.id, " +
" B.active, " +
" B.games, " +
" B.ownerAccount, " +
" B.boosts, " +
" B.boosterNickname, " +
" B.additionalInformation) " +
"from " +
" Booster B " +
" left join Boost Bo on B.id = Bo.boostingAccount.id " +
"where " +
" B.games like %?1% " +
" and Bo.finished = true")
List<Booster> findBoostersForOverview(String game);

SQLGrammarException: could not execute query: Column not found?

I have the following hibernate query string:
String queryString = "select \r\n" +
"cms.my_status_id as 'myStatusId',\r\n" +
"cms.status_label as 'statusLabel',\r\n" +
"csl.status_id as 'companyStatusLabel'\r\n" +
"from "+client+".corresponding_status cms \r\n" +
"join "+client+".company_status_label csl on csl.status_id = cms.my_status_id";
My Corresponding Entity is:
#Entity(name = "corresponding_status")
#Table(name = "corresponding_status")
public class CorrespondingStatus implements Serializable {
#Id
#JsonProperty
#Column(name = "my_status_id")
private Integer myStatusId;
// varchar(255)
#JsonProperty
#Column(name = "status_label")
private String statusLabel;
#JsonProperty
#Transient
private String companyStatusLabel;
However when I run the query I get:
Column 'my_status_id' not found
even though it is definitely in the DB.
What is the issue here?
In HQL you must use properties instead of database column names. Change your HQL to
String queryString = "select \r\n" +
"cms.myStatusId as 'myStatusId',\r\n" +
"cms.statusLabel as 'statusLabel',\r\n" +
"csl.status_id as 'companyStatusLabel'\r\n" +
"from "+client+".corresponding_status cms \r\n" +
"join "+client+".company_status_label csl with csl.status_id = cms.myStatusId";
EDIT:
You probably need to change company_status_label entity accordingly
EDIT2: Changed to WITH
Instead of building JPA queries by hand, I would suggest the criteria API. Your query above would change from:
String queryString = "select \r\n" +
"cms.my_status_id as 'myStatusId',\r\n" +
"cms.status_label as 'statusLabel',\r\n" +
"csl.status_id as 'companyStatusLabel'\r\n" +
"from "+client+".corresponding_status cms \r\n" +
"join "+client+".company_status_label csl on csl.status_id = cms.my_status_id";
to something akin to:
Session session = HibernateUtil.getHibernateSession();
CriteriaBuilder cb = session.getCriteriaBuilder();
CriteriaQuery<Entity> cq = cb.createQuery(Entity.class);
Root<Entity> root = cq.from(Entity.class);
cq.select(root);
Query<Entity> query = session.createQuery(cq);
List<Entity> results = query.getResultList();

Filtering the Products of my Caddie with JPQL

My entity Caddiecontains a #OneToMany relation with its Product
#Entity
public class Caddie {
#Id
#GeneratedValue
int id;
#OneToMany
#OrderBy("price")
List<Product> products = new ArrayList<>();
}
#Entity
public class Product{
#Id
#GeneratedValue
int id;
#Column(nullable=false)
String name;
}
I want all products from my caddie that are more expensive than 2$.
I have tried many things including this one :
String query = " SELECT p FROM Product p "
+ "JOIN Caddie c "
+ "WHERE c.id = 1 "
+ "AND p MEMBER OF c.products AND p.price > :price";
Unfortunately, I have two Path there. Trying :
String query = " SELECT c.products FROM Caddie c "
+ "JOIN c.products prods "
+ "WHERE c.id = 1 AND prods.price > :price";
I don't have the objects I wanted.
I could create an Entity named CaddieProduct, but I would like something cleaner.

Retrieving object children while using Transformers.aliasToBean

I've got a question regarding children retrieval when using Transformers.aliasToBean function in Hibernate. I've wrote a query which populates my object, but when I try to populate children query doesn't work. I'll post sample code first and add generated SQL and exception below.
Parent
public class Parent {
#Id
#Column(name="id")
#GeneratedValue
private long id;
#Column(name="name")
private String name;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name="parent_id")
#LazyCollection(LazyCollectionOption.FALSE)
private List<Child> children;
#Transient
private long someCount;
// public constructor + getters and setters
}
Child
public class Child {
#Id
#Column(name="id")
#GeneratedValue
private long id;
// public constructor + getters and setters
}
Query
Query query = session.createQuery("SELECT p.id as id, p.name as name, "
+ " (SELECT COUNT(*) FROM stat WHERE parent_id=p.id) as someCount, "
+ " FROM " + Parent.class.getSimpleName() + " p ")
.setResultTransformer(Transformers.aliasToBean(Parent.class));
Basically when I try to include p.children as children right in the first line of query I get the following sql generated by hibernate:
select parent0_.id as col_0_0_, parent0_.name as col_1_0_, . as col_2_0_,
(select count(*) from stats stat3_ where parent_id=parent0_.id) as col_3_0_, child2_.id
as id1_2_1_ from inner join child child2_ on parent0_.id=child2_.parent_id
And the error is:
WARN: SQL Error: 0, SQLState: 42601
ERROR: ERROR: syntax error at or near "." at position 60
That position 60 corresponds to , . as col_2_0_, the position before the dot in the first line of the query. Basically it seems to retrieve the children, but generated SQL prevents it from successfully executing the query.
Any help appreciated!
Simply speaking, you can not add p.children to result columns of SELECT, because it's a collection, and only entities or scalars are allowed here. But you can try this alternative:
Query query = session.createQuery("SELECT p, "
+ " (SELECT COUNT(*) FROM stat WHERE parent_id=p.id) "
+ " FROM " + Parent.class.getSimpleName() + " p " +
+ " LEFT JOIN FETCH p.children ");
for (Object[] row : (List<Object[]>)query.list()) {
Parent p = (Parent)row[0];
p.setSomeCount(((Number)row[1]).longValue());
}
Please tell if it really works for you.

How can i generate dynamically collections of entities with hibernate or jpa?

i have the following two jpa hibernate entities,
#Entity
public class Product {
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name="uuid", strategy="uuid2")
private String id;
#ManyToOne
private Type type;
#ManyToOne
private Attribute attribute;
}
and
#Entity
public class ProductFamily {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(generator = "uuid")
#GenericGenerator(name="uuid", strategy="uuid2")
private String id;
#ManyToMany
#Formula("("
+ " SELECT t.id "
+ " FROM Type t "
+ " WHERE t.id IN ( "
+ " SELECT p.type_id "
+ " FROM product p"
+ " WHERE p.family_id = id"
+ " ) "
+ " order by t.value asc "
+ " )")
private Set<Type> types;
#ManyToMany()
#Formula("("
+ " SELECT a.id "
+ " FROM Attribute a "
+ " WHERE a.id IN ( "
+ " SELECT p.attribute_id "
+ " FROM product p"
+ " WHERE p.family_id = id"
+ " ) "
+ " order by a.value asc "
+ " )")
private Set<Attribute> attributes;
#OneToMany(mappedBy="family")
#LazyCollection(LazyCollectionOption.FALSE)
private Set<Product> products;
}
i am trying to generate the types and attributes fields in the product family as sets of the types and attributes of the family's products. (note, the type and attribute classes are themselves entities)
the following formula are not allowed as i get the following
Caused by: javax.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.persistenceException(EntityManagerFactoryBuilderImpl.java:1225)
... 51 more
Caused by: org.hibernate.MappingException: Could not determine type for: java.util.Set, at table: ProductFamily, for columns: [org.hibernate.mapping.Formula( ( SELECT t.id FROM Type t WHERE t.id IN ( SELECT p.type_id FROM product p WHERE p.family_id = id ) ) )]
at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:336)
... 56 more
which seems to indicate a problem with mapping the result of the formula to a set of entities
is this possible with formulae? if so how?
if not, is there a standard way to do this sort of thing? if not what would you recommend as a better way to do it.
lastly, i would prefer jpa wherever possible, but as I'm looking at formula already i am open to using hibernate specific solutions
5 years ago, but for people reading this nowadays :
Near #Formula, you should add :
#ElementCollection(targetClass = X.class) where X is the type of object of the collection.

Categories