Reputation: 321
I have a very basic Spring Boot/JPA stack app, with a controller, service layer, and repository that does not persist updates as I understand it should.
A trivial Entity:
@Entity
public class Customer {
@Id
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
protected Customer() {}
public Customer(String name) { this.name = name; }
// standard getters,setters //
}
A trivial Repository:
@Repository
public interface CustomerRepository extends CrudRepository<Customer, Long> {}
A simple Service layer:
// If the service is @Transactional and the controller is not, the update does NOT occur
@Transactional
@Service
public class CustomerService {
private static final Logger LOG = getLogger(CustomerService.class);
@Autowired
private CustomerRepository customerRepository;
boolean updateCustomerName(Long id, String name) {
Customer customer = customerRepository.findOne(id);
if (customer == null) { return false; }
// Modifies the entity
customer.setName(name);
// No explicit save()
return true;
}
}
And a REST controller that uses it all:
// If the controller is @Transactional and the service is not, the update occurs
@RestController
@RequestMapping("/mvc")
public class CustomerController {
@Autowired
private CustomerService customerService;
@RequestMapping(path = "{id}", method = RequestMethod.PUT)
public ResponseEntity updateCustomerName(@PathVariable Long id, @RequestParam("name") String name) {
customerService.updateCustomerName(id,name);
return ResponseEntity.noContent().build();
}
}
These are wired together with a simple one-liner SpringBootApplication
I have SQL debug logs enabled and see the selects, update, etc.
With the code above: When the service method is invoked by the controller, the modified entity is not persisted. SQL logs show the select
of the entity but no update
.
There is also no update if nothing is marked @Transactional
However, simply by moving the @Transactional
annotation from the service class to the controller class, the SQL update
does occur.
If I add an explicit customerRepository.save(customer)
to the service method, the update also occurs. But my understanding is that the ORM should automatically save modified persistent entities.
I'm sure the issue has something to do with the EntityManager lifecycle in the web request, but I'm puzzled. Do I need to do additional configuration?
Complete example at https://github.com/monztech/SO-41515160
EDIT: This was solved, see below. Per the Spring spec @Transactional
does not work in package-private methods and mistakenly did not make the update service method public.
The update will occur if the method is public
and the service class has the @Transactional
annotation.
I do have another question, however. Why is the @Transactional
annotation necessary? (the update does not occur without it) Shouldn't the entity manager still persist the object because of the open session in view mechanism that Spring uses, independent of any transaction?
Upvotes: 1
Views: 5111