Spring boot, #ManyToMany, create a repository for the join Table - java

I have the two entities with a manyToMany relationship:
#Entity
#Table(name="categories")
public class CategoryEntity implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private int categoryId;
#Column(name="name")
private String CategoryName;
#ManyToMany(mappedBy = "categories")
private List<ProductEntity> products = new ArrayList<ProductEntity>();
}
#Entity
#Table(name="products")
public class ProductEntity implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id")
private Integer productId;
#Column(name="name")
private String productName;
#Column(name="description")
private String description;
#Column(name="price")
private Float price;
#Column(name="rating")
private Float rating;
#Column(name="image")
private String image;
#Column(name="quantity")
private Integer quantity;
#ManyToMany(cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "product_category",
joinColumns = {#JoinColumn(name = "product_id")},
inverseJoinColumns = {#JoinColumn(name = "category_id")}
)
private List<CategoryEntity> categories = new ArrayList<>();
}
In the database I have a join Table product_category that hold the product_id and category_id.
my question is how to add element to the joinTable product_category? is it possible to create a Repository even if we don't have an entities??
I tried this with my controller:
public class ProductController {
#Autowired
private ProductService productService;
#Autowired
private ProductMapper productMapper;
#Autowired
private CategoryMapper categoryMapper;
#Autowired
private CategoryService categoryService;
#Autowired
private ProductReviewService reviewService;
#Autowired
private ProductReviewMapper reviewMapper;
#PostMapping("/products")
public ResponseEntity<ProductDto> createProduct(#RequestBody ProductDto productDto) {
ProductEntity productEntity=productMapper.dtoToEntity(productDto);
for(CategoryDto categoryDto:productDto.getCategories()){
CategoryEntity categoryEntity=categoryMapper.dtoToEntity(categoryDto);
productEntity.getCategories().add(categoryEntity);
}
productEntity=productService.saveProduct(productEntity);
productDto.setProductId(productEntity.getProductId());
return ResponseEntity.created(null).body(productDto);
}
}
but I got this:
org.hibernate.PersistentObjectException: detached entity passed to persist: com.be.ec.entities.CategoryEntity
at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:127) ~[hibernate-core-5.4.8.Final.jar:5.4.8.Final]
at

You have relationship consistency issue. you are adding a category to a product but not adding product into category
add this method into your ProductEntity class:
public void addCategory(CategoryEntity category) {
this.getCategories().add(category);
category.getProducts().add(this);
}
and use this method to add category into product.
ProductEntity productEntity=productMapper.dtoToEntity(productDto);
for(CategoryDto categoryDto:productDto.getCategories()){
CategoryEntity categoryEntity=categoryMapper.dtoToEntity(categoryDto);
productEntity.addCategory(categoryEntity); //changed line
}
productEntity=productService.saveProduct(productEntity);
productDto.setProductId(productEntity.getProductId());

Related

Jpa and LazyInitializationException in tests

I use SpringBoot and Hibernate.
I have the following entities:
#Entity
#Table(name = "product")
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "product_id")
private Long id;
private String productName;
private Long productPrice;
private String productDescription;
#ManyToOne(cascade = CascadeType.PERSIST)
#JoinColumn(name = "product_type_id")
private ProductType productType;
//getters and setters
and
#Entity
#Table(name = "product_type")
public class ProductType {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "product_type_id")
private Long id;
private String productTypeName;
private String productTypeDescription;
#OneToMany(mappedBy = "productType", cascade = CascadeType.ALL)
private List<Product> products;
//getters and setters
I have the following service:
#Service
public class ProductService {
ProductRepository productRepository;
ProductTypeRepository productTypeRepository;
public ProductService(ProductRepository productRepository, ProductTypeRepository productTypeRepository) {
this.productRepository = productRepository;
this.productTypeRepository = productTypeRepository;
}
#Transactional
public Product saveProduct(Product product){
return productRepository.save(product);
}
#Transactional
public List<Product> findAllByProductTypeName(String type){
ProductType productType = productTypeRepository.findByProductTypeName(type);
return productType.getProducts();
}
and at last, I have the following simple test to understand everything is working:
#SpringBootTest
class ProductServiceTest extends BaseDAOTest {
#Autowired
ProductService productService;
#Autowired
ProductTypeService productTypeService;
#Test
void findAllByProductTypeTest(){
Product product = new Product("Test product", 100L, "Test product description");
product.setProductType(new ProductType("Test type", "Test description"));
productService.saveProduct(product);
List<Product> productList = productService.findAllByProductTypeName("Test type");
productList.get(0);
}
}
Here productList.get(0) i have the following error:
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.krasovsky.warehouse.models.ProductType.products, could not initialize proxy - no Session
at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:606)
at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:218)
at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:585)
at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:149)
at org.hibernate.collection.internal.PersistentBag.get(PersistentBag.java:561)
What is the correct way to address this issue? Say if you need any additional info. Thanks in advance.
You are trying to do a fetch outside of a transaction. You need to annotate your test class with #DataJpaTest:
Data JPA tests are transactional and rollback at the end of each test by default
https://docs.spring.io/spring-boot/docs/1.5.2.RELEASE/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-testing-autoconfigured-jpa-test

Making primary key same as foreign key in Hibernate entity

I am new to Hibernate. I am working on two entities as follows:
Entity 1 is as follows:
#Entity
#Table(name = "vm_user")
public class VmUser implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "created_by")
private String createdBy;
#Column(name = "last_modified_by")
private String lastModifiedBy;
#Column(name = "created_date")
private Instant createdDate;
#Column(name = "last_modified_date")
private Instant lastModifiedDate;
#OneToOne
#JoinColumn(unique = true)
private User user; <--- HOW WILL I DENOTE THIS PRIMARY KEY OF VMUSER ENTITY ?
In the associated table in mysql i.e. vm_user, user_id is both primary key as well as foreign key which refers to id of user table associated with User entity.
Entity 2 is as follows:
#Entity
#Table(name = "my_entity")
public class MyEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "created_by")
private String createdBy;
#Column(name = "last_modified_by")
private String lastModifiedBy;
#Column(name = "created_date")
private Instant createdDate;
#Column(name = "last_modified_date")
private Instant lastModifiedDate;
#ManyToOne
private A a;
#OneToOne
#JoinColumn(unique = true)
private B b;
In the associated table in mysql i.e. my_entity, primary key is a combination of id of a and id of b. I am not getting how to denote this in Hibernate entity MyEntity.
Regarding the same, I have gone through a few posts: Hibernate foreign key as part of primary key and JPA & Hibernate - Composite primary key with foreign key, but no getting idea how to do these two ?
The solution is #MapsId
For example
#Entity
#Table(name = "vm_user")
public class VmUser implements Serializable {
#Id
#Column(name = "user_id")
private Integer id;
#MapsId
#OneToOne
private User user;
You can also remove the #JoinColumn(unique = true) because #Id makes it unique already.
public class MyEntityPk implements Serializable {
private Integer aId;
private Integer bId;
// IMPORTANT: Override equals() and hashCode()
}
#IdClass(MyEntityPk.class)
#Entity
#Table(name = "my_entity")
public class MyEntity implements Serializable {
#Id
private Integer aId;
#Id
private Integer bId;
#MapsId("aId")
#ManyToOne
private A a;
#MapsId("bId")
#OneToOne
private B b;
Please find more information in the Hibernate documentation https://docs.jboss.org/hibernate/orm/5.4/userguide/html_single/Hibernate_User_Guide.html#identifiers-derived
You need to use #EmbeddedId and #MapsId ,
#Entity
#Table(name = "vm_user")
public class VmUser implements Serializable {
#Id
#Column(name = "user_id")
private Integer id;
#MapsId("user_id")
#OneToOne
private User user;
}
You can do the same thing for MyEntity as below,
#Embeddable
class BKey {
private int aId;
private int bId;
}
#Entity
#Table(name = "my_entity")
public class MyEntity implements Serializable {
#EmbeddedId
private BKey primaryKey;
#MapsId("aId")
#ManyToOne
private A a;
#MapsId("bId")
#OneToOne
#JoinColumn(unique = true)
private B b;
}
VM Users class
public class VmUser implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#OneToOne
#JoinColumn(name="ID")
private Users user;
Users class
public class Users implements Serializable {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private long id;
#OneToOne(mappedBy="user")
private VmUser vmUser;
A class
#Entity
public class A implements Serializable {
#Id
private long id;
#OneToMany(mappedBy="a")
private List<MyEntity> myEntitys;
B Class
#Entity
public class B implements Serializable {
#Id
private long id;
#OneToMany(mappedBy="b")
private List<MyEntity> myEntitys;
MyEntity class
#Entity
public class MyEntity implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
private MyEntityPK id;
#ManyToOne
#JoinColumn(name="ID1")
private A a;
#ManyToOne
#JoinColumn(name="ID2")
private B b;
MyEntityPK class
#Embeddable
public class MyEntityPK implements Serializable {
#Column(insertable=false, updatable=false)
private long id1;
#Column(insertable=false, updatable=false)
private long id2;

Saving relation JPA with Spring boot

I'm doing a registration and I have the fields
Nome:
Data de Nascimento:
Inscrição Estadual:
Nome Responsável:
CPF Responsável:
Cep:
Bloco:
Número:
when i saving, I can not write data from the PessoasEnderecos class, the other data is recording normal. I'm getting all the data on the screen so much that I debugged the browser to see ..
It shows no error. Does anyone know what I'm missing ??
my class Pacientes
#Entity
#Table(name = "pacientes", schema = "sau")
public class Pacientes implements Serializable {
private static final long serialVersionUID = 5776384003601026304L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "idPaciente")
private Long idPaciente;
#JoinColumn(name="idPessoa")
#ManyToOne(cascade = CascadeType.ALL)
private Pessoas pessoa;
#Column(name = "nomeResponsavel")
private String nomeResponsavel;
#Column(name = "cpfResponsavel")
private String cpfResponsavel;
public Pacientes() {
}
//gets and sets
}
my class Pessoas
#Entity
#Table(name = "pessoas", schema="glb")
public class Pessoas implements Serializable {
private static final long serialVersionUID = -4042023941980758267L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
private Long idPessoa;
#Temporal(TemporalType.DATE)
private Date dataNascimento;
private String inscricaoEstadual;
private String inscricaoMunicipal;
private String nome;
public Pessoas() {
}
//gets and sets
}
#Entity
#Table(name = "pessoas_enderecos" ,schema="glb")
public class PessoasEnderecos implements Serializable {
private static final long serialVersionUID = -2560542418318988673L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long idPessoaEndereco;
private String bloco;
private String cep;
private String numero;
#JoinColumn(name="idPessoa")
#ManyToOne(optional = false, cascade = CascadeType.ALL)
private Pessoas pessoa;
public PessoasEnderecos() {
}
//gets and sets
}
my methods
class Controller
#RequestMapping(method = RequestMethod.POST, value = "/pacientes")
public Pacientes cadastrarPacientes(#RequestBody Pacientes pac) {
return pacientesService.cadastrar(pac);
}
class service
public Pacientes cadastrar(Pacientes pacientes){
return pacRepository.save(pacientes);
}
class repository
public interface PacientesRepository extends JpaRepository<Pacientes, Integer> {
}
You should also add the linkage #OneToMany in Pacientes:
public class Pacientes implements Serializable {
...
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, mappedBy = "pessoa")
#PrimaryKeyJoinColumn
private List<PessoasEnderecos> pessoasEnderecos = new ArrayList<>();
Update:
and your JSON should be something like this:
{
"nomeResponsavel": "abc",
"pessoasEnderecos": [
{
"bloco": "sdds",
"cep": "sdasdsad",
"numero": "sdasdsa"
}
]
}

"Detached entity passed to persist:" when entity is not detached

I'm getting an error saying that my entity is detached when I try to persist it. When debugging I can see that the objects I try to save have an ID. I am guessing that this might be related to my annotations in JPA but I cannot figure out what is the cause of this problem.
Class with method that causes error(the last line of doStuff() is responsible):
#RestController
#RequestMapping("/test")
public class ConcreteMyController implements MyController {
private final CategoryService categoryService;
private final OrmFactory ormFactory;
private final SongService songService;
private final CategoryForSongService categoryForSongService;
#Autowired
public ConcreteMyController(CategoryService categoryService, OrmFactory ormFactory, SongService songService, CategoryForSongService categoryForSongService) {
this.categoryService = categoryService;
this.ormFactory = ormFactory;
this.songService = songService;
this.categoryForSongService = categoryForSongService;
}
#RequestMapping(method = RequestMethod.GET)
#ResponseStatus(value = HttpStatus.OK)
#Override
public void doStuff() {
String title = "BestSongInTheWorld";
String popCategoryName = "pop";
String rockCategoryName = "rock";
String jazzCategoryName = "jazz";
Song song = this.ormFactory.createSong(title, "3:14");
this.songService.save(song);
Set<Category> categories = Sets.newHashSet(new Category(popCategoryName), new Category(rockCategoryName), new Category(jazzCategoryName));
this.categoryService.save(categories);
Song retrievedSong = songService.get(title);
Set<Category> retrievedCategories = categoryService.get(Sets.newHashSet(popCategoryName, rockCategoryName));
CategoryForSong categoryForSong = new CategoryForSong(retrievedSong.getTitle(), retrievedCategories);
this.categoryForSongService.save(categoryForSong);
}
}
Entities:
#Entity
#Table
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "name")
private String name;
#Id
#GeneratedValue
private Long id;
}
#Entity
#Table
public class Song implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "duration")
private String duration;
#Column(name = "title")
private String title;
#Id
#GeneratedValue
private Long id;
}
#Entity
#Table
public class CategoryForSong implements Serializable {
private static final long serialVersionUID = 1L;
#JoinColumn(name = "categories")
#ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Category> categories;
#Column(name = "songTitle")
private String songTitle;
#Id
#GeneratedValue
private Long id;
}
Getters, setters, equals etc. has been omitted. The reason that CategoryForSong contains a string with a songTitle instead of an actual Song is just me trying to isolate the problem. That did not seem to help.
My services are not very interesting, so I will add the one for Song here just to show the structure:
#Service
public class ConcreteSongService implements SongService {
#Autowired
private SongRepository songRepository;
#Transactional
#Override
public void save(Song song) {
songRepository.save(song);
}
#Transactional
#Override
public Song get(String title) {
return songRepository.getByTitle(Sets.newHashSet(title)).stream().findFirst().get();
}
}
And the repository is as simple as this:
public interface SongRepository extends CrudRepository<Song, Long> {
Set<Song> getByTitle(Set<String> titles);
}
The exception looks like this:
org.hibernate.PersistentObjectException: detached entity passed to persist: proofofconcept.springmvc.model.orm.Category
Help would be very appreciated.

How make a composite primary key using auto generated value and one foreign key using hibernate and spring MVC

I am developing a web application that customer call to a employee and he put a customer's order to the system.
From my ER diagram, the OrderDetail class contain a composite primary key that orderId and productId. Also it contain orderedQuentity column.
What I want is when customer place a order it has to have what products he/she ordered and the quantity of each product he/she order and who place the order(the employee).....
But I feel that in my way of doing that, the orderDetail class can only contain list of product , not the quantity of each product.
How do I do what I want ??
Here is my entity classes that I try to implement.
Customer Class :
#Entity
public class Customer {
#Id
#GeneratedValue
private Integer id;
private String firstName;
private String lastName;
private String companyName;
private Integer teleponeNumber;
#OneToMany(mappedBy="customer")
private List<OrderDetail> orders;
}
Product Class :
#Entity
public class Product {
#Id
#GeneratedValue
private Integer id;
private String name;
private Integer availableQuantity;
private String unitType;
private Integer unitPrice;
private String description;
#ManyToMany(mappedBy="products")
private List<OrderDetail> details;
}
Order Class :
#Entity
public class OrderDetail {
#Id
#GeneratedValue
private Integer orderId;
private Integer orderedQuentity;
private String customerAddress;
private String shipingAddress;
private String orderStatus;
#ManyToOne
#JoinColumn(name="Employee_Id")
private Employee employee;
#ManyToOne
#JoinColumn(name="customer_id")
private Customer customer;
#ManyToMany
#JoinTable
private List<Product> products;
}
Employee Class :
#Entity
public class Employee {
#Id
#GeneratedValue
private Integer id;
private String firstName;
private String lastName;
private String designation;
private String email;
private String password;
#OneToMany(mappedBy="employee")
private List<OrderDetail> orders;
}
From Customer to OrderDetail you have a OneToMany relation, I would suggest you to create a JoinTable
So the customer entity will look like:
#Entity
public class Customer {
#Id
#GeneratedValue
private Integer id;
private String firstName;
private String lastName;
private String companyName;
private Integer teleponeNumber;
#OneToMany(mappedBy="customer")
#JoinTable(name = “customer_order_details”, joinColumns= { #JoinColumn(name = “customer_id”, referencedColumnName=”id”) }, inverseJoinColumns = { #JoinColumn(name = “order_id”, referencedColumnName = “id”) })
private List<OrderDetail> orders;
}
The same situation in Employee entity:
#Entity
public class Employee {
#Id
#GeneratedValue
private Integer id;
private String firstName;
private String lastName;
private String designation;
private String email;
private String password;
#OneToMany(mappedBy="employee")
#JoinTable(name = “employee_order_details”, joinColumns= { #JoinColumn(name = “employee_id”, referencedColumnName=”id”) }, inverseJoinColumns = { #JoinColumn(name = “order_id”, referencedColumnName = “id”) })
private List<OrderDetail> orders;
}
And Product entity:
#Entity
public class Product {
#Id
#GeneratedValue
private Integer id;
private String name;
private Integer availableQuantity;
private String unitType;
private Integer unitPrice;
private String description;
#ManyToMany(mappedBy="products")
#JoinTable(name = “product_order_details”, joinColumns= { #JoinColumn(name = “product_id”, referencedColumnName=”id”) }, inverseJoinColumns = { #JoinColumn(name = “order_id”, referencedColumnName = “id") })
private List<OrderDetail> details;

Categories

Resources