0

I have two entities First and Second. There are @PreRemove and PostRemove implementations for First. The preRemove() creates a record in Second and caches the result to a transient property First.second. This is working. I want the postRemove to update the same record, which is not working. It is not updating the record.

@Entity
@Getter@Setter@NoArgsConstructor@AllArgsConstructor
public class Second {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", nullable = false)
    private Long id;

    private String prop;
}


@Entity
@Getter@Setter@NoArgsConstructor@AllArgsConstructor
public class First {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  @Column(name = "id")
  private Long id;

  private String prop;

  @Transient@JsonIgnore
  private Second second;

  @PreRemove
  public void preRemove() {
      second = MyBeanUtils.getBean(SecondRepository.class).save(new Second());
  }

  @PostRemove
  public void postRemove() {
      second.setProp("after remove");
      MyBeanUtils.getBean(SecondRepository.class).save(second);
  }

}

Controller and Repository codes

@Repository
public interface FirstRepository extends JpaRepository<First, Long> {
}
@Repository
public interface SecondRepository extends JpaRepository<Second, Long> {
}
@RestController
@RequiredArgsConstructor
public class DemoController {

    private final FirstRepository repository;

    @PostMapping("/first")
    public First postBo3(@RequestBody First bo3) {
        return repository.save(bo3);
    }

    @DeleteMapping("/first/{id}")
    void postBo3(@PathVariable long id) {
        repository.deleteById(id);
    }

}

When I put a break point on the line second.setProp("after remove"); in the postRemove(), it shows the field second does have an ID, because it is the cached instance after save().

With spring.jpa.show-sql=true, these are the queries logged. Can't see any update query and the record is not updated in the database too. Please suggest a reason for this and a workaround.

Hibernate: select first0_.id as id1_0_0_, first0_.prop as prop2_0_0_ from first first0_ where first0_.id=?
Hibernate: insert into second (prop) values (?)
Hibernate: delete from first where id=?
Ramanujan R
  • 1,601
  • 2
  • 23
  • 43

1 Answers1

0

Adding @Transactional annotation above any method involved in database operations in your (micro)services ensure you are staying inside a current database transaction.

Please stop using repositories in the controllers layer. It's a very bad way to develop things for your application.

Disable OpenSessionInView property to optimize things around your database queries.

BendaThierry.com
  • 2,080
  • 1
  • 15
  • 17
  • 1
    I don't think the transaction is closed, otherwise, it would have returned an error. Here, it is not updating the record `second` even if the `@PostRemove` is executed without any error. I tried this after making the function with `@DeleteMapping` transactional, but still the same behaviour. I agree with you that we should not call repository methods inside controller. My actual project don't. Here I am using a sample for simplicity. – Ramanujan R Jul 23 '23 at 12:00
  • Using `@Transactional(propagation = Propagation.REQUIRED, readOnly = false)` may help, maybe ? – BendaThierry.com Jul 24 '23 at 12:39
  • Maybe that is related to your `transient` field not saved by default in database. So JPA events are not supported by this kind of qualifier type. – BendaThierry.com Jul 24 '23 at 12:49
  • Maybe it is related to `@RestController` and `@JsonIgnore` on that field. So your backend don't care about it and cannot manage it. – BendaThierry.com Jul 24 '23 at 12:52