Skip to content

Commit

Permalink
HSEARCH-1383 Don't accept interfaces and allow mapped superclasses
Browse files Browse the repository at this point in the history
  • Loading branch information
marko-bekhta committed Apr 13, 2023
1 parent ce972ea commit 9e45282
Show file tree
Hide file tree
Showing 7 changed files with 322 additions and 9 deletions.
Expand Up @@ -14,9 +14,11 @@
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.ManyToOne;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToMany;

import org.hibernate.search.mapper.orm.Search;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.DocumentId;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.GenericField;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.Indexed;
import org.hibernate.search.mapper.pojo.mapping.definition.annotation.IndexedEmbedded;
Expand Down Expand Up @@ -58,10 +60,12 @@ public void setup(OrmSetupHelper.SetupContext setupContext) {
.expectSchema( EntityA.INDEX, b -> b.field( "indexedField", String.class ) )
.expectSchema( Entity1A.INDEX, b -> b.field( "indexedField", String.class ) )
.expectSchema( Entity2A.INDEX, b -> b.field( "indexedField", String.class ) )
.expectSchema( Entity1B.INDEX, b -> b.field( "indexedField", String.class ) );
.expectSchema( Entity1B.INDEX, b -> b.field( "indexedField", String.class ) )
.expectSchema( EntityFromSuperclass.INDEX, b -> b.field( "indexedField", String.class ) );

setupContext.withAnnotatedTypes( IndexedEntity.class, OtherIndexedEntity.class, ContainedEntity.class,
EntityA.class, Entity1A.class, Entity1B.class, Entity2A.class
EntityA.class, Entity1A.class, Entity1B.class, Entity2A.class, EntityFromSuperclass.class, SuperClass.class,
SimpleNotIndexedEntity.class, NotIndexedEntityFromSuperclass.class
);
}

Expand Down Expand Up @@ -322,4 +326,111 @@ public Entity2A(Integer id, String indexedField) {
super( id, indexedField );
}
}


@MappedSuperclass
public static class SuperClass {
@Id
private Integer id;

@Basic
@GenericField
private String indexedField;

public SuperClass() {
}

public SuperClass(Integer id, String indexedField) {
this.id = id;
this.indexedField = indexedField;
}
}

public interface InterfaceA {
}

public interface InterfaceB {
}

@Entity(name = EntityFromSuperclass.INDEX)
@Indexed
public static class EntityFromSuperclass extends SuperClass implements InterfaceA, InterfaceB {
static final String INDEX = "EntityFromSuperclass";

public EntityFromSuperclass() {
}

public EntityFromSuperclass(Integer id, String indexedField) {
super( id, indexedField );
}
}

@Entity(name = NotIndexedEntityFromSuperclass.INDEX)
public static class NotIndexedEntityFromSuperclass extends SuperClass implements InterfaceA, InterfaceB {
static final String INDEX = "NotIndexedEntityFromSuperclass";

public NotIndexedEntityFromSuperclass() {
}

public NotIndexedEntityFromSuperclass(Integer id, String indexedField) {
super( id, indexedField );
}
}

@Entity
public static class SimpleNotIndexedEntity {
@Id
private Integer id;

@Basic
@GenericField
private String indexedField;

public SimpleNotIndexedEntity() {
}

public SimpleNotIndexedEntity(Integer id, String indexedField) {
this.id = id;
this.indexedField = indexedField;
}
}

public static class NotAnEntity {
}

@Indexed(index = IndexedNotAnEntity.INDEX)
public static class IndexedNotAnEntity {
static final String INDEX = "IndexedNotAnEntity";

@DocumentId
private Integer id;

@GenericField
private String indexedField;

public IndexedNotAnEntity() {
}

public IndexedNotAnEntity(Integer id, String indexedField) {
this.id = id;
this.indexedField = indexedField;
}

public Integer getId() {
return id;
}

public void setId(Integer id) {
this.id = id;
}

public String getIndexedField() {
return indexedField;
}

public void setIndexedField(String indexedField) {
this.indexedField = indexedField;
}
}

}
Expand Up @@ -319,4 +319,143 @@ public void applicationFilterExcludeSessionInclude() {
} );
backendMock.verifyExpectationsMet();
}

@Test
public void filterByMappedSuperclass() {
setupHolder.runInTransaction( session -> {
Search.session( session ).automaticIndexingFilter( ctx -> ctx.exclude( SuperClass.class ) );

session.persist( new EntityFromSuperclass( 100, "test" ) );
} );
backendMock.verifyExpectationsMet();
}

@Test
public void filterByNotIndexedEntity() {
setupHolder.runInTransaction( session -> {
assertThatThrownBy( () ->
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( SimpleNotIndexedEntity.class )
)
).isInstanceOf( SearchException.class )
.hasMessageContainingAll(
"No matching entity type for class",
SimpleNotIndexedEntity.class.getName(),
"Either this class is not an entity type, or the entity type is not mapped in Hibernate Search"
);
} );
}

@Test
public void filterByRandomClass() {
setupHolder.runInTransaction( session -> {
assertThatThrownBy( () ->
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( NotAnEntity.class )
)
).isInstanceOf( SearchException.class )
.hasMessageContainingAll(
"No matching entity type for class",
NotAnEntity.class.getName(),
"Either this class is not an entity type, or the entity type is not mapped in Hibernate Search"
);
} );
}

@Test
public void filterByNotIndexedEntityFormSupertypeWithIndexedSubtype() {
setupHolder.runInTransaction( session -> {
assertThatThrownBy( () ->
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( NotIndexedEntityFromSuperclass.class )
)
).isInstanceOf( SearchException.class )
.hasMessageContainingAll(
"No matching entity type for class",
NotIndexedEntityFromSuperclass.class.getName(),
"Either this class is not an entity type, or the entity type is not mapped in Hibernate Search"
);
} );
}

@Test
public void filterByIndexedTypeNotAnEntity() {
setupHolder.runInTransaction( session -> {
assertThatThrownBy( () ->
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( IndexedNotAnEntity.class )
)
).isInstanceOf( SearchException.class )
.hasMessageContainingAll(
"No matching entity type for class",
IndexedNotAnEntity.class.getName(),
"Either this class is not an entity type, or the entity type is not mapped in Hibernate Search"
);
} );
}

@Test
public void filterByContainedEntityWontAffectContainingOnes() {
setupHolder.runInTransaction( session -> {
// to prepare data we ignore containing/indexed entity
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( IndexedEntity.class )
);

IndexedEntity entity1 = new IndexedEntity();
entity1.setId( 1 );
entity1.setIndexedField( "initialValue" );

ContainedEntity entity2 = new ContainedEntity();
entity2.setId( 100 );
entity2.setIndexedField( "initialValue" );

entity2.setContainingAsIndexedEmbedded( entity1 );
entity1.setContainedIndexedEmbedded( Arrays.asList( entity2 ) );

session.persist( entity1 );
session.persist( entity2 );

} );
backendMock.verifyExpectationsMet();

setupHolder.runInTransaction( session -> {
// now disable contained entity to not produce updates on containing:
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( ContainedEntity.class )
);
ContainedEntity entity1 = session.get( ContainedEntity.class, 100 );
entity1.setIndexedField( "updatedValue" );
} );

setupHolder.runInTransaction( session -> {
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( IndexedEntity.class )
);
IndexedEntity entity1 = session.get( IndexedEntity.class, 1 );

entity1.getContainedIndexedEmbedded().forEach( e -> e.setContainingAsIndexedEmbedded( null ) );

session.remove( entity1 );

} );
backendMock.verifyExpectationsMet();
}

@Test
public void filterByInterfaceMustFail() {
setupHolder.runInTransaction( session -> {
assertThatThrownBy( () ->
Search.session( session ).automaticIndexingFilter(
ctx -> ctx.exclude( InterfaceA.class )
)
).isInstanceOf( SearchException.class )
.hasMessageContainingAll(
"No matching entity type for class",
InterfaceA.class.getName(),
"Either this class is not an entity type, or the entity type is not mapped in Hibernate Search.",
"Valid classes for mapped entity types are: "
);
} );
}
}
Expand Up @@ -13,7 +13,7 @@
import java.util.Set;

import org.hibernate.search.mapper.orm.logging.impl.Log;
import org.hibernate.search.mapper.orm.session.impl.HibernateOrmSessionTypeContextProvider;
import org.hibernate.search.mapper.orm.session.impl.HibernateOrmAutomaticIndexingTypeFilterTypeContextProvider;
import org.hibernate.search.mapper.pojo.automaticindexing.filter.PojoAutomaticIndexingTypeFilterContext;
import org.hibernate.search.mapper.pojo.automaticindexing.filter.spi.PojoAutomaticIndexingTypeFilterHolder;
import org.hibernate.search.mapper.pojo.model.spi.PojoRawTypeIdentifier;
Expand All @@ -24,12 +24,13 @@ public class HibernateOrmAutomaticIndexingTypeFilterContext implements PojoAutom

private static final Log log = LoggerFactory.make( Log.class, MethodHandles.lookup() );

private final HibernateOrmSessionTypeContextProvider contextProvider;
private final HibernateOrmAutomaticIndexingTypeFilterTypeContextProvider contextProvider;

private final Set<PojoRawTypeIdentifier<?>> includes = new HashSet<>();
private final Set<PojoRawTypeIdentifier<?>> excludes = new HashSet<>();

public HibernateOrmAutomaticIndexingTypeFilterContext(HibernateOrmSessionTypeContextProvider typeManager) {
public HibernateOrmAutomaticIndexingTypeFilterContext(
HibernateOrmAutomaticIndexingTypeFilterTypeContextProvider typeManager) {
this.contextProvider = typeManager;
}

Expand All @@ -46,7 +47,7 @@ public PojoAutomaticIndexingTypeFilterContext include(String name) {
@Override
public PojoAutomaticIndexingTypeFilterContext include(Class<?> clazz) {
addIfNotPresentInOther(
contextProvider.typeIdentifierResolver().resolveByJavaClass( clazz ),
contextProvider.indexedWithSuperTypesByExactClass().getOrFail( clazz ),
includes,
excludes
);
Expand All @@ -66,7 +67,7 @@ public PojoAutomaticIndexingTypeFilterContext exclude(String name) {
@Override
public PojoAutomaticIndexingTypeFilterContext exclude(Class<?> clazz) {
addIfNotPresentInOther(
contextProvider.typeIdentifierResolver().resolveByJavaClass( clazz ),
contextProvider.indexedWithSuperTypesByExactClass().getOrFail( clazz ),
excludes,
includes
);
Expand Down
Expand Up @@ -44,6 +44,7 @@ class HibernateOrmTypeContextContainer
private final KeyValueProvider<String, AbstractHibernateOrmTypeContext<?>> byHibernateOrmEntityName;
private final KeyValueProvider<String, AbstractHibernateOrmTypeContext<?>> byJpaEntityName;
private final KeyValueProvider<String, HibernateOrmIndexedTypeContext<?>> indexedByJpaEntityName;
private final KeyValueProvider<Class<?>, PojoRawTypeIdentifier<?>> indexedWithSuperTypesByExactClass;

private HibernateOrmTypeContextContainer(Builder builder, SessionFactoryImplementor sessionFactory) {
this.typeIdentifierResolver = builder.basicTypeMetadataProvider.getTypeIdentifierResolver();
Expand All @@ -57,6 +58,7 @@ private HibernateOrmTypeContextContainer(Builder builder, SessionFactoryImplemen
Map<String, AbstractHibernateOrmTypeContext<?>> byJpaEntityNameContent = new LinkedHashMap<>();
Map<String, HibernateOrmIndexedTypeContext<?>> indexedByJpaEntityNameContent = new LinkedHashMap<>();
Map<String, AbstractHibernateOrmTypeContext<?>> byHibernateOrmEntityNameContent = new LinkedHashMap<>();
Map<Class<?>, PojoRawTypeIdentifier<?>> indexedWithSuperTypesByExactClassContent = new LinkedHashMap<>();
for ( HibernateOrmIndexedTypeContext.Builder<?> contextBuilder : builder.indexedTypeContextBuilders ) {
HibernateOrmIndexedTypeContext<?> typeContext = contextBuilder.build( sessionFactory );
PojoRawTypeIdentifier<?> typeIdentifier = typeContext.typeIdentifier();
Expand All @@ -80,6 +82,12 @@ private HibernateOrmTypeContextContainer(Builder builder, SessionFactoryImplemen
indexedByJpaEntityNameContent.put( typeContext.jpaEntityName(), typeContext );

byHibernateOrmEntityNameContent.put( typeContext.hibernateOrmEntityName(), typeContext );

for ( PojoRawTypeIdentifier<?> superType : typeContext.ascendingSuperTypes() ) {
if ( isManagedType( sessionFactory, superType.javaClass() ) ) {
indexedWithSuperTypesByExactClassContent.put( superType.javaClass(), superType );
}
}
}
for ( HibernateOrmContainedTypeContext.Builder<?> contextBuilder : builder.containedTypeContextBuilders ) {
HibernateOrmContainedTypeContext<?> typeContext = contextBuilder.build( sessionFactory );
Expand All @@ -99,6 +107,12 @@ private HibernateOrmTypeContextContainer(Builder builder, SessionFactoryImplemen
byJpaEntityNameContent.put( typeContext.jpaEntityName(), typeContext );

byHibernateOrmEntityNameContent.put( typeContext.hibernateOrmEntityName(), typeContext );

for ( PojoRawTypeIdentifier<?> superType : typeContext.ascendingSuperTypes() ) {
if ( isManagedType( sessionFactory, superType.javaClass() ) ) {
indexedWithSuperTypesByExactClassContent.put( superType.javaClass(), superType );
}
}
}
this.byTypeIdentifier = new KeyValueProvider<>( byTypeIdentifierContent, log::unknownTypeIdentifierForMappedEntityType );
this.indexedByTypeIdentifier = new KeyValueProvider<>( indexedByTypeIdentifierContent, log::unknownTypeIdentifierForIndexedEntityType );
Expand All @@ -109,6 +123,17 @@ private HibernateOrmTypeContextContainer(Builder builder, SessionFactoryImplemen
this.byJpaEntityName = new KeyValueProvider<>( byJpaEntityNameContent, log::unknownJpaEntityNameForMappedEntityType );
this.indexedByJpaEntityName = new KeyValueProvider<>( indexedByJpaEntityNameContent, log::unknownJpaEntityNameForIndexedEntityType );
this.byHibernateOrmEntityName = new KeyValueProvider<>( byHibernateOrmEntityNameContent, log::unknownHibernateOrmEntityNameForMappedEntityType );
this.indexedWithSuperTypesByExactClass = new KeyValueProvider<>( indexedWithSuperTypesByExactClassContent, log::unknownClassForMappedEntityType );
}

private boolean isManagedType(SessionFactoryImplementor sessionFactory, Class<?> javaClass) {
try {
sessionFactory.getMetamodel().managedType( javaClass );
}
catch (Exception e) {
return false;
}
return true;
}

@Override
Expand Down Expand Up @@ -162,6 +187,10 @@ public KeyValueProvider<String, AbstractHibernateOrmTypeContext<?>> byHibernateO
return byHibernateOrmEntityName;
}

public KeyValueProvider<Class<?>, PojoRawTypeIdentifier<?>> indexedWithSuperTypesByExactClass() {
return indexedWithSuperTypesByExactClass;
}

Collection<? extends HibernateOrmIndexedTypeContext<?>> allIndexed() {
return indexedByTypeIdentifier.values();
}
Expand Down

0 comments on commit 9e45282

Please sign in to comment.