lazy loading return null value - spring

i use spring 3.2, spring data and jpa.
i save an Advertisement object,
after i save message
i try to access message from Advertisement but it's null
#Entity
public class Advertisement implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy="id", cascade={CascadeType.REMOVE}, fetch=FetchType.LAZY)
private Set<Message> messages;
}
#Entity
public class Message implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne
private Advertisement advertisement;
}
test unit
Advertisement ads = new Advertisement();
ads = advertisementRepo.save(ads);
assertNotNull(ads);
assertNotNull(ads.getId());
Message message = new Message();
message.setAdvertisement(ads);
message = msgRepo.save(message);
ads = advertisementRepo.findOne(ads.getId());
ads.getMessages(); //return null
why ads.getMessages() don't return messages?

The problem is that bidirectional relationships are not transparently managed by JPA. When manipulating one side of a bidirectional relationship, the application has to ensure that the other side is updated accordingly.
This can easily be done by writing setter methods that update the associated entity as well. For example, when setting the Advertisment of a Message, you can add the Message instance to the collection in Advertisment:
#Entity
public class Message implements Serializable {
...
public void setAdvertisement(Advertisement advertisement) {
this.advertisement = advertisement;
advertisement.getMessages().add(this);
}
}

Try to save the Advertisement after you assign it a new Message collection:
Advertisement ads = new Advertisement();
ads = advertisementRepo.save(ads);
assertNotNull(ads);
assertNotNull(ads.getId());
Message message = new Message();
message.setAdvertisement(ads);
ads.setMessages(new HashSet<Message>());
ads.getMessages().add(message);
ads = advertisementRepo.save(ads);
message = msgRepo.save(message);
ads = advertisementRepo.findOne(ads.getId());
ads.getMessages(); //return null

This is returning null because you are saving the non-owning entity first and then the owning entity. If you save message before ads it should return non-null value.

Related

Data is being sent to database as null after sending from frontend

I am trying to make a connection between an Event and User entity, so that I can save entrants into an event. I have never used #ManyToMany mapping before and so have been following a tutorial. When I try to post the data via postman (eventid and userid), I get null values for both.
So far I have, User and Event entity,
#Data
//Entity maps object to database
#Entity
#NoArgsConstructor
#Table(name = "member")
public class User implements UserDetails, Serializable {
//More fields
//Relationship between user and events to get entrants
#OneToMany(mappedBy = "userid", fetch = FetchType.LAZY)
Set<Entrants> entrants;
#Data
//Entity maps object to database
#Entity
#NoArgsConstructor
#Table(name = "event")
public class Event implements Serializable {
//More fields
//Relationship with event and users for entrants to an event
#OneToMany(mappedBy = "eventid",fetch = FetchType.LAZY)
Set<Entrants> entrants;
Then I have an Entrant Entity to hold the entrants to an event.
#Entity
#Data
#NoArgsConstructor
#AllArgsConstructor
public class Entrants implements Serializable {
#Id
#GeneratedValue
Long id;
#ManyToOne
#JoinColumn(name = "user_id")
User userid;
#ManyToOne
#JoinColumn(name = "event_id")
Event eventid;
}
Then in my controller,
#PostMapping("/management/events/entrants")
GenericResponse createEntrant(#Valid #RequestBody Entrants entrant) {
System.out.println("entrant is: " +entrant);
entrantService.save(entrant);
return new GenericResponse("Entrant saved");
}
EntrantService
public Entrants save(Entrants entrants) {
return entrantRepository.save(entrants);
}
and the repository is the standard and the above utilises the save() method.
If I post the following in Postman,
{
"user_id": 1,
"event_id": 1
}
I get this
entrant is: Entrants(id=null, userid=null, eventid=null)
id is obviously created by Spring, but the userid and eventid are null.
From my limited knowledge I think this is something to do with the 2 fields in the Entrants entity, being of type User and Event rather than int. But I am not sure how to get around this.
The tutorial I followed wasnt really based on my implementation so I have had to change quite a lot.
You could use a Dto in your controller like this:
#PostMapping("/management/events/entrants")
createEntrant(#Valid #RequestBody EntrantDto entrant) {
System.out.println("entrant is: " +entrant);
entrantService.save(entrant);
return new GenericResponse("Entrant saved");
}
EntrantDto.java
public class EntrantDto {
private Long user_id;
private Long event_id;
// no-args constructor, getter, setter,...
}
and modify a little bit your service like
public Entrants save(EntrantDto entrant) {
User user = this.userRepository.findById(entrant.getUser_id()).orElseThrown(IllegalArgumentException::new);
Event event = this.eventRepository.findById(entrant.getEvent_id()).orElseThrown(IllegalArgumentException::new);
Entrants entrants = new Entrants(user, event);
return entrantRepository.save(entrants);
}

Passing parent id reference when creating child object through REST api

I am using spring boot (version - 2.1.1). I have a one to many database model exposed for CRUD operations through rest api's. The model looks as below. How do I configure the POST /departments api (that creates a department object) to accept just the organization id in the input json body?
#PostMapping
public Long createDepartment(#RequestBody Department Department) {
Department d = departmentService.save(Department);
return d.getId();
}
Note - I do not want to allow creating organization object when creating a department.
Model object mapping
#Entity
#Table(name="ORGANIZATIONS")
public class Organization{
#Id
#GeneratedValue
Private long id;
#Column(unique=true)
Private String name;
#OneToMany(mappedBy = "organization", fetch = FetchType.EAGER)
private List<Department> departments;
}
#Entity
#Table(name="DEPARTMENTS")
Public class Department{
#Id
#GeneratedValue
Private long id;
#Column(unique=true)
Private String name;
#ManyToOne(fetch = FetchType.EAGER)
private Organization organization;
}
Thanks!
The easiest and most sane way in my opinion is to utilize the DTO (Data Transfer Object) pattern.
Create a class that represent the model you want to get as your input:
public class CreateDepartmentRequest {
private long id;
// getters and setters
}
Then use it in your controller:
#PostMapping
public Long createDepartment(#RequestBody CreateDepartmentRequest request) {
Department d = new Department();
d.setId(request.getId());
Department d = departmentService.save(d);
return d.getId();
}
Side note, its better to ALWAYS return JSON through REST API (unless you use some other format across your APIs) so you can also utilize the same pattern as I mentioned above to return a proper model as a result of the POST operation or a simple Map if you don't want to create to many models.

How to add One to One relationship with an Embeddable

Requirement:
To fetch the list of requests along with the requests feedback status.
What I'm doing now :
Fetch all the requests using JPQL query. Loop through each request and fetch status and set into response dto.
What I want to doing.
Fetch all the request along with the status using JPQl query
What I'm looking for :
How can I add one to one mapping for requests & status, so that I can fetch status.
Sample code
This is the MSREQUEST entity
#Entity
#Table(name = "MSREQUEST")
public class Request implements Serializable {
#Id
private long requestId;
#Column(name = "DESC")
private string desc;
//getter.. setter...tostring and hashcode
}
This is the status entity
#Entity
#Table(name="FEEDBACKSTATUS")
public class FeedbackStatus implements Serializable {
// composite-id key
#EmbeddedId
private RequestFeedBackId requestFeedbackKey = new RequestFeedBackId();
#Column(name="STATUS")
private Long status;
//getter.. setter...tostring and hashcode
}
This is the embeddable entity
#Embeddable
public class RequestFeedBackId implements Serializable {
#Column(name="REQUESTID")
private Long requestId;
#Column(name="FEEDBACKID")
private Long feedbackId;
}
Service
#Override
public List<MsaRequestSearchDto> searchMsaRequests(MsaRequestSearchDto msaRequestSearchDto)
throws MsaException, Exception {
List<MsaRequestSearchDto> msaRequestSearchDtoList = msaRequestRepoCustom.findMsaRequests(msaRequestSearchDto);
*// get feedback status loop thru and fetch status for each one. nee to avoid this
if(msaRequestSearchDtoList != null && msaRequestSearchDtoList.size() > 0){
// code to fetch dstatus
}*/
return msaRequestSearchDtoList;
}
JPQL query that Im using..
public String GET_MSA_REQUEST = "SELECT dsr FROM Request dsr WHERE 1=1";
E-R

Spring Data JPA findOne returns null

I'm using Spring Data JPA and I'm facing a very weird issue with findOne method which is returning null even though the row is present in DB.
I have a consumer thread which takes an id from a queue and tries to fetch the entity from DB which always returns null, however if I pause thread (by putting a breakpoint before method call) then it fetches the entity from DB but returns null in normal execution of program, I know it sounds weird with breakpoint stuff but it is what it is, may be I'm missing something. My code looks like below:-
if (id > 0) {
employee = employeeService.get(id);
if (employee == null) {
logger.error(String.format("No employee found for id : %d",
id));
return;
}
I'm not using any transaction in "employeeService" as it is not required as it is a read operation.
My service looks like
public Employee get(long id) {
return employeeDao.findOne(id);
}
And my model looks like:-
#Entity
#Table(name = "employee")
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Employee implements Serializable {
private static final long serialVersionUID = 1681182382236322985L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "name")
private String name;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "emplopyee_id")
#Fetch(FetchMode.SELECT)
private List<Address> addresses;
// getter/setter and few fields have been omitted
}
Can somebody point me where I'm mistaking.
The Spring 4.2 way to do this would be to introduce a #TransactionEventListener annotated method on a spring component to handle the callback. You then simply need to publish an event and let the event framework do its thing:
// Spring component that handles repository interactions
#Component
public class ProducerService implements ApplicationContextAware {
private ApplicationContext applicationContext;
#Transactional
public void doSomeThingAwesome(String data) {
MyAwesome awesome = new MyAwesome( data );
myAwesomeRepository.save( awesome );
applicationContext.publishEvent( new MyAwesomeSaved( awesome.getId() ) );
}
}
// Spring component that handles the AFTER_COMMIT transaction callback
// This component only fires when a MyAwesomeSaved event is published and
// the associated transaction it is published in commits successfully.
#Component
public class QueueIdentifierHandler {
#TransactionalEventListener
public void onMyAwesomeSaved(MyAwesomeSaved event) {
Long entityId = event.getId();
// post the entityId to your queue now
}
}

How to save child entities without saving parent for each transaction

I am using Spring Data JPA repositories. I have a Card entity and a Transaction entity. When user perform a transaction with card then i would like to save Card and transaction(purchase/refund) entities both. But when user performs next transaction then i want to save Transaction entity only. My Entities are :
Card Entity
#Entity
#Table(name = "CARD")
public class Card {
#Id
private Long card_id;
public Long getCard_id() {
return card_id;
}
public void setCard_id(Long card_id) {
this.card_id = card_id;
}
private String type;
}
Transaction Entity
#Entity
#Table(name="Transaction")
public class Transaction {
#Id
#SequenceGenerator( name="TRAN_SEQ1", initialValue=5,sequenceName="TRAN_SEQ1", allocationSize=1 )
#GeneratedValue( strategy=GenerationType.SEQUENCE, generator="TRAN_SEQ1")
private long id;
#ManyToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "card_id")
private Card card;
public Card getCard() {
return card;
}
public void setCard(Card card) {
this.card = card;
}
}
I have tried with below approach but it throws below exception on save:
Transaction t = new Transaction();
Card c = cardRepository.getOne(123L);
t.setCard(c);
transactionRepository.save(t);
**Exception :
org.hibernate.PersistentObjectException: uninitialized proxy passed to persist()**
I am not sure what I am missing. Can anyone guide me here..
Have you tried to add the reverse relationship?
#Entity
#Table(name = "CARD")
public class Card {
#Id
private Long card_id;
#OneToMany
private List<Transaction> transactions = new ArrayList<>();
// Getters and Setters
}

Resources