Spring boot Hibernate batch insert - java

application.property:
jpa:
properties:
hibernate:
order_inserts: true
order_updates: true
batch_versioned_data: true
jdbc:
batch_size: 10
database: postgresql
hibernate:
ddl-auto: create-drop
logging:
level:
org:
hibernate:
SQL: DEBUG
Entity Product class:
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String name;
private String description;
private LocalDateTime createdDate;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "product_id")
private List<ProductPrice> pricesList;
#ManyToOne
private User user;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "product_id")
private List<ProductLink> productLinks;
public Product(String name, String description, LocalDateTime createdDate, User user) {
this.name = name;
this.description = description;
this.createdDate = createdDate;
this.user = user;
}
}
Entity ProductLink class:
public class ProductLink {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String link;
#ManyToOne
private Product product;
public ProductLink(String link, Product product){
this.product = product;
this.link = link;
}
}
Entity ProductPrice class:
public class ProductPrice {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
private String storeName;
private String price;
private LocalDateTime updatedDate;
#ManyToOne
private Product product;
public ProductPrice(String storeName, String price, LocalDateTime updatedDate, Product product) {
this.product = product;
this.storeName = storeName;
this.price = price;
this.updatedDate = updatedDate;
}
}
Repository, same for the other
#Repository
public interface ProductLinkRepository extends JpaRepository<ProductLink, Long> {
}
I want to use Hibernate batch insert, but that doesn't Work, i only have multiple insert query in console (One insert for one ProductLink, ProductPrice)
Save method:
Product product = new Product(productDto.getName(), productDto.getDescription(), LocalDateTime.now(), user);
Product save = productRepository.save(product);
List<ProductLink> productLinks = new ArrayList<>();
List<ProductPrice> pricesList = new ArrayList<>();
for (ProductLinksDto productLink : productDto.getProductLinks()) {
if (!productLink.getLink().isEmpty()) {
URL url = new URL(productLink.getLink());
storeParser = new StoreParser(url);
productLinks.add(new ProductLink(productLink.getLink(), save));
pricesList.add(new ProductPrice(url.getHost(), storeParser.getPrice(), LocalDateTime.now(), save));
}
}
productPriceRepository.saveAll(pricesList);
productLinkRepository.saveAll(productLinks);

Related

Modifying Data in a Many-to-One Relationship java spring boot

I'm having trouble updating my data in a Many-to-One relationship
Product Model:
#Entity
#Getter
#Setter
#Table(name = "Products")
public class ProductModel {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long product_id;
#Column(name = "ProductName")
#Lob
private String productName;
#Column(name = "ShortName")
private String shortName;
#Column(name = "Price")
private int price;
#Column(name = "Slider")
private Boolean slider;
#Column(name = "SpecialOffer")
private Boolean specialOffer;
#Column(name = "NewPrice")
private int newPrice;
#Column(name = "ShortDesc")
private String shortDesc;
#Column(name = "FullDescription")
#Lob
private String fullDescription;
#Column(name = "Image")
#Lob
private String image;
#ManyToOne
#JoinColumn(name="category_id")
private CategoryModel categoryModel;
}
Category Model:
#Table(name = "Category")
public class CategoryModel {
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Id
private Long category_id;
#Column(name = "CategoryName")
private String cateGoryName;
#Column(name = "Image")
#Lob
private String image;
}
Service:
Add Product:
public ResponseEntity<ProductModel> addProduct(#PathVariable Long id,
#RequestBody ProductModel productDetails){
ProductModel productModel = categoryRespository.findById(id).map(category -> {
productDetails.setCategoryModel(category);
return productRepository.save(productDetails);
}).orElseThrow(() -> new RuntimeException("Fail "));
return new ResponseEntity<>(productModel,HttpStatus.CREATED);}
Update Product:
public ResponseEntity<ProductModel> updateProduct(#PathVariable long id, long categoryId, #RequestBody ProductModel productDetails) {
ProductModel updateProduct = productRepository.findById(id).orElseThrow(() -> new RuntimeException("Fail " + id));
updateProduct.setFullDescription(productDetails.getFullDescription());
updateProduct.setImage(productDetails.getImage());
updateProduct.setNewPrice(productDetails.getNewPrice());
updateProduct.setPrice(productDetails.getPrice());
updateProduct.setShortDesc(productDetails.getShortDesc());
updateProduct.setShortName(productDetails.getShortName());
updateProduct.setProductName(productDetails.getProductName());
updateProduct.setSlider(productDetails.getSlider());
updateProduct.setSpecialOffer(productDetails.getSpecialOffer());
productRepository.save(updateProduct);
return ResponseEntity.ok(updateProduct);
}
Add product working fine, adds a product according to the category. Update product also works fine without modifying the categories.The problem is that I cannot correctly assign the modified category to the model.

Creating association between entity to-many JPA

I want to make association to some many-to-many relation entity
#Entity
#Table(name = "users")
#Data
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private String userId;
private String name;
#OneToMany(mappedBy = "user")
private List<UserGroups> userGroups;
#Table(name = "groups")
#Data
public class Group {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "group_id")
private String groupId;
private String category;
private String name;
private String description;
#OneToMany(mappedBy = "group")
private List<UserGroups> userGroups;
public void addUser(User user){
UserGroup newUserGroup = new UserGroup();
newUserGroup.setName(user.getName())
userGroups.add(newUserGroup);
user.getUserGroups().add(newUserGroup)
}
#Entity
#Data
#Table(name = "user_groups")
public class UserGroups {
#EmbeddedId
UserGroupsCompositeKey id;
#ManyToOne
#MapsId("userId")
#JoinColumn(name = "user_id")
private Users user;
#ManyToOne
#MapsId("featureId")
#JoinColumn(name = "group_id")
private Group group;
private Date created;
I trying to add POST method to service where I should get group_id from endpoint url and assosiate user_id in request body. My Service method looks like this.
#Override
public ResponseEntity<String> createUsersGroup(String groupId,
String userId) {
Optional<Group> group = groupRepository.findById(groupId).get();
Optional<User> user = userRepository.findById(userId).get();
group.addUser(user);
return ResponseEntity.ok(userId);
};
}
Is there some more proper way to do this or when I will add more users in request body I will have to pull out every user from the database and add it like that ?

Spring Boot Data JPA detached entity passed to persist on #OneToMany and #OneToOne

Problem
I am trying to store an object in my Postgres database. This consists of the Order.class, (List) OrderDevice.class, and a Department.class.
The important thing is that the OrderDevices are always stored new in the DB, but a Department may already exist.
When I try to save the object to my database using save I get the following error message: (shown below)
I get the error message "detached entity passed to persist: com.niclas.model.OrderDevice" if the department does not exist yet, if the department exists the error message looks like this: "detached entity passed to persist: com.niclas.model.Department".
Solution attempts
This solution cannot be used because I do not use bidirectional mapping.
(I don't want to use a bidirectional mapping because I want to access the departments without an order.)
I also tried to change the Cascade types to MERGE like in this solution
I also tried using #Transactional on the method
I also tried to save the children in the database first and then the parent like this:
departmentRepository.save(order.getDepartment()); orderDeviceRepository.saveAll(order.getDevices()); orderRepository.save(order);
I hope I have described my good enough and I am happy about suggestions for solutions
Error.log
The log can be viewed here. (The formatting did not work here)
Order.class
#Entity
#Table(name = "orders")
public class Order extends AuditModel {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column(name = "order_id")
private String orderId;
#Column(name = "department_id")
private long departmentId;
#OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "department", referencedColumnName = "id")
private Department department;
#JsonProperty("deviceList")
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "order_id", referencedColumnName = "order_id")
private List<OrderDevice> devices;
#JsonProperty("forename")
#Column(name = "sender_forename")
private String senderForename;
#JsonProperty("surname")
#Column(name = "sender_surname")
private String senderSurname;
#Column(name = "notes", columnDefinition = "TEXT")
private String notes;
#Column(name = "month")
private int month;
#Column(name = "year")
private int year;
public Order() {
}
... Getter/Setters
}
OrderDevice.class
#Entity
#Table(name = "order_devices")
public class OrderDevice extends AuditModel{
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column( name = "order_id", insertable = false, updatable = false )
private String orderId;
#Column(name = "device_id")
private long deviceId;
#Column(name = "device_name")
private String deviceName;
#Column(name = "priceName")
private String priceName;
#Column(name = "price")
private double price;
#Column(name = "count")
private int count;
public OrderDevice() {
}
... Getters/Setters
}
Department.class
#Entity
#Table(name = "departments")
public class Department {
//TODO add Form Validation
//TODO better Naming for From Attributes on Frontend and Backend
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO) //TODO better config for GenerationType
private long id;
#Column(name = "department_name")
private String department;
#Column(name = "contact_person_forename")
private String forename;
#Column(name = "contact_person_surname")
private String surname;
#Column(name = "contact_person_mail")
private String mail;
#Column(name = "street")
private String street;
#Column(name = "house_number")
private String houseNumber;
#Column(name = "location")
private String location;
#Column(name = "postal_code")
private int postalCode;
#Column(name = "country")
private String country;
#Column(name = "auto_send_invoice")
private boolean autoSend;
#Column(name = "registered")
private boolean registered;
public Department() {
}
... Getter/Setters
}
OrderController.class
#Slf4j
#RestController
public class OrderController {
private final DepartmentRepository departmentRepository;
private final OrderRepository orderRepository;
private final OrderDeviceRepository orderDeviceRepository;
public OrderController(OrderRepository orderRepository, DepartmentRepository departmentRepository,
OrderDeviceRepository orderDeviceRepository) {
this.orderRepository = orderRepository;
this.departmentRepository = departmentRepository;
this.orderDeviceRepository = orderDeviceRepository;
}
#PostMapping("/orders/add")
public ResponseEntity<Order> addDepartment(#RequestBody Order order) throws JsonProcessingException {
order.setOrderId(order.generateOrderId());
DateTime dateTime = new DateTime();
order.setMonth(dateTime.getMonthOfYear());
order.setYear(dateTime.getYear());
order.getDevices().forEach(orderDevice -> {
orderDevice.setOrderId(order.getOrderId());
});
//departmentRepository.save(order.getDepartment());
//orderDeviceRepository.saveAll(order.getDevices());
orderRepository.save(order);
return new ResponseEntity<>(order, HttpStatus.CREATED);
}
Update
If the objects are created in this way, no error will occur and the order will be successfully saved in the database.
However, I don't understand why it works this way and not via ObjectMapper. Does anyone know why?
#PostMapping("/orders/add")
public ResponseEntity<Order> addDepartment(#RequestBody JsonNode jsonNode) throws JsonProcessingException {
Order order = new Order();
JsonNode departmentJson = jsonNode.get("department");
Department department;
if ( departmentJson.get("id").canConvertToInt() ) {
department = departmentRepository.findDepartmentById(departmentJson.get("id").asInt());
} else {
department = new Department();
department.setDepartment(departmentJson.get("department").asText());
department.setForename(departmentJson.get("forename").asText());
department.setSurname(departmentJson.get("surname").asText());
department.setMail(departmentJson.get("mail").asText());
department.setStreet(departmentJson.get("street").asText());
department.setHouseNumber(departmentJson.get("houseNumber").asText());
department.setLocation(departmentJson.get("location").asText());
department.setPostalCode(departmentJson.get("postalCode").asInt());
department.setCountry(departmentJson.get("country").asText());
department.setAutoSend(departmentJson.get("autoSend").asBoolean());
department.setRegistered(departmentJson.get("registered").asBoolean());
}
order.setDepartment(department);
order.setOrderId(order.generateOrderId());
order.setDepartmentId(department.getId());
List<OrderDevice> orderDevices = new ArrayList<>();
JsonNode devices = jsonNode.get("deviceList");
for (JsonNode node : devices) {
//TODO replace this mess with objectMapper
if (node.has("count") && node.get("count").asInt() != 0){
OrderDevice device = new OrderDevice();
device.setOrderId(order.getOrderId());
device.setDeviceId(node.get("id").asLong());
device.setDeviceName(node.get("deviceName").asText());
device.setPriceName(node.get("priceName").asText());
device.setPrice(node.get("price").asDouble());
device.setCount(node.get("count").asInt());
orderDevices.add(device);
}
}
order.setDevices(orderDevices);
order.setSenderForename(jsonNode.get("forename").asText());
order.setSenderSurname(jsonNode.get("surname").asText());
order.setNotes(jsonNode.get("notes").asText());
DateTime dateTime = new DateTime();
order.setMonth(dateTime.getMonthOfYear());
order.setYear(dateTime.getYear());
orderRepository.save(order);
return new ResponseEntity<>(order, HttpStatus.CREATED);
}
You can try to use instead of orderRepository.save(order) use orderRespostiory.saveOrUpdate(order).

#ManyToMany Hibernate only update JoinTable

I have below entities and #ManyToMany mapping between the two.
#Entity
#Table(name = "user")
public class User implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 5340562707217344212L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long userId;
private String userName;
private String password;
private String firstName;
private String lastName;
private String emailId;
private Date createdDate;
private Byte status;
private Date lastModifiedDate;
#ManyToMany
#JoinTable(name = "user_products_mapper",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "product_id")
)
private List<Products> products = new ArrayList<Products>();
public void addProducts(Products product){
this.products.add(product);
}
#Entity
#Table(name="products")
public class Products implements java.io.Serializable {
/**
*
*/
private static final long serialVersionUID = 1895580713896126954L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long productId;
private String productName;
private String description;
private double minBalance;
public Long getProductId() {
return this.productId;
}
public void setProductId(Long productId) {
this.productId = productId;
}
public String getProductName() {
return this.productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
My question is :
1)I already have these entities persisted. (User and Products)
2)User and Products are already exists in databasae and relation between them is #ManyToMany
3)I want to insert records ONLY IN JoinTable (user_products_mapper) since the rows are already present for User and Products.
4)One way to achieve this is to use UNIDIRECTIONAL mapping as I have above and just call
User.addProducts
5) Before doing this User and Products entities will be fetched like below :
User user = this.userDao.findOne(userId);
if(user == null){
//throw exception
}
Products product = this.productDao.findOne(productId);
6)Is there any way of doing this ?
When using Hibernate/JPA you do not control the actual queries being run, but you are not supposed to either.
What you would do to connect a user and a product would be something like:
#Transactional
public void addProductToUser(final Long userId, final Long productId) {
final User user = em.find(User.class, userId);
final Product product = em.find(Product.class, productId);
user.addProduct(product);
}
This will result in more queries than just inserting a row (since you will fetch both Product and User), but what you gain is simple and plain Java code. The cost of the extra queries in most cases is well within what is acceptable.

Hibernate joining two table and fetch all records?

I have two entity class Category and Events.I need to join both the tables and fetch all records which matching the given condition
My sql query for this
SELECT * FROM category c inner join `events` e on e.category_i=c.category_id where c.parent_category_id=1;
How i can convert this sql query to hql and fetch the data ? I tried below but not getting the result ? Am very new to hibernate
Events entity class for hibernate mapping
import java.io.Serializable;
import java.util.Date;
import javax.persistence.*;
/**
* The persistent class for the user database table.
*
*/
#Entity
#Table(name = "events")
public class Events implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "event_id")
private int eventId;
#Column(name = "event_name")
private String eventName;
#Column(name = "event_description")
private String eventDescription;
#Column(name = "category_i")
private Integer categoryI;
public Integer getCategoryI() {
return categoryI;
}
public void setCategoryI(Integer categoryI) {
this.categoryI = categoryI;
}
#Column(name = "is_trending_event")
private Integer isTrendingEvent;
#Column(name = "image_url")
private String imageUrl;
private Integer status;
#Column(name = "created_date")
#Temporal(javax.persistence.TemporalType.DATE)
private Date createdDate;
#Column(name = "last_updated_date")
#Temporal(javax.persistence.TemporalType.DATE)
private Date lastUpdatedDate;
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
public Date getLastUpdatedDate() {
return lastUpdatedDate;
}
public void setLastUpdatedDate(Date lastUpdatedDate) {
this.lastUpdatedDate = lastUpdatedDate;
}
public int getEventId() {
return eventId;
}
public void setEventId(int eventId) {
this.eventId = eventId;
}
public String getEventName() {
return eventName;
}
public void setEventName(String eventName) {
this.eventName = eventName;
}
public String getEventDescription() {
return eventDescription;
}
public void setEventDescription(String eventDescription) {
this.eventDescription = eventDescription;
}
public Integer getIsTrendingEvent() {
return isTrendingEvent;
}
public void setIsTrendingEvent(Integer isTrendingEvent) {
this.isTrendingEvent = isTrendingEvent;
}
public String getImageUrl() {
return imageUrl;
}
public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
}
Category entity
import java.io.Serializable;
import java.util.Date;
import javax.persistence.*;
/**
* The persistent class for the user database table.
*
*/
#Entity
#Table(name = "category")
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "category_id")
private int categoryId;
#Column(name = "category_name")
private String categoryName;
#Column(name = "parent_category_id")
private Integer parentCategoryId;
#Column(name = "created_date")
#Temporal(javax.persistence.TemporalType.DATE)
private Date createdDate;
#Column(name = "last_updated_date")
#Temporal(javax.persistence.TemporalType.DATE)
private Date lastUpdatedDate;
#ManyToOne
#JoinTable(name="events", joinColumns = #JoinColumn(name="category_i"))
private Events events;
public int getCategoryId() {
return categoryId;
}
public void setCategoryId(int categoryId) {
this.categoryId = categoryId;
}
public String getCategoryName() {
return categoryName;
}
public void setCategoryName(String categoryName) {
this.categoryName = categoryName;
}
public Integer getParentCategoryId() {
return parentCategoryId;
}
public void setParentCategoryId(Integer parentCategoryId) {
this.parentCategoryId = parentCategoryId;
}
public Date getCreatedDate() {
return createdDate;
}
public void setCreatedDate(Date createdDate) {
this.createdDate = createdDate;
}
public Date getLastUpdatedDate() {
return lastUpdatedDate;
}
public void setLastUpdatedDate(Date lastUpdatedDate) {
this.lastUpdatedDate = lastUpdatedDate;
}
}
Fetch category method
public List<Object[]> getCategoryList(int id) throws SQLException, ClassNotFoundException, IOException {
List<Object[]> groupList = null;
try {
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("select e from Category e inner join e.events where e.parentCategoryId=1");
//query.setParameter("id", id);
groupList = query.list();
} catch (Exception e) {
}
return groupList;
}
You need to think in terms of Java objects when using ORM tools.
From your question I think the query that you're trying to write will look something like:
public List<Category> getCategoryList(int id) {
List<Category> groupList;
Session session = sessionFactory.getCurrentSession();
Query query = session.createQuery("select c from Category c join fetch c.events where c.parentCategory.categoryId = 1");
//query.setParameter("id", id);
groupList = query.list();
return groupList;
}
One of the benefits of using an ORM is that it works out the full join query for you.
For this to work you need to update your class model as follows:
import java.io.Serializable;
import java.util.Date;
import javax.persistence.*;
#Entity
#Table(name = "events")
public class Event implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "event_id")
private int eventId;
#Column(name = "event_name")
private String eventName;
#Column(name = "event_description")
private String eventDescription;
#ManyToOne
#JoinColumn(name = "category_i")
private Category category;
#Column(name = "is_trending_event")
private Integer isTrendingEvent;
#Column(name = "image_url")
private String imageUrl;
private Integer status;
#Column(name = "created_date")
#Temporal(javax.persistence.TemporalType.DATE)
private Date createdDate;
#Column(name = "last_updated_date")
#Temporal(javax.persistence.TemporalType.DATE)
private Date lastUpdatedDate;
...
}
and
import java.io.Serializable;
import java.util.Date;
import javax.persistence.*;
#Entity
#Table(name = "category")
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "category_id")
private int categoryId;
#Column(name = "category_name")
private String categoryName;
#ManyToOne
#JoinColumn(name="parent_category_id")
private Category parentCategory;
#Column(name = "created_date")
#Temporal(javax.persistence.TemporalType.DATE)
private Date createdDate;
#Column(name = "last_updated_date")
#Temporal(javax.persistence.TemporalType.DATE)
private Date lastUpdatedDate;
#OneToMany(mappedBy="category")
private List<Event> events;
...
}
Hibernate is about mapping objects and the relations, however you are mapping simple id fields.
In your Events class you have the followingL
#Entity
#Table(name = "events")
public class Events implements Serializable {
#Column(name = "category_i")
private Integer categoryI;
}
However it should be a Category instead of an Integer.
#Entity
#Table(name = "events")
public class Events implements Serializable {
#ManyToOne
#Column(name = "category_i")
private Category category;
}
Then in your Category you should add the mappedBy field to the #ManyToOne on the events field and remove the #JoinColumn.
#Entity
#Table(name = "category")
public class Category implements Serializable {
#OneToMany(mappedBy="category")
private Events events;
}
The same applies to the parentCategoryId of the Category class.
Now that you have your mapping corrected you should be able to write the query as you wanted to.
This query should do the job:
from Category as c
inner join c.events as e
where c.parentCategoryId = 1
Plus you seem to have a typo (missing "d" at the end) here:
#JoinColumn(name="category_i"))
Ty this below code
public List<Object[]> getCategoryList(int id) throws SQLException, ClassNotFoundException, IOException {
Session session = sessionFactory.getCurrentSession();
return session.createCriteria(Catagory.class)
.setFetchMode("events", FetchMode.JOIN)
.add(Restrictions.eq("parentCatagoryId", 1))
.list();
}
Hope this stuff works.

Categories