Spring-Data JPA: How to make a jpa criteria query - criteria-api

Given this two entities:
post post_category
- id - post_id
- title - name
- text
I'd like to make this query using jpa criteria query:
select * from post
where post.id in (
select post_id from post_category
where name = '<category1>' and name = '<category2>' ... and name = '<categoryN>')

Looking at your query sketch I think it will not return any results. I have changed it to mean name in ('<category1>','<category2>', ...) instead. Which may not match your case.
This assumes that you have your mapping set up properly on your classes for JPA to work. For example
class PostCategory {
private String name;
private Post post;
...
#ManyToOne
public Post getPost() {
return post;
}
...
In which case your DAO code would resemble this (given a List nameValues object)
// Set up the subquery
DetachedCriteria dc = DetachedCriteria.forClass(PostCategory.class)
.setProjection(Projections.property("post.id"))
.add(Restrictions.in("name", nameValues));
Criteria crit = session.createCriteria(Post.class)
.add(Subqueries.eqProperty("id", dc));
But if you want a list of Post objects which have ALL of the categories then you will need to make a separate DetachedCriteria instance for each category and add each as an AND'ed Subquery.

Related

Spring Open Projection - Object and Additional Value

Let's assume we have a model like this:
class User {
String name;
Integer age;
}
The query returns results, and the extraField is mapped, but the user isn't.
I have a query that joins the User table with another table and returns the User object and one more field that comes from this other table.
Is it possible to define an open projection in Spring that would map the return values from the query? I guess it should look something like the following, but I can't get it to work.
interface UserProjection {
User getUser();
#Value("#{target.extrafield}")
String getExtraField();
}

Problem with MongoDB #Query annotation/Query

I got problems with #Query annotation or Query query = new Query() - I can't include/exclude some fields.
I find code samples like:
#Query(value = "{'id': ?0}",fields = "{'id':1}")
User findUserById(String id);
Result should be user with only one field -> id but its showing other fields to..
So I found other samples like:
Query query = new Query();
query.fields().include("id");
query.addCriteria(Criteria.where("id").is(id));
User one = mongoTemplate.findOne(query, User.class);
Same result there..
Any ideas?
Integer findUserById(String id);
Use the DataType of Id [like Integer] to get only id.
Change datatype of according the requirement/usecase

SpringData JPA: Query with collection of entity as parameter

I have a list of entities on which I want to perform an update, I know I could update the table with list of String/Integer.. etc as the parameter with something like
#Query("update tableName i set i.isUpdated = true where i.id in :ids")
void markAsUpdated(#Param("ids") List<Integer> itemIds);
I'm trying to avoid repeated conversion of list of entities to list of Ids for making the query in DB. I know there are deleteAll and deleteInBatch commands which accept parameter as list of entities.
How do I do this in JPA Query, I tried the following but it didn't work yet.
#Modifying(flushAutomatically = true, clearAutomatically = true)
#Query("update tableName i set i.updated = true where i in :items")
void markAsUpdated(#Param("items") List<Item> items)
The query needs ids, it doesn't know how to deal with entities.
You have multiple options:
Just pass ids to the method, the client is responsible for extracting ids.
Pass entities and use SpEL for extracting ids
As suggested in the comments use a default method to offer both APIs and to delegate from one to the other.
As for the question that came up in the comments: You can move the method for extracting the id into a single method by either have relevant entities implement an interface similar to this one:
interface WithId {
Long getId();
}
Or by passing a lambda to the method, doing the conversion for a single entity:
List<ID> extractIds(List<E> entities, Function<E, ID> extractor) {
// ...
}

How to update a row by id in jpa hibernate?

I was trying to update a row in db by fetching the idByName, but instead it's adding a new row? Please help me out.
This is my controller code:
#PostMapping("/retailer/update")
public ModelAndView updateRetailer(#ModelAttribute("retailer") Retailer retailerDetails)
{
System.out.println(retailerDetails.toString());
System.out.println("method called");
UUID id=repository.findIdByName(retailerDetails.getBusinessName());
System.out.println(retailerDetails.getBusinessName());
System.out.println(id);
String name=retailerDetails.getBusinessName();
System.out.println(name);
//Retailer retailer=repository.findOne(rid);
if(id == null)
{
return null;
}
retailerDetails.setBusinessName(retailerDetails.getBusinessName());
retailerDetails.setCity(retailerDetails.getCity());
retailerDetails.setIsActive(retailerDetails.getIsActive());
retailerDetails.setStartDate(retailerDetails.getStartDate());
retailerDetails.setUrl(retailerDetails.getUrl());
repository.save(retailerDetails);
return new ModelAndView("welcome");
}
This is my Repository code:
public interface RetailerRepository extends JpaRepository<Retailer, UUID>{
#Query("SELECT r.id FROM Retailer r where r.businessName=:name")
UUID findIdByName(#Param("name") String name);
}
You should forget to set the "id" to the retailerDetails entity. Spring JPA will only execute the update action when the primary key is set.
Added the following line in your code.
retailer.setId(id);
repository.save(retailer);
You should simply fetch by BusinessName.
Repository method be:
Retailer findByBusinessName(#Param("name") String name);
updateRetailer method be like:
Retailer retailer = findByBusinessName(retailerDetails.getBusinessName());
if(retailer!=null){
retailer.setBusinessName(retailerDetails.getBusinessName());
retailer.setCity(retailerDetails.getCity());
retailer.setIsActive(retailerDetails.getIsActive());
retailer.setStartDate(retailerDetails.getStartDate());
retailer.setUrl(retailerDetails.getUrl());
repository.save(retailer);
}
Your controller looks great and so is your repository. There is something that you should know when it comes to updating an entity with hibernate. Hibernate will save(create new object) if the id for the given item is null during the transaction and hibernate will save(update the current instance in the database) if the id exist.
The flow for this update I presume is when the request to update is requested, you retrieve the object based on the specified entity then return to the view. One thing to take note of is to carry your id with your even in the view.
Make sure you include the id in a hidden input like
from there the id will be submitted in addition to the updated data to the post method.
Most update issues similar to this can be fixed by versioning entity with the #Version
Use the format of the Spring Data JPA to successfully query without writing any query.
#Repository
public interface RetailerRepository extends JpaRepository{
// Assuming there is an attribute businessName in the Retailer entity.
UUID findByBusinessName(String businessName);
}

Hibernate = Criteria to query tables

I would like to query hibernate just by passing it an object and I thought this was supported but I guess it isnt as my query returns all the objects in my Product table.
I have a product and a product has a set of categories and I would like to return all products that have that category.
Category is just an id (which I am passing as null as I want to get by name) and a name which is a string which I am setting on the category object, and then passing to the Product object by adding it to a set attached to the product.
I pass an object to the spring rest client and convert this to a hibernate entity and then I thought I could just pass it to hibernate with the properties I want to filter by set:
public List<Product> getProductsByFilterCriteria(Product productToLocate) {
Session session = sessionFactory.getCurrentSession();
List<Product> products = new ArrayList<Product>();
//Just maps the values
ProductEntity criteria = mapProductCriteriaToEntity(productToLocate);
#SuppressWarnings("unchecked")
List<ProductEntity> productsMatchingCriteria = (List<ProductEntity>)session.createCriteria(ProductEntity.class).add(Example.create(criteria).excludeZeroes()).list();
for(ProductEntity productEntity : productsMatchingCriteria) {
products.add(mapProductEntityToProduct(productEntity));
}
return products;
}
I have set up the product entity with a list which contains a single category, and no other properties are set.
How do I just pass the Product and its set of Categories to hibernate and get out all products which have a Category of whatever the category name is?
From the documentation:
17.8. Example queries
The class org.hibernate.criterion.Example allows you to construct a query criterion from a given instance.
[...]
Version properties, identifiers and associations are ignored.
(emphasis mine)

Resources