Monz
Monz

Reputation: 321

Spring Boot JPA @Transactional @Service does not update, but @Transactional in controller does

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

Answers (1)

MarkOfHall
MarkOfHall

Reputation: 3353

Make your updateCustomerName method public.

Upvotes: 4

Related Questions