How to save multiple Entities with relationship - java

In my spring boot application I have 3 Entities, meetingSetting, meetingDate and time. MeetingSettingsId is referenced in both entities date and time. time also have dateId as a reference. I know want to send such a json to my backend:
{
"meetingName":"hallo",
"meetingUrl":"",
"meetingPw":"s",
"dates":"2021-05-30",
"times":[
{
"startTime":"15:30",
"endTime":"16:30"
},
{
"startTime":"18:30",
"endTime":"19:30"
}
]
}
But I am getting the following error:
not-null property references a null or transient value : com.cbc.coorporateblinddateservice.entities.times.Time.meetingDate
I want to save with one method meetingSetting witht the shown json.
This is how my entities look:
#Entity
#Table(name = "meeting_date")
#Data
public class Date {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "meeting_date")
private String date;
#OneToMany(mappedBy = "meetingDate", cascade = CascadeType.ALL)
private List<Time> meetingTime = new ArrayList<>();
#ManyToOne()
#JoinColumn(name = "meeting_settings_name", nullable = false)
private MeetingsSetting meetingsSetting;
#JsonCreator
public static Date fromDateAsString(String dateString){
Date date = new Date();
date.setDate(dateString);
return date;
}
}
meetingSettingEntity:
#Entity
#Table(name = "meeting_settings")
#Data
public class MeetingsSetting {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "meeting_name")
private String meetingName;
#Column(name = "meeting_url")
private String meetingUrl;
#Column(name = "meeting_pw")
private String meetingPw;
#OneToMany(mappedBy = "meetingsSetting", cascade = CascadeType.ALL)
private Date dates = new Date();
#OneToMany(mappedBy = "meetingsSetting", cascade = CascadeType.ALL)
private List<Time> times = new ArrayList<>();
}
and finally time:
#Entity
#Table(name = "meeting_times")
#Data
public class Time {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "start_time")
private String startTime;
#Column(name = "end_time")
private String endTime;
#ManyToOne()
#JoinColumn(name = "meeting_settings_name", nullable = false)
private MeetingsSetting meetingsSetting;
#ManyToOne()
#JoinColumn(name = "meeting_date_id")
private Date meetingDate;
}
this is the save method I am using and here probably the error occurs. I guess I kind of have to tell to save time and date too if I am not wrong, but I did not know how to do it the service method calls the save from a jparepository:
#PostMapping("/")
public void saveMeeting(#RequestBody MeetingsSetting meetingsSetting){
meetingSettingService.saveMeeting(meetingsSetting);
}

The problem with your implementation is that you are providing dates field as a String, but in the backend model it's represented as Object of type Date.
You have to make sure your backend model represent your json, by for example introduce DTOs ( Data Transfert Object ) that matches your json model and then map to your entities.
However, if you are just developing this application for tests or as a proof of concept. Here a working solution, just add this method in your Date.java class :
#JsonCreator
public static Date fromDateAsString(String dateString){
Date date = new Date();
date.setDate(dateString);
return date;
}
The idea here is to help Jackson (which is the library responsible for deserializing json to Java Object) to create a Date Instance from the String provided in json.

Related

#UpdateTimestamp not working on updating the user defined member variable

I am trying to update an entity by modifying the user defined member variable using the repository.save() method. The object is correctly updated and persisted but the timestamp of the member variable in the entity with the annotation #UpdateTimestamp is not getting updated.
My Entity classes are CASH & RATE:
#Entity
#Table(name = "CASH")
public class Cash{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "cash_id")
private List<Rate> rates = new ArrayList<>();
#ManyToOne(fetch = FetchType.EAGER)
private User createdBy;
#CreationTimestamp
#Column(name = "CREATED", nullable = false)
private ZonedDateTime created;
#Column(name = "LAST_MODIFIED", nullable = false)
#UpdateTimestamp
private ZonedDateTime lastModified;
#Entity
#Table(name = "RATE")
public class Rate {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#Column(name = "PRODUCT")
private String product;
#Column(name = "PRICE")
private String product;
Repositories for both the CASH and RATE class :
#Repository
public interface CashRepository extends JpaRepository<Cash, Long> {
Optional<Cash> findTopOrderByCreatedDesc();
}
#Repository
public interface RateRepository extends JpaRepository<Rate, Long> {
}
The update() method written in the service of Cash looks like :-
public void updateRate(Long productId, RateDto rateDto, Authentication authentication) {
User user = userService.getCurrentUser(authentication);
Optional<Cash> cashRate = cashRepository.findTopOrderByCreatedDesc();
if (Cash.isPresent()) {
Cash latestCash = cashRate.get();
List<Rate> rateList = latestCash.getRates();
for(Rate rate: rateList){
if(Objects.equals(rate.getId(), productId))){
rate.setProduct("Update Product");
}
}
rate newRates = mapToRates(rateDto, user); // the rateDto will have all the fields different(updated fields)
rateList.add(newRates);
latestCash.setRates(rateList);
Cash updatedCashAlternative = cashAlternativeProductsRepository.save(latestCash);
} else{
throw new CashException();
}
}
The latestCash object is perfectly getting saved with the updated list of rates but the lastModified field in the object is not getting updated.
Is #UpdateTimestamp only works if any of the non composite member variable is changed/updated. ??
Also i tried extending the CrudRepository instead of JpaRepository and tried updating it using the repository.update() method but it didn't work.
Changing the contents of an associated object (Rate) or the join table (cash_rates?) does not make the referring object Cash "dirty". I think if you switch to an #ElementCollection and make the Rate an #Embeddable, any change to these objects will make the object Cash be considered as "dirty" and hence the lastModified attribute will be updated.
If that doesn't work, you'll have to update the timestamp yourself in the code.

how to save entities in a relation to database in spring boot

I have a spring boot application with two entities in a relationship. MeetingSetting and MeetingTime meetingSetting can have unlimited meetingTimes. So far the databases are generating without problem, but When I try to save my Entity they are saved but different from each other, they are saved independently. Meaning MeetingName which is a foreign key inside MeetingTime is not saved but seen as null (I debugged and tried finding out why but could not find anything) THe other values are saved-
could someone point me out what my error is?
this is the json I am sending:
{
"meetingName":"TEst",
"meetingPw":"",
"meetingTime":[
{
"date":"2021-05-31",
"startTime":"15:30",
"endTime":"16:30"
},
{
"date":"2021-06-21",
"startTime":"15:30",
"endTime":"17:30"
},
{
"date":"2021-06-21",
"startTime":"11:01",
"endTime":"11:01"
}
]
}
MeetingSettings:
#Entity
#Table(name = "meeting_settings")
#Data
public class MeetingsSetting {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "meeting_name", unique = true)
private String meetingName;
#Column(name = "meeting_url")
private String meetingUrl;
#Column(name = "meeting_pw")
private String meetingPw;
#OneToMany(mappedBy = "meeting_Name", cascade = CascadeType.ALL)
private Set<MeetingTime> meetingTime = new HashSet<>();
}
MeetingTime:
#Entity
#Table(name = "meeting_times")
#Data
public class MeetingTime {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "meeting_date")
private String date;
#Column(name = "start_time")
private String startTime;
#Column(name = "end_time")
private String endTime;
#ManyToOne
#JoinColumn(name = "meeting_name" ,insertable = false, updatable = false , referencedColumnName = "meeting_name")
private MeetingsSetting meeting_Name;
}
this is how I try to save the entity:
#RestController
#RequestMapping("/api/meetingSetting")
public class MeetingSettingController {
#Autowired
MeetingSettingService meetingSettingService;
#PostMapping("/")
public void saveMeeting(#RequestBody MeetingsSetting meetingsSetting){
meetingSettingService.saveMeeting(meetingsSetting);
}
}
My service calls the save method of an jpaRepository.
In a bi-directional One to Many, you have to synchronize both sides of the association.
You can simply iterate over all MeetingTime objects and set the corresponding MeetingSetting to it.
Your MeetingSettingService's saveMeeting method could do this:
public void saveMeeting(MeetingsSetting meetingsSetting) {
// ...
// here you're synchronizing both sides of the association
meetingsSetting.getMeetingTime()
.forEach(mt -> mt.setMeetingSetting(meetingSetting));
// ...
repository.save(meetingSetting);
}
Solution to my question, I am not sure if this is a good or correct way of solving this maybe someone can advice me a better solution:
#Entity
#Table(name = "meeting_times")
#Data
public class MeetingTime implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Column(name = "meeting_date")
private String date;
#Column(name = "start_time")
private String startTime;
#Column(name = "meeting_name")
private String meeting_name;
THIS IS THE PART WHICH IS CALLED FROM THE METHOD INSIDE MEETINGSCONTROLLER
#Column(name = "end_time")
private String endTime;
#ManyToOne
#JoinColumn(name = "meeting_name" ,insertable = false, updatable = false, referencedColumnName = "meeting_name")
private MeetingsSetting meetingName;
}
MeetingsTime Entity:
#RestController
#RequestMapping("/api/meetingSetting")
public class MeetingSettingController {
#Autowired
MeetingSettingService meetingSettingService;
#PostMapping("/")
public void saveMeeting(#RequestBody MeetingsSetting meetingsSetting){
meetingsSetting.getMeetingTime()
.forEach(mt -> mt.setMeeting_name(meetingsSetting.getMeetingName()));
// ...
meetingSettingService.saveMeeting(meetingsSetting);
}
}

Hibernate not saving some properties

I'm working on a project using Spring Data JPA and Hibernate and one of my entities has a pretty weird behaviour: it's not saving some of the properties. The code below is my model class. It has some more properties and but all of them are String with the columnDefinition="TEXT". Debugging the object I'm sending to the repository all properties have values in it, get and set methods are working fine, etc.. but it only saves the title.
Now comes the weird part, if I do this Column(name = "isbn_v", columnDefinition="TEXT") it saves the data normally. The same for all columns that are not saving. I don't understand why. Model class:
#Entity
#Table(name = "notice")
public class Notice implements Serializable {
private static final long serialVersionUID = 4521230269805147556L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "seq_no", nullable = false, updatable = false)
private Integer id;
#Column(name = "title", columnDefinition="TEXT")
private String title;
#Column(name = "isbn", columnDefinition="TEXT")
private String isbn;
#Column(name = "issn", columnDefinition="TEXT")
private String issn;
#Column(name = "author", columnDefinition="TEXT")
private String author;
#Column(name = "publisher", columnDefinition="TEXT")
private String publisher;
// get / set
}
Repository class:
public interface NoticeRepository extends JpaRepository<Notice, Integer>, JpaSpecificationExecutor<Notice> {
#Override
#Modifying
#Transactional
Notice save(Notice notice);
}
Any clue of what I'm doing wrong?

Hibernate - Entity audit

I have one entity and I want to track all changes therefore I created new Entity for audit.
Below is my primary entity:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "primary")
public class PrimaryEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "primary_id")
private Long id;
private String name;
#LazyCollection(LazyCollectionOption.FALSE)
#ElementCollection
#CollectionTable(
name = "primary_attachments",
joinColumns = #JoinColumn(name = "primary_id")
)
private List<String> attachments;
#CreatedDate
#Temporal(TemporalType.DATE)
private Date createDate;
#LastModifiedDate
#Temporal(TemporalType.DATE)
private Date lastModifiedDate;
}
And below is my entity for audit:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#EntityListeners(AuditingEntityListener.class)
#Table(name = "primary_audit")
public class PrimaryEntityAudit {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "audit_id")
private Long id;
#NotNull
#Column(name = "primary_entity_id")
private Long primaryId;
private String name;
#LazyCollection(LazyCollectionOption.FALSE)
#ElementCollection
#CollectionTable(
name = "primary_attachments_audit",
joinColumns = #JoinColumn(name = "primary_entity_id")
)
private List<String> attachments = new ArrayList<>();
#CreatedDate
#Temporal(TemporalType.DATE)
private Date createDate;
public PrimaryEntityAudit(PrimaryEntity primaryEntity) {
this.primaryId = primaryEntity.getId();
this.attachments.addAll(primaryEntity.getAttachments());
this.createDate = new Date();
}
}
And before update primary entity I create new PrimaryEntityAudit and save this object and then update the primary entity.
And operation is successful and object PrimaryEntityAudit is saved, but attachments from PrimaryEntityAudit aren't saved.
I tried also in constructor of ProjectEntityAudit do setAttachments, but then I got an Exception: HibernateExcpetion: Found shared references to collection.
How should I map the collections of audit for saving old state of PrimaryEntity attachments?
You should look at the following hibernate module Envers
It provides features for versioning and auditing
It is preferable to not reinvent the wheel except if you have technicals constraints which prevent you to use some frameworks or others.

Spring Data Rest : Foreign key is update with null after post call in one to many relationship

I am using spring-data-rest.
update and daily_update are 2 table which is having one to many relationship. Running this application with spring boot.
When i am adding data using post request, entries being added into both table without any error but in child table (daily_update) column "update_id" (foreign key to update table) is coming null.
I am using Lombok for setter and getter.
Can you please help me with this?
UpdateEntity class :
#Data
#Entity
#Table(name = "update")
public class UpdateEntity {
#Id
#Column(name = "id")
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private String id;
#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
#Column(name = "start_time")
private Date startTime;
#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
#Column(name = "end_time")
private Date endTime;
#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
#Column(name = "date_created")
private Date dateCreated;
#JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
#Column(name = "date_modified")
private Date dateModified;
#OneToMany(mappedBy = "updateEntity", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<DailyUpdateEntity> dailyUpdateEntities = new HashSet<>();
}
DailyUpdateEntity class :
#Data
#Entity
#Table(name = "daily_update")
public class DailyUpdateEntity {
#Id
#Column(name = "id")
#GeneratedValue(generator = "UUID")
#GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
private String id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "update_id")
private UpdateEntity updateEntity;
#Column(name = "dimension_id")
private String dimensionId;
#Column(name = "hour")
private Integer hour;
#Column(name = "updated_type_id")
private String updatedTypeId;
}
UpdateRepository :
#RepositoryRestResource(collectionResourceRel = "update", path = "update")
public interface UpdateRepository extends CrudRepository<UpdateEntity, String> {
}
POST request hitting from postman http://localhost:8080/update
{
"startTime" : "2016-08-18 10:34:26",
"endTime" : "2016-08-19 10:34:26",
"dateCreated" : "2016-06-18 10:34:26",
"dateModified" : "2016-06-18 10:34:26",
"dailyUpdateEntities" :
[
{
"dimensionId" : "6ea91f60-2b1d-11e7-93ae-92361f002671",
"hour" : "01",
"updatedTypeId" : "6ea9273a-2b1d-11e7-93ae-92361f002671"
},
{
"dimensionId" : "6ea92636-2b1d-11e7-93ae-92361f002671",
"hour" : "02",
"updatedTypeId" : "6ea92816-2b1d-11e7-93ae-92361f002671"
}
]
}
and running this application from spring boot
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I have had the exact problem, the issue stems from the declaration of table relationship. From UpdateEntity to DailyUpdateEntity, you are declaring the relationship in the following form;
#OneToMany(mappedBy = "updateEntity", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<DailyUpdateEntity> dailyUpdateEntities = new HashSet<>();
which causes the issue, by separating the insertion of dependent entity, and addition of foreign key to it. So first create operation will always lack a foreign key. With the following declaration, this issue is resolved, and creation will contain a foreign key;
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn(name = "update_id", nullable = false, updatable = false)
private Set<DailyUpdateEntity> dailyUpdateEntities = new HashSet<>();
It seems the problem is that you try to persist the relation on the wrong side.
You are posting an UpdateEntity (against the UpdateRepository) where you mapped the Set<DailyUpdateEntity> collection as mappedBy = "updateEntity" which means that this is just the readonly side of the bidirectional association and there will be no connection between the entities.
You would face a not-null constraint error if you would enhance the DailyUpdateEntity mapping like:
#ManyToOne(optional = false, fetch = FetchType.EAGER)
#JoinColumn(name = "update_id")
private UpdateEntity updateEntity;
Maybe you should adjust your mapping or think about another storing strategy / order.

Categories

Resources