Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.hibernate.boot.internal.ClassLoaderAccessImpl;
import org.hibernate.boot.registry.classloading.spi.ClassLoaderService;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.event.spi.PreCollectionUpdateEvent;
import org.hibernate.event.spi.PreCollectionUpdateEventListener;
import org.hibernate.event.spi.PreDeleteEvent;
Expand Down Expand Up @@ -59,8 +60,9 @@ public class BeanValidationEventListener
private Validator validator;
private GroupsPerOperation groupsPerOperation;

public BeanValidationEventListener(
ValidatorFactory factory, Map<String, Object> settings, ClassLoaderService classLoaderService) {
private SessionFactoryImplementor sessionFactory;

public BeanValidationEventListener(ValidatorFactory factory, Map<String, Object> settings, ClassLoaderService classLoaderService) {
traversableResolver = new HibernateTraversableResolver();
validator = factory.usingContext()
.traversableResolver( traversableResolver )
Expand All @@ -70,17 +72,15 @@ public BeanValidationEventListener(

@Override
public void sessionFactoryCreated(SessionFactory factory) {
SessionFactoryImplementor implementor = factory.unwrap( SessionFactoryImplementor.class );
implementor
.getMappingMetamodel()
.forEachEntityDescriptor( entityPersister -> traversableResolver.addPersister( entityPersister, implementor ) );
sessionFactory = factory.unwrap( SessionFactoryImplementor.class );
sessionFactory.getMappingMetamodel()
.forEachEntityDescriptor( entityPersister -> traversableResolver.addPersister( entityPersister, sessionFactory ) );
}

public boolean onPreInsert(PreInsertEvent event) {
validate(
event.getEntity(),
event.getPersister(),
event.getFactory(),
GroupsPerOperation.Operation.INSERT
);
return false;
Expand All @@ -90,7 +90,6 @@ public boolean onPreUpdate(PreUpdateEvent event) {
validate(
event.getEntity(),
event.getPersister(),
event.getFactory(),
GroupsPerOperation.Operation.UPDATE
);
return false;
Expand All @@ -100,7 +99,6 @@ public boolean onPreDelete(PreDeleteEvent event) {
validate(
event.getEntity(),
event.getPersister(),
event.getFactory(),
GroupsPerOperation.Operation.DELETE
);
return false;
Expand All @@ -111,7 +109,6 @@ public boolean onPreUpsert(PreUpsertEvent event) {
validate(
event.getEntity(),
event.getPersister(),
event.getFactory(),
GroupsPerOperation.Operation.UPSERT
);
return false;
Expand All @@ -122,16 +119,24 @@ public void onPreUpdateCollection(PreCollectionUpdateEvent event) {
final Object entity = castNonNull( event.getCollection().getOwner() );
validate(
entity,
event.getSession().getEntityPersister( event.getAffectedOwnerEntityName(), entity ),
event.getFactory(),
getEntityPersister( event.getSession(), event.getAffectedOwnerEntityName(), entity ),
GroupsPerOperation.Operation.UPDATE
);
}

private EntityPersister getEntityPersister(SharedSessionContractImplementor session, String entityName, Object entity) {
if ( session != null ) {
return session.getEntityPersister( entityName, entity );
}
return entityName == null
? sessionFactory.getMappingMetamodel().getEntityDescriptor( entity.getClass().getName() )
: sessionFactory.getMappingMetamodel().getEntityDescriptor( entityName )
.getSubclassEntityPersister( entity, sessionFactory );
}

private <T> void validate(
T object,
EntityPersister persister,
SessionFactoryImplementor sessionFactory,
GroupsPerOperation.Operation operation) {
if ( object == null || persister.getRepresentationStrategy().getMode() != RepresentationMode.POJO ) {
return;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* SPDX-License-Identifier: Apache-2.0
* Copyright Red Hat Inc. and Hibernate Authors
*/
package org.hibernate.orm.test.annotations.beanvalidation;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.validation.ConstraintViolationException;
import jakarta.validation.constraints.Size;
import org.hibernate.cfg.AvailableSettings;
import org.hibernate.testing.orm.junit.DomainModel;
import org.hibernate.testing.orm.junit.Jira;
import org.hibernate.testing.orm.junit.ServiceRegistry;
import org.hibernate.testing.orm.junit.SessionFactory;
import org.hibernate.testing.orm.junit.SessionFactoryScope;
import org.hibernate.testing.orm.junit.Setting;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertThrows;

@SessionFactory
@DomainModel(annotatedClasses = {
CollectionActionsValidationStatelessTest.Author.class,
CollectionActionsValidationStatelessTest.Book.class,
})
@ServiceRegistry(settings = @Setting(name = AvailableSettings.JAKARTA_VALIDATION_MODE, value = "auto"))
@Jira("https://hibernate.atlassian.net/browse/HHH-19843")
public class CollectionActionsValidationStatelessTest {

@Test
void smoke(SessionFactoryScope scope) {
scope.inStatelessTransaction( session -> {
final ConstraintViolationException e = assertThrows( ConstraintViolationException.class, () -> {
ArrayList<Book> books = new ArrayList<>();
Author author = new Author( 1L, "first", "last", books );
Book book = new Book( 10L, "", author );
books.add( book );

session.upsertMultiple( List.of( author ) );
} );
assertThat( e.getConstraintViolations() ).hasSize( 1 );
} );
}

@AfterEach
public void tearDown(SessionFactoryScope scope) {
scope.getSessionFactory().getSchemaManager().truncate();
}

@Table(name = "author")
@Entity
static class Author {

public Author() {
}

public Author(long id, String firstName, String lastName, List<Book> books) {
this.firstName = firstName;
this.lastName = lastName;
this.books = books;
this.id = id;
}

@Id
Long id;

String firstName;

String lastName;

@OneToMany
@JoinColumn(name = "bookId")
@Size(min = 10)
List<Book> books;

}

@Table(name = "book")
@Entity
static class Book {

public Book() {
}

public Book(long id, String title, Author author) {
this.id = id;
this.title = title;
this.author = author;
}

@Id
Long id;

String title;

@ManyToOne
Author author;
}
}
Loading