Springboot Mapping and DTO - java

I'm new to Spring and I'm encountering a lot of doubts when making an insertion of the example below.
I currently have three tables whose models are those below:
#Entity
#Table(name = "namespace")
public class Namespace {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull
#Size(max = 100)
#Column(unique = true)
private String namespacename;
}
#Entity
#Table(name = "services")
public class Services {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#NotNull
#Size(max = 100)
#Column(unique = true)
private String servicename;
}
#Entity
#Table(name = "historiquedeploiement")
public class HistoriqueDeploiement {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "idnamespace", nullable = false)
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
#JsonIdentityReference(alwaysAsId=true)
#JsonProperty("idnamespace")
private Namespace namespace;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(name = "idservices", nullable = false)
#JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
#JsonIdentityReference(alwaysAsId=true)
#JsonProperty("idservices")
private Services services;
#NotNull
#Size(max = 100)
#Column(unique = true)
private String tagvalue;
}
And this is my DTO :
public class HistoriqueDeploiementReadingDTO {
#NotNull
private Integer id;
#NotNull
private String namespacename;
#NotNull
private String servicename;
#NotNull
private String tagvalue;
}
So the problem is :
I receive an object of type HistoriqueDeploiementReadingDTO and i have to insert it in historiquedeploiement table.
The problem is that i receive "namespacename", "servicename" and i need to save the id of each one that i can find in the table namespace and service.
When i have the id of each one, i can save it in historiquedeploiement table.
I hope you understand that little problem and hope you can purpose me something :)
Thanks !

You should first validate what you receive(against db records of each table). More or less the following will give you a highlight, so you should do for the others too.
Don't forget that all should be on the same transaction.
== updated==
#Transactional(rollbackFor=Exception.class)
public boolean saveHistoriqueDeploiement(HistoriqueDeploiementReadingDTO dto) {
Services service = getServices(dto.getServicename());
// do the same for the others
HistoriqueDeploiement deploiment = new HistoriqueDeploiement();
deploiment.setService(service);
//same for the others
deploiementRepository.save(deploiment);
}
public Services getServices(String serviceName) {
Services service = serviceRepository.findByServicename(serviceName); //if it exists, use the existing
if(service == null) {
service = new Services();
service.setServicename(dto.getServicename());
service = serviceRepository.save(service);
}
return service;
}

You have 2 ways:
First of all
if relation is many to one your field is List of services and List of namespaces instead services and namespaces.
if you mean OneToOne
HistoriqueDeploiementReadingDTO historiqueDeploiementReadingDTO;
NameSpace nameSpace = new Namespace();
namespace.setNameSpaceName(historiqueDeploiementReadingDTO.getNameSpaceName());
Services service = new Service();
service.setServicename(historiqueDeploiementReadingDTO.getServiceName())
HistoriqueDeploiement historiqueDeploiement = new HistoriqueDeploiement;
historiqueDeploiement.setTagValue(historiqueDeploiementReadingDTO.getTagValue())
historiqueDeploiement.setService(service)
historiqueDeploiement.setNameSpaceName(nameSpace)
IHistoriqueDeploiementRepository.save(historiqueDeploiement);
2 -

Related

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

Foreign key is null after saving objects

I have a relationship between Citizen:
#Entity
#Table(name = "citizens")
public class Citizen {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Size(max = 10, min = 10, message = "CPR must be exactly 10 characters")
private String cpr;
#OneToMany(mappedBy = "citizen", cascade = CascadeType.ALL, orphanRemoval = true)
private List<WeeklyCare> weeklyCare;
}
and WeeklyCare:
#Entity
public class WeeklyCare {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "citizen_id")
private Citizen citizen;
}
I have a REST API that recieves a list of Citizen each with a list of WeeklyCare and saves them:
#Autowired
private CitizenRepository citizenRepository;
#CrossOrigin(origins = "http://localhost:4200")
#PostMapping(path = "/add") // Map ONLY GET Requests
#Secured({"ROLE_ADMIN", "ROLE_DATAMANAGER"})
public ResponseEntity addNewCitizens(
#RequestBody List<Citizen> citizens) {
citizenRepository.saveAll(citizens);
return new ResponseEntity(new ApiResponse(true, "Filen er blevet indlæst", "CITIZENS_SAVED"), HttpStatus.OK);
}
After this, when I look in the weekly_care table in the database, all rows have null on the citizen_id column. What am I missing?
This is a common scenario in Hibernate and results from not setting the inverse of the relationship:
Citizen c = new Citizen();
WeeklyCare w = new WeeklyCare();
c.getWeeklyCare().add(w);
//The missing link:
w.setCitizen(c);
citizenRepository.save(c);
I'm not sure how this is configured in your web-service request though...

Mapping a database view entity with a simple entity and pass to DTO using Spring Data

I'm just learning Spring Data. I want to map a database view Entity with a simple Entity and pass to DTO which will contain columns both entities. I understand that I can use a special database view but I need to map precisely entities of Spring Data.
I have a database view Entity "MentorStudents":
#Entity
#Table(name = "mentor_students")
#Immutable
public class MentorStudents implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "mentor_id", updatable = false, nullable = false)
private Long mentorId;
//This entity I need to map
private Mentor mentor;
#Column(name = "active_students")
private Integer activeStudents;
public MentorStudents() {
}
//getters, setters, equals, hashCode
}
A database view sql of an above entity is:
SELECT id AS mentor_id, active_students
FROM mentor
LEFT JOIN ( SELECT mentor_id, count(mentor_id) AS active_students
FROM contract
WHERE close_type IS NULL
GROUP BY mentor_id) active ON mentor.id = active.mentor_id
ORDER BY mentor.id;
And I have a simple Entity "Mentor":
#Entity
#Table(name = "mentor")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class Mentor implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
#NotNull
#Column(name = "first_name", nullable = false)
private String firstName;
#NotNull
#Column(name = "last_name", nullable = false)
private String lastName;
#Column(name = "patronymic")
private String patronymic;
#Column(name = "phone")
private String phone;
#NotNull
#Column(name = "email", nullable = false)
private String email;
#Column(name = "skype")
private String skype;
#Column(name = "country")
private String country;
#Column(name = "city")
private String city;
#Column(name = "max_students")
private Long maxStudents;
//getters, setters, equals, hashCode
I have to get a DTO which contains all Mentor fields and an "activeStudents" MentorStudents field without a "mentorId" field. How do it?
Use spring data projection:
public interface YourDto {
// all Mentor get fields
String getFirstName();
...
// activeStudents get field
Integer getActiveStudents();
}
public interface YourRepository extends JpaRepository<YourEntity, Integer> {
#Query(value = "select ...(all fields match YourDto) from Mentor m, MentorStudents s where m.id = s.mentorId and m.id = ?1")
Optional<YourDto> findMyDto(Integer mentorId);
}

Infinite Loop When Collection Mapping With Dozer

I'm developing a project which uses BackboneJS in front-end and Java - Spring Core in back-end. I have a problem about mapping entity(domain) objects to DTO objects. I am getting an error message like that :
org.apache.cxf.interceptor.Fault: Infinite recursion (StackOverflowError) (through reference chain: com.countdown.dto.CategoryDTO["countdownList"]->java.util.ArrayList[0]->com.countdown.dto.CountdownDTO["category"]->.......
User.java
#Entity
#Table(name = "Users")
public class User implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "USER_ID", nullable = false)
private int id;
#Column(name = "EMAIL", nullable = false, unique = true)
private String email;
#Column(name = "NAME_SURNAME", nullable = false)
private String nameSurname;
#Column(name = "PASSWORD", nullable = false)
private String password;
#Column(name = "USERNAME", nullable = false, unique = true)
private String username;
#Column(name = "REGISTER_DATE", nullable = false)
private Date registerDate;
#ManyToOne
#JoinColumn(name = "ROLE_ID")
private Role role;
#OneToMany(mappedBy = "createUser")
private List<Countdown> createCountdownList = new ArrayList<Countdown>();
#OneToMany(mappedBy = "updateUser")
private List<Countdown> updateCountdownList = new ArrayList<Countdown>();
#ManyToMany
#JoinTable(name = "FOLLOWINGS",
joinColumns = #JoinColumn(name = "USER_ID"),
inverseJoinColumns = #JoinColumn(name = "COUNTDOWN_ID"))
private List<Countdown> followings = new ArrayList<Countdown>();
//Getters and setters..
}
Role.java
#Entity
public class Role implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "ROLE_ID")
private int id;
#Column(name = "ROLE_NAME", nullable = false)
private String roleName;
#OneToMany(mappedBy = "role",fetch = FetchType.LAZY)
List<User> userList = new ArrayList<User>();
}
Countdown.java
#Entity
#Table(name = "COUNTDOWN")
public class Countdown implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "COUNTDOWN_ID")
private int id;
#Column(name = "COUNTDOWN_NAME", nullable = false)
private String countdownName;
#Column(name = "COUNTDOWN_DATE", nullable = false)
private Date countdownDate;
#Column(columnDefinition = "varchar(5000)")
private String countdownDescription;
#JoinColumn(name = "CATEGORY_ID", nullable = false)
#ManyToOne
private Category category;
#JoinColumn(name = "CREATE_USER", nullable = false)
#ManyToOne
private User createUser;
#Column(name = "CREATE_DATE", nullable = false)
private Date createDate;
#JoinColumn(name = "UPDATE_USER", nullable = false)
#ManyToOne
private User updateUser;
#Column(name = "UPDATE_DATE", nullable = false)
private Date updateDate;
#Column(name = "CREATE_USER_IP", nullable = false)
private int createIP;
#ManyToMany
private List<User> followers = new ArrayList<User>();
}
Category.java
#Entity
#Table(name="CATEGORY")
public class Category implements Serializable{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="CATEGORY_ID")
private int id;
#Column(name = "CATEGORY_NAME" , nullable = false)
private String categoryName;
#OneToMany(mappedBy = "category")
private List<Countdown> countdownList = new ArrayList<Countdown>();
}
Business Logic : CategoryServiceImpl.java
I'm getting error in forEach loop.
#Transactional(readOnly = true)
public List<CategoryDTO> getAllCategories() {
List<Category> categoryList;
List<CategoryDTO> categoryDTOList = new ArrayList<CategoryDTO>();
logger.debug("getAllCategories called");
try {
categoryList = categoryDAO.findAll();
for(Category category : categoryList){
categoryDTOList.add(mapper.map(category,CategoryDTO.class));
}
}catch (NoResultException e){
logger.error("getAllCategories method : No Category wasn't found");
logger.warn(e,e);
}catch (Exception e){
logger.error("getAllCategories method : Categories wasn't found");
logger.warn(e,e);
}
return categoryDTOList;
}
Also Do I have to use DTO object in Presentation Layer? Can I use entity objects in presentation layer instead of DTO objects?
How can I solve this problem? Sorry my bad english. Thank you!
Please Try :
#Transactional(readOnly = true)
public List<CategoryDTO> getAllCategories() {
List<Category> categoryList;
List<CategoryDTO> categoryDTOList = new ArrayList<CategoryDTO>();
logger.debug("getAllCategories called");
try {
categoryList = categoryDAO.findAll();
for(Category category : categoryList){
if(category.getCountdownList() != null && !category.getCountdownList().isEmpty()){
for(Countdown countdown : category.getCountdownList()){
countdown.setCategory(null);
}
}
categoryDTOList.add(mapper.map(category,CategoryDTO.class));
}
}catch (NoResultException e){
logger.error("getAllCategories method : Hata: No Category wasn't found");
logger.warn(e,e);
}catch (Exception e){
logger.error("getAllCategories method : Hata: Categories wasn't found");
logger.warn(e,e);
}
return categoryDTOList;
}
For those who are struggling with infinite recursion issue in Dozer.
I use mapId to define a leaf object and stops the recursion.
Let assume we have two entities Course and Teacher, which contains a Many-to-Many relationship, and we want to convert the following object graph to one represented by CourseDTO and TeacherDto. And we hope Dozer stops at the 3rd level.
Teacher 1 ---> m Course 1 ---> m Teacher ---> ...
1st level 2nd level 3rd level
We can first define the following definition for Teacher to TeacherDTO conversion.
This first mapping is used for the root Teacher entity.
Include any other fields you need in the mapping.
mapping(Teacher.class, TeacherDTO.class,
TypeMappingOptions.oneWay()
, mapNull(false)
).fields("courses", "courses");
The following mapping will prevent Dozer from going further to map the contained Course. We define a mapId teacherLeaf for it.
Exclude the fields that cause the infinite recursion. (In my example, it's courses)
Include any other fields you need in the mapping.
mapping(Teacher.class, TeacherDTO.class,
TypeMappingOptions.oneWay(), TypeMappingOptions.mapId("teacherLeaf")
, mapNull(false)
).exclude("courses");
The last one is the mapping rule for Course to courseDTO. The key is that we tell the mapper to use the teacherLeaf mapping rule defined previously to convert the contained Teachers.
mapping(Course.class, CourseDTO.class,
TypeMappingOptions.oneWay()
, mapNull(false)
).fields("teachers", "teachers", useMapId("teacherLeaf"));
Hope this helps!
I use Dozer 6.1.0.

POST a Complex JSON using Javax-RS & Java Persistence & RESTful

I would like to apologize in advanced if this is a duplicate, however, I've been looking around for the last 2 days and have not found anything that solves my problem.
I have created a web service to which I would like to POST a JSON object. My issue is the following:
Let's say I have three objects.
ObjectA:{
"name":"",
"address":"",
"id":""
}
ObjectB:{
"id:"",
"name":"",
"objectA":{ [ObjectA]}
}
ObjectC:{
"id:"",
"name":"",
"objectA":{},
"objectB":{}
}
As you can see,ObjectC references ObjectA and ObjectB, which also references ObjectA. When inserting a new ObjectC, ObjectC.objectA should be the same as ObjectC.objectB.objectA.
The POST is consumed by the following method:
#POST
#Override
#Consumes({"application/xml", "application/json"})
public void create(ObjectC entity) {
super.create(entity);
}
The classes look like this: (plus the getters and setters)
#Entity
#Table(name = "object_a")
public class ObjectA{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#NotNull
#Column(name = "u_name")
private String uName;
#Column(name = "address")
private String address;
}
#Entity
#Table(name = "object_b")
public class ObjectB{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#JoinColumn(name = "object_a", referencedColumnName = "id")
#ManyToOne(optional = false, cascade = CascadeType.PERSIST)
#Valid
private ObjectA objectA;
}
#Entity
#Table(name = "object_c")
public class ObjectC{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "id")
private Integer id;
#Column(name = "name")
private String name;
#JoinColumn(name = "object_a", referencedColumnName = "id")
#ManyToOne(optional = false, cascade = CascadeType.PERSIST)
#Valid
private ObjectA objectA;
#JoinColumn(name = "object_b", referencedColumnName = "id")
#ManyToOne(optional = false, cascade = CascadeType.PERSIST)
#Valid
private ObjectB objectB;
}
Note: I am able to POST an ObjectA, and an ObjectB without any problem.
PROBLEM
The problem is that ObjectC.objectA and ObjectC.objectB.objectA are being inserted (or attempted to) as different values, which in throws an exception because ObjectA.uName is unique. If this wasn't the case, two new ObjectA's would have been created.
I was able to solve this problem in Hibernate, by doing something like the following:
ObjectA a = new ObjectA();
ObjectB b = new ObjectB();
ObjectC c = new ObjectC();
b.setObjectA(a);
c.setObjectA(a);
c.setObjectB(b);
session.beginTransaction();
session.save(a);
session.save(b);
session.save(c);
session.getTransaction().commit();
How can I go about this?
For anyone out there who might come across this issue, I have found the solution.
I did it in a very similar way to what I did in hibernate (see above).
I edited the create method I posted above, to this:
#POST
#Override
#Consumes({"application/xml", "application/json"})
public void create(ObjectC entity) {
if(entity.getObjectA().getId()==null){//If objectA doesn't have an ID, it must be new
entity.getObjectB().setObjectA(entity.getObjectA());
em.persist(entity.getObjectA());
em.persist(entity.getObjectB());
em.persist(entity);
}else{//Otherwise, everything may be inserted at once.
super.create(entity);
}
}
By the way, the super.create(entity) method contains the following:
public void create(T entity){
getEntityManager().persist(entity);
}

Categories

Resources