The Parent doensn't retrieve the childs hibertnate JPA - java

I have a Controller from rest service that I call a Hibernate method to get the result, but I really don't know why the children components didn't come. When I call this method using Junit, It works.
This is the Code:
{
#Entity
public class Product implements Serializable {
private static final long serialVersionUID = -6131311050358241535L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#Column(nullable = false)
private String name;
private String description;
#OneToMany(mappedBy = "product")
private List<Image> images = new ArrayList<Image>();
}
{
#Entity
public class Image implements Serializable {
private static final long serialVersionUID = 2128787860415180858L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#JoinColumn(name = "product_id")
#ManyToOne
private Product product;
private ImageType type;
}
{
#PersistenceContext
private EntityManager entityManager;
public List<Product> findAllWithParentProductsAndImage() {
String hpql = "select distinct p from Product p left join fetch p.images";
List<Product> resultList = entityManager.createQuery(hpql,
Product.class).getResultList();
return resultList;
}
}

By default #OneToMany will load lazily.
You should use #OneToMany( mappedBy = "product", fetch=FetchType.Eager ) to do Eager fetch

You can definitely use
#OneToMany(mappedBy = "product", fetch=FetchType.Eager)
However this has a downside. You will always be fetching children even if you only want the Parent and its few properties.
Use JOIN FETCH within your #Query if you are using JpaRepositories.
Check out the following related questions
How to properly express JPQL "join fetch" with "where" clause as JPA 2 CriteriaQuery?
http://www.objectdb.com/java/jpa/query/jpql/from#LEFT_OUTER_INNER_JOIN_FETCH_
https://stackoverflow.com/a/29667050/3094731

Related

JPA StackOverflow -- Many to Many and one to one mapping

I am trying to save a JPA entity which has ManytoMany Relationship (Consumer and Product table) and OnetoOne relation with ConsumerDetailstable.Below are my entities
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class)
#Entity
public class Consumer {
#Id
#GeneratedValue
private Long id;
private String firstName;
private String lastName;
#JsonManagedReference
#OnToMany(mappedBy = "consumer")
private Set<ConsumerProduct> consumerProducts;
#OneToOne
private CustomerDetails consumerDetails;
}
#Entity
public class Product {
#Id
#GeneratedValue
private Long productId;
private String productCode;
#OneToMany(mappedBy = "product")
private Set<ConsumerProduct> consumerProducts;
}
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonIgnoreProperties(ignoreUnknown = true)
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class)
#Entity(the join table)
public class ConsumerProduct {
#EmbeddedId
ConsumerProductKey id;
#JsonBackReference
#ManyToOne
#MapsId("id")
#JoinColumn(name = "id")
private Consumer consumer;
#ManyToOne
#MapsId("productId")
#JoinColumn(name = "product_id")
private Product product;
}
#Embeddable (forgein keys combined as embeded id)
public class ConsumerProductKey implements Serializable {
#Column(name="id")
private Long id;
#Column(name = "product_id")
private Long productId;
}
#Enitity (one to one relation table)
public class CustomerDetails {
#Id
#GeneratedValue
private Long consumerDtlId;
#OneToOne
private Consumer consumer;
private String city;
private String state;
private String country;
}
To save the entity am have just extended JPARepository and called save method
public class ConsumerRepository<Consumer> Implements JPARepository<Consumer, Long> {
#Override
public Consumer S save(Consumer entity) {
return save(entity);
};
}
I get java.lang.StackOverFlowError at save method.
Anything wrong with my Mappings ?
Question: Since this will be save operation and since Consumer Id is yet to be generated how do I assign to below Entities
ConsumerProduct.ConsumerProductKey (how do i assign Id of consumer table once it is inserted to join table ? will JPA take care of it)
CustomerDetails (how do i assign Id of consumer table once it is inserted to join table ? will JPA take care of it)
EDIT: I have updated the entity with JsonManagedReference and JsonBackedReference but still i have am facing stackoverflow error
It is due to Consumer trying to access ConsumerProduct and ConsumerProduct trying to access consumer entity and end up with StackOverflow error.
You should use #JsonManagedReference and #JsonBackReference annotation in consumer and ConsumerProduct respectivly.

JPQL - JOIN query with filter

I have the entitys:
First
#Entity
#Getter
#Setter
#NoArgsConstructor
public class Technic implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String gosNumber;
private String invNumber;
private String shassisNumber;
private String engineNumber;
#Column(length = 100)
private String yearOfMake;
#ManyToOne
private Farm farm;
#JsonManagedReference
#ManyToOne
private TechGroup techGroup;
#JsonManagedReference
#ManyToOne
private TechType techType;
#JsonManagedReference
#ManyToOne
private TechMark techMark;
#JsonIgnore
#CreationTimestamp
#Column(name = "creation_date", updatable = false)
private LocalDateTime createdDate;
#JsonIgnore
#Column(name = "updated_date")
#UpdateTimestamp
private LocalDateTime updatedDate;
#JsonIgnore
#Column(columnDefinition = "Bool default false")
private Boolean isDel;
#JsonManagedReference
#OneToMany(mappedBy = "technic")
private List<TechnicStatus> technicStatusList = new ArrayList<>();
public List<TechnicStatus> getTechnicStatusList() {
return technicStatusList;
}
public void setTechnicStatus(TechnicStatus technicStatus) {
this.technicStatusList = new ArrayList<>();
this.technicStatusList.add(technicStatus);
}
Second:
#Entity
#Getter
#Setter
#NoArgsConstructor
public class TechnicStatus implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "technic_status_id")
private Long id;
#JsonBackReference
#ManyToOne
private Technic technic;
#JsonManagedReference
#ManyToOne
private Status status;
private Boolean isGarantia;
private Boolean isLizing;
private LocalDate visitedDate;
private LocalDate notWorkDate;
private String description;
#JsonIgnore
private boolean isActive;
#JsonIgnore
#CreationTimestamp
#Column(name = "creation_date", updatable = false)
private LocalDateTime createdDate;
}
I want to get result from my db which contains the List in each object Technic i have List technicStatusList = new ArrayList<>() in which i want to have a TechnicStatus only with value is isActive=true.
For this i right same JPQL query:
TypedQuery<Technic> query = em.createQuery("Select t from Technic t join TechnicStatus ts on t.id = ts.technic.id where t.isDel=false and ts.isActive=true and t.farm.id=:farmId order by t.techGroup.name, t.techType.name, t.techMark.name", Technic.class);
But get a result containing TechnicStatus, which returns a TechnicStatus with true and false (TechnicStatus.isActive=true, TechnicStatus.isActive=false).
I want to get result as this native query:
SELECT
*
FROM
technic
JOIN
technic_status ON technic.id = technic_status.technic_id
WHERE
technic.is_del = FALSE
AND technic_status.is_active = TRUE
AND technic.farm_id = 1722
The List of TechnicalStatus associated with a Technic will always be the complete list as defined by your mappings.
Essentially then you have 2 options. If you are only ever interested in TechnicalStatus with a status of Active then you can use the non-portable, Hibernate specific #Where clause on the association.
#JsonManagedReference
#OneToMany(mappedBy = "technic")
#Where("active = 1")
private List<TechnicStatus> technicStatusList = new ArrayList<>();
https://dzone.com/articles/hibernate-where-clause
Otherwise all you do is return a List of TechnicalStatus from query method which is not what you want but is all you have.
Its not possible to filter related collection in query conditions. You can get it by doing select on TechnicalStatus: select ts from TechnicStatus ts join Technic t where ...
Other thing I noticed:
You are overwriting list of existing statuses when adding new one:
public void setTechnicStatus(TechnicStatus technicStatus) {
this.technicStatusList = new ArrayList<>();
this.technicStatusList.add(technicStatus);
}
Initialize technicStatusList in field declaration. Method in Technic for adding:
public void addTechnicStatus(TechnicStatus technicStatus) {
getTechnicStatusList().add(technicStatus);
technicStatus.setTechnic(this);
}
Other thing I noticed:
When using join don't use on t.id = ts.technic.id. JPA will create correct native SQL just when you write: join TechnicStatus ts WHERE ...
Using join fetch should solve the issue, this would force the query to run eagerly all at once and only bring back the records that match the where clause.
so your query would be:
TypedQuery<Technic> query = em.createQuery("Select t from Technic t join fetch TechnicStatus ts where t.isDel=false and ts.isActive=true and t.farm.id=:farmId order by t.techGroup.name, t.techType.name, t.techMark.name", Technic.class);

Loading DTO with collection

#Entity
#Table(name = "person")
public class Consignment implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "person_id")
private String personId;
#Column(name = "person_name")
private String personName;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "person")
#Column(name = "cars_owned")
private Set<Cars> casrsowned = new HashSet<>();
}
#Entity
#Table(name = "cars")
public class Cars implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "cars_id")
private String carsId;
#ManyToOne
#JoinColumn(name = "person")
private Person person;
#OneToOne
private CarsDetail carsDetail;
}
#Entity
#Table(name = "carsDetail")
public class CarsDetail implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "brand")
private String brand;
#Column(name = "color")
private String color;
#Column(name = "model")
private String model;
}
class CarModelDTO {
String personName;
List<String> models;
}
In the above relation, want to return CarModelDTO
JPA query where,
#Query("Select CarModelDTO(p.personName, p.casrsowned.carsDetail.model) from Person as p where p`enter code here`.id = :id"))
public CarModelDTO getCarmodelOwnedByAperson(#Param("id") Long id);
I tried multiple ways but it gives
org.hibernate.QueryException: illegal attempt to dereference collection
As I have already described Retrieve List from repository interface to DTO list you should go through the following step :
first create a constructor using the fields you want to be returned from the query output
in you query you should create new instance of your dto and pass the field from db to new instalnce :
so you need these changes:
1. In the constructor:
You should not use a list as List<String> models; as you should consider that your dto as a result row of DB. so you need to have a simple String model;
public CarModelDTO (String name,String model){
this.name=name;
this.model=model;
}
2. In the #Query:
you should use multi inner join appropriately
you should also append your package name to CarModelDTO in the query (here i used com.example you should change it)
#Query("Select com.example.CarModelDTO(p.personName, d.model ) from Person as p inner join p.carsowned c inner join c.carDetail d where p`enter code here`.id = :id"))
public CarModelDTO getCarmodelOwnedByAperson(#Param("id") Long id)

Save object with OneToMany from JSON to database

I'm trying to send the following JSON to a REST API and persist on database, but only the Product is created, the Image it is not.
{"name":"pen",
"description":"red pen",
"images":[{"type":"jpeg"}]
}
#Controller
#POST
#Path("/product/add")
#Consumes("application/json")
public Response addProduct(Product product) {
service.createProduct(product);
}
#Service
#Autowired
private ProductDAO productDAO;
#Autowired
private ImageDAO imageDAO;
public void createProduct(Product product) {
productDAO.save(product);
}
#Product
#Entity
#Table
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer productId;
#Column(name = "NAME")
private String name;
#Column(name = "DESCRIPTION")
private String description;
#OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="product")
private Set<Image> images;
#OneToMany(cascade=CascadeType.ALL, fetch = FetchType.EAGER, mappedBy="parent")
private Set<Product> children;
#ManyToOne
#JoinColumn(name = "PARENT_PRODUCT_ID")
private Product parent;
#Image
#Entity
#Table
public class Image implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer imageId;
#Column(name = "TYPE")
private String type;
#ManyToOne
#JoinColumn(name = "PRODUCT_ID", nullable = false)
private Product product;
At the #POST method, when print the Product object received, this is what returns:
Product [productId=null, name=pen, description=red pen, images=[Image [id=null, type=jpeg, product=null]], children=null, parent=null]
The correct way is to first persist the Product, and then persist the Image or the Hibernate can automatically persist the Image when I persist the Product?
Hibernate takes care of persisting your child entities if your bidirectional mapping is correctly implemented and you have set proper relationships between your entity objects.
You have a Product entity that has a collection of Image. Product entity is the parent entity here. You can simply set proper relations between Product and Image entities and persist only Product. Hibernate will persist your parent as well as your child entities.
What you need to do
Product product = new Product();
product.setName("PRODUCT_NAME");
Set<Image> productImages = new HashSet<>();
Image productProfileImage = new Image();
productProfileImage.setType("PROFILE");
productProfileImage.setProduct(product);
//..set other fields
productImages.add(productProfileImage);
Image productCoverImage = new Image();
productCoverImage.setType("COVER");
productCoverImage.setProduct(product);
//..set other fields
productImages.add(productCoverImage);
product.setImages(productImages);
productRepository.save(product); //Persist only your product entity and the mapped child entities will be persisted
Check out this similar answer.
PS: I have not tested the code but this should work.

How can I convert this 3 JOIN query into a Spring Data JPA named query method?

I am not so into Spring Data JPA and I have the following problem trying to implement a named query (the query defined by the method name).
I have these 3 entity classes:
#Entity
#Table(name = "room_tipology")
public class RoomTipology implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#Column(name = "tipology_name")
private String name;
#Column(name = "tipology_description")
private String description;
#Column(name = "time_stamp")
private Date timeStamp;
#OneToMany(mappedBy = "roomTipology")
private List<Room> rooms;
#OneToOne(mappedBy = "roomTipology")
private RoomRate roomRate;
// GETTER AND SETTER METHODS
}
That represents a tipology of room and that contains this field
#OneToMany(mappedBy = "roomTipology")
private List<Room> rooms;
So it contains the list of room associated to a specific room tipology, so I have this Room entity class:
#Entity
#Table(name = "room")
public class Room implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#ManyToOne
#JoinColumn(name = "id_accomodation_fk", nullable = false)
private Accomodation accomodation;
#ManyToOne
#JoinColumn(name = "id_room_tipology_fk", nullable = false)
private RoomTipology roomTipology;
#Column(name = "room_number")
private String number;
#Column(name = "room_name")
private String name;
#Column(name = "room_description")
#Type(type="text")
private String description;
#Column(name = "max_people")
private Integer maxPeople;
#Column(name = "is_enabled")
private Boolean isEnabled;
// GETTER AND SETTER METHODS
}
Representing a room of an accomodation, it contains this annoted field:
#ManyToOne
#JoinColumn(name = "id_accomodation_fk", nullable = false)
private Accomodation accomodation;
And finally the Accomodation entity class:
#Entity
#Table(name = "accomodation")
public class Accomodation implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id")
private Long id;
#OneToMany(mappedBy = "accomodation")
private List<Room> rooms;
#Column(name = "accomodation_name")
private String name;
#Column(name = "description")
#Type(type="text")
private String description;
// GETTER AND SETTER METHODS
}
Ok, so now I have this Spring Data JPA repository class for RoomTipology:
#Repository
#Transactional(propagation = Propagation.MANDATORY)
public interface RoomTipologyDAO extends JpaRepository<RoomTipology, Long> {
}
Here I want to define a named query method that return to me the list of all the RoomTipology object related to a specific accomodation, I have done it using SQL and it works fine:
SELECT *
FROM room_tipology as rt
JOIN room r
ON rt.id = r.id_room_tipology_fk
JOIN accomodation a
ON r.id_accomodation_fk = a.id
WHERE a.id = 7
But now I want to translate it in a named query method (or at least using HQL)
How can I do it?
Please Try:
#Repository
#Transactional(propagation = Propagation.MANDATORY)
public interface RoomTipologyDAO extends JpaRepository<RoomTipology, Long> {
List<RoomTipology> findByRooms_Accomodation(Accomodation accomodation);
}
The query builder mechanism built into Spring Data repository infrastructure is useful for building constraining queries over entities of the repository. The mechanism strips the prefixes find…By, read…By, query…By, count…By, and get…By from the method and starts parsing the rest of it
At query creation time you already make sure that the parsed property is a property of the managed domain class. However, you can also define constraints by traversing nested properties.
Doc:Here

Categories