package io.dropwizard.hibernate; import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.collect.ImmutableList; import io.dropwizard.Application; import io.dropwizard.Configuration; import io.dropwizard.db.DataSourceFactory; import io.dropwizard.db.PooledDataSourceFactory; import io.dropwizard.jersey.errors.ErrorMessage; import io.dropwizard.setup.Bootstrap; import io.dropwizard.setup.Environment; import io.dropwizard.testing.ConfigOverride; import io.dropwizard.testing.DropwizardTestSupport; import io.dropwizard.testing.ResourceHelpers; import org.glassfish.jersey.client.JerseyClientBuilder; import org.hibernate.FlushMode; import org.hibernate.HibernateException; import org.hibernate.Session; import org.hibernate.SessionFactory; import org.hibernate.Transaction; import org.hibernate.exception.ConstraintViolationException; import org.junit.After; import org.junit.Test; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.PUT; import javax.ws.rs.client.Client; import javax.ws.rs.client.Entity; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.ext.ExceptionMapper; import java.util.Optional; import static java.util.Objects.requireNonNull; import static org.assertj.core.api.Assertions.assertThat; public class LazyLoadingTest { public static class TestConfiguration extends Configuration { DataSourceFactory dataSource = new DataSourceFactory(); TestConfiguration(@JsonProperty("dataSource") DataSourceFactory dataSource) { this.dataSource = dataSource; } } public static class TestApplication extends io.dropwizard.Application<TestConfiguration> { final HibernateBundle<TestConfiguration> hibernate = new HibernateBundle<TestConfiguration>( ImmutableList.of(Person.class, Dog.class), new SessionFactoryFactory()) { @Override public PooledDataSourceFactory getDataSourceFactory(TestConfiguration configuration) { return configuration.dataSource; } }; @Override public void initialize(Bootstrap<TestConfiguration> bootstrap) { bootstrap.addBundle(hibernate); } @Override public void run(TestConfiguration configuration, Environment environment) throws Exception { final SessionFactory sessionFactory = hibernate.getSessionFactory(); initDatabase(sessionFactory); environment.jersey().register(new UnitOfWorkApplicationListener("hr-db", sessionFactory)); environment.jersey().register(new DogResource(new DogDAO(sessionFactory))); environment.jersey().register(new PersistenceExceptionMapper()); environment.jersey().register(new ConstraintViolationExceptionMapper()); } private void initDatabase(SessionFactory sessionFactory) { try (Session session = sessionFactory.openSession()) { Transaction transaction = session.beginTransaction(); session.createNativeQuery( "CREATE TABLE people (name varchar(100) primary key, email varchar(16), birthday timestamp with time zone)") .executeUpdate(); session.createNativeQuery( "INSERT INTO people VALUES ('Coda', 'coda@example.com', '1979-01-02 00:22:00+0:00')") .executeUpdate(); session.createNativeQuery( "CREATE TABLE dogs (name varchar(100) primary key, owner varchar(100), CONSTRAINT fk_owner FOREIGN KEY (owner) REFERENCES people(name))") .executeUpdate(); session.createNativeQuery( "INSERT INTO dogs VALUES ('Raf', 'Coda')") .executeUpdate(); transaction.commit(); } } } public static class TestApplicationWithDisabledLazyLoading extends TestApplication { @Override public void initialize(Bootstrap<TestConfiguration> bootstrap) { hibernate.setLazyLoadingEnabled(false); bootstrap.addBundle(hibernate); } } public static class DogDAO extends AbstractDAO<Dog> { DogDAO(SessionFactory sessionFactory) { super(sessionFactory); } Optional<Dog> findByName(String name) { return Optional.ofNullable(get(name)); } Dog create(Dog dog) throws HibernateException { currentSession().setHibernateFlushMode(FlushMode.COMMIT); currentSession().save(requireNonNull(dog)); return dog; } } @Path("/dogs/{name}") @Produces(MediaType.APPLICATION_JSON) public static class DogResource { private final DogDAO dao; DogResource(DogDAO dao) { this.dao = dao; } @GET @UnitOfWork(readOnly = true) public Optional<Dog> find(@PathParam("name") String name) { return dao.findByName(name); } @PUT @UnitOfWork public void create(Dog dog) { dao.create(dog); } } public static class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException> { @Override public Response toResponse(ConstraintViolationException e) { return Response.status(Response.Status.BAD_REQUEST) .entity(new ErrorMessage(Response.Status.BAD_REQUEST.getStatusCode(), e.getCause().getMessage())) .build(); } } private DropwizardTestSupport dropwizardTestSupport; private Client client = new JerseyClientBuilder().build(); public void setup(Class<? extends Application<TestConfiguration>> applicationClass) { dropwizardTestSupport = new DropwizardTestSupport<>(applicationClass, ResourceHelpers.resourceFilePath("hibernate-integration-test.yaml"), ConfigOverride.config("dataSource.url", "jdbc:hsqldb:mem:DbTest" + System.nanoTime() + "?hsqldb.translate_dti_types=false")); dropwizardTestSupport.before(); } @After public void tearDown() { dropwizardTestSupport.after(); client.close(); } private String getUrlPrefix() { return "http://localhost:" + dropwizardTestSupport.getLocalPort(); } @Test public void serialisesLazyObjectWhenEnabled() throws Exception { setup(TestApplication.class); final Dog raf = client.target(getUrlPrefix() + "/dogs/Raf").request(MediaType.APPLICATION_JSON).get(Dog.class); assertThat(raf.getName()) .isEqualTo("Raf"); assertThat(raf.getOwner()) .isNotNull(); assertThat(raf.getOwner().getName()) .isEqualTo("Coda"); } @Test public void sendsNullWhenDisabled() throws Exception { setup(TestApplicationWithDisabledLazyLoading.class); final Dog raf = client.target(getUrlPrefix() + "/dogs/Raf").request(MediaType.APPLICATION_JSON).get(Dog.class); assertThat(raf.getName()) .isEqualTo("Raf"); assertThat(raf.getOwner()) .isNull(); } @Test public void returnsErrorsWhenEnabled() throws Exception { setup(TestApplication.class); final Dog raf = new Dog(); raf.setName("Raf"); // Raf already exists so this should cause a primary key constraint violation final Response response = client.target(getUrlPrefix() + "/dogs/Raf").request().put(Entity.entity(raf, MediaType.APPLICATION_JSON)); assertThat(response.getStatusInfo()).isEqualTo(Response.Status.BAD_REQUEST); assertThat(response.getHeaderString(HttpHeaders.CONTENT_TYPE)).isEqualTo(MediaType.APPLICATION_JSON); assertThat(response.readEntity(ErrorMessage.class).getMessage()).contains("unique constraint", "table: DOGS"); } }