2017-01-06 3 views
1

У меня очень простое приложение для загрузки Spring Boot/JPA с контроллером, уровнем сервиса и репозиторием, который не сохраняет обновления, насколько я понимаю должен.Spring Boot JPA @Transactional @Service не обновляется, но @Transactional в контроллере

Тривиальным 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 // 
} 

Тривиальным Repository:

@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 аннотации из класса обслуживания в класс контроллера, то SQL updateделает происходит.

Если я добавлю явный метод customerRepository.save(customer) к методу службы, это обновление также произойдет. Но я понимаю, что ORM автоматически сохраняет измененные постоянные объекты.

Я уверен, что проблема имеет какое-то отношение к жизненному циклу EntityManager в веб-запросе, но я озадачен. Нужно ли выполнять дополнительную настройку?

Полный пример на https://github.com/monztech/SO-41515160

EDIT: Эта задача была решена, смотри ниже. В спецификации Spring @Transactional не работает в пакетах-частных методах и по ошибке не делает метод службы обновлений общедоступным.

Обновление произойдет, если метод public, а класс обслуживания содержит аннотацию @Transactional.

У меня есть другой вопрос, однако. Почему нужна аннотация для @Transactional? (обновление не происходит без него). Должен ли диспетчер объектов по-прежнему сохранять объект из-за механизма открытого сеанса, который использует Spring, независимо от какой-либо транзакции?

+0

Попробуйте использовать @Transactional (readOnly = false), хотя значение по умолчанию - false.Это немного странно, потому что, если метод findOne извлекает значения из БД, этот метод корректно работает с @Transactional (работающим внутри транзакционного контекста, завернутым декодером TransactionalManager и так далее ...). И да, Клиент в этом транзакционном контексте управляется в постоянном контексте jpa, поэтому он должен быть обновлен при возврате метода. – Dani

+0

readOnly = false не влияет. Благодарю. Это странно. Я ветеран Hibernate/Spring, но раньше не использовал его в Spring Boot. Кажется, я пропустил какой-то шаг, но мне не повезло. – Monz

+1

Интересный вопрос. Можете ли вы попробовать 2 вещи, чтобы увидеть, что происходит. 1. Сделайте общедоступным метод службы и попробуйте добавить транзакционную аннотацию непосредственно к методу. 2. Независимо от 1, в вашем коде, как есть (кроме создания общедоступного метода), создайте интерфейс, который определяет метод обновления, и ваша служба реализует этот интерфейс. –

ответ

3

Сделайте свой способ updateCustomerName общедоступным.

+0

Спасибо, но это не помогло. Я бы не ожидал, что модификаторы доступа будут модифицировать поведение Spring. Метод вызван. – Monz

+0

Но они делают http://stackoverflow.com/questions/4396284/does-spring-transactional-attribute-work-on-a-private-method –

+0

Я пробовал несколько вещей, основанных на ответе от @alanhay, и получается это решение. Я, должно быть, допустил ошибку, когда я попробовал это раньше, но я подтвердил, что простое изменение метода в службе 'public' вызывает требуемое поведение. Благодаря! – Monz

Смежные вопросы