我有一个非常基本的Spring Boot/JPA堆栈应用程序,它有一个控制器、服务层和存储库,但我认为它不会持久更新。
一个琐碎的实体:
@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 //
}
一个琐碎的存储库:
@Repository
public interface CustomerRepository extends CrudRepository<Customer, Long> {}
一个简单的服务层:
// 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;
}
}
以及一个使用所有功能的REST控制器:
// 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();
}
}
它们通过一个简单的单线SpringBootApplication
连接在一起
我启用了SQL调试日志,并查看了选择、更新等
使用上面的代码:当控制器调用服务方法时,修改后的实体是而不是持久化的。SQL日志显示实体的select
,但没有显示update
。
如果没有任何内容标记为@Transactional
,则也没有更新
但是,只需将@Transactional
注释从服务类移动到控制器类,SQLupdate
就会发生。
如果我向服务方法添加一个显式的customerRepository.save(customer)
,那么更新也会发生。但我的理解是ORM应该自动保存修改后的持久实体。
我确信这个问题与web请求中的EntityManager生命周期有关,但我感到困惑。我需要做额外的配置吗?
完整示例位于https://github.com/monztech/SO-41515160
编辑:此问题已解决,请参阅下文。根据Spring规范,@Transactional
在包私有方法中不起作用,并且错误地没有公开更新服务方法。
如果方法是public
并且服务类具有@Transactional
注释,则会发生更新。
不过,我还有一个问题。为什么@Transactional
注释是必要的?(如果没有它,更新就不会发生)实体管理器不应该因为Spring使用的视图中的打开会话机制而独立于任何事务而仍然保留对象吗?
将updateCustomerName
方法公开。