how handle null in nested JSON object? - json

I have Dto with field private Category category2;
#Entity
#Getter
#Setter
#ToString
#RequiredArgsConstructor
#Builder
#AllArgsConstructor
public class Procurement implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id", nullable = false, unique = true)
private Long id;
#Column(name = "name",columnDefinition = "varchar(1000)")
private String name;
#Column(name = "category",columnDefinition = "varchar(1000)")
private String category;
#Column(name = "type",columnDefinition = "varchar(1000)")
private String type;
#Enumerated(EnumType.ORDINAL)
#Column(name = "status")
private ProcStatus status;
#OneToOne
private Category category2;
CategoryDto:
#Data
#Builder
#NoArgsConstructor
#AllArgsConstructor
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class,
property = "id", scope = Long.class)
public class CategoryDto {
private Long id;
private String name;
}
I have controller and method of create new Procurement:
#PutMapping
ResponseEntity<Procurement> createNewProcurement(#RequestBody ProcurementDto procurementDto) {
return ResponseEntity.ok(procurementService.createNewProcurement(procurementDto));
}
I provide json to controller via fetch in js
function createnewprocuremet() {
fetch(`${url}`, {
method: 'PUT', headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
name: name.value,
category2: {id:$('#categoryprocselector').val()}
})
}).then((res) => {
return res.json();
})
.then((data) => {
$table.bootstrapTable('refresh')
tab.show()
name.value = ''
})
.catch(error => console.log(error))
}
category2 not required and user cannot choose it in selector
If not select category id:$('#categoryprocselector').val() = null and send null to controller and i have error org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error
If category selected it`s work ok.
How i can handle this exception?

Related

How do you fetch an object from MySQL that contains a property with a value of another object in Spring Boot?

I have a few models that has relationship to each other within this object I am tryin to save and fetch from the database, but I can't seem to get it to work properly. The object contains a property that also contains another object. My approach was to save to all related models and for those models, I would also save this current model to them as well so it shows relation on the database table.
Below the models for the app.
#Entity
#Table(name = "daily_entry")
#Setter
#Getter
#AllArgsConstructor
#NoArgsConstructor
public class DailyEntry {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "daily_entry_id", unique = true)
private Long id;
#Column(unique = true)
private LocalDate date;
private int weight;
#ManyToOne
#JoinColumn(name="user_id")
private User user;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "daily_macros_id", unique = true)
private DailyMacros dailyMacros;
#OneToMany(mappedBy = "dailyEntry", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Exercise> exercise = new ArrayList<Exercise>();
}
#Entity
#Table(name = "daily_macros")
#Setter
#Getter
#AllArgsConstructor
#NoArgsConstructor
public class DailyMacros {
#Id
#GeneratedValue
#Column(name = "daily_macros_id")
private Long id;
private int calories;
private int protein;
private int fat;
private int carbs;
#OneToOne(mappedBy = "dailyMacros", cascade = CascadeType.ALL, orphanRemoval = true)
private DailyEntry dailyEntry;
}
#Setter
#Getter
#Entity
#AllArgsConstructor
#NoArgsConstructor
#Table(name= "exercises")
public class Exercise {
#Id
#GeneratedValue
#Column(name = "exercise_id")
private Long id;
private String name;
private int sets;
private int reps;
private int weight;
#ManyToOne
#JoinColumn(name = "daily_entry_id", unique = true)
private DailyEntry dailyEntry;
}
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
#Entity
#Table(name = "users")
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "user_id")
private Long id;
#Column(nullable = false)
private String firstName;
#Column(nullable = false)
private String lastName;
private String role = "user";
private String token;
#Column(unique = true, nullable = false)
protected String username;
#Column(unique = true ,nullable = false)
private String emailAddress;
#Column(nullable = false)
private String password;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "macros_goal_id")
private MacrosGoal macrosGoal;
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<DailyEntry> dailyEntry = new ArrayList<>();
}
Im tryin to save the model of DailyEntry to the database.
User is the owner of DailyEntry
DailyEntry owns MacrosGoal (#OneToOne) and Exercise (#OneToMany). How exactly can I save and fetch this model into the database?
This is what I've tried -
For my PutMapping, I get this error : Cannot call sendError() after the response has been committed
My GetMapping just returns a empty response although http status code 200
DailyEntryController file
#RequestMapping(path = "/api/v1/")
#RequiredArgsConstructor
public class DailyEntryController {
#Autowired
DailyEntryService dailyEntryService;
#GetMapping("/getDailyEntry")
public ResponseEntity<DailyEntry> getDailyEntry(#RequestParam("username") String username, #RequestParam String date) throws ResponseStatusException {
DailyEntry dailyEntry = dailyEntryService.getDailyEntry(username, date);
return new ResponseEntity<>(dailyEntry, HttpStatus.OK);
}
#PutMapping("/addDailyEntry")
public ResponseEntity<DailyEntry> addDailyEntry(#RequestBody DailyEntry dailyEntry, #RequestParam("username") String username) throws ResponseStatusException {
DailyEntry dailyEntryInfo = dailyEntryService.createDailyEntry(dailyEntry, username);
return new ResponseEntity<>( dailyEntryInfo, HttpStatus.OK);
}
}
#Slf4j
#Service
public class DailyEntryServiceImpl implements DailyEntryService {
#Autowired
DailyEntryRepository dailyEntryRepository;
#Autowired
ExerciseRepository exerciseRepository;
#Autowired
UserRepository userRepository;
#Autowired
DailyMacrosRepository dailyMacrosRepository;
#Override
public DailyEntry addExercise(Exercise exercise) {
return null;
}
#Override
public DailyEntry getDailyEntry(String username, String date) {
DailyEntry entry = null;
Optional<User> userFromDatabase = userRepository.findByUsername(username);
User user = userFromDatabase.get();
LocalDate localDate = LocalDate.parse(date);
List<DailyEntry> dailyEntryList = user.getDailyEntry();
for(DailyEntry e : dailyEntryList) {
if(e.getDate() == localDate){
entry = e;
}
}
return entry;
}
#Override
public DailyEntry createDailyEntry(DailyEntry dailyEntry, String username) {
DailyEntry entry = new DailyEntry();
// Find user
Optional<User> userFromDatabase = userRepository.findByUsername(username);
User user = userFromDatabase.get();
entry.setUser(user);
entry.setDate(LocalDate.parse(dailyEntry.getDate().toString()));
entry.setDailyMacros(dailyEntry.getDailyMacros());
entry.setWeight(dailyEntry.getWeight());
entry.setExercise(dailyEntry.getExercise());
DailyMacros dailyMacros = dailyEntry.getDailyMacros();
dailyMacros.setDailyEntry(entry);
dailyMacrosRepository.save(dailyMacros);
List<Exercise> exercise = dailyEntry.getExercise();
for (Exercise e : exercise) {
e.setDailyEntry(entry);
exerciseRepository.save(e);
}
List<DailyEntry> dailyEntryList = user.getDailyEntry();
dailyEntryList.add(entry);
user.setDailyEntry(dailyEntryList);
userRepository.save(user);
return entry;
}
I also tried with Query inside repository, but I may have done it wrong.
You should probably add #Transactional to createDailyEntry.
I don't really understand what's happening in createDailyEntry but I think it should look something like this:
#Override
#Transactional
public DailyEntry createDailyEntry(DailyEntry dailyEntry, String username) {
User user = userRepository
.findByUsername(username)
.get();
dailyEntryRepository.save(dailyEntry);
user.getDailyEntry().add(dailyEntry);
dailyEntry.setUser(User);
DailyMacros dailyMacros = dailyEntry.getDailyMacros();
dailyMacros.setDailyEntry(dailyEntry);
dailyMacrosRepository.save(dailyMacros);
dailyEntry.getExcercise()
.forEach( e -> {
e.setDailyEntry(dailyEntry);
exerciseRepository.save(e);
});
// I don't think you need this because user is already managed
// and userFromDatabase.get() will throw an exception
// if the user does not exist.
// userRepository.save(user);
return dailyEntry;
}
I think getDailyEntry doesn't work because you are using e.getDate() == localDate and it's always going to be false.
One solution is to add a method to the DailyEntryRepository that accept username and date:
interface DailyEntryRepository extends JpaRepository<DailyEntry, Long> {
#Query("select de from DailyEntry de where de.user.username = :username and de.date = :localDate")
DailyEntry findByUsernameAndDate(#Param("username") String username, #Param("localDate") LocalDate date);
}
...
#Override
public DailyEntry getDailyEntry(String username, String date) {
LocalDate localDate = LocalDate.parse(date);
return dailyEntryRepository.findByUsernameAndDate(username, localdate);
}
This solution will run a query and load only the entry you need.
But, if you need to validate the username, this should work:
#Override
public DailyEntry getDailyEntry(String username, String date) {
User user = userRepository
.findByUsername(username)
.get();
LocalDate localDate = LocalDate.parse(date);
for(DailyEntry e : user.getDailyEntry()) {
if (localDate.equals(e.getDate)){
// No need to continue the loop, you've found the entry
return entry;
}
}
// daily entry not found
return null;
}
Another thing that's missing is that in your model you are not handling the bidirectional association during convertion to JSON.
The solution is to use I end up using #JsonManagedReference and #JsonBackReference where you have the association.

UUID with additional string as prefix in primary key Spring Boot

I had an idea to implement UUID as my primary key in the SQL database with spring boot technology. Currently, I have a dilemma on how to implement a custom fixed string with UUID as my primary key. Here is an example:
This is a classic UUID:
08d88683-20fc-4884-a523-8f39a06d037f
But I wanted to my UUID looks something like this:
USER-08d88683-20fc-4884-a523-8f39a06d037f
How could I achieve that with Spring boot and Hibernate?
Here is my user model:
#Data
#Entity
#Table( name = "users",
uniqueConstraints = {
#UniqueConstraint(columnNames = "username"),
#UniqueConstraint(columnNames = "email")
})
#NoArgsConstructor
public class User {
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
#Column(name = "id", updatable = false, nullable = false, columnDefinition = "VARCHAR(36)")
#Type(type = "uuid-char")
private UUID id;
#NotBlank
#Size(max = 20)
private String username;
#NotBlank
#Size(max = 50)
#Email
private String email;
#NotBlank
#Size(max = 120)
private String password;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable( name = "user_roles",
joinColumns = #JoinColumn(name = "user_id"),
inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<>();
public User(String username, String email, String password) {
this.username = username;
this.email = email;
this.password = password;
}
}
The best way to implement custom id generator in Hibernate.
Using the following class you can create Custom Generator:
import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.IdentifierGenerator;
import java.io.Serializable;
import java.util.UUID;
public class CustomUserIdGenerator implements IdentifierGenerator {
#Override
public Serializable generate(SharedSessionContractImplementor sharedSessionContractImplementor, Object o) throws HibernateException {
return "USER-"+ UUID.randomUUID();
}
}
And then you can use the above created class as the generator strategy in entity class for the GenricGenerator
#Id
#GenericGenerator(name = "string_based_custom_sequence", strategy = "com.package.CustomUserIdGenerator")
#GeneratedValue(generator = "string_based_custom_sequence")
private UUID id;

Spring boot JPA - Why is no data inserted in the intermediate table using using embeddable?

I am doing a API Rest with Spring Boot and MySQL and I have the next relation:
To do this, the first thing I do is create a type #Embeddable.
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.ManyToOne;
import java.io.Serializable;
import java.util.Objects;
#Embeddable
public class UserInterestKey implements Serializable {
#Column(name = "interest_id",nullable = false)
private long interestId;
#Column(name = "user_id",nullable = false)
private long userId;
public UserInterestKey() {
}
public UserInterestKey(long interestId, long userId) {
this.interestId = interestId;
this.userId = userId;
}
//get and set for interestId and userId
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
UserInterestKey that = (UserInterestKey) o;
return Objects.equals(interestId, that.interestId) &&
Objects.equals(userId, that.userId);
}
#Override
public int hashCode() {
return Objects.hash(interestId, userId);
}
}
Then, I create the entity that uses the EmbeddedId type:
import javax.persistence.*;
import javax.validation.constraints.NotNull;
#Entity
#Table(name = "rel_user_interest")
public class RelUserInterest {
#EmbeddedId
private UserInterestKey id;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("userId")
#JoinColumn(name = "user_id", nullable = false)
User user;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("interestId")
#JoinColumn(name = "interest_id", nullable = false)
Interest interest;
int priority;
public RelUserInterest() {
}
//get and set for id, user, interest and priority
This is the User entity:
import com.fasterxml.jackson.annotation.JsonIgnore;
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.HashSet;
import java.util.Set;
#Entity
public class User {
#Id #GeneratedValue private long id;
#NotNull
#Column (unique = true) private String email;
#NotNull private int age;
#NotNull private String genre;
#NotNull private String userName;
#NotNull private String password;
#OneToMany(mappedBy = "user")
#NotNull
Set<RelUserInterest> priority = new HashSet<>();
public User (){
}
public User(#NotNull String email, #NotNull int age, #NotNull String genre, #NotNull String userName, #NotNull String password) {
this.email = email;
this.age = age;
this.genre = genre;
this.userName = userName;
this.password = password;
}
// set and get for id, email, age, genre, username, password and priority.
}
This is the interest entity:
import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.util.HashSet;
import java.util.Set;
#Entity
public class Interest {
#Id
#GeneratedValue
private long id;
#NotNull private String nameInterest;
#OneToMany(mappedBy = "interest")
#NotNull
Set<RelUserInterest> priority = new HashSet<>();
public Interest() {
}
public Interest(String nameInterest) {
this.nameInterest = nameInterest;
}
// get and set for id, nameInterest and priority
}
This is the part of the controller
#PostMapping("/signup")
public ResponseEntity<?> addUser(#Validated #RequestBody NewUserDTO userDTO, BindingResult result) {
//more code
User user = new User(userDTO.getEmail(), userDTO.getAge(), userDTO.getGenre(), userDTO.getUserName(),userDTO.getPassword());
Set<InterestDTO> interestStr = userDTO.getInterest();
RelUserInterest rel;
for(InterestDTO interest: interestStr){
rel = new RelUserInterest();
rel.setPriority(interest.getPriority());
Interest i = interestService.getInterestByName(interest.getNameInterest());
rel.setInterest(i);
rel.setUser(user);
user.getPriority().add(rel);
i.getPriority().add(rel);
}
usersService.addUser(user);
return new ResponseEntity(new Mensaje("Usuario añadido"), HttpStatus.CREATED);
}
PROBLEM: The database is well created as you can see in the first image. The problem is that the intermediate table (rel_user_interest) is empty, nothing is inserted. Does anyone find any problem in the code and can help me please?
UPDATE 17/11/2020
This is a JSON example that I want to send
{
"email": "sd#email.com",
"userName": "sd",
"password": "1234",
"passwordConfirm": "1234",
"age": 21,
"genre": "Women",
"roles": [
"user"
],
"interest": [
{
"nameInterest": "Museum",
"priority": 5
}
]
}
I followed this tutorial Many-to-Many Using a Composite Key
Spring Boot version: 2.3.5.RELEASE
Java version: 1.8

Many to many relationships doesn't shown in JSON. Spring + JPA

The relationships works fine, the tables are created and the data is inserted correctly. But when page loads in browser, many to many relationships doesn't shown.
But professions are not null:
Here is my entities:
#Entity
#Table(name = "users")
public class User {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "login", nullable = true, length = 50)
private String login;
#Column(name = "pass", nullable = true, length = 50)
private String password;
#Column(name = "name", nullable = false, length = 50)
private String name;
....
public User() {
}
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name="worker_profession", joinColumns =#JoinColumn(name="id_user", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name="id_profession", referencedColumnName="id")
)
private Set<Profession> profession;
public Set<Profession> getProfession() {
return profession;
}
public void setProfession(Set<Profession> profession) {
this.profession = profession;
}
...
}
And:
#Entity
#Table(name = "profession")
public class Profession {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name = "profession_name", nullable = false, length = 50)
private String professionName;
public Profession() {
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getProfessionName() {
return professionName;
}
public void setProfessionName(String professionName) {
this.professionName = professionName;
}
#ManyToMany(fetch = FetchType.EAGER, mappedBy = "profession")
private Set<User> users;
public Set<User> getUsers() {
return users;
}
public void setUsers(Set<User> users) {
this.users = users;
}
}
in UserController.class I have:
#RestController
#RequestMapping("/user")
public class UserController {
#Autowired
private UserRepository userRepository;
#RequestMapping(value = "", method = RequestMethod.GET)
#ResponseBody
public List<User> getAllUsers() {
return userRepository.findAll();
}
...
}
So, where is my mistake? Thanks.

Spring MVC-Angularjs : How do I bind object property

I dont understand why I am getting this error when i try sumbit my form ,so I have an Invalid property ‘siteesTypeSite[idTypeSite]’ of bean class in my web application ,I have tried to find the solution, but still no luck.
org.springframework.beans.InvalidPropertyException: Invalid property ‘siteesTypeSite[idTypeSite]’ of bean class [model.Sites]:
Property referenced in indexed property path ‘siteesTypeSite[idTypeSite]’ is neither an array nor a List nor a Map; returned value was [2]
Is there something wrong with my mapping?
Sites.java mapping
public class Sites implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
#JsonBackReference("site-typeSite")
#LazyCollection(LazyCollectionOption.FALSE)
#ManyToOne
#JoinColumn(name = “idTypeSite”)
private TypeSites siteesTypeSite;
Getter/setters
}
TypeSites.java mapping :
public class TypeSites implements java.io.Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="idTypeSite")
private int idTypeSite;
private String typeSite;
#LazyCollection(LazyCollectionOption.FALSE)
#JsonManagedReference("site-typeSite")
#OneToMany(mappedBy = “siteesTypeSite”,fetch = FetchType.LAZY)
// private Set<Sites> sitees= new HashSet<Sites>(0);
private List<Sites> sitees = new AutoPopulatingList<Sites>(Sites.class);
Getter/setters
}
controller class :
#RequestMapping(method = RequestMethod.POST, produces = "application/json")
public ResponseEntity<?> create(#ModelAttribute("site") #Valid Sites site,
#ModelAttribute("typeSites") TypeSites typeSites,
#RequestParam(required = false) String searchFor,
#RequestParam(required = false,
defaultValue = DEFAULT_PAGE_DISPLAYED_TO_USER) int page,
Locale locale) {
// TypeSites test=new TypeSites("test");
// site.setSiteesTypeSite(typeSites);
// Set type = new HashSet ();
// type.add(typeSites);
siteService.save(site);
}
Angularjs code :
$scope.createObject = function (newObjectForm) {
if (!newObjectForm.$valid) {
$scope.displayValidationError = true;
return;
}
$scope.lastAction = ‘create';
var url = $scope.url;
var config = {headers: {‘Content-Type': ‘application/x-www-form-urlencoded; charset=UTF-8′}};
$scope.addSearchParametersIfNeeded(config, false);
$scope.startDialogAjaxRequest();
$scope.sites.siteesTypeSite =JSON.parse($scope.sites.siteesTypeSite);
$http.post(url, $.param($scope.sites), config)
.success(function (data) {
$scope.finishAjaxCallOnSuccess(data, “#addObjectsModal”, false);
})
.error(function(data, status, headers, config) {
$scope.handleErrorInDialogs(status);
});
};
JSP :
<select required
ng-model=”sites.siteesTypeSite”
name=”siteesTypeSite”
>
<option ng-repeat=”typesites in page.source” value=”{{typesites}}” >{{typesites.typeSite}}</option>
</select>
and i have this before submit in browser :
Object {codeOracle: "test", codeGSM: "test", area: "test", siteesTypeSite: Object}
area: "test"
codeGSM: "test"
codeOracle: "test"
siteesTypeSite: Object
idTypeSite: 2
sitees: Array[0]
typeSite: "Public"
__proto__: Object
__proto__: Object
Edite 1 :
i was trying to use MAP like this , but the same error
#LazyCollection(LazyCollectionOption.FALSE)
#JsonManagedReference("site-typeSite")
#OneToMany(mappedBy="siteesTypeSite")
#MapKey//(name="typeSite")
public Map<Integer, Sites> getSitees() {
return sitees;
}
Edite 2 :
i have edite my siteType class like bellow , but i get the same error :
#Entity
#Table(name = "TypeSites")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
#JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property="idTypeSite")
public class TypeSites implements java.io.Serializable {
private static final long serialVersionUID = 1L;
private int idTypeSite;
private String typeSite;
//private List<Sites> sitees = new AutoPopulatingList<Sites>(Sites.class);;
private Map<Integer, Sites> sitees;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="idTypeSite")
public int getIdTypeSite() {
return idTypeSite;
}
public void setIdTypeSite(int idTypeSite) {
this.idTypeSite = idTypeSite;
}
#LazyCollection(LazyCollectionOption.FALSE)
#JsonManagedReference("site-typeSite")
#OneToMany(mappedBy = "siteesTypeSite", cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#MapKey(name="id")
public Map<Integer, Sites> getSitees() {
return sitees;
}
public void setSitees(Map<Integer, Sites> sitees) {
this.sitees = sitees;
}
#SuppressWarnings("unchecked")
public TypeSites() {
this.sitees = MapUtils.lazyMap(new HashMap<Integer,Object>(), new Factory() {
public Object create() {
return LazyList.decorate(new ArrayList<Sites>(),
FactoryUtils.instantiateFactory(Sites.class));
}
});
}