Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions build/config/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@
<argument>^org\.hibernate\.query\.spi\.QueryParameterBindingTypeResolver$</argument>
<argument>^org\.hibernate\.query\.sqm\.spi\.SqmCreationContext$</argument>
<argument>^org\.hibernate\.sql\.results\.graph\.Fetchable$</argument>

<argument>^org\.hibernate\.engine\.extension\.spi\.[a-zA-Z]+$</argument>
<argument>^org\.hibernate\.engine\.spi\.SharedSessionContractImplementor#getExtension.+$</argument>
</arguments>
<!--
While this will include more than we really need, it is easier to do so.
Expand Down
2 changes: 1 addition & 1 deletion build/parents/build/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
NOTE: when Hibernate ORM updates Byte Buddy, make sure to check Jenkinsfile to see if
`net.bytebuddy.experimental` property can be removed.
-->
<version.org.hibernate.orm>7.2.0.CR2</version.org.hibernate.orm>
<version.org.hibernate.orm>7.2.0-SNAPSHOT</version.org.hibernate.orm>

<javadoc.org.hibernate.orm.url>https://docs.jboss.org/hibernate/orm/${parsed-version.org.hibernate.orm.majorVersion}.${parsed-version.org.hibernate.orm.minorVersion}/javadocs/</javadoc.org.hibernate.orm.url>
<documentation.org.hibernate.orm.url>https://docs.jboss.org/hibernate/orm/${parsed-version.org.hibernate.orm.majorVersion}.${parsed-version.org.hibernate.orm.minorVersion}/userguide/html_single/Hibernate_User_Guide.html</documentation.org.hibernate.orm.url>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,9 @@
import org.hibernate.search.integrationtest.spring.jta.dao.SnertDAO;
import org.hibernate.search.integrationtest.spring.jta.entity.Snert;
import org.hibernate.search.integrationtest.spring.testsupport.AbstractMapperOrmSpringIT;
import org.hibernate.search.mapper.orm.session.impl.HibernateOrmSearchSessionHolder;
import org.hibernate.search.util.impl.integrationtest.common.extension.BackendMock;
import org.hibernate.search.util.impl.test.annotation.PortedFromSearch5;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
Expand Down Expand Up @@ -50,11 +48,6 @@ void checkJta() {
.returns( true, TransactionCoordinatorBuilder::isJta );
}

@AfterEach
void checkNoMemoryLeak() {
assertThat( HibernateOrmSearchSessionHolder.staticMapSize() ).isZero();
}

@Test
void test() {
Snert snert = new Snert();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@
import org.hibernate.search.integrationtest.spring.jta.entity.Doughnut;
import org.hibernate.search.integrationtest.spring.jta.entity.Muffin;
import org.hibernate.search.integrationtest.spring.testsupport.AbstractMapperOrmSpringIT;
import org.hibernate.search.mapper.orm.session.impl.HibernateOrmSearchSessionHolder;
import org.hibernate.search.util.impl.integrationtest.common.extension.BackendMock;
import org.hibernate.search.util.impl.test.annotation.PortedFromSearch5;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
Expand Down Expand Up @@ -52,11 +50,6 @@ void checkJta() {
.returns( true, TransactionCoordinatorBuilder::isJta );
}

@AfterEach
void checkNoMemoryLeak() {
assertThat( HibernateOrmSearchSessionHolder.staticMapSize() ).isZero();
}

@Test
void testMuffins() {
Box box = new Box();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@
import org.hibernate.search.integrationtest.spring.jta.dao.SnertDAO;
import org.hibernate.search.integrationtest.spring.jta.entity.Snert;
import org.hibernate.search.integrationtest.spring.testsupport.AbstractMapperOrmSpringIT;
import org.hibernate.search.mapper.orm.session.impl.HibernateOrmSearchSessionHolder;
import org.hibernate.search.util.impl.integrationtest.common.extension.BackendMock;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;
Expand Down Expand Up @@ -48,11 +46,6 @@ void checkJta() {
.returns( true, TransactionCoordinatorBuilder::isJta );
}

@AfterEach
void checkNoMemoryLeak() {
assertThat( HibernateOrmSearchSessionHolder.staticMapSize() ).isZero();
}

@Test
void test() {
Snert snert = new Snert();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,10 +377,9 @@ public PojoIndexingPlan currentIndexingPlanIfExisting(SessionImplementor session

@Override
public PojoTypeIndexingPlan currentIndexingPlanIfTypeIncluded(
SharedSessionContractImplementor sessionImplementor,
SharedSessionContractImplementor session,
PojoRawTypeIdentifier<?> typeIdentifier) {
try {
SessionImplementor session = sessionImplementor.unwrap( SessionImplementor.class );
HibernateOrmSearchSession searchSession = HibernateOrmSearchSession.get( this, session, false );
if ( searchSession != null ) {
// If the session exist, rely on the session-level filter
Expand All @@ -403,7 +402,7 @@ public PojoTypeIndexingPlan currentIndexingPlanIfTypeIncluded(
}
}
catch (PersistenceException e) {
throw OrmMiscLog.INSTANCE.unsupportedSessionType( sessionImplementor.getClass() );
throw OrmMiscLog.INSTANCE.unsupportedSessionType( session.getClass() );
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
class AfterCommitIndexingPlanSynchronization implements Synchronization {

private final PojoIndexingPlan indexingPlan;
private final HibernateOrmSearchSessionHolder sessionHolder;
private final HibernateOrmSearchSessionExtension sessionExtension;
private final Transaction transactionIdentifier;
private final ConfiguredIndexingPlanSynchronizationStrategy synchronizationStrategy;

AfterCommitIndexingPlanSynchronization(PojoIndexingPlan indexingPlan,
HibernateOrmSearchSessionHolder sessionHolder, Transaction transactionIdentifier,
HibernateOrmSearchSessionExtension sessionExtension, Transaction transactionIdentifier,
ConfiguredIndexingPlanSynchronizationStrategy synchronizationStrategy) {
this.indexingPlan = indexingPlan;
this.sessionHolder = sessionHolder;
this.sessionExtension = sessionExtension;
this.transactionIdentifier = transactionIdentifier;
this.synchronizationStrategy = synchronizationStrategy;
}
Expand All @@ -51,7 +51,7 @@ public void afterCompletion(int i) {
}
finally {
//clean the Synchronization per Transaction
sessionHolder.clear( transactionIdentifier );
sessionExtension.clear( transactionIdentifier );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
class BeforeCommitIndexingPlanSynchronization implements Synchronization {

private final PojoIndexingPlan indexingPlan;
private final HibernateOrmSearchSessionHolder sessionHolder;
private final HibernateOrmSearchSessionExtension sessionExtension;
private final Transaction transactionIdentifier;
private final ConfiguredIndexingPlanSynchronizationStrategy synchronizationStrategy;

BeforeCommitIndexingPlanSynchronization(PojoIndexingPlan indexingPlan,
HibernateOrmSearchSessionHolder sessionHolder, Transaction transactionIdentifier,
HibernateOrmSearchSessionExtension sessionExtension, Transaction transactionIdentifier,
ConfiguredIndexingPlanSynchronizationStrategy synchronizationStrategy) {
this.indexingPlan = indexingPlan;
this.sessionHolder = sessionHolder;
this.sessionExtension = sessionExtension;
this.transactionIdentifier = transactionIdentifier;
this.synchronizationStrategy = synchronizationStrategy;
}
Expand All @@ -47,7 +47,7 @@ public void afterCompletion(int i) {
}
finally {
//clean the Synchronization per Transaction
sessionHolder.clear( transactionIdentifier );
sessionExtension.clear( transactionIdentifier );
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -238,15 +238,15 @@ public PojoIndexingPlan createIndexingPlan(HibernateOrmSearchSession context,
}

public Synchronization createTransactionWorkQueueSynchronization(PojoIndexingPlan indexingPlan,
HibernateOrmSearchSessionHolder sessionProperties,
HibernateOrmSearchSessionExtension sessionExtension,
Transaction transactionIdentifier,
ConfiguredIndexingPlanSynchronizationStrategy synchronizationStrategy) {
if ( enlistsInTransaction ) {
return new BeforeCommitIndexingPlanSynchronization( indexingPlan, sessionProperties, transactionIdentifier,
return new BeforeCommitIndexingPlanSynchronization( indexingPlan, sessionExtension, transactionIdentifier,
synchronizationStrategy );
}
else {
return new AfterCommitIndexingPlanSynchronization( indexingPlan, sessionProperties, transactionIdentifier,
return new AfterCommitIndexingPlanSynchronization( indexingPlan, sessionExtension, transactionIdentifier,
synchronizationStrategy );
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.hibernate.action.spi.BeforeTransactionCompletionProcess;
import org.hibernate.engine.spi.ActionQueue;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.search.engine.backend.common.spi.EntityReferenceFactory;
import org.hibernate.search.engine.search.common.NonStaticMetamodelScope;
import org.hibernate.search.engine.search.query.dsl.SearchQuerySelectStep;
Expand Down Expand Up @@ -66,7 +67,7 @@ public class HibernateOrmSearchSession extends AbstractPojoSearchSession
* @return The {@link HibernateOrmSearchSession} to use within the context of the given session.
*/
public static HibernateOrmSearchSession get(HibernateOrmSearchSessionMappingContext context,
SessionImplementor sessionImplementor) {
SharedSessionContractImplementor sessionImplementor) {
return get( context, sessionImplementor, true );
}

Expand All @@ -75,14 +76,9 @@ public static HibernateOrmSearchSession get(HibernateOrmSearchSessionMappingCont
* @return The {@link HibernateOrmSearchSession} to use within the context of the given session.
*/
public static HibernateOrmSearchSession get(HibernateOrmSearchSessionMappingContext context,
SessionImplementor sessionImplementor, boolean createIfDoesNotExist) {
HibernateOrmSearchSessionHolder holder =
HibernateOrmSearchSessionHolder.get( sessionImplementor, createIfDoesNotExist );
if ( holder == null ) {
// Can only happen if createIfDoesNotExist is false
return null;
}
HibernateOrmSearchSession searchSession = holder.searchSession();
SharedSessionContractImplementor sessionImplementor, boolean createIfDoesNotExist) {
HibernateOrmSearchSessionExtension extension = HibernateOrmSearchSessionExtension.get( sessionImplementor );
HibernateOrmSearchSession searchSession = extension.searchSession();
if ( searchSession != null ) {
return searchSession;
}
Expand All @@ -91,9 +87,16 @@ public static HibernateOrmSearchSession get(HibernateOrmSearchSessionMappingCont
return null;
}

searchSession = context.createSessionBuilder( sessionImplementor ).build();
holder.searchSession( searchSession );
return searchSession;
if ( sessionImplementor instanceof SessionImplementor implementor ) {
searchSession = context.createSessionBuilder( implementor ).build();
extension.searchSession( searchSession );
return searchSession;
}
else {
// TODO: For a stateless session need a different impl of the search session
throw new UnsupportedOperationException(
"Cannot create Search Session using a non stateful ORM session: " + sessionImplementor.getClass() );
}
}

private final HibernateOrmSearchSessionMappingContext mappingContext;
Expand Down Expand Up @@ -203,6 +206,10 @@ public <SR, T> TypedSearchScope<SR, T> typedScope(Class<SR> rootScope, Collectio
return mappingContext.createScope( rootScope, classes );
}

// TODO: won't be able to use these (toEntityManager / toOrmSession / session) in a stateless session case...
// options ?
// = deprecate and introduce some shared session interface instead ?
// = keep two search session interfaces one for stateless one for stateful ?
@Override
public EntityManager toEntityManager() {
return sessionImplementor;
Expand All @@ -213,6 +220,11 @@ public Session toOrmSession() {
return sessionImplementor;
}

@Override
public SessionImplementor session() {
return sessionImplementor;
}

@Override
public SearchIndexingPlan indexingPlan() {
if ( indexingPlan == null ) {
Expand Down Expand Up @@ -254,11 +266,6 @@ public ConfiguredSearchIndexingPlanFilter configuredIndexingPlanFilter() {
return configuredIndexingPlanFilter;
}

@Override
public SessionImplementor session() {
return sessionImplementor;
}

@Override
public EntityReferenceFactory entityReferenceFactory() {
return mappingContext.entityReferenceFactory();
Expand All @@ -276,9 +283,9 @@ public PojoRuntimeIntrospector runtimeIntrospector() {

@Override
public PojoIndexingPlan currentIndexingPlan(boolean createIfDoesNotExist) {
HibernateOrmSearchSessionHolder holder =
HibernateOrmSearchSessionHolder.get( sessionImplementor, createIfDoesNotExist );
if ( holder == null ) {
HibernateOrmSearchSessionExtension extension =
HibernateOrmSearchSessionExtension.get( sessionImplementor );
if ( extension.searchSession() == null ) {
// Can only happen if createIfDoesNotExist is false
return null;
}
Expand All @@ -291,7 +298,7 @@ public PojoIndexingPlan currentIndexingPlan(boolean createIfDoesNotExist) {
transactionIdentifier = null;
}

PojoIndexingPlan plan = holder.pojoIndexingPlan( transactionIdentifier );
PojoIndexingPlan plan = extension.pojoIndexingPlan( transactionIdentifier );
if ( plan != null ) {
return plan;
}
Expand All @@ -303,11 +310,11 @@ public PojoIndexingPlan currentIndexingPlan(boolean createIfDoesNotExist) {
ConfiguredIndexingPlanSynchronizationStrategy currentSynchronizationStrategy =
indexingPlanSynchronizationStrategy;
plan = automaticIndexingStrategy.createIndexingPlan( this, currentSynchronizationStrategy );
holder.pojoIndexingPlan( transactionIdentifier, plan );
extension.pojoIndexingPlan( transactionIdentifier, plan );

if ( sessionImplementor.isTransactionInProgress() ) {
Synchronization txSync = automaticIndexingStrategy.createTransactionWorkQueueSynchronization(
plan, holder, transactionIdentifier,
plan, extension, transactionIdentifier,
currentSynchronizationStrategy
);
registerSynchronization( sessionImplementor, txSync );
Expand Down Expand Up @@ -347,6 +354,10 @@ private void registerSynchronization(SessionImplementor sessionImplementor, Sync
* In a JTA env, the before transaction completion is called before the flush, so not all changes are yet
* written. However, Synchronization-s do propagate exceptions, so they can be safely used.
*/

// TODO: There's no action queue with stateless session ...
// Hence it would be great if we could just have an SPI in ORM to deal with this instead?
// TODO: Have a closer look at this part to figure the best way to deal with stateless sessions..
Comment on lines +358 to +360
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we won't have a queue in stateless session so we'd need to deal with this one way or the other 😃,
but an ORM SPI would be nice 🙂

final ActionQueue actionQueue = sessionImplementor.getActionQueue();
SynchronizationAdapter adapter = new SynchronizationAdapter( synchronization );

Expand All @@ -372,7 +383,7 @@ private boolean isLocalTransaction(SessionImplementor sessionImplementor) {
.isJta();
}

private static void checkOpen(SessionImplementor session) {
private static void checkOpen(SharedSessionContractImplementor session) {
try {
session.checkOpen();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.search.mapper.orm.session.impl;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

import org.hibernate.Transaction;
import org.hibernate.engine.extension.spi.Extension;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.search.mapper.pojo.work.spi.PojoIndexingPlan;

public class HibernateOrmSearchSessionExtension implements Serializable, Extension {

public static HibernateOrmSearchSessionExtension get(SharedSessionContractImplementor session) {
return session.getExtension( HibernateOrmSearchSessionExtension.class );
}

// Everything here should be transient because the holder might get serialized along with a Hibernate ORM session.
// The Hibernate Search data (indexing plans in particular) will be lost in the process,
// but that's the best we can do.
private transient HibernateOrmSearchSession searchSession;
private transient Map<Transaction, PojoIndexingPlan> planPerTransaction;

public static HibernateOrmSearchSessionExtension init() {
return new HibernateOrmSearchSessionExtension();
}

public HibernateOrmSearchSession searchSession() {
return searchSession;
}

public void searchSession(HibernateOrmSearchSession searchSession) {
this.searchSession = searchSession;
}

public PojoIndexingPlan pojoIndexingPlan(Transaction transaction) {
return planPerTransaction == null ? null : planPerTransaction.get( transaction );
}

public void pojoIndexingPlan(Transaction transaction, PojoIndexingPlan plan) {
if ( planPerTransaction == null ) {
planPerTransaction = new HashMap<>();
}
planPerTransaction.put( transaction, plan );
}

public void clear(Transaction transactionIdentifier) {
if ( planPerTransaction == null ) {
return;
}
planPerTransaction.remove( transactionIdentifier );
}
}
Loading
Loading