Merge remote-tracking branch 'upstream/master' into wip/6.0
This commit is contained in:
commit
e861604805
|
@ -34,13 +34,13 @@ import org.jboss.logging.Logger;
|
||||||
*/
|
*/
|
||||||
class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
|
class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
|
||||||
private static final Logger log = Logger.getLogger( EventListenerGroupImpl.class );
|
private static final Logger log = Logger.getLogger( EventListenerGroupImpl.class );
|
||||||
|
private static final Set<DuplicationStrategy> DEFAULT_DUPLICATION_STRATEGIES = Collections.unmodifiableSet( makeDefaultDuplicationStrategy() );
|
||||||
|
|
||||||
private final EventType<T> eventType;
|
private final EventType<T> eventType;
|
||||||
private final CallbackRegistry callbackRegistry;
|
private final CallbackRegistry callbackRegistry;
|
||||||
private final boolean isJpaBootstrap;
|
private final boolean isJpaBootstrap;
|
||||||
|
|
||||||
private final Set<DuplicationStrategy> duplicationStrategies = new LinkedHashSet<>();
|
private Set<DuplicationStrategy> duplicationStrategies = DEFAULT_DUPLICATION_STRATEGIES;
|
||||||
|
|
||||||
private T[] listeners = null;
|
private T[] listeners = null;
|
||||||
|
|
||||||
public EventListenerGroupImpl(
|
public EventListenerGroupImpl(
|
||||||
|
@ -50,21 +50,6 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
|
||||||
this.eventType = eventType;
|
this.eventType = eventType;
|
||||||
this.callbackRegistry = callbackRegistry;
|
this.callbackRegistry = callbackRegistry;
|
||||||
this.isJpaBootstrap = isJpaBootstrap;
|
this.isJpaBootstrap = isJpaBootstrap;
|
||||||
|
|
||||||
duplicationStrategies.add(
|
|
||||||
// At minimum make sure we do not register the same exact listener class multiple times.
|
|
||||||
new DuplicationStrategy() {
|
|
||||||
@Override
|
|
||||||
public boolean areMatch(Object listener, Object original) {
|
|
||||||
return listener.getClass().equals( original.getClass() );
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Action getAction() {
|
|
||||||
return Action.ERROR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -85,7 +70,13 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
duplicationStrategies.clear();
|
//Odd semantics: we're expected (for backwards compatibility) to also clear the default DuplicationStrategy.
|
||||||
|
duplicationStrategies = new LinkedHashSet<>();;
|
||||||
|
listeners = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearListeners() {
|
||||||
listeners = null;
|
listeners = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,6 +116,9 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addDuplicationStrategy(DuplicationStrategy strategy) {
|
public void addDuplicationStrategy(DuplicationStrategy strategy) {
|
||||||
|
if ( duplicationStrategies == DEFAULT_DUPLICATION_STRATEGIES ) {
|
||||||
|
duplicationStrategies = makeDefaultDuplicationStrategy();
|
||||||
|
}
|
||||||
duplicationStrategies.add( strategy );
|
duplicationStrategies.add( strategy );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -308,4 +302,24 @@ class EventListenerGroupImpl<T> implements EventListenerGroup<T> {
|
||||||
|
|
||||||
return Arrays.asList( listeners );
|
return Arrays.asList( listeners );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static Set<DuplicationStrategy> makeDefaultDuplicationStrategy() {
|
||||||
|
final Set<DuplicationStrategy> duplicationStrategies = new LinkedHashSet<>();
|
||||||
|
duplicationStrategies.add(
|
||||||
|
// At minimum make sure we do not register the same exact listener class multiple times.
|
||||||
|
new DuplicationStrategy() {
|
||||||
|
@Override
|
||||||
|
public boolean areMatch(Object listener, Object original) {
|
||||||
|
return listener.getClass().equals( original.getClass() );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action getAction() {
|
||||||
|
return Action.ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return duplicationStrategies;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,8 +61,20 @@ public interface EventListenerGroup<T> extends Serializable {
|
||||||
public void prependListener(T listener);
|
public void prependListener(T listener);
|
||||||
public void prependListeners(T... listeners);
|
public void prependListeners(T... listeners);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears both the list of event listeners and all DuplicationStrategy,
|
||||||
|
* including the default duplication strategy.
|
||||||
|
* @deprecated likely want to use {@link #clearListeners()} instead, which doesn't
|
||||||
|
* also reset the registered DuplicationStrategy(ies).
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public void clear();
|
public void clear();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all registered listeners
|
||||||
|
*/
|
||||||
|
public void clearListeners();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fires an event on each registered event listener of this group.
|
* Fires an event on each registered event listener of this group.
|
||||||
*
|
*
|
||||||
|
|
|
@ -194,6 +194,7 @@ public class SessionFactoryImpl implements SessionFactoryImplementor {
|
||||||
private final transient FastSessionServices fastSessionServices;
|
private final transient FastSessionServices fastSessionServices;
|
||||||
private final transient SessionBuilder defaultSessionOpenOptions;
|
private final transient SessionBuilder defaultSessionOpenOptions;
|
||||||
private final transient SessionBuilder temporarySessionOpenOptions;
|
private final transient SessionBuilder temporarySessionOpenOptions;
|
||||||
|
private final transient StatelessSessionBuilder defaultStatelessOptions;
|
||||||
|
|
||||||
public SessionFactoryImpl(
|
public SessionFactoryImpl(
|
||||||
final MetadataImplementor bootMetamodel,
|
final MetadataImplementor bootMetamodel,
|
||||||
|
@ -364,8 +365,9 @@ public class SessionFactoryImpl implements SessionFactoryImplementor {
|
||||||
fetchProfiles.put( fetchProfile.getName(), fetchProfile );
|
fetchProfiles.put( fetchProfile.getName(), fetchProfile );
|
||||||
}
|
}
|
||||||
|
|
||||||
this.defaultSessionOpenOptions = withOptions();
|
this.defaultSessionOpenOptions = createDefaultSessionOpenOptionsIfPossible();
|
||||||
this.temporarySessionOpenOptions = buildTemporarySessionOpenOptions();
|
this.temporarySessionOpenOptions = this.defaultSessionOpenOptions == null ? null : buildTemporarySessionOpenOptions();
|
||||||
|
this.defaultStatelessOptions = this.defaultSessionOpenOptions == null ? null : withStatelessOptions();
|
||||||
this.fastSessionServices = new FastSessionServices( this );
|
this.fastSessionServices = new FastSessionServices( this );
|
||||||
|
|
||||||
this.observer.sessionFactoryCreated( this );
|
this.observer.sessionFactoryCreated( this );
|
||||||
|
@ -392,6 +394,17 @@ public class SessionFactoryImpl implements SessionFactoryImplementor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SessionBuilder createDefaultSessionOpenOptionsIfPossible() {
|
||||||
|
final CurrentTenantIdentifierResolver currentTenantIdentifierResolver = getCurrentTenantIdentifierResolver();
|
||||||
|
if ( currentTenantIdentifierResolver == null ) {
|
||||||
|
return withOptions();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//Don't store a default SessionBuilder when a CurrentTenantIdentifierResolver is provided
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private SessionBuilder buildTemporarySessionOpenOptions() {
|
private SessionBuilder buildTemporarySessionOpenOptions() {
|
||||||
return withOptions()
|
return withOptions()
|
||||||
.autoClose( false )
|
.autoClose( false )
|
||||||
|
@ -502,25 +515,23 @@ public class SessionFactoryImpl implements SessionFactoryImplementor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Session openSession() throws HibernateException {
|
public Session openSession() throws HibernateException {
|
||||||
final CurrentTenantIdentifierResolver currentTenantIdentifierResolver = getCurrentTenantIdentifierResolver();
|
//The defaultSessionOpenOptions can't be used in some cases; for example when using a TenantIdentifierResolver.
|
||||||
//We can only reuse the defaultSessionOpenOptions as a constant when there is no TenantIdentifierResolver
|
if ( this.defaultSessionOpenOptions != null ) {
|
||||||
if ( currentTenantIdentifierResolver != null ) {
|
return this.defaultSessionOpenOptions.openSession();
|
||||||
return this.withOptions().openSession();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return this.defaultSessionOpenOptions.openSession();
|
return this.withOptions().openSession();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Session openTemporarySession() throws HibernateException {
|
public Session openTemporarySession() throws HibernateException {
|
||||||
final CurrentTenantIdentifierResolver currentTenantIdentifierResolver = getCurrentTenantIdentifierResolver();
|
//The temporarySessionOpenOptions can't be used in some cases; for example when using a TenantIdentifierResolver.
|
||||||
//We can only reuse the defaultSessionOpenOptions as a constant when there is no TenantIdentifierResolver
|
if ( this.temporarySessionOpenOptions != null ) {
|
||||||
if ( currentTenantIdentifierResolver != null ) {
|
return this.temporarySessionOpenOptions.openSession();
|
||||||
return buildTemporarySessionOpenOptions()
|
|
||||||
.openSession();
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return this.temporarySessionOpenOptions.openSession();
|
return buildTemporarySessionOpenOptions()
|
||||||
|
.openSession();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -542,8 +553,13 @@ public class SessionFactoryImpl implements SessionFactoryImplementor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public StatelessSession openStatelessSession() {
|
public StatelessSession openStatelessSession() {
|
||||||
|
if ( this.defaultStatelessOptions != null ) {
|
||||||
|
return this.defaultStatelessOptions.openStatelessSession();
|
||||||
|
}
|
||||||
|
else {
|
||||||
return withStatelessOptions().openStatelessSession();
|
return withStatelessOptions().openStatelessSession();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public StatelessSession openStatelessSession(Connection connection) {
|
public StatelessSession openStatelessSession(Connection connection) {
|
||||||
return withStatelessOptions().connection( connection ).openStatelessSession();
|
return withStatelessOptions().connection( connection ).openStatelessSession();
|
||||||
|
|
|
@ -184,6 +184,22 @@ public class EventListenerDuplicationStrategyTest {
|
||||||
listenerGroup.appendListener( new ExpectedListener( tracker ) );
|
listenerGroup.appendListener( new ExpectedListener( tracker ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDuplicationStrategyRemovedOnClear() {
|
||||||
|
listenerGroup.clear();
|
||||||
|
//As side-effect, it's now allowed to register the same event twice:
|
||||||
|
listenerGroup.appendListener( new OriginalListener( tracker ) );
|
||||||
|
listenerGroup.appendListener( new OriginalListener( tracker ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDefaultDuplicationStrategy() {
|
||||||
|
thrown.expect( EventListenerRegistrationException.class );
|
||||||
|
//By default, it's not allowed to register the same type of listener twice:
|
||||||
|
listenerGroup.appendListener( new OriginalListener( tracker ) );
|
||||||
|
listenerGroup.appendListener( new OriginalListener( tracker ) );
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keep track of which listener is called and how many listeners are called.
|
* Keep track of which listener is called and how many listeners are called.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,562 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.hql.size;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToMany;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
|
||||||
|
import org.hibernate.Hibernate;
|
||||||
|
import org.hibernate.boot.SessionFactoryBuilder;
|
||||||
|
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
|
||||||
|
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
@TestForIssue(jiraKey = "HHH-13944")
|
||||||
|
public class ManyToManySizeTest2 extends BaseNonConfigCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
private SQLStatementInterceptor sqlStatementInterceptor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
|
||||||
|
sqlStatementInterceptor = new SQLStatementInterceptor( sfb );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
inTransaction( session -> {
|
||||||
|
Skill mathSkill = new Skill();
|
||||||
|
Skill frenchSkill = new Skill();
|
||||||
|
|
||||||
|
session.persist( mathSkill );
|
||||||
|
session.persist( frenchSkill );
|
||||||
|
|
||||||
|
Teacher teacherWithNoSkills = new Teacher();
|
||||||
|
|
||||||
|
Teacher teacherWithOneSkill = new Teacher();
|
||||||
|
teacherWithOneSkill.addSkill( mathSkill );
|
||||||
|
|
||||||
|
Teacher teacherWithTwoSkills = new Teacher();
|
||||||
|
teacherWithTwoSkills.addSkill( mathSkill );
|
||||||
|
teacherWithTwoSkills.addSkill( frenchSkill );
|
||||||
|
|
||||||
|
session.persist( teacherWithNoSkills );
|
||||||
|
session.persist( teacherWithOneSkill );
|
||||||
|
session.persist( teacherWithTwoSkills );
|
||||||
|
|
||||||
|
Student studentWithTeacherWithNoSkills = new Student();
|
||||||
|
studentWithTeacherWithNoSkills.setTeacher( teacherWithNoSkills );
|
||||||
|
session.persist( studentWithTeacherWithNoSkills );
|
||||||
|
|
||||||
|
Student studentWithTeacherWithOneSkill = new Student();
|
||||||
|
studentWithTeacherWithOneSkill.setTeacher( teacherWithOneSkill );
|
||||||
|
session.persist( studentWithTeacherWithOneSkill );
|
||||||
|
|
||||||
|
Student studentWithTeacherWithTwoSkills = new Student();
|
||||||
|
studentWithTeacherWithTwoSkills.setTeacher( teacherWithTwoSkills );
|
||||||
|
session.persist( studentWithTeacherWithTwoSkills );
|
||||||
|
} );
|
||||||
|
sqlStatementInterceptor.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
inTransaction( session -> {
|
||||||
|
session.createQuery( "delete from Student" ).executeUpdate();
|
||||||
|
session.createQuery( "delete from Teacher" ).executeUpdate();
|
||||||
|
session.createQuery( "delete from Skill" ).executeUpdate();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSize() {
|
||||||
|
inTransaction( session -> {
|
||||||
|
List<Teacher> teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher join teacher.skills skills where size(skills) > 2",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0, teachers.size() );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher join teacher.skills skills where size(skills) > 1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 1, teachers.size() );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher join teacher.skills skills where size(skills) > 0",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, teachers.size() );
|
||||||
|
|
||||||
|
// Using "join" (instead of "left join") removes the teacher with no skills from the results.
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher join teacher.skills skills where size(skills) > -1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, teachers.size() );
|
||||||
|
|
||||||
|
// Using "left join" includes the teacher with no skills in the results.
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join teacher.skills skills where size(skills) > -1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3, teachers.size() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeAddFetch() {
|
||||||
|
inTransaction( session -> {
|
||||||
|
List<Teacher> teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.skills join teacher.skills skills where size(skills) > 2",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0, teachers.size() );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.skills join teacher.skills skills where size(skills) > 1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 1, teachers.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.skills join teacher.skills skills where size(skills) > 0",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, teachers.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||||
|
|
||||||
|
|
||||||
|
// Using "join" (instead of "left join") removes the teacher with no skills from the results.
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.skills join teacher.skills skills where size(skills) > -1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, teachers.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||||
|
|
||||||
|
// Using "left join" includes the teacher with no skills in the results.
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.skills left join teacher.skills skills where size(skills) > -1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3, teachers.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 2 ).getSkills() ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithoutNestedPath() {
|
||||||
|
inTransaction( session -> {
|
||||||
|
List<Teacher> teachers = session.createQuery(
|
||||||
|
"select teacher from Teacher teacher where size(teacher.skills) > 2",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0, teachers.size() );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select teacher from Teacher teacher where size(teacher.skills) > 1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 1, teachers.size() );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select teacher from Teacher teacher where size(teacher.skills) > 0",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, teachers.size() );
|
||||||
|
|
||||||
|
// If there is no "join", then results include the teacher with no skills
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select teacher from Teacher teacher where size(teacher.skills) > -1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3, teachers.size() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithoutNestedPathAddFetchDistinct() {
|
||||||
|
inTransaction( session -> {
|
||||||
|
List<Teacher> teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.skills where size(teacher.skills) > 2",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 2 );
|
||||||
|
assertEquals( 0, teachers.size() );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.skills where size(teacher.skills) > 1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 1, teachers.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.skills where size(teacher.skills) > 0",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, teachers.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.skills where size(teacher.skills) > -1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3, teachers.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 2 ).getSkills() ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithNestedPathAndImplicitJoin() {
|
||||||
|
doInJPA( this::sessionFactory, em -> {
|
||||||
|
List<Student> students = em.createQuery(
|
||||||
|
"select student from Student student where size(student.teacher.skills) > 2",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0, students.size() );
|
||||||
|
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 1 );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student where size(student.teacher.skills) > 1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 1, students.size() );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student where size(student.teacher.skills) > 0",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, students.size() );
|
||||||
|
|
||||||
|
// If there is no "join" in the query, then results include the student with the teacher with no skills
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student where size(student.teacher.skills) > -1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3, students.size() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithNestedPathAndImplicitJoinAddFetchDistinct() {
|
||||||
|
doInJPA( this::sessionFactory, em -> {
|
||||||
|
List<Student> students = em.createQuery(
|
||||||
|
"select distinct student from Student student left join fetch student.teacher t left join fetch t.skills where size(student.teacher.skills) > 2",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0, students.size() );
|
||||||
|
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 3 );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student left join fetch student.teacher t left join fetch t.skills where size(student.teacher.skills) > 1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 1, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student left join fetch student.teacher t left join fetch t.skills where size(student.teacher.skills) > 0",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student left join fetch student.teacher t left join fetch t.skills where size(student.teacher.skills) > -1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getSkills() ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithNestedPath() {
|
||||||
|
doInJPA( this::sessionFactory, em -> {
|
||||||
|
List<Student> students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(student.teacher.skills) > -1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3L, students.size() );
|
||||||
|
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 1 );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(student.teacher.skills) > 0",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2L, students.size() );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(student.teacher.skills) > 1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 1L, students.size() );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(student.teacher.skills) > 2",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0L, students.size() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithNestedPathAddFetchDistinct() {
|
||||||
|
doInJPA( this::sessionFactory, em -> {
|
||||||
|
List<Student> students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.skills where size(student.teacher.skills) > -1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
// NOTE: An INNER JOIN is done on Teacher twice, which results in 4 joins.
|
||||||
|
// A possible optimization would be to reuse this INNER JOIN.
|
||||||
|
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 4 );
|
||||||
|
assertEquals( 3L, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getSkills() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.skills where size(student.teacher.skills) > 0",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2L, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.skills where size(student.teacher.skills) > 1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 1L, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.skills where size(student.teacher.skills) > 2",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0L, students.size() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithNestedPath2() {
|
||||||
|
doInJPA( this::sessionFactory, em -> {
|
||||||
|
List<Student> students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(t.skills) > -1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3L, students.size() );
|
||||||
|
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 1 );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(t.skills) > 0",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2L, students.size() );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(t.skills) > 1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 1L, students.size() );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(t.skills) > 2",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0L, students.size() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithNestedPath2AddFetchDistinct() {
|
||||||
|
doInJPA( this::sessionFactory, em -> {
|
||||||
|
List<Student> students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch left join fetch tfetch.skills where size(t.skills) > -1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
// NOTE: An INNER JOIN is done on Teacher twice, which results in 4 joins.
|
||||||
|
// A possible optimization would be to reuse this INNER JOIN.
|
||||||
|
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 4 );
|
||||||
|
assertEquals( 3L, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getSkills() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch left join fetch tfetch.skills where size(t.skills) > 0",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2L, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getSkills() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch left join fetch tfetch.skills where size(t.skills) > 1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 1L, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getSkills() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch left join fetch tfetch.skills where size(t.skills) > 2",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0L, students.size() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class<?>[] { Skill.class, Teacher.class, Student.class };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Skill")
|
||||||
|
public static class Skill {
|
||||||
|
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private Set<Teacher> teachers = new HashSet<>();
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "skill_id")
|
||||||
|
@GeneratedValue
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManyToMany(mappedBy = "skills")
|
||||||
|
public Set<Teacher> getTeachers() {
|
||||||
|
return teachers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeachers(Set<Teacher> teachers) {
|
||||||
|
this.teachers = teachers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTeacher(Teacher teacher) {
|
||||||
|
teachers.add( teacher );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Teacher")
|
||||||
|
public static class Teacher {
|
||||||
|
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private Set<Student> students = new HashSet<>();
|
||||||
|
|
||||||
|
private Set<Skill> skills = new HashSet<>();
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "teacher_id")
|
||||||
|
@GeneratedValue
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "teacher")
|
||||||
|
public Set<Student> getStudents() {
|
||||||
|
return students;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStudents(Set<Student> students) {
|
||||||
|
this.students = students;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addStudent(Student student) {
|
||||||
|
students.add( student );
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManyToMany
|
||||||
|
public Set<Skill> getSkills() {
|
||||||
|
return skills;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSkill(Skill skill) {
|
||||||
|
skills.add( skill );
|
||||||
|
skill.addTeacher( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSkills(Set<Skill> skills) {
|
||||||
|
this.skills = skills;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Student")
|
||||||
|
public static class Student {
|
||||||
|
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private Teacher teacher;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "student_id")
|
||||||
|
@GeneratedValue
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManyToOne(optional = false)
|
||||||
|
@JoinColumn(name = "teacher_fk_id")
|
||||||
|
public Teacher getTeacher() {
|
||||||
|
return teacher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeacher(Teacher teacher) {
|
||||||
|
this.teacher = teacher;
|
||||||
|
teacher.addStudent( this );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int countNumberOfJoins(String query) {
|
||||||
|
return query.toLowerCase( Locale.ROOT ).split( " join ", -1 ).length - 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,559 @@
|
||||||
|
/*
|
||||||
|
* Hibernate, Relational Persistence for Idiomatic Java
|
||||||
|
*
|
||||||
|
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||||
|
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||||
|
*/
|
||||||
|
package org.hibernate.test.hql.size;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Set;
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
|
import javax.persistence.ManyToMany;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
|
||||||
|
import org.hibernate.Hibernate;
|
||||||
|
import org.hibernate.boot.SessionFactoryBuilder;
|
||||||
|
|
||||||
|
import org.hibernate.testing.TestForIssue;
|
||||||
|
import org.hibernate.testing.jdbc.SQLStatementInterceptor;
|
||||||
|
import org.hibernate.testing.junit4.BaseNonConfigCoreFunctionalTestCase;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.hibernate.testing.transaction.TransactionUtil.doInJPA;
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
@TestForIssue(jiraKey = "HHH-13944")
|
||||||
|
public class OneToManySizeTest2 extends BaseNonConfigCoreFunctionalTestCase {
|
||||||
|
|
||||||
|
private SQLStatementInterceptor sqlStatementInterceptor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void configureSessionFactoryBuilder(SessionFactoryBuilder sfb) {
|
||||||
|
sqlStatementInterceptor = new SQLStatementInterceptor( sfb );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
inTransaction( session -> {
|
||||||
|
Student mathStudent = new Student();
|
||||||
|
Student frenchStudent = new Student();
|
||||||
|
Student scienceStudent = new Student();
|
||||||
|
|
||||||
|
Teacher teacherWithNoStudents = new Teacher();
|
||||||
|
Teacher teacherWithOneStudent = new Teacher();
|
||||||
|
Teacher teacherWithTwoStudents = new Teacher();
|
||||||
|
|
||||||
|
session.persist( teacherWithNoStudents );
|
||||||
|
session.persist( teacherWithOneStudent );
|
||||||
|
session.persist( teacherWithTwoStudents );
|
||||||
|
|
||||||
|
mathStudent.setTeacher( teacherWithOneStudent );
|
||||||
|
teacherWithOneStudent.addStudent( mathStudent );
|
||||||
|
|
||||||
|
frenchStudent.setTeacher( teacherWithTwoStudents );
|
||||||
|
teacherWithTwoStudents.addStudent( frenchStudent );
|
||||||
|
|
||||||
|
scienceStudent.setTeacher( teacherWithTwoStudents );
|
||||||
|
teacherWithTwoStudents.addStudent( scienceStudent );
|
||||||
|
|
||||||
|
session.persist( mathStudent );
|
||||||
|
session.persist( frenchStudent );
|
||||||
|
session.persist( scienceStudent );
|
||||||
|
} );
|
||||||
|
sqlStatementInterceptor.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void tearDown() {
|
||||||
|
inTransaction( session -> {
|
||||||
|
session.createQuery( "delete from Student" ).executeUpdate();
|
||||||
|
session.createQuery( "delete from Teacher" ).executeUpdate();
|
||||||
|
session.createQuery( "delete from Skill" ).executeUpdate();
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSize() {
|
||||||
|
inTransaction( session -> {
|
||||||
|
List<Teacher> teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher join teacher.students students where size(students) > 2",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0, teachers.size() );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher join teacher.students students where size(students) > 1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 1, teachers.size() );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher join teacher.students students where size(students) > 0",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, teachers.size() );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher join teacher.students students where size(students) > -1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, teachers.size() );
|
||||||
|
|
||||||
|
// Using "left join" includes the teacher with no students in the results.
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join teacher.students students where size(students) > -1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3, teachers.size() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeAddFetch() {
|
||||||
|
inTransaction( session -> {
|
||||||
|
List<Teacher> teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.students join teacher.students students where size(students) > 2",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0, teachers.size() );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.students join teacher.students students where size(students) > 1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 1, teachers.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.students join teacher.students students where size(students) > 0",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, teachers.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||||
|
|
||||||
|
|
||||||
|
// Using "join" (instead of "left join") removes the teacher with no students from the results.
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.students join teacher.students students where size(students) > -1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, teachers.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||||
|
|
||||||
|
// Using "left join" includes the teacher with no students in the results.
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.students left join teacher.students students where size(students) > -1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3, teachers.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 2 ).getStudents() ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithoutNestedPath() {
|
||||||
|
inTransaction( session -> {
|
||||||
|
List<Teacher> teachers = session.createQuery(
|
||||||
|
"select teacher from Teacher teacher where size(teacher.students) > 2",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0, teachers.size() );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select teacher from Teacher teacher where size(teacher.students) > 1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 1, teachers.size() );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select teacher from Teacher teacher where size(teacher.students) > 0",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, teachers.size() );
|
||||||
|
|
||||||
|
// If there is no "join", then results include the teacher with no students
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select teacher from Teacher teacher where size(teacher.students) > -1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3, teachers.size() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithoutNestedPathAddFetchDistinct() {
|
||||||
|
inTransaction( session -> {
|
||||||
|
List<Teacher> teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.students where size(teacher.students) > 2",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 1 );
|
||||||
|
assertEquals( 0, teachers.size() );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.students where size(teacher.students) > 1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 1, teachers.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.students where size(teacher.students) > 0",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, teachers.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||||
|
|
||||||
|
teachers = session.createQuery(
|
||||||
|
"select distinct teacher from Teacher teacher left join fetch teacher.students where size(teacher.students) > -1",
|
||||||
|
Teacher.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3, teachers.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 0 ).getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 1 ).getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( teachers.get( 2 ).getStudents() ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithNestedPathAndImplicitJoin() {
|
||||||
|
doInJPA( this::sessionFactory, em -> {
|
||||||
|
List<Student> students = em.createQuery(
|
||||||
|
"select student from Student student where size(student.teacher.students) > 2",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0, students.size() );
|
||||||
|
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 1 );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student where size(student.teacher.students) > 1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, students.size() );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student where size(student.teacher.students) > 0",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3, students.size() );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student where size(student.teacher.students) > -1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3, students.size() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithNestedPathAndImplicitJoinAddFetchDistinct() {
|
||||||
|
doInJPA( this::sessionFactory, em -> {
|
||||||
|
List<Student> students = em.createQuery(
|
||||||
|
"select distinct student from Student student left join fetch student.teacher t left join fetch t.students where size(student.teacher.students) > 2",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0, students.size() );
|
||||||
|
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 2 );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student left join fetch student.teacher t left join fetch t.students where size(student.teacher.students) > 1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student left join fetch student.teacher t left join fetch t.students where size(student.teacher.students) > 0",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student left join fetch student.teacher t left join fetch t.students where size(student.teacher.students) > -1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithNestedPath() {
|
||||||
|
doInJPA( this::sessionFactory, em -> {
|
||||||
|
List<Student> students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(student.teacher.students) > -1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3L, students.size() );
|
||||||
|
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 1 );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(student.teacher.students) > 0",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3L, students.size() );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(student.teacher.students) > 1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2L, students.size() );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(student.teacher.students) > 2",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0L, students.size() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithNestedPathAddFetchDistinct() {
|
||||||
|
doInJPA( this::sessionFactory, em -> {
|
||||||
|
List<Student> students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.students where size(student.teacher.students) > -1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
// NOTE: An INNER JOIN is done on Teacher twice, which results in 4 joins.
|
||||||
|
// A possible optimization would be to reuse this INNER JOIN.
|
||||||
|
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 3 );
|
||||||
|
assertEquals( 3L, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.students where size(student.teacher.students) > 0",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3L, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.students where size(student.teacher.students) > 1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2L, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tjoin left join fetch tjoin.students where size(student.teacher.students) > 2",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0L, students.size() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithNestedPath2() {
|
||||||
|
doInJPA( this::sessionFactory, em -> {
|
||||||
|
List<Student> students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(t.students) > -1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3L, students.size() );
|
||||||
|
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 1 );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(t.students) > 0",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3L, students.size() );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(t.students) > 1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2L, students.size() );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select student from Student student join student.teacher t where size(t.students) > 2",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0L, students.size() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSizeWithNestedPath2AddFetchDistinct() {
|
||||||
|
doInJPA( this::sessionFactory, em -> {
|
||||||
|
List<Student> students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch join fetch tfetch.students where size(t.students) > -1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
// NOTE: An INNER JOIN is done on Teacher twice, which results in 4 joins.
|
||||||
|
// A possible optimization would be to reuse this INNER JOIN.
|
||||||
|
assertEquals( countNumberOfJoins( sqlStatementInterceptor.getSqlQueries().get( 0 ) ), 3 );
|
||||||
|
assertEquals( 3L, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch join fetch tfetch.students where size(t.students) > 0",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 3L, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 2 ).getTeacher().getStudents() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch join fetch tfetch.students where size(t.students) > 1",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 2L, students.size() );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 0 ).getTeacher().getStudents() ) );
|
||||||
|
assertTrue( Hibernate.isInitialized( students.get( 1 ).getTeacher().getStudents() ) );
|
||||||
|
|
||||||
|
students = em.createQuery(
|
||||||
|
"select distinct student from Student student join student.teacher t join fetch student.teacher tfetch join fetch tfetch.students where size(t.students) > 2",
|
||||||
|
Student.class
|
||||||
|
).getResultList();
|
||||||
|
assertEquals( 0L, students.size() );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Class<?>[] getAnnotatedClasses() {
|
||||||
|
return new Class<?>[] { Skill.class, Teacher.class, Student.class };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Skill")
|
||||||
|
public static class Skill {
|
||||||
|
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private Set<Teacher> teachers = new HashSet<>();
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "skill_id")
|
||||||
|
@GeneratedValue
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManyToMany(mappedBy = "skills")
|
||||||
|
public Set<Teacher> getTeachers() {
|
||||||
|
return teachers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeachers(Set<Teacher> teachers) {
|
||||||
|
this.teachers = teachers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addTeacher(Teacher teacher) {
|
||||||
|
teachers.add( teacher );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Teacher")
|
||||||
|
public static class Teacher {
|
||||||
|
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private Set<Student> students = new HashSet<>();
|
||||||
|
|
||||||
|
private Set<Skill> skills = new HashSet<>();
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "teacher_id")
|
||||||
|
@GeneratedValue
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OneToMany(mappedBy = "teacher")
|
||||||
|
public Set<Student> getStudents() {
|
||||||
|
return students;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStudents(Set<Student> students) {
|
||||||
|
this.students = students;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addStudent(Student student) {
|
||||||
|
students.add( student );
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManyToMany
|
||||||
|
public Set<Skill> getSkills() {
|
||||||
|
return skills;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addSkill(Skill skill) {
|
||||||
|
skills.add( skill );
|
||||||
|
skill.addTeacher( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSkills(Set<Skill> skills) {
|
||||||
|
this.skills = skills;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity(name = "Student")
|
||||||
|
public static class Student {
|
||||||
|
|
||||||
|
private Integer id;
|
||||||
|
|
||||||
|
private Teacher teacher;
|
||||||
|
|
||||||
|
@Id
|
||||||
|
@Column(name = "student_id")
|
||||||
|
@GeneratedValue
|
||||||
|
public Integer getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Integer id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ManyToOne(optional = false)
|
||||||
|
@JoinColumn(name = "teacher_fk_id")
|
||||||
|
public Teacher getTeacher() {
|
||||||
|
return teacher;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTeacher(Teacher teacher) {
|
||||||
|
this.teacher = teacher;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int countNumberOfJoins(String query) {
|
||||||
|
return query.toLowerCase( Locale.ROOT ).split( " join ", -1 ).length - 1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ package org.hibernate.test.multitenancy.discriminator;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.hibernate.MultiTenancyStrategy;
|
import org.hibernate.MultiTenancyStrategy;
|
||||||
|
@ -108,6 +109,7 @@ public class DiscriminatorMultiTenancyTest extends BaseUnitTestCase {
|
||||||
|
|
||||||
final SessionFactoryBuilder sfb = metadata.getSessionFactoryBuilder();
|
final SessionFactoryBuilder sfb = metadata.getSessionFactoryBuilder();
|
||||||
sessionFactory = (SessionFactoryImplementor) sfb.build();
|
sessionFactory = (SessionFactoryImplementor) sfb.build();
|
||||||
|
currentTenantResolver.setHibernateBooted();
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -181,9 +183,19 @@ public class DiscriminatorMultiTenancyTest extends BaseUnitTestCase {
|
||||||
|
|
||||||
private static class TestCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver {
|
private static class TestCurrentTenantIdentifierResolver implements CurrentTenantIdentifierResolver {
|
||||||
private String currentTenantIdentifier;
|
private String currentTenantIdentifier;
|
||||||
|
private final AtomicBoolean postBoot = new AtomicBoolean(false);
|
||||||
|
|
||||||
|
public void setHibernateBooted() {
|
||||||
|
postBoot.set( true );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String resolveCurrentTenantIdentifier() {
|
public String resolveCurrentTenantIdentifier() {
|
||||||
|
if ( postBoot.get() == false ) {
|
||||||
|
//Check to prevent any optimisation which might want to cache the tenantId too early during bootstrap:
|
||||||
|
//it's a common use case to want to provide the tenantId, for example, via a ThreadLocal.
|
||||||
|
throw new IllegalStateException( "Not booted yet: illegal to try reading the tenant Id at this point!" );
|
||||||
|
}
|
||||||
return currentTenantIdentifier;
|
return currentTenantIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue