Hibernate = Criteria to query tables - spring

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)

Related

How to retrieve an Entity Object just with #Id (Proxy object), check if it exists and assign it to a #ManyToOne association

I have an entity Product which have many fields and associations (around 60).
And a table ProductView which has a #ManyToOne(fetch = FetchType.LAZY) association with Product.
Is there a optimal way to retrieve Product object and assign it to ProductView ?
If its used JPA findById(productId) or JPQL/EntityManager selects-> It will retrieve all products fields and associations
Product product = productRepository.findById(productId);
ProductView productView = new ProductView(product);
save(productView);
If its used JPA getOne -> It solves the problem but the Proxy can throw error if Product does not exists. And this error can not be handled because it happens at runtime.
Product product = productRepository.getOne(productId);
ProductView productView = new ProductView(product);
save(productView);
If a DTO is used or Interface which refers to the same Product Table -> We will get just an object with Id field, but a lot more processes will need to be added (Which I am not familiar with)
Delete foreign keys from ProductView table (#ManyToOne -> #Column) and simple assign productIds. But in this way, there will be no logic connection between tables.
ProductView DB
How usually developers avoid this problem ?
I don't understand what the problem is. Just use getOne approach and at the end of your method, use flush which will throw the constraint violation exception that you can handle. This is the way to go.

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

Map extra column from stored procedure to Entity Framework code first model

I am using Entity Framework code first with a generic repository pattern with ASP.NET MVC. I have two tables Category and Product.
My model class of product is like this
Public class Product
{
public int ProductID{get;set;}
Public int CategoryID{get;set;}
[ForeignKey("CategoryID")]
public virtual Category Category{get;set;}
[NotMapped]
public string CategoryName{get;set;}
}
The model is binding correctly as long as I am getting data using DBContext.
But I am having a problem when I am getting list of products from stored procedure mapped to Product object. So it is not mapping the Category property of Product object and hence I cannot able to get Category.CategoryName.
So I added a new property with [NotMapped] attribute in product class as CategoryName. But it is also not binding from stored procedure.
And if I remove the [NotMapped] attribute then it is correctly binding from stored procedure but error occurs again when getting product by DbContext (Linq).
Please help me in this regards.
You don't need to add an extra property, use the DbSet.SqlQuery method for queries that return entity types. The returned objects must be of the type expected by the DbSet object, and they are automatically tracked by the database context unless you turn tracking off.
var products= _context.Products.SqlQuery("storedProcedureName",params);
The columns returned by SP should match the properties of your entity type otherwise, it will throw an exception.
After execute your SP, you should be able of get the CategoryName through your Category navigation property:
var catName=someProduct.Category.CategoryName;
On the other hand, the returned data by the Database.SqlQuery isn't tracked by the database context, even if you use this method to retrieve entity types. If you want to track the entities that you get after execute your SP using this method, you can try this:
//Attach the entity to the DbContext
_context.Product.Attach(someProduct);
//The Category navigation property will be lazy loaded
var catName=someProduct.Category.CategoryName;
If you have disabled lazy loading you can load explicitly your navigation property:
//Load the Category navigation property explicitly
_context.Entry(someProduct).Reference(c => c.Category).Load();

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

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.

Entity Framework: Get Model with Linked Models in Many to Many Relationship

I'm coming from TSQL + C# land and have been trying to adapt to linq and EF. Many-to-many relationships have been tripping me up. I have models with many-to-many relationships that I want to query from a database. Such as:
class Product{
public int ID {get;set;}
public string ProductName {get;set;}
public virtual ICollection<Tag> Tags {get;set;}
}
class Tag {
public int ID {get;set;}
public string TagName {get;set;}
public virtual ICollection<Product> Products {get;set;}
}
I'm able to get a product itself out of the DbContext, and then later fetch it's associated Tags like this:
// product exists in memory as a Product with an empty product.Tags
var query = from p in db.Product
from t in db.Tags
where p.ID == product.ID
select p.Tags;
Then I can assign the product.Tags with the fetched Tags. Obviously, this is very inefficient when dealing with multiple products if I have to query for every product.
With linq and EF, I want to be able to get a Product with all of its associated Tags in one round trip to the database. Also, I want to be able to get all Products and their associated Tags (or a filtered list of Products). How do would the linq look?
Edit:
Ok, after some more fiddling around, I've got this:
var query = db.Product.Include("Tags")
.Where(p => p.Tags.Any(t => t.Products.Select(m => m.ID).Contains(p.ID)));
This is almost what I need. The results are all products with tags. Missing are the products that don't have tags. I think of this as the equivalent of a SQL inner join. I want to left outer join the tags to the product, and return all products with tags optional. How to get all products with their associated tags without excluding products that have no tags?
Edit:
This was easier than I thought.
var query2 = db.Product.Include("Tags").DefaultIfEmpty();
This gets all the products and their respective tags, including products without tags. Hopefully it works for the right reasons...
The purpose of using an object-relational mapper like EF is that it maps relationships for you. If you are manually joining objects that have foreign keys in the database, you are doing it wrong.
See my question Why use LINQ Join on a simple one-many relationship?
The correct answer is simply context.Products.Include("Tags"), which will auto-magically join Products and Tags for you. This is literally the biggest (only?) benefit of using an ORM.

Resources