I have category class as list in entity. How can I fill this entity with a native query?
Product.java
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer pid;
private String name;
#ManyToMany
#JoinTable(
name = "product_category ",
joinColumns = #JoinColumn(
name = "pro_id", referencedColumnName = "pid"),
inverseJoinColumns = #JoinColumn(
name = "cat_id", referencedColumnName = "cid"))
private List<Category> Categories;
}
Category.java
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long cid;
private String catname;
private String desc;
#ManyToMany(mappedBy = "categories")
private List<User> users;
}
How should I write a query? How can I fill the Categories list
#Query(value = "*****", nativeQuery = true)
List<Product> productList();
Instead of native query you can use hibernate queries like the below to find something useful.
Find all
ProductRepository.findAll();
Hibernate query
#Query(select product from Product product join product.categories categories)
List<Product> getAllProducts();
you can also sort the above queries using Pageable Object.
Could you please elaborate your question. As you can get the collection list if it is mapped just by find all function of Product Repository.
However, if you are looking to insert collection you can use cascade type in Categories field of Product Repository. This will automatically insert the categories entity once you save the Product entity to database by setting categories list in Product entity.
Related
I have two entities mapped Board and Tag by #ManyToMany to a join table board_tag_table.
How would I return the top 5 most common tag_id in the board_tag_table?
enter image description here
public class Board {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToMany(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
#JoinTable(name = "board_tag_table",
joinColumns = {
//primary key of Board
#JoinColumn(name = "id", referencedColumnName = "id")
},
inverseJoinColumns = {
//primary key of Tag
#JoinColumn(name = "tag_id", referencedColumnName = "tag_id")
})
private Set<Tag> tags = new HashSet<>();
}
public class Tag {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer tag_id;
private String tagname;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "tags")
private Set<Board> boards = new HashSet<>();
}
Unable to find how to query within a many to many table
you can pass through foreach and write your query in Tag repository, but I think you can't write query, because they are have list from two sides
Consider using a native query instead.
If you want to use the JPA, you can add a field (Eg. usedCount) in the Tag entity and follow the instructions here https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.limit-query-result.
The query should look like this:
List<Tag> findByUsedCount(Sort sort, Pageable pageable);
Don't look at it as trying to access the board_tag_table, and instead look at how you would do this with the java entities themselves. This would be just selecting the 5 top Tags based on the number of boards they have. "select t.tag_id, count(b) as boardCount from Tag t join t.boards b group by t.tag_id order by boardCount", then use maxResults to limit the returned values to 5
I have a Product entity. One product can be a child of another. The relationship is mapped with OneToMany as follows.
#OneToMany(mappedBy = "parentProd")
private Set<Products> childProd = new HashSet<>();
#ManyToOne
#JsonIgnoreProperties("childProd")
private Products parentProd;
The parent_product_id column generated from this mapping somehow contains invalid parent_ids. Hence I am getting the following error when i try to query all products using JPA criteria query.
javax.persistence.EntityNotFoundException: Unable to find com.test.Products with id 101
at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl$JpaEntityNotFoundDelegate.handleEntityNotFound(EntityManagerFactoryBuilderImpl.java:162)
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:234)
How to write a criteria query to select all Product entities without considering the parent_child relation?
#Entity
#Table(name = "products")
public class Products implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "productName")
private String productName;
#OneToMany(mappedBy = "parentProd")
private Set<Products> childProd = new HashSet<>();
#ManyToOne
#JsonIgnoreProperties("childProd")
private Products parentProd;
//Getters And Setters
}
Change the oneToMany annotation this way:
#OneToMany(fetch = FetchType.LAZY, mappedBy = "parentProd")
Here fetch = FetchType.Lazy means that data will not be fetched if you don't ask it explicitly.
I have 2 Entities : User.java and PushNotification.java
and mapping table named as userpushnotification where many-to-many mapping takes place.
the existing data in userpushnotification table is a kind of important for me.
So if i try to add users(let's say id=5,6,7) for pushnotification(id=2), hibernate deletes the previous data for pushnotification(id=2) in relationship table and then it adds the new users for that pushnotification(id=2).
My need is to keep all the records in relationship table.
So how can i restrict the Hibernate/JPA to execute only insert queries intstead of executing delete and insert queries.
In simple words, I just want to append data in relationship table instead of overwriting.
User.java :-
#Entity
#Table(name = "user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "id")
#GenericGenerator(name = "gen", strategy = "identity")
#GeneratedValue(generator = "gen")
private long id;
#Column(name = "username")
private String username;
#Column(name = "password")
private String password;
#Column(name = "authkey")
private String authKey;
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "users")
private Set<PushNotifications> pushNotifications = new HashSet<PushNotifications>();
PushNotifications.java :-
#Entity
#Table(name = "pushnotifications", uniqueConstraints = #UniqueConstraint(columnNames = { "id" }))
public class PushNotifications implements Serializable {
#Id
#Column(name = "id")
#GenericGenerator(name="gen",strategy="identity")
#GeneratedValue(generator="gen")
private long id;
#Column(name = "shortdescription")
private String shortDescription;
#Column(name = "title")
private String title;
#JsonIgnore
#ManyToMany(cascade = { CascadeType.ALL }, fetch = FetchType.LAZY)
#JoinTable(name = "userpushnotifications",
joinColumns = {#JoinColumn(name = "pushnotificatoinId") },
inverseJoinColumns = { #JoinColumn(name = "userId") })
private Set<User> users = new HashSet<User>();
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
When i try to do this :
PushNotifications notifications = iNotificationService
.getNotification(notificationId);
Set<User> newUsers = new HashSet<User>();
newUsers .add(newUserToBeNotified_1);
newUsers .add(newUserToBeNotified_2);
notifications.setUsers(newUsers);
sessionFactory.getCurrentSession().merge(notifications);
Here, am tring to add 2 users for that notification type, Already there is one user for that notification type in relationship table.
Hibernate executing these queries :
Hibernate:
delete
from
userpushnotifications
where
pushnotificatoinId=?
and userId=?
Hibernate:
insert
into
userpushnotifications
(pushnotificatoinId, userId)
values
(?, ?)
Hibernate:
insert
into
userpushnotifications
(pushnotificatoinId, userId)
values
(?, ?)
So, i hope u got me, i dont want hibernate to make delete operations.
Please help me resolve this, Looking for answers...
Thanks in advance.
You could use Blaze-Persistence for this which works on top of JPA and provides support for DML operations for collections. A query could look like the following
criteriaBuilderFactory.insertCollection(entityManager, PushNotifications.class, "users")
.fromIdentifiableValues(User.class, "u", newUsers)
.bind("id", notification.getId())
.bind("users").select("u")
.executeUpdate();
After setting Blaze-Persistence up like described in the documentation you can create a repository like this:
#Component
class MyRepository {
#Autowired CriteriaBuilderFactory cbf;
#Autowired EntityManager em;
public void addUsers(Collection<User> newUsers) {
cbf.insertCollection(em, PushNotifications.class, "users")
.fromIdentifiableValues(User.class, "u", newUsers)
.bind("id", notification.getId())
.bind("users").select("u")
.executeUpdate();
}
}
This will issue just the insert into the join table.
I see a lot of posts where Eager fetch performs left join of child table parent table in hibernate. But when I use springboot , hibernate fires seperate sql queries - means one select query for parent table and one select query for child table. Why is there a difference? Has there been any upgrades in springboot or is it something I am doing wrong ?
Below are the entities I am using:
Order Entity:
#Entity
#Table(name="Ordertable", schema="cf_2583f365_c3c6_499a_a60d_138e7e7023eb")
public class Order {
#Id
#Column(name = "ORDER_ID")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int orderId;
#Column(name = "DAT_SRC_ID")
private String dataSourceId;
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "ORDER_CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID")
private Customer customer;
}
Customer Entity:
#Entity
#Table(name="Customer", schema="cf_2583f365_c3c6_499a_a60d_138e7e7023eb")
public class Customer {
#Id
#Column(name = "CUSTOMER_ID")
#GeneratedValue(strategy=GenerationType.IDENTITY)
private Long customerId;
#Column(name = "CUSTOMER_NAME")
private String customer_name;
#Column(name = "CUSTOMER_address_id")
private int customer_address_id;
#Column(name = "DAT_SRC_ID")
private String dataSourceId;
#OneToMany(fetch=FetchType.EAGER)
#JoinColumn(name = "ORDER_CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID")
private List<Order> order;
}
Controller:
#RequestMapping(value="/getByCustid/{id}", method=RequestMethod.GET,produces=MediaType.APPLICATION_JSON_VALUE)
public Customer getByCustid (#PathVariable Long id) {
Customer s1 = customerRepository.findByCustomerId(id);
return s1;
}
Repository:
public interface CustomerRepository extends JpaRepository<Customer,Long> {
public Customer findByCustomerId(Long customerId);
}
Below are the queries that are getting executed:
select
customer0_.CUSTOMER_ID as CUSTOMER1_0_,
customer0_.CUSTOMER_address_id as CUSTOMER2_0_,
customer0_.CUSTOMER_NAME as CUSTOMER3_0_,
customer0_.DAT_SRC_ID as DAT_SRC_4_0_
from
Customer customer0_
where
customer0_.CUSTOMER_ID=?
select
order0_.ORDER_CUSTOMER_ID as ORDER_CU3_5_0_,
order0_.ORDER_ID as ORDER_ID1_5_0_,
order0_.ORDER_ID as ORDER_ID1_5_1_,
order0_.ORDER_CUSTOMER_ID as ORDER_CU3_5_1_,
order0_.DAT_SRC_ID as DAT_SRC_2_5_1_
from
Ordertable order0_
where
order0_.ORDER_CUSTOMER_ID=?
EDIT : is this related to #Fetch(FetchMode.JOIN) -- Link:JPA eager fetch does not join
Hibernate: Multiple select queries made by Hibernate for Fetch mode Eager
To check whether FetchMode.JOIN works, I have added FetchMode.JOIN in entities as shown below but still no success with Join Query:
Customer Entity:
#Entity
#Table(name="Customer", schema="cf_2583f365_c3c6_499a_a60d_138e7e7023eb")
public class Customer {
#OneToMany(fetch=FetchType.EAGER)
#JoinColumn(name = "ORDER_CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID")
#Fetch(FetchMode.JOIN)
private List<Order> order;
}
Order Entity:
#Entity
#Table(name="Ordertable", schema="cf_2583f365_c3c6_499a_a60d_138e7e7023eb")
public class Order {
#JsonIgnore
#ManyToOne()
#JoinColumn(name = "ORDER_CUSTOMER_ID", referencedColumnName = "CUSTOMER_ID")
#Fetch(FetchMode.JOIN)
private Customer customer;
}
The findByCustomerId will actually generate a query based on that method instead of using em.find. It will create something along the lines of SELECT c FROM Customer c WHERE c.customerId=:customerId. afterwards it will notice the fetch strategy and obtain the needed references. This is also explained here. The query will do exactlly what you instruct it to do.
If you want to eagerly load the reference you would need to write the query yourself along the lines of SELECT c FROM Customer c JOIN FETCH c.orders o WHERE c.customerId=:customerId, this will automatically retrieve the orders.
However the customerId is actually the primary key or identitifier for your entity and thus you should actually be using the findById or findOne method (depending on your Spring Data JPA version). This will use the EntityManager.find which should take the mapping information into account and create the appropriate query.
I've read many tutorials about spring-hibernate relationships but I'm a bit confused about how to use them in my case... I've product/category entities defined as follow:
Product
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column
private int id;
#Column
private int category;
.
.
.
Category
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column
private int id;
#NotEmpty
#Column
#Size (max = 25)
private String name;
.
.
.
So, I'd like in the product list page, under the voice "category" would appear the category name, and in the product form the category list...
In my case a product fits only one category so if I'm right it should be a #ManyToOne but I don't know how to implement this... in my product database I've the categoryId field, but if I mark the category entity field as #OneToMany it will not be stored to the db...
EDIT
I've changed like this (as suggested):
Product.class
#Table(name = "products")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column
private int id;
#NotEmpty
#Column
#Size (max = 25)
private String name;
#Column
#Size (max = 255)
private String description;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "category_id", nullable = false)
private Category category;
Category.class
#Entity
#Table(name = "categories")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column
private int id;
#NotEmpty
#Column
#Size (max = 25)
private String name;
#Column
#Size (max = 255)
private String description;
//Here mappedBy indicates that the owner is in the other side
#OneToMany(fetch = FetchType.EAGER, mappedBy = "category", cascade = CascadeType.ALL)
private Set<Product> products = new HashSet<Product>();
Controller
#RequestMapping(value = "/add/", method = RequestMethod.POST)
public String addProduct(
#ModelAttribute(value = "product") #Valid Product product,
BindingResult result, ModelMap model, Category category) {
if (result.hasErrors()) {
return "forms/productForm";
}
try {
category.addProduct(product);
product.setCategory(category);
// Add product to db
productService.addProduct(product);
} catch (Exception e) {
log.error("/add/---" + e);
return "redirect:/product/deniedAction/?code=0";
}
return "redirect:/admin/product/";
}
I also added a #initbinder on the product controller to translate the data from the product form string to Category... but now when I save a product it automatically saves a category instead of attach the existing selected one...
As the Product will have only one Category and Category will have a list of Products, you can relate these two by creating a Foreign Key in the Product table to refer to the primary key in the Category table:
Category Table: id, name, other fields...
Product Table: id, category_id (FK), and other fields.
And the mapping can be defined as below:
public class Category {
//Here mappedBy indicates that the owner is in the other side
#OneToMany(fetch = FetchType.LAZY, mappedBy = "category", cascade = CascadeType.ALL)
private Set<Product> products = new HashSet<Product>();
...
}
public class Product {
//Here JoinColumn states that this entity is the owner of the relationship
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "category_id", nullable = false)
private Category category;
...
}
The mappedBy attribute tells Hibernate that the collection is a mirror image of the many-to-one association on the other side. Its like telling Hibernate that it should propagate changes made at the Product end of the association to the database, ignoring changes made only to the products collection that you have in the Category. Thus if we only call category.getProducts().add(product), no changes will be made persistent. As the association is bidirectional, you have to create the link on two sides, not just one.
For your convenience, you can add one addProduct method in the Category class to save the association:
public void addProduct(Product product) {
product.setCategory(this);
products.add(product);
}
You appear to have a one-to-many relationship between Category and Product (one category has many products)
In Java (and OO generally) you'd expect the Category class to contain a list of Products, so the Category can be said to 'own' products.
In SQL it's the other way round - you'd expect Product table to hold a foreign key reference to a Category, so here, the Product can be said to 'own' a Category.
Looks like your using JPA, so you could have something like this:
Category class:
#Entity
public class Category {
//other stuff...
#OneToMany(cascade=CascadeType.ALL, mappedBy="category")
private Set<Product> products;
}
Product class:
#Entity
public class Product {
//other stuff...
#ManyToOne
private Category category;
}
so you have this:
Product{
atributtes...
#ManyToOne
Category category; --so every product has a category
}
Category {
attributtes...
#OneToMany(cascade=CascadeType.ALL)
#JoinColumn(name="id_Product")
private List<Product> products;
}
try this, if not we can look another solution..
You are right, you should use #ManyToOne because "...a product fits only one category...".
In Product entity declare a Category field instead of int category and annotate it with #ManyToOne. Also add #JoinColumn to specify the name of product.category_id column in the database.
Product:
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column
private int id;
#ManyToOne
#JoinColumn(name = "category_id")
private Category category;
.
.
.