У меня есть небольшой пример с некоторыми сопоставлениями get / post и вызовами JpaRepository в Spring Boot.
Во-первых, у меня есть два класса сущностей:
@Entity
@Table(name = "stock")
public class Stock extends BaseEntity
{
@Column(name = "value")
public String value;
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
@Entity
@Table(name = "stock_item")
public class StockItem extends BaseEntity
{
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "stock_id", insertable = false, updatable = false)
public Stock stock;
@Column(name = "stock_id")
public Long stockId;
@Column(name = "value")
public String value;
}
У меня многоодна ассоциация от StockItem
до Stock
.Я вставляю Stock
и у меня есть контроллер, как показано ниже:
@Autowired
public Controller(StockItemRepository stockItemRepository) {
this.stockItemRepository = stockItemRepository;
}
@RequestMapping("/")
@Transactional(readOnly = true)
public String get() {
List<StockItem> stockItemList = stockItemRepository.getItemsById(1L);
System.out.println("TX MANAGER: " + TransactionSynchronizationManager.isActualTransactionActive());
for (StockItem stockItem : stockItemList) {
System.out.println(stockItem.getStock().getValue());
}
return "get";
}
@RequestMapping("/fromSave")
@Transactional
public String post() {
StockItem stockItem = new StockItem();
stockItem.setStockId(1L);
stockItemRepository.saveAndFlush(stockItem);
System.out.println("saveCalled");
return get();
}
и getItemsById
в хранилище определяется следующим образом:
@Query("FROM StockItem si " +
"JOIN FETCH si.stock stk " +
"WHERE si.stockId = :id")
List<StockItem> getItemsById(@Param("id") Long id);
Из моего понимания, когда я звонюметод post:
- создает новый элемент
- устанавливает идентификатор связанного атрибута
- сохраняет и завершает транзакцию
Вот где все становится странным ... Я вызываю get после поста и выполняю вышеуказанный вызов репозитория, у которого есть выборка соединения, и когда я вызываю stockitem.getStock().getValue()
, я получаю нулевой указатель, когда ожидаю LazyInitializationException
.
Если я вызываю get()
из сопоставления вне класса, он успешно загружает связанный объект.
Я даже удалил аннотацию @Transaction
из get, а также соединениеизвлекать из моего запроса и снова, если я звоню извне класса, он работает и из почты, он вылетает с NullPointerException
.
Я поместил get
внутри TransactionTemplate.execute()
иЯ все еще получаю NullPointerException
при звонке из класса.
Итак, основные вопросы:
- Почему я получаю
NullPointerException
вместо LazyInitializationException
? - В чем заключается магия транзакции, заключающаяся в отсутствии транзакции, но успешной выборке ленивого атрибута ??