Java – Spring JPA Auditing empty createdBy

auditingjavaspringspring-data

I am using the auditing capabilities of Spring Data and have a class similar to this:

@Entity
@Audited
@EntityListeners(AuditingEntityListener.class)
@Table(name="Student")
public class Student {
    @Id
    @GeneratedValue (strategy = GenerationType.AUTO)
    private Long id;

    @CreatedBy
    private String createdBy;

    @CreatedDate
    private Date createdDate;

    @LastModifiedBy
    private String lastModifiedBy;

    @LastModifiedDate
    private Date lastModifiedDate;
...

Now, I believe I have configured auditing fine because I can see that createdBy, createdDate, lastModifiedBy and lastModifiedDate all are getting the correct values when I update the domain objects.

However, my problem is that when I update an object I am losing the values of createdBy and createdDate. So, when I first create the object I have all four values, but when I update it createdBy and createdDate are nullified ! I am also using the Hibernate envers to keep a history of the domain objects.

Do you know why do I get this behavior ? Why do createdBy and createdDate are empty when I update the domain object ?

Update: To answer @m-deinum 's questions: Yes spring data JPA is configured correctly – everything else works fine – I really wouldn't like to post the configuration because as you udnerstand it will need a lot of space.

My AuditorAwareImpl is this

@Component
public class AuditorAwareImpl implements AuditorAware {
    Logger logger = Logger.getLogger(AuditorAwareImpl.class);

    @Autowired
    ProfileService profileService;

    @Override
    public String getCurrentAuditor() {
        return profileService.getMyUsername();
    }
}

Finally, here's my update controller implementation:

    @Autowired  
    private StudentFormValidator validator;
    @Autowired
    private StudentRepository studentRep;

@RequestMapping(value="/edit/{id}", method=RequestMethod.POST)  
public String updateFromForm(
         @PathVariable("id")Long id,
         @Valid Student student, BindingResult result,
         final RedirectAttributes redirectAttributes)   {  

     Student s =  studentRep.secureFind(id); 
     if(student == null || s == null) {
         throw new ResourceNotFoundException();
     }
     validator.validate(student, result);
     if (result.hasErrors()) {  
         return "students/form";
     } 
     student.setId(id);
     student.setSchool(profileService.getMySchool());
     redirectAttributes.addFlashAttribute("message", "Επιτυχής προσθήκη!");
     studentRep.save(student);
     return "redirect:/students/list";  
}  

Update 2: Please take a look at a newer version

@RequestMapping(value="/edit/{id}", method=RequestMethod.GET)  
     public ModelAndView editForm(@PathVariable("id")Long id)  {  
         ModelAndView mav = new ModelAndView("students/form");  
         Student student =  studentRep.secureFind(id); 
         if(student == null) {
             throw new ResourceNotFoundException();
         }
         mav.getModelMap().addAttribute(student);
         mav.getModelMap().addAttribute("genders", GenderEnum.values());
         mav.getModelMap().addAttribute("studentTypes", StudEnum.values());
         return mav;  
     }  

     @RequestMapping(value="/edit/{id}", method=RequestMethod.POST)  
     public String updateFromForm(
             @PathVariable("id")Long id,
             @Valid @ModelAttribute Student student, BindingResult result,
             final RedirectAttributes redirectAttributes, SessionStatus status)   {  

         Student s =  studentRep.secureFind(id); 
         if(student == null || s == null) {
             throw new ResourceNotFoundException();
         }

         if (result.hasErrors()) {  
             return "students/form";
         } 
         //student.setId(id);
         student.setSchool(profileService.getMySchool());
         studentRep.save(student);
         redirectAttributes.addFlashAttribute("message", "Επιτυχής προσθήκη!");
         status.setComplete();
         return "redirect:/students/list";  
     }  

This still leaves empty the createdBy and createdDate fields when I do an update 🙁

Also it does not get the School value (which is not contained in my form because it is related to the user currently editing) so I need to get it again from the SecurityContext… Have I done anything wrong ?

Update 3: For reference and to not miss it in the comments: The main problem was that I needed to include the @SessionAttributes annotation to my controller.

Best Answer

Use updatable attribute of @Column annotation like below.

@Column(name = "created_date", updatable = false)
private Date createdDate;

This will retain the created date on update operation.