Spring Data Jpa how to select all columns by using CriteriaQuery - spring-data-jpa

In the following code, if I used query.multiselect(generalVecEntityRoot) to query all columns in entity, it throws exception, Unable to locate appropriate constructor as in
org.hibernate.hql.internal.ast.QuerySyntaxException: Unable to locate appropriate constructor on class [general.GeneralVecEntity]. Expected arguments are: general.GeneralVecEntity [select new general.GeneralVecEntity(generatedAlias0) from general.GeneralVecEntity as generatedAlias0 where ( generatedAlias0.groupId in (:param0) ) and ( generatedAlias0.appId=:param1 )]
However, instead, listing columns one by one would work, but code could be tedious and hard to read, so I would like to use Root object to query all columns, but no idea what was wrong. The following are query function and entity definition. Thanks.
private List<GeneralVecEntity> findGeneralVecByGroupIdInAndAppIdGroupingByGroupId(String appId, String[] groupIds) {
CriteriaBuilder cb = em.getCriteriaBuilder();
CriteriaQuery<GeneralVecEntity> query = cb.createQuery(GeneralVecEntity.class);
Root<GeneralVecEntity> generalVecEntityRoot = query.from(GeneralVecEntity.class);
query.where(generalVecEntityRoot.get("groupId").in(groupIds), cb.equal(generalVecEntityRoot.get("appId"), appId));
CriteriaQuery<GeneralVecEntity> cq = query.multiselect(generalVecEntityRoot.get("id"),
generalVecEntityRoot.get("groupId"),
generalVecEntityRoot.get("appId"),
generalVecEntityRoot.get("vec"),
generalVecEntityRoot.get("createTime"));
return em.createQuery(cq).getResultList();
}
#Entity
#Getter
#Setter
#ToString
#Table(name = "general_vec")
#AllArgsConstructor
#NoArgsConstructor
public class GeneralVecEntity {
#Id
#GeneratedValue
private Long id;
private String groupId;
private String appId;
#Lob
#Column(name="vec", columnDefinition="TEXT NOT NULL")
#Convert(converter= DbConverter.ListToJson.class)
private float[] vec;
#Column(name="create_time",columnDefinition="timestamp", updatable = false)
#CreationTimestamp
private LocalDateTime createTime;
}

Related

How to append where clause to all queries that run with spring data MongoRepository?

I have entities that are persisted in MongoDB and use spring data MongoRepository to fetch data. Now i want to apply filter to all queries that executed on the entites, so i decided to use hibernate filter, something like this:
#Entity
#QueryEntity
#Document(collection = "Opportunity")
#NoArgsConstructor
#AllArgsConstructor
#Getter
#Setter
#CompoundIndexes({
#CompoundIndex(name = "productGroup_userId_uniqueness", def = "{'productGroupCode' : 1, 'userId': 1}", unique = true)
})
#FilterDef(name = "defaultFilter",parameters = #ParamDef(name = "unitCode",type = "string"))
#Filter(name = "defaultFilter" , condition = " unitCode like :unitCode")
public class Opportunity {
#Id
#Indexed
private String id;
#Indexed
#Enumerated(EnumType.STRING)
private OpportunityStatus opportunityStatus = OpportunityStatus.OPEN;
private LeadType leadType;
#Indexed
private String userId;
#Indexed
private String productCode;
#Indexed
private String productGroupCode;
#Indexed
private Long actionId;
private String assigneeId;
#Transient
private List<AbstractCommand> commandHistory = new ArrayList<>();
#Transient
private Map<Long, Boolean> actionStatus = new HashMap<>();
private String unitCode;
}
and this is the repository class:
#Repository
public interface OpportunityRepository extends MongoRepository<Opportunity, String>, QuerydslPredicateExecutor<Opportunity> {
// this repository contains more than 20 methods
// and all of theme removed for question brevity
}
And I enabled hibernate filter on session with this way:
Session session = (entityManager).unwrap(Session.class);
session.enableFilter(filterName).setParameter("unitCode", this.getCurrentUserUnitCode());
Now, when I call OpportunityRepository.findAll(Predicate predicate, Pageable pageable) i expected to apply the defined filter on the entity, but it didn't work.
I think the reason is that MongoRepository hasn't any sense of hibernate #Filter and i should use another way to append where clause to all mongo queries that running on the Opportunity entity.

Relation XX does not exist; could not extract Resultset error while using a view convertHibernateAccessException

I am using a view in my entity class. Although the view-column mappings are exactly the same as in entity class, I am receiving the exception "ERROR: relation "location_view" does not exist - Position: 15 - underlying exception is SQLGrammarException could not extract ResultSet at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:261)"
When I replace the view to the underlying SQL query, it works in SpringBoot. Can you please let me know where I am going wrong?
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#Data
#ToString
#Entity
#Table(name = "location_view")
public class Location{
#Id
#Column(name = "id")
long id;
#Column(name = "name")
String name;
#Column(name = "total")
long total;
#Column(name = "created_by")
long createdBy;
#Column(name = "created_on")
Timestamp createdOn;
}
public interface LocationDAO extends JpaRepository<Location, Long> {
#Query(value = "select * from location_view", nativeQuery = true)
List<Location> getLocations();
}
In my controller, I am making call to locationDAO.getLocations();
Probably the table/view is in a different schema? Or maybe the user for the JDBC connection has no permission to read from that table/view?

Spring batch JdbcCursorItemReader : reading from tables having FK relation

Here's my Reader :
private static final String SELECT_ALL_BOOKS = "SELECT * FROM BOOKS WHERE COLOR = 'yellow'";
#Bean
public JdbcCursorItemReader<BookEntity> itemReader(final DataSource dataSource) {
return new JdbcCursorItemReaderBuilder<BookEntity>()
.name("book_reader")
.sql(SELECT_ALL_BOOKS)
.dataSource(dataSource)
.rowMapper(new BeanPropertyRowMapper<>(BookEntity.class))
.build();
}
And my entity :
#Entity
#Getter
#Setter
#Table(name = "book")
#AllArgsConstructor
#NoArgsConstructor
public class BookEntity implements java.io.Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_book")
private Integer idBook;
#Column(name = "color")
private String color;
#Column(name = "page_number")
private Integer pageNumber;
#Column(name = "read")
private Boolean read;
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.ALL})
#JoinColumn(name = "id_author")
private Author author;
...
...
}
Problem is : with my job executing my step, I'm having the books but with an author = null. So the foreign key is not mapped correctly, all the other String/Integer/Boolean fields are retrieved correctly.
I'm new with Spring batch so maybe I'm using a wrong kind of reader.
Any one has an idea about the problem ? Thanks
Please refer this- You are using JDBC Item reader that is mapped to native columns and properties are binded by BeanPropertyRowMapper
https://docs.spring.io/spring-batch/docs/current/reference/html/readersAndWriters.html#JdbcCursorItemReader
Change the query join with AUTHOR tables as a native query or use JPA readers to support ORM
Below reference will give repository reader
https://docs.spring.io/spring-batch/docs/current/api/org/springframework/batch/item/data/RepositoryItemReader.html
Below example give some idea to implement and its SO references
https://github.com/gpassini/Spring-Batch-RepositoryItemReader-NativeQuery-Example/tree/master/src/main

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

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

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));