Я установил spring.jpa.open-in-view
на false
, и предупреждение больше не появляется в журнале, так что это не причина.
Весь граф объектов выбирается, как только find()
илиfindById()
метод выполняется, поэтому я не думаю, что он также вызван сериализацией (сущности не реализуют интерфейс Serializable
в любом случае).
Все объектные отношения много-к-одному,и все сопоставления объектов аннотированы как ленивые.
Единственный способ, которым я смог избежать нетерпеливой загрузки, был выбор отдельных полей, а не целых объектов, но это отнимает много времени и хрупок, поэтому я 'я предпочитаю не использовать это решение.
Приложение использует Hibernate 5.3.7 и Spring Boot 2.1.0.У старого приложения у нас нет этой проблемы;он использует Hibernate 4.3.8 и не использует Spring Boot.
Вот код (уменьшенный из бизнес-кейса, чтобы попытаться определить проблему).
@Entity
@Table(name = "VT_Invoice")
class Invoice(
@Id
@Column(name = "InvoiceID")
val invoiceId: Long
) {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "EntityCode#ClientCore", referencedColumnName = "EntityCode#ClientCore")
var client: Client? = null
}
@Entity
@Table(name = "VT_ClientCore")
class Client(
@Id
@Column(name = "EntityCode#ClientCore")
val id: String
) {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "EntityCode#Branch", referencedColumnName = "EntityCode#Branch")
var branch: Branch? = null
}
@Entity
@Table(name = "VT_Branch")
class Branch(
@Id
@Column(name = "EntityCode#Branch")
val id: String
)
interface InvoiceRepository : JpaRepository<Invoice, Long>
@Service
class InvoiceRetrievalService(
@Autowired val invoiceRepository: InvoiceRepository
) {
@PersistenceContext
private lateinit var entityManager: EntityManager
fun fetchInvoice(invoiceId: Long): Invoice {
val hibernateInvoice = entityManager.find(Invoice::class.java, invoiceId)
val springInvoice = invoiceRepository.findById(invoiceId)
return hibernateInvoice
}
}
РЕДАКТИРОВАТЬ
После отладки кода Hibernate оказывается, что активная загрузка запускается этим блоком кода внутри DefaultLoadEventListener.proxyOrLoad()
:
if ( !persister.hasProxy() ) {
return load( event, persister, keyToLoad, options );
}
Реализация AbstractEntityPersister.hasProxy()
отличается в двух версиях Hibernate.
Hibernate 4.3.8:
public boolean hasProxy() {
return entityMetamodel.isLazy();
}
entityMetamodel.isLazy()
имеет значение true, поэтому связаннаязагрузка объекта пропущена.
Hibernate 5.3.7:
public boolean hasProxy() {
// skip proxy instantiation if entity is bytecode enhanced
return entityMetamodel.isLazy() && !entityMetamodel.getBytecodeEnhancementMetadata().isEnhancedForLazyLoading();
}
entityMetamodel.isLazy()
имеет значение false, поэтому связанный объект загружен.Проверка на улучшение байт-кода оказывается несущественной.
Это то, что я смог получить в ходе расследования.Я не мог определить, почему две версии Hibernate инициализируют атрибут lazy
по-разному;он записан в нескольких местах, но ни одна из установленных мной точек останова не была достигнута, поэтому я предполагаю, что она где-то инициализируется с помощью отражения.