0

I've been trying to figure out this apparent easy issue, but I'm having no sucess in it. In essence, My entity "Job" has a One To One Relation with entity "User".

When trying to post a new Job, I do not want to send all the info regarding the User, but only the userID encapsulated in the User object inside Job. Here is my code:

Job:

@Entity
@Table(name="TB_Job")
public class Job implements Serializable {

    @GeneratedValue(strategy = GenerationType.SEQUENCE)
    @Id long jobId;

    private String title, description, location;

    private boolean job_assigned, time_to_assigned, job_active;

    Date createDate;

    Time licitationTime;

    private int stateId;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn(insertable = false, updatable = false)
    private User ownerUserID;

    @OneToOne(cascade = CascadeType.ALL)
    @JoinColumn()
    private User assignedUserID;


    private int categoryID;

}

User:

    @Entity
    @Table(name = "TB_User")
    public class User implements Serializable {

        @Id
        @GeneratedValue(strategy = GenerationType.AUTO)
        @Column(name = "user_id")
        private long userId;

        @Column(name = "email", nullable = false, unique = true)
        @Email(message = "*Please provide a valid Email")
        @NotEmpty(message = "*Please provide an email")
        private String email;

        @Column(name = "password")
        @Length(min = 5, message = "*Your password must have at least 5 characters")
        @NotEmpty(message = "*Please provide your password")
        private String password;

        @NotEmpty(message = "*Please provide your name")
        @Column(name = "first_name")
        private String firstName;

        @Column(name = "last_name")
        @NotEmpty(message = "*Please provide your last name")
        private String lastName;

        @Column(name = "active")
        private int active;

        @Column(name = "city")
        @NotEmpty(message = "*Please provide your City name")
        private String city;



        // --------- FOREIGN KEYS ---------

        // Column used to FK in AssocUserRole
        @OneToOne(mappedBy = "user")
        private AssocUserRole user;

        // Column used to FK in Job
        @OneToMany(mappedBy = "ownerUserID")
        private Set<Job> ownerUserID;

        // Column used to FK in Job
        @OneToMany(mappedBy = "assignedUserID")
        private Set<Job> assignedUserID;

        // --------- FOREIGN KEYS ---------

    }

Controller:

@RestController
@RequestMapping(path = "/api")
public class JobController {


    @Autowired
    private JobRepository jobRepository;
    @Autowired
    private JobService jobservice;


    @PostMapping("/job/{ownerId}")
    public Job createJob(@RequestBody Job job, @PathVariable(value = "ownerId") long ownerId) {
        return jobservice.addJob(job, ownerId);
    }

    @PostMapping("/job")
    public Job createJob(@RequestBody Job job) {
        return jobservice.addJob(job);
    }
}

Service:

@Service
public class JobService {

    @Autowired
    JobRepository jobRepository;

    @Autowired
    UserRepository userRepository;

    public Job addJob(Job job, long ownerId) {
        try {
            // Checks if owner actually exists
            User ownerUser = userRepository.findById(ownerId);

            // If it does
            if(ownerUser != null) {
                // Checks if job does not exists already
                if (jobRepository.findById(job.getJobId()) == null) {
                    // Gets and Sets Job Owner FK to Job
                    job.setOwnerUserID(ownerUser);

                    return jobRepository.save(job);
                } else {
                    return null;
                }
            }
            else{
                return null;
            }
        } catch (Exception Ex) {
            return null;
        }
    }

    public Job addJob(Job job) {
        try {
            // Checks if job does not exists already
            if (jobRepository.findById(job.getJobId()) == null) {
                // Gets and Sets Job Owner FK to Job
                //job.setOwnerUserID(ownerUser);

                return jobRepository.save(job);
            } else {
                return null;
            }
        } catch (Exception Ex) {
            return null;
        }
    }
}

As you guys can see, I have different methods in JobService. One recieves only the Job, other the Job and the userID. My objective is quite easy: I post an Job, and I want to properly accept the Foreign key to the User correspondent. Unfortunally, using the 2º method, I am having this issue:

org.springframework.transaction.TransactionSystemException: Could not commit JPA transaction; nested exception is javax.persistence.RollbackException: Error while committing the transaction
Validation failed for classes [com.homelancer.models.User] during persist time for groups [javax.validation.groups.Default, ]
List of constraint violations:[
    ConstraintViolationImpl{interpolatedMessage='*Please provide your password', propertyPath=password, rootBeanClass=class com.homelancer.models.User, messageTemplate='*Please provide your password'}
    ConstraintViolationImpl{interpolatedMessage='*Please provide your last name', propertyPath=lastName, rootBeanClass=class com.homelancer.models.User, messageTemplate='*Please provide your last name'}
    ConstraintViolationImpl{interpolatedMessage='*Please provide an email', propertyPath=email, rootBeanClass=class com.homelancer.models.User, messageTemplate='*Please provide an email'}
    ConstraintViolationImpl{interpolatedMessage='*Please provide your name', propertyPath=firstName, rootBeanClass=class com.homelancer.models.User, messageTemplate='*Please provide your name'}
    ConstraintViolationImpl{interpolatedMessage='*Please provide your City name', propertyPath=city, rootBeanClass=class com.homelancer.models.User, messageTemplate='*Please provide your City name'}
]

This is only happenning, because JCA is trying to insert in DB the Job AND the User. I do only want to get the Job inserted, and the ID from the user referenced.

This is my POST request :

{
  "category": "batatas",
  "categoryID": 0,
  "createDate": "2019-12-29T21:50:42.847Z",
  "description": "teste bc",
  "job_active": true,
  "job_assigned": true,
  "location": "povoa city",
  "stateID": 1,
  "time_to_assigned": true,
  "title": "granda job",
  "ownerUserID": {
    "userId": 116  
  }
}

If anyone has any ideia about a solution to my implementation issue it would be very helpful, since I lost quite a few hours around this and have yet not found and good solution.

Thanks

Wambosy
  • 3
  • 1
  • 2

2 Answers2

0

This if statement is not good, because you don't have id in your request body, so its always null.

if (jobRepository.findById(job.getJobId()) == null) {

Its better to create a new method in your JobRepository, for example:

Job findFirstByTitle(String title);

This one return job for title. But the main issue is to delete insertable = false at ownerUserID field. So this one must be:

@JoinColumn(updatable = false)
private User ownerUserID;

Because if you saving the job object, the reference should be inserted in this field.

alexej K
  • 152
  • 3
0

Is addJob transactional?

If not, then ownerUser is in a detached state when you call jobRepository.save(job). This means JPA will try to add it as though it was a new user. Adding @Transactional should resolve the issue.

BTW if you're not planning on updating the User together with its Job, you probably don't want cascade = ALL on Job.ownerUserID

crizzis
  • 9,978
  • 2
  • 28
  • 47