Named Query with List of object as input using JPA named query - jpa

Getting below QueryExecutionRequestException when I try to excecute update Named query using JPA repository.
org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations [update com.company.farmer.entity.FarmProducts p set p.isDeleted=:isDeleted where p.productId IN (:productIdsList_0, :productIdsList_1)]; nested exception is java.lang.IllegalStateException: org.hibernate.hql.internal.QueryExecutionRequestException: Not supported for DML operations [update com.company.farmer.entity.FarmProducts p set p.isDeleted=:isDeleted where p.productId IN (:productIdsList_0, :productIdsList_1)]
Code:
#Repository
public interface FarmProductRepository extends JpaRepository<FarmProducts, Long> {
void deleteProduct(#Param("isDeleted") String isDeleted, #Param("productIdsList") List<Long> productIdsList);
}
#Override
public String deleteProductAndCategory(long categoryId, FarmProductIdsDTO farmProductIds) {
farmProductRepository.deleteProduct(FarmerProductCategoryConstants.DELETE_YES_FLAG, farmProductIds.getFarmProductIds());
return FarmerProductCategoryConstants.SUCCESS;
}
#Entity
#Table(name="farm_products")
#Getter
#Setter
#ToString(exclude= "productCategory")
#NoArgsConstructor
#AllArgsConstructor
#NamedQueries({#NamedQuery(name="FarmProducts.deleteProduct", query="update FarmProducts p set p.isDeleted=:isDeleted where p.productId IN (:productIdsList)") })
public class FarmProducts extends BaseModel {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "FARM_PRODUCT_GENERATOR")
#SequenceGenerator(name = "FARM_PRODUCT_GENERATOR", sequenceName = "FARM_PRODUCT_GENERATOR_SEQ", allocationSize = 1)
#Column(name = "farm_product_id")
public Long productId;
#ManyToOne
#JoinColumn(name = "farm_product_category_id")
#JsonIgnoreProperties("products")
public ProductCategory productCategory;
#Column(name = "product_name")
public String product;
#Column(name = "is_deleted")
public String isDeleted;
}
I am trying to pass a list of productId to make the isDeleted as "N". But update functionality fails because of the QueryExecutionRequestException.

By default Spring Data treats all queries as SELECT statements. If you have an UPDATE (or DELETE) statement you have you have to apply de #Modifying annotation on the query.
See https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.modifying-queries

Related

JPA Repository Query on additional table #ManytoMany

I want to do select like this in my jpa spring repository
SELECT sicknes_id, count(symptomp_id) as ilosc FROM symptomp_sicknes where symptomp_id IN (1,2) group by sicknes_id Order by ilosc DESC;
My enitity
#Entity
#Table(name = "symptomp")
public class Symptomp {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "symptomp_id")
private Long symptomp_id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#ManyToMany(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH}, fetch = FetchType.LAZY)
#JoinTable(name = "symptomp_sicknes",joinColumns = #JoinColumn(name = "symptomp_id"),inverseJoinColumns = #JoinColumn(name = "sicknes_id"))
private Set<Sicknes> sicknes = new HashSet<>();
#Entity
#Table(name = "sicknes")
public class Sicknes {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "sicknes_id")
private Long sicknes_id;
#Column(name = "name")
private String name;
#Column(name = "description")
private String description;
#ManyToOne(cascade = {CascadeType.DETACH,CascadeType.MERGE,CascadeType.PERSIST,CascadeType.REFRESH}, fetch = FetchType.LAZY)
#JoinColumn(name = "speciesId")
private Species species;
My Symptomp repository:
public interface SymptompRepository extends JpaRepository<Symptomp, Long> {
#Query("select p from Symptomp p where name like ?1%")
public List<Symptomp> findAllBySymptompName(String symptomp);
public Symptomp findByName(String symptomp);
public List<Symptomp> findByNameIn(List<String> list);
Integer countDistinctSymptompByName(String id);
}
How I can create this select in my JPA repository?
I try get value like in select but i got error mapping bean.
You can get query result as List<Object[]> using nativeQuery=true parameter
#Query("SELECT sicknes_id, count(symptomp_id) as ilosc FROM symptomp_sicknes where symptomp_id IN (1,2) group by sicknes_id Order by ilosc DESC", nativeQuery=true)
List<Object[]> getQueryResult();
Other option is to create dto class with appropriate constructor
public class QueryResultDto {
Long sicknesId;
Long count;
public QueryResultDto(Long sicknesId, Long count) {
this.sicknesId = sicknesId;
this.count = count;
}
}
Then using JPQL
#Query("select new yourproject.dtopath.QueryResultDto(...")
List<QueryResultDto> getQueryResult(#Param("symptompIds") List<Long> symptompIds);
If you want to avoid a native Query the best way is to create an Entity for that JoinTable. Then you can query it easily. Additional benefit if this is that if in future a requirement will pop up that you have to store additional attributes in that relation you will have the Entity already there to do that easily.

Query for joins in Spring JPA

I have the below entities
#Entity
#Getter
#Setter
public class Aggregate {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "aggregate")
private Set<Single> singleSet;
}
#Entity
#Getter
#Setter
public class Single {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private String id;
private Integer number;
#ManyToOne
#JoinColumn(name = "agg_id")
private Aggregate aggregate;
}
I also have the below repository
public interface AggregateRepo extends CrudRepository<Aggregate, Long> {
}
I want to return all associated Single records where number in object Single is equal to some random number
I am assuming that the query will be something like this
public interface AggregateRepo extends CrudRepository<Aggregate, Long> {
public List<Single> findBySingleSet_Number(Integer number);
}
However when I try to use Intellij to complete my named query it always populates like this
public interface AggregateRepo extends CrudRepository<Aggregate, Long> {
public List<Single> findBySingleSet_Empty_Number(Integer number);
}
I am wondering what the Empty stands for ?
Also should I create another Single repository since the query is related to returning Single records.

spring data error when trying to sort by a field of joined entity inside a crudrepository

I am using springboot and springdata with Mysql.
I have 2 entities, Customer & Order:
#Entity
#Table(name = "customers")
public class Customer {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id", nullable = false)
protected long id;
#Column(name = "name")
private String name;
}
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id", nullable = false)
protected long id;
#Column(name="customer_id")
private long customerId;
}
I also have a repository:
#Repository
public interface OrdersRepository extends JpaRepository<Order, Long> {
#Query("select o from Order o, Customer c where o.customerId = c.id")
Page<Order> searchOrders(final Pageable pageable);
}
The method has some more arguments for searching, but the problem is when I send a PageRequest object with sort that is a property of Customer.
e.g.
Sort sort = new Sort(Sort.Direction.ASC, "c.name");
ordersRepository.search(new PageRequest(x, y, sort));
However, sorting by a field of Order works well:
Sort sort = new Sort(Sort.Direction.ASC, "id");
ordersRepository.search(new PageRequest(x, y, sort));
The error I get is that c is not a property of Order (but since the query is a join of the entities I would expect it to work).
Caused by: org.hibernate.QueryException: could not resolve property c of Order
Do you have any idea how I can sort by a field of the joined entity?
Thank you
In JPA , the thing that you sort with must be something that is returned in the select statement, you can't sort with a property that is not returned
You got the error because the relationship is not modeled properly. In your case it is a ManyToOne relation. I can recomend the wikibooks to read further.
#Entity
#Table(name = "orders")
public class Order {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
#Column(name="id", nullable = false)
protected long id;
#ManyToOne
#JoinColumn(name="customer_id", referencedColumnName = "id")
private Customer customer;
}
The query is not needed anymore because the customer will be fetched.
#Repository
public interface OrdersRepository extends PagingAndSortingRepository<Order, Long> {
}
Now you can use nested properties.
Sort sort = new Sort(Sort.Direction.ASC, "customer.name");
ordersRepository.findAll(new PageRequest(x, y, sort));

spring data jpa for multiple joined table

I have two tables: ProductUsage and Learner. ProductUsage have field Learner, Learner has fields id and guid. now I need to create a query to pull out all productUsage whose learner guid is in specified user ids:
SQL:
select * from product_usage
inner join learner
on product_usage.learner_id = learner.id
where
learner.guid in ("1234", "2345")
domain class:
#Data
#NoArgsConstructor
#Entity
#Table(name = "core_product_usage_increments")
public class ProductUsage {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#ManyToOne
#JoinColumn(name = "learner_id", nullable = false)
private Learner learner;
#ManyToOne
#JoinColumn(name = "learning_language_id", nullable = false)
private Language language;
}
#Data
#NoArgsConstructor
#Entity
#Table(name = "learners")
public class Learner {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "user_guid", nullable = false, unique = true)
private String guid;
#Column(name = "first_name")
private String firstName;
#Column(name = "last_name")
private String lastName;
}
and repository class:
#Repository
public interface ProductUsageRepository extends CrudRepository<ProductUsage, Integer> {
#Query("SELECT p FROM ProductUsage p WHERE p.learnerGuid = :learnerGuid")
List<ProductUsage> findByLearnerGuid(String learnerGuid);
}
client class that call the repository
#Component
public class MyClient {
#Autowired
private ProductUsageRepository repository;
public MyClient(ProductUsageRepository repository) {
this.repository = repository;
}
public List<ProductUsage> getProductUageByLeanrerGuid(String learnerGuid) {
return repository.findByLearnerGuid(learnerGuid);
}
}
and my test:
#Test
public void testClient() throws Exception {
MyClient client = new MyClient(repository);
List<ProductUsage> results = client.getProductUageByLeanrerGuid("1234");
assertNotNull(result);
}
and it failed:
Caused by: java.lang.IllegalArgumentException: org.hibernate.QueryException: could not resolve property: learnerGuid of: com.acme.domain.spectrum.ProductUsage [SELECT p FROM com.acme.domain.spectrum.ProductUsage p WHERE p.learnerGuid = :learnerGuid]
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1364)
at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1300)
at org.hibernate.ejb.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:294)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
it cannot recognize the 'learnerGuid' field in ProductUsage, but that's actually defined in Learner class. how can I perform the query that join multiple tables?
ProductUsage has no learnerGuid property, only learner. Try
#Query("SELECT p FROM ProductUsage p WHERE p.learner.guid = :learnerGuid")
If that doesn't work, I have another tip:
#Query("SELECT p FROM ProductUsage p join p.Learner l WHERE l.guid = :learnerGuid")
You do not have use a #query like you did
#Query("SELECT p FROM ProductUsage p WHERE p.learnerGuid = :learnerGuid")
List<ProductUsage> findByLearnerGuid(String learnerGuid);
Spring JPA framework can build the query by method name itself. Try this
List<ProductUsage> findByLearnerGuid(String learnerGuid);
or
List<ProductUsage> findByLearner_guid(String learnerGuid);
as you have a relation to Learner from ProductUsage the findBy method can traverse through the related tables and their fields. "_" gives the framework a clear indication that query by joining the Learner table where guid =?
Otherwise the framework tries below two combinations:
where learnerGuid=?
join learner where guid=?

JPA query many to one association

I want to build the following pseudo query
Select a From APDU a where a.group.id= :id
group is a field in APDU class of the type APDUGroup.class.
I just want to get a list of APDUs based on APDUGroup's id.
How do i do that using a standard JPA query?
UPDATE
Yes, I have tried the above query and tried other variations for hours before posting in S/O. Here is the generated SQL for the query above:
SELECT t1.ID, t1.status, t1.type, t1.modified, t1.response, t1.expectedSize, t1.created, t1.description, t1.sequence, t1.name, t1.command, t1.recurring, t1.auth, t1.createdBy, t1.APDUGroup, t1.modifiedBy FROM APDUGroup t0, APDU t1 WHERE ((t0.ID = ?) AND (t0.ID = t1.APDUGroup))
The query looks okay but nothing get selected from my table.
There are at least 100 APDUs with APDUGroup = 1 in my test database.
I'm using eclipselink as the JPA provider.
Given the following Entities:
#Entity
public class APDU implements Serializable {
#Id
#GeneratedValue
private Long id;
#ManyToOne
private APDUGroup group;
//...
}
#Entity
public class APDUGroup implements Serializable {
#Id
#GeneratedValue
private Long id;
//...
}
The following query will return a list of APDUs for a given APDUGroup id:
select a from APDU a where a.group.id = :id
Oh, wait, that's your query :)
Entity 1:
#Entity
#Getter
#Setter
#Table(name = "invoices")
public class Invoice implements Serializable {
#Id
#GeneratedValue
#Column(name = "invoice_id", updatable = false, nullable = false)
private Long invoiceId;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "person_id", referencedColumnName = "person_id", insertable = false, updatable = false, nullable = false)
private Person person;
//...
}
Entity 2:
#Entity
#Getter
#Setter
#Table(name = "people")
public class Person implements Serializable {
#Id
#GeneratedValue
#Column(name = "person_id", updatable = false, nullable = false)
private Long personId;
//...
}
Finally, Your Data Access Object (JPA Repository)
#Repository
public interface InvoiceRepository extends JpaRepository<Invoice, Long> {
#Query(value="SELECT x FROM Invoice x WHERE x.person.personId = :myPersonId")
List<Invoice> findInvoiceByPersonId (long myPersonId);
}
I hope this example has been helpful :)