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

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);
}

Related

fetch list based on id present in another entity

this is my order entity,
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
#Table(name = "ordertab")
public class Order {
#Id
private int orderId;
private String orderDate;
#ManyToMany(targetEntity = Medicine.class,cascade = CascadeType.ALL)
#JoinTable(name="ord_med",
joinColumns = {#JoinColumn(name="ord_id")},
inverseJoinColumns = {#JoinColumn(name="med_id")})
private List<Medicine> medicineList;
private String dispatchDate;
private float totalCost;
#ManyToOne(targetEntity = Customer.class,cascade = CascadeType.ALL)
#JoinColumn(name= "custord_fk",referencedColumnName = "customerId")
private Customer customer;
private String status;
}
and this is my medicine entity,
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
#Entity
public class Medicine {
#Id
private String medicineId;
private String medicineName;
private float medicineCost;
private LocalDate mfd;
private LocalDate expiryDate;
**#ManyToMany(cascade = CascadeType.ALL, mappedBy = "medicineList")
private List<Order> orderList;** //order/ medicine many to many mapping
// OneToOne Mapping
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "categoryId", referencedColumnName = "categoryId")
private Category category;
in my order service interface i have a method,
List showAllOrder(string medId);
I have to fetch all orders that has the matching med id.
this many to many mapping have created a additional table ord_med with two columns named ord_id,med_id(type foreign keys).In addition to that due to this bidirectional mapping(i believe it is) while creating object of medicine entity its asking me to add orderlist ,how to approach this method or how exactly should i solve this. thankyou.
in your OrderRepository you can implements this method
findByMedicineId(String id);
if i go for findByMedicineId(String id);
it gives error saying no property medicineId is found in Order entity,cuz the property medicineId is in Medicine entity,while defining custom method in repository follows rules, refer https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.query-creation
anyway I have found the solution for this,
public List<Order> getOrderListBasedOnMedicineId(String medicineid) {
Optional<Medicine> med=medicineRepo.findById(medicineid);//find if medicine is present in database with the id.
if(med.isEmpty()) {
return null;
}
List<Order> orders = medicineServ.getOrderList(); //getorderlist defined in service implementation of medicine.
List<Order> ordersWithMedId = new ArrayList();//new list to add all orders that has atleast one medicineId that matches.
for(int i=0;i<orders.size();i++) {
List<Medicine> medicines= orders.get(i).getMedicineList();
for(int j=0;j<medicines.size();j++) {
ordersWithMedId.add(orders.get(i));
}
}
return ordersWithMedId;//returning the list of orders.
}
#Override
public List<Order> getOrderList() {//medicine service implementation
return orderRepo.findAll();
}
//OrderController
#GetMapping("/orders/list/{id}")
public ResponseEntity<List<Order>> getOrderListBasedOnMedicineId(#PathVariable("id") String id) {
List<Order> ord= orderService.getOrderListBasedOnMedicineId(id);
if(ord==null) {
throw new OrderNotFoundException("Order not found with medicine id:"+id);
}
return new ResponseEntity<List<Order>>(orderService.getOrderListBasedOnMedicineId(id),HttpStatus.OK);
}

Issue in persisting nested comments using Spring Hibernate

I am trying to create a simple CRUD application using Spring Boot with User, UserEntity, Post, Comment entities.
-> UserEntity is super class of Comment and Post.
-> Each comment has a ManyToOne relationship to a UserEntity (which can be a Post or another Comment)
UserEntity
   |
   #ManyToOne
   createdBy - refers to user table (id)
   |
--------------------
|        |
|        |
Post    Comment
        |
        #ManytoOne
          UserEntity - refers to PK(entity_id) of user_entity table as comment can be on post or reply to another comment
On trying to save a comment on post from the CommentService class,
//Controller
#PostMapping(path = "api/v1/addComment")
public void addComment(#RequestBody Comment comment){ commentService.addCommentOnPost(comment); }
//Service
public void addCommentOnEntity(Comment comment){ commentRepos.save(comment); }
the foreign key in comment table (parent_entity_id) referring to entity_id in user_entity table is not getting updated. The value is blank.
On the other hand UserEntity has a manytoone relationship with User -- createdBy -- which is updating foriegn key user_id in user_entity table properly
Can someone guide me what could be wrong, I have been trying since yesterday night but no luck. Have checked some other answers but could not get an answer for this case.
User.java
#Entity
#Table(name="[user]")
public class User {
#Id
#SequenceGenerator(name="student_sequence",
sequenceName = "student_sequence",
allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "student_sequence")
private long id;
private String name;
private String email;
private int age;
private LocalDate DOB;
//Setters and Getters and default constructor
}
UserEntity.java
#Entity
#Inheritance(strategy = InheritanceType.JOINED)
public abstract class UserEntity {
#Id
#SequenceGenerator(sequenceName = "entity_sequence", name="entity_sequence", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "post_sequence")
private long entityId;
private char entityType;
private LocalDate createdOn;
private LocalDate modifiedOn;
#ManyToOne
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User createdBy;
//Setters and Getters and default constructor
}
Post.java
#Entity
public class Post extends UserEntity{
private String postHeading;
private String postBody;
//Setters and Getters and default constructor
}
Comment.java
#Entity
public class Comment extends UserEntity{
private String comment;
#ManyToOne
#JoinColumn(name="parent_entity_id", referencedColumnName = "entityId")
private UserEntity parentEntity;
//Setters and Getters and default constructor
}
and their repositories
#NoRepositoryBean
public interface UserEntityBaseRepos<T extends UserEntity> extends JpaRepository<T, Long>{
Optional<List<T>> findByCreatedBy_Id(Long user_id);
Optional<List<T>> findByEntityId(Long entity_id);
}
#Repository
public interface UserRespository extends JpaRepository<User, Long> {
Optional<User> findUserByEmail(String email);
Optional<User> findUserByName(String name);
}
#Repository
public interface PostRepos extends UserEntityBaseRepos<Post>, JpaRepository<Post, Long> {
}
#Repository
public interface CommentRepos extends UserEntityBaseRepos<Comment>, JpaRepository<Comment, Long> {
}
Json for postComment service
{
"entityType" : "C",
"createdOn" : "2020-02-05",
"createdBy" : {
"id" : 1
},
"comment": "I am the comment",
"parentEntity" : {
"entityId" : 1
}
}
//User with id = 1 and UserEntity(Post) with entityId = 1 available in database.
Here createdBy.id (user id) is getting updated in the user_entity table, but userEntity.entityId is not getting updated in the comment table
You have very complex entity relationships, it seems to me...
Anyway, I found that you added a generator property to the UserEntity entity with a post_sequence value, but I can't find any relationship to the Post entity in your database. This is probably the reason of the breakdown. You have to connect UserEntity to Post as shown on your diagram or change the generator value.
I was able to solve the problem. The issue was in the following piece of code in Comment concrete class
#ManyToOne
#JoinColumn(name="parent_entity_id", referencedColumnName = "entityId")
private UserEntity parentEntity;
and this Json input
"parentEntity" : {
"entityId" : 1
}
It seems the parentEntity in json input was not being parsed. This was solved on placing JsonProperty("parentEntity") above parentEntity in the Json input was being parsed correctly.
However there was another issue. The parentEntity was not being deserialized to UserEntity as UserEntity is an abstract class. I had to use JacksonPolymorphicDeserialization by introducing a new field parentType("P" for post, "C" for comment) along with some Annotations like below to deserialize parentEntity to corresponding concrete class object.
public class Comment extends UserEntity{
private String comment;
#Transient
#JsonProperty("parentType")
private char parentType;
#ManyToOne
#JoinColumn(name="parent_entity_id", referencedColumnName = "entity_id", foreignKey = #ForeignKey(value=ConstraintMode.CONSTRAINT))
#JsonProperty("parentEntity")
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME , property = "parentType", include = JsonTypeInfo.As.EXTERNAL_PROPERTY)
#JsonSubTypes(value = {
#JsonSubTypes.Type(value = Comment.class, name = "C"),
#JsonSubTypes.Type(value = Post.class, name = "P")
})
private UserEntity parentEntity;
reference - Jackson Polymorphic Deserialization via field. I am not really sure how this works. Will try to make sense of it and update the answer.
If anyone knows a better way to deserialize json, do mention it in the comments or as a new answer.

Foreign Keys causing issues with Many to One relationship in Spring. Null Pointer error

We have a User Entity and an Activity Entity where we have a Many to one relationship with User and a One to Many relationships with Activity. We keep getting a null pointer error every time we try to post something. Basically, When I try to add a new activity with an associated foreign key int(for the user). It gives me this message. Don't know why it has a null pointer exception Error
This is what my Activity Controller looks like:
#RestController
public class Activitiy_Contoller {
#Autowired
private User_Service userService;
private Activity_Service activityService;
#RequestMapping(method=RequestMethod.GET, value="/getactivity")
public List<Activity_Entity> getAllActivity()
{
return Activity_Service.getAllActivity();
}
#GetMapping(value="/getactivityfromid/{id}")
public Activity_Entity getActivity(#PathVariable int id)
{
Activity_Entity temp = activityService.getActivity(id);
return temp;
}
#RequestMapping(method=RequestMethod.POST, value="/addActivity")
public void addActivity(#RequestBody Activity_Entity activity)
{
activityService.addActivity(activity);
}
#DeleteMapping(value="/deleteActivity/{id}")
public void deleteActivity(#PathVariable int id)
{
activityService.deleteActivity(id);
}
}
My Relationship in Activity Entity looks like this:
#Entity
#Table(name ="activity_type")
public class Activity_Entity {
//-----------Values-------------
#Id
#Column(name="Id")
#GeneratedValue(strategy=GenerationType.AUTO)
public int id;
//can get the activities for one user with
//this value using the activity_repo
#JsonIgnore
#ManyToOne
#JoinColumn(name ="user_id")
public User_Entity user;
My Relationship in User Entity looks like this:
#Entity
public class User_Entity {
// ----------------Values------------------
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
public int Userid;
//Many groups to one user
//so can get all groups for one user
//from this value
#OneToMany(mappedBy = "user")
Set<Activity_Entity> Activities;
private Set<Group_Entity> Groups;

Spring Data Rest #EmbeddedId cannot be constructed from Post Request

I have a JPA entity Person and an entity Team. Both are joined by an entity PersonToTeam. This joining entity holds a many-to-one relation to Person and one to Team. It has a multi-column key consisting of the ids of the Person and the Team, which is represented by an #EmbeddedId. To convert the embedded id back and forth to the request id I have a converter. All this follows the suggestion on Spring Data REST #Idclass not recognized
The code looks like this:
#Entity
public class PersonToTeam {
#EmbeddedId
#Getter
#Setter
private PersonToTeamId id = new PersonToTeamId();
#ManyToOne
#Getter
#Setter
#JoinColumn(name = "person_id", insertable=false, updatable=false)
private Person person;
#ManyToOne
#Getter
#Setter
#JoinColumn(name = "team_id", insertable=false, updatable=false)
private Team team;
#Getter
#Setter
#Enumerated(EnumType.STRING)
private RoleInTeam role;
public enum RoleInTeam {
ADMIN, MEMBER
}
}
#EqualsAndHashCode
#Embeddable
public class PersonToTeamId implements Serializable {
private static final long serialVersionUID = -8450195271351341722L;
#Getter
#Setter
#Column(name = "person_id")
private String personId;
#Getter
#Setter
#Column(name = "team_id")
private String teamId;
}
#Component
public class PersonToTeamIdConverter implements BackendIdConverter {
#Override
public boolean supports(Class<?> delimiter) {
return delimiter.equals(PersonToTeam.class);
}
#Override
public Serializable fromRequestId(String id, Class<?> entityType) {
if (id != null) {
PersonToTeamId ptid = new PersonToTeamId();
String[] idParts = id.split("-");
ptid.setPersonId(idParts[0]);
ptid.setTeamId(idParts[1]);
return ptid;
}
return BackendIdConverter.DefaultIdConverter.INSTANCE.fromRequestId(id, entityType);
}
#Override
public String toRequestId(Serializable id, Class<?> entityType) {
if (id instanceof PersonToTeamId) {
PersonToTeamId ptid = (PersonToTeamId) id;
return String.format("%s-%s", ptid.getPersonId(), ptid.getTeamId());
}
return BackendIdConverter.DefaultIdConverter.INSTANCE.toRequestId(id, entityType);
}
}
The problem with this converter is, that the fromRequestId method gets a null as id parameter, when a post request tries to create a new personToTeam association. But there is no other information about the payload of the post. So how should an id with foreign keys to the person and the team be created then? And as a more general question: What is the right approach for dealing many-to-many associations in spring data rest?
After running into the same issue I found a solution. Your code should be fine, except I return new PersonToTeamId() instead of the DefaultIdConverter if id is null in fromRequestId().
Assuming you are using JSON in your post request you have to wrap personId and teamId in an id object:
{
"id": {
"personId": "foo",
"teamId": "bar"
},
...
}
And in cases where a part of the #EmbeddedId is not a simple data type but a foreign key:
{
"id": {
"stringId": "foo",
"foreignKeyId": "http://localhost:8080/path/to/other/resource/1"
},
...
}

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