org.hibernate.PersistentObjectException Many-to-one relationship - java

When running the application I get a org.hibernate.PersistentObjectException error from MockData. What is causing this? Are my cascadetypes wrong?
org.hibernate.PersistentObjectException: detached entity passed to
persist
The products are created and added after the categories, so the category should exist?
Product.java
#Entity
#Data
#Table(name = "product")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#Column(name = "manufacturer")
private String manufacturer;
#Column(name = "price")
private double price;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "category_id")
private Category category;
public Product(String name, String manufacturer, double price, Category category) {
this.name = name;
this.manufacturer = manufacturer;
this.price = price;
this.category = category;
}
public Product() {
}
}
Category.java
#Entity
#Data
#Table(name = "category")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "name")
private String name;
#OneToMany(mappedBy = "category", cascade = CascadeType.ALL)
private List<Product> products;
public Category(String name) {
this.name = name;
}
public Category() {
}
}
MockData.java
#Component
class MockData {
private final ProductRepository productRepository;
private final CategoryRepository categoryRepository;
#Autowired
public MockData(ProductRepository productRepository, CategoryRepository categoryRepository) {
this.productRepository = productRepository;
this.categoryRepository = categoryRepository;
loadData();
}
private void loadData() {
Category IT = new Category("IT");
Category beauty = new Category("Beauty");
Category food = new Category("Food");
categoryRepository.save(IT);
categoryRepository.save(beauty);
categoryRepository.save(food);
Product computer = new Product("Computer", "Dell", 5000, IT);
Product computer2 = new Product("Computer2", "HP", 5000, IT);
Product eyeliner = new Product("Eyeliner", "Chanel", 100, beauty);
Product hamburger = new Product("Angus", "Burger King", 100, food);
productRepository.save(computer);
productRepository.save(computer2);
productRepository.save(eyeliner);
productRepository.save(hamburger);
}
}

Changing mockdata to the following solved the problem:
#Component
class MockData {
private final ProductRepository productRepository;
private final CategoryRepository categoryRepository;
#Autowired
public MockData(ProductRepository productRepository, CategoryRepository categoryRepository) {
this.productRepository = productRepository;
this.categoryRepository = categoryRepository;
loadData();
}
private void loadData() {
Category IT = new Category("IT");
Category beauty = new Category("Beauty");
Category food = new Category("Food");
Product computer = new Product("Computer", "Dell", 5000, IT);
Product computer2 = new Product("Computer2", "HP", 5000, IT);
Product eyeliner = new Product("Eyeliner", "Chanel", 100, beauty);
Product hamburger = new Product("Angus", "Burger King", 100, food);
IT.setProducts(Arrays.asList(computer, computer2));
beauty.setProducts(Collections.singletonList(eyeliner));
food.setProducts(Collections.singletonList(hamburger));
categoryRepository.save(IT);
categoryRepository.save(beauty);
categoryRepository.save(food);
productRepository.save(computer);
productRepository.save(computer2);
productRepository.save(eyeliner);
productRepository.save(hamburger);
}
Products are created and added to categories before being saved.

Related

Saving/Updating entities with multiple relations

I'm coding for fun, and i was wondering what would be the best approach (clean code) for storing entities with multiple relations (OneToMany, ManyToMany, OneToOne etc...)
I have three Entitiy classes: Product, Shop, ShopProducts.
A Shop has #OneToOne ShopProducts, and ShopProducts has #ManyToMany List products (see below).
For creating a shop (with or without products for that shop), i do this
public Shop createShop(ShopDto shopDto) {
if(shopRepository.findShopByCity(shopDto.getCity()).isPresent()) {
throw new IllegalArgumentException("A shop already exists with that name.");
}
/* Persist shopProducts instance */
Shop shop = new Shop();
ShopProducts shopProducts = new ShopProducts();
if(shopDto.getProducts() != null) {
shopProducts.setAvailableProducts(shopDto.getProducts().getAvailableProducts());
}
ShopProducts save = shopProductsRepository.save(shopProducts);
shop.setCity(shopDto.getCity());
shop.setStoreName(shopDto.getStoreName());
shop.setProducts(save);
return shopRepository.save(shop);
}
Adding a product to an existing shop
public Shop addProductsToShop(String city, List<ProductDto> products) {
Shop shop = shopRepository.findShopByCity(city)
.orElseThrow(() -> new RuntimeException("Invalid city"));
products.stream()
.filter(p -> productRepository.findByProductId(p.getProductId()).isPresent())
.forEach(product -> {
Product p = productRepository.findByProductId(product.getProductId()).get();
shop.getProducts().addProductToShop(p);
shopProductsRepository.save(shop.getProducts());
shopRepository.save(shop);
});
return shop;
}
--- MODEL CLASSES ---
Product class
public class Product implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
private BigDecimal price;
private Boolean available;
private String productId;
private String imageUrl;
private ProductType productType;
}
Shop class
public class Shop implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String storeName;
#Column(unique = true)
private String city; /* Only one store per city */
#OneToOne
#JoinColumn(name = "shop_products_id", referencedColumnName = "id")
private ShopProducts products;
}
ShopProduct class
public class ShopProducts implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "shop_product",
joinColumns = {#JoinColumn(name = "shop_product_id")},
inverseJoinColumns = {#JoinColumn(name = "product_id")})
private List<Product> availableProducts = new ArrayList<>();
public void addProductToShop(Product product) {
availableProducts.add(product);
}
}

Create a product in a category hibernate - Transcient issue

I use two class as models to build a JSON :
The productCreateRequestModel:
#Getter #Setter
public class ProductCreateRequestModel {
private Long id;
private String name;
private double price;
private int qty;
private String imgPath;
private CategoryRequestCreateProductModel category;
}
My CategoryRequestCreateProductModel
#Getter #Setter
public class CategoryRequestCreateProductModel {
private Long id;
private String name;
private String categoryKeyId;
}
I created 2 entities to manage categories and products.
#Entity
#Table(name="products")
#Getter #Setter
public class ProductEntity {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
#Column(nullable = false)
private String productKeyId;
// many to one relationship with category
#ManyToOne
#JoinColumn(name = "category_id")
private CategoryEntity category;
#Column(nullable = false)
private String name;
#Column(nullable = false)
private double price;
#Column(nullable = false)
private int qty;
private String imgPath;
}
And :
#Entity
#Table(name="categories")
#Getter #Setter
public class CategoryEntity {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private Long id;
#Column(length = 30, nullable = false)
private String categoryKeyId;
#Column(nullable = false)
private String name;
#ManyToOne(optional = true, fetch = FetchType.LAZY)
#JoinColumn(name="parent_id", nullable=true)
private CategoryEntity parentCategory;
// allow to delete also subcategories
#OneToMany(mappedBy="parentCategory", cascade = CascadeType.ALL)
private List<CategoryEntity> subCategories;
//Here mappedBy indicates that the owner is in the other side
#OneToMany(fetch = FetchType.EAGER, mappedBy = "category", cascade = CascadeType.REMOVE)
private List<ProductEntity> products;
}
This generated this table in the database.
In my controller:
public ProductRestResponseModel createProduct(#RequestBody ProductCreateRequestModel productCreateRequestModel) throws Exception {
ProductRestResponseModel returnValue = new ProductRestResponseModel();
if(productCreateRequestModel.getName().isEmpty() || productCreateRequestModel.getPrice() <= 0)
throw new ApplicationServiceException(ErrorMessages.MISSING_REQUIRED_FIELDS.getErrorMessage());
ModelMapper modelMapper = new ModelMapper();
ProductDto productDto = modelMapper.map(productCreateRequestModel, ProductDto.class);
ProductDto createdProduct = productService.createProduct(productDto);
returnValue = modelMapper.map(createdProduct, ProductRestResponseModel.class);
return returnValue;
}
In my Service I use the DTO:
#Override
public ProductDto createProduct(ProductDto productDto) {
ProductDto returnValue = new ProductDto();
if (productRepository.findByName(productDto.getName()) != null)
throw new ApplicationServiceException("Record already in Database");
ModelMapper modelMapper = new ModelMapper();
ProductEntity productEntity = modelMapper.map(productDto, ProductEntity.class);
String productKeyId = utils.generateProductKeyId(30);
productEntity.setProductKeyId(productKeyId);
ProductEntity storedProduct = productRepository.save(productEntity);
returnValue = modelMapper.map(storedProduct, ProductDto.class);
return returnValue;
}
My issue is when I send a post request with this object :
{
"name": "Pizza",
"price": 344.0,
"qty": 15,
"imgPath": "new/pathImage",
"category": {
"categoryKeyId": "23ume70Fu6yqyGUWfQkW110P4ko3gZ",
"name": "CatName"
}
}
When i send this request I obtain an error message : org.hibernate.TransientPropertyValueException: object references an unsaved transient instance - save the transient instance before flushing : com.app.ws.io.entity.ProductEntity.category -> com.app.ws.io.entity.CategoryEntity
My problem is that the Category already exists in the database and that i just need to set the foreign key in the product table

Springboot How to query a many to many relationship

I have two entities lets call them Categories and Products. These two entities are mapped by a many to many relationship.
My problem is that i am trying to get category information from products. Trying this results in empty categories.
This is my code :
PersistenceEntity
#MappedSuperclass
public class PersistenceEntity implements Serializable {
private static final long serialVersionUID = 4056818895685613967L;
// Instance Variables
#Id
#Column(unique = true)
#GeneratedValue(strategy = GenerationType.TABLE)
protected Long id;
#JsonIgnore
#Temporal(javax.persistence.TemporalType.TIMESTAMP)
protected Date creationDate = new Date();
...Getters and Setters omitted for brevity
}
Category
#Entity
#Table(name = "category")
#JsonIgnoreProperties(ignoreUnknown = true)
public class Category extends PersistenceEntity{
private static final long serialVersionUID = 1L;
#Column(nullable = false)
private String categoryName;
#Column(nullable = false)
private Boolean active;
#Column(nullable = true)
private String picture;
#JsonIgnore
private MetaData metadata;
#ManyToMany(fetch = FetchType.EAGER,mappedBy = "categories")
private Set<Product> products;
...Getters and Setters omitted for brevity
}
Product
#Entity
#Table(name = "products",uniqueConstraints = { #UniqueConstraint(columnNames = "productCode")})
#JsonIgnoreProperties(ignoreUnknown = true)
public class Product extends PersistenceEntity {
private static final long serialVersionUID = 8727166810127029053L;
#Column(name = "product_name")
private String name;
private String productImageUrl;
#JsonIgnore
#ManyToMany(cascade = CascadeType.MERGE, fetch = FetchType.LAZY)
#JoinTable(name="category_products",
joinColumns={#JoinColumn(name="product_id", unique = false)},
inverseJoinColumns={#JoinColumn(name="category_id", unique = false)})
private Set<Category> categories;
...Getters and Setters omitted for brevity
}
ProductServiceImplementation
#Service
public class ProductService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
private ProductRepository productRepository;
public List<Product> getProductsByShopId( Long id) {
List<Product> productList = new ArrayList<>();
productList = productRepository.findByShopId(id);
return productList;
}
public Set<Long> getCategoryIds(List<Product> products){
Set<Long> categoriesIDs = new HashSet<Long>();
for (Product product : products) {
product.getCategories().forEach(category -> {
categoriesIDs.add(category.getId());
});
}
return categoriesIDs;
}
}
The problem is getting the categoryIds that are mapped to the list of products.
How can i get CategoryIds from Product. My getCategoryIds function returns empty always
public Set<Long> getCategoryIds(List<Product> products){
Set<Long> categoriesIDs = new HashSet<Long>();
for (Product product : products) {
product.getCategories().forEach(category -> {
categoriesIDs.add(category.getId());
});
}
return categoriesIDs;
}

How to create One to many relationship in Springboot

I am trying to create an api for a user to have the ability to add products to a specific category such that when, getCategory by id is called it gives a result as follows:
{
"product": [
{
"productId": 1,
"productName": "TestProdut1"
},{
"productId": 2,
"productName": "TestProdut2"
}
],
"categoryId": 1,
"categoryName": "Test1",
"parentCategoryId": 123
}
Please note i am using a postgres database
This is what i have done so far but when i try to get a category there
is no response back. When using swagger the response comes back as no
content
Category Entity :
#Entity
#Table(name = "category")
public class Category {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "category_id", unique = true, nullable = false)
private Long id;
#Column(nullable = false)
private String categoryName;
#JsonIgnore
#OneToMany(fetch = FetchType.LAZY, mappedBy = "categories", cascade = CascadeType.REMOVE, orphanRemoval = true)
private List<Product> products = new ArrayList<Product>();
#ManyToOne
#JoinColumn(name = "parent_id")
#JsonIgnore
private Category parentId;
#OneToMany(mappedBy = "parentId")
private List<Category> subCategories = new ArrayList<>();
}
Product Entity :
#Entity
#Table(name = "products")
public class Product {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "product_id")
private Long id;
private String name;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "category_id")
private Category categories;
}
Category Repository :
public interface CategoryRepository extends CrudRepository<Category, Long>{
public List<Category> findByCategoryName(String categoryName);
}
Category Service :
#Service
public class CategoryService {
#Autowired
private CategoryRepository categoryRepository;
public void addCategory(Category category) {
categoryRepository.save(category);
}
public List<Category> getAllCategories() {
List<Category> categoryList = new ArrayList<>();
categoryRepository.findAll().forEach(categoryList::add);
return categoryList;
}
public List<Category> getAllCategoriesByName(String categoryName) {
List<Category> categoryList = new ArrayList<>();
categoryList = categoryRepository.findByCategoryName(categoryName);
return categoryList;
}
public Optional<Category> getCategoryById(Long categoryId) {
Optional<Category> category = categoryRepository.findById(categoryId);
return category;
}
public void deleteCategory(Long id) {
categoryRepository.deleteById(id);
}
}
Category Controller :
#RestController
#Api(value = "CategoryAPI", produces = MediaType.APPLICATION_JSON_VALUE)
#RequestMapping("/ss")
public class CategoryController {
#Autowired
private CategoryService categoryService;
#RequestMapping(method=RequestMethod.POST , value="/category/add", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Boolean> addCategory(#RequestBody Category category) {
categoryService.addCategory(category);
return new ResponseEntity<Boolean>(true, HttpStatus.CREATED);
}
#RequestMapping(method= RequestMethod.GET, value="/category/get/by/id/{categoryId}")
public void getCategoryById(#PathVariable Long categoryId) {
categoryService.getCategoryById(categoryId);
}
#RequestMapping(method= RequestMethod.GET, value="/category/get/by/name/{categoryName}")
public void getCategoryByName(#PathVariable String categoryName) {
categoryService.getAllCategoriesByName(categoryName);
}
#RequestMapping(method= RequestMethod.GET, value="/all/category")
public void getCategories() {
categoryService.getAllCategories();
}
}
It is because you are having void return type when retrieving the list of categories and that is why you are not getting any response.
#RequestMapping(method= RequestMethod.GET, value="/category/get/by/name/{categoryName}")
public ResponseEntity<List<Category>> getCategoryByName(#PathVariable String categoryName) {
return new ResponseEntity<>(categoryService.getAllCategoriesByName(categoryName),HttpStatus.OK);
}

Hibernate foreign key is null in OrderItem (order_orderID)

I have two table Orders and OrderItem as below:
#Entity
#Table(name = "orders")
public class Order implements Serializable {
#Id
#GeneratedValue
private Integer orderID;
#Column(nullable = false)
private Date orderDate;
#ManyToOne(cascade = CascadeType.ALL)
private User user;
#OneToMany(cascade = CascadeType.ALL)
private Set<OrderItem> orderItems = new HashSet<OrderItem>();
public Order() {
}
public Order(Date orDate, User currentUser) {
this.orderDate = orDate;
this.user = currentUser;
}
public Set<OrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(Set<OrderItem> orderItems) {
this.orderItems = orderItems;
}
//getter/setters for orderDate, user
And
#Entity
public class OrderItem implements Serializable {
#Id
#GeneratedValue
private Integer id;
#ManyToOne(cascade = CascadeType.ALL)
private Book book; // F.K to Book table
#Column(nullable = false)
private int quantity;
#Column(nullable = false)
private double totalPrice;
public OrderItem() {}
public OrderItem( Book currentBook, int qty, double totalPrice) {
this.book = currentBook;
this.quantity = qty;
this.totalPrice = totalPrice;
}
//getter/setters
And here i initialize them to store in database:
#Transactional
public void storeOrderInDB() {
order = new Order(currentDate(), currentUser); //date,user
orderService.addOrder(order);
OrderItem orderItem = new OrderItem();
orderItem.setBook(currentBook);
orderItem.setQuantity(qty);
orderItem.setTotalPrice(getTotalCost(qty, unitPrice));
orderItemService.addOrderItem(orderItem);
}
This is the result:
Orders:
OrderID: 5, OrderDate: "2015-04-25 23:11:16", userId: 1
OrderItem:
id:2 , quantity:1 , totalPrice:5000 , bookId:5 , order_orderID: null
Why order-orderID is null?
I need it to be not null.
In Order Entity You have declared
#OneToMany(cascade = CascadeType.ALL)
private Set<OrderItem> orderItems = new HashSet<OrderItem>();
Above declaration says that you foreign-key is maintain by Order Entity ,
To resolve your problem
#Transactional
public void storeOrderInDB() {
Order order = new Order(currentDate(), currentUser); //date,user
OrderItem orderItem = new OrderItem();
orderItem.setBook(currentBook);
orderItem.setQuantity(qty);
orderItem.setTotalPrice(getTotalCost(qty, unitPrice));
//orderItemService.addOrderItem(orderItem);
Set<OrderItem> orderItemSet=order.getOrderItems();
orderItemSet.add(orderItem);
order.setOrderItems(orderItemSet);
orderService.addOrder(order);
}
Hope this will resolve your problem
Thanks!

Categories

Resources