package com.github.marschall.threeten.jpa; import static com.github.marschall.threeten.jpa.Constants.PERSISTENCE_UNIT_NAME; import static java.util.Collections.singletonMap; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.hasSize; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertThat; import static org.junit.Assume.assumeThat; import java.math.BigInteger; import java.sql.Connection; import java.sql.SQLException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; import java.time.ZoneId; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Map; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.Query; import javax.sql.DataSource; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; import org.springframework.core.env.MutablePropertySources; import org.springframework.jdbc.core.JdbcOperations; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.init.DatabasePopulator; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionTemplate; import com.github.marschall.threeten.jpa.configuration.DerbyConfiguration; import com.github.marschall.threeten.jpa.configuration.EclipseLinkConfiguration; import com.github.marschall.threeten.jpa.configuration.H2Configuration; import com.github.marschall.threeten.jpa.configuration.HibernateConfiguration; import com.github.marschall.threeten.jpa.configuration.HsqlConfiguration; import com.github.marschall.threeten.jpa.configuration.MysqlConfiguration; import com.github.marschall.threeten.jpa.configuration.PostgresConfiguration; import com.github.marschall.threeten.jpa.configuration.TransactionManagerConfiguration; @RunWith(Parameterized.class) public class ConverterTest { private final Class<?> datasourceConfiguration; private final Class<?> jpaConfiguration; private final String persistenceUnitName; private AnnotationConfigApplicationContext applicationContext; private TransactionTemplate template; public ConverterTest(Class<?> datasourceConfiguration, Class<?> jpaConfiguration, String persistenceUnitName) { this.datasourceConfiguration = datasourceConfiguration; this.jpaConfiguration = jpaConfiguration; this.persistenceUnitName = persistenceUnitName; } @Parameters(name = "{2}") public static Collection<Object[]> parameters() { return Arrays.asList( new Object[]{DerbyConfiguration.class, EclipseLinkConfiguration.class, "threeten-jpa-eclipselink-derby"}, new Object[]{H2Configuration.class, EclipseLinkConfiguration.class, "threeten-jpa-eclipselink-h2"}, new Object[]{HsqlConfiguration.class, EclipseLinkConfiguration.class, "threeten-jpa-eclipselink-hsql"}, new Object[]{MysqlConfiguration.class, EclipseLinkConfiguration.class, "threeten-jpa-eclipselink-mysql"}, new Object[]{PostgresConfiguration.class, EclipseLinkConfiguration.class, "threeten-jpa-eclipselink-postgres"}, // new Object[]{SqlServerConfiguration.class, EclipseLinkConfiguration.class, "threeten-jpa-eclipselink-sqlserver"}, new Object[]{DerbyConfiguration.class, HibernateConfiguration.class, "threeten-jpa-hibernate-derby"}, new Object[]{H2Configuration.class, HibernateConfiguration.class, "threeten-jpa-hibernate-h2"}, new Object[]{HsqlConfiguration.class, HibernateConfiguration.class, "threeten-jpa-hibernate-hsql"}, new Object[]{MysqlConfiguration.class, HibernateConfiguration.class, "threeten-jpa-hibernate-mysql"}, // new Object[]{SqlServerConfiguration.class, HibernateConfiguration.class, "threeten-jpa-hibernate-sqlserver"}, new Object[]{PostgresConfiguration.class, HibernateConfiguration.class, "threeten-jpa-hibernate-postgres"} ); } @Before public void setUp() { this.applicationContext = new AnnotationConfigApplicationContext(); this.applicationContext.register(this.datasourceConfiguration, TransactionManagerConfiguration.class, this.jpaConfiguration); ConfigurableEnvironment environment = this.applicationContext.getEnvironment(); MutablePropertySources propertySources = environment.getPropertySources(); Map<String, Object> source = singletonMap(PERSISTENCE_UNIT_NAME, this.persistenceUnitName); propertySources.addFirst(new MapPropertySource("persistence unit name", source)); this.applicationContext.refresh(); PlatformTransactionManager txManager = this.applicationContext.getBean(PlatformTransactionManager.class); this.template = new TransactionTemplate(txManager); this.template.execute(status -> { Map<String, DatabasePopulator> beans = this.applicationContext.getBeansOfType(DatabasePopulator.class); DataSource dataSource = this.applicationContext.getBean(DataSource.class); try (Connection connection = dataSource.getConnection()) { for (DatabasePopulator populator : beans.values()) { populator.populate(connection); } } catch (SQLException e) { throw new RuntimeException("could initialize database", e); } return null; }); } @After public void tearDown() { this.applicationContext.close(); } private void mysqlHack() { // http://stackoverflow.com/questions/43560374/mysql-client-vs-server-time-zone if (this.persistenceUnitName.contains("mysql")) { DataSource dataSource = this.applicationContext.getBean(DataSource.class); JdbcOperations jdbcOperations = new JdbcTemplate(dataSource); String serverTimeZone = jdbcOperations.queryForObject("SELECT @@system_time_zone", String.class); String systemTimeZone = ZoneId.systemDefault().getId(); if (systemTimeZone.equals("Etc/UTC")) { systemTimeZone = "UTC"; } assumeThat(serverTimeZone, equalTo(systemTimeZone)); } } @Test public void runTest() { mysqlHack(); EntityManagerFactory factory = this.applicationContext.getBean(EntityManagerFactory.class); EntityManager entityManager = factory.createEntityManager(); try { // read the entity inserted by SQL this.template.execute((s) -> { Query query = entityManager.createQuery("SELECT t FROM JavaTime t"); List<?> resultList = query.getResultList(); assertThat(resultList, hasSize(1)); // validate the entity inserted by SQL JavaTime javaTime = (JavaTime) resultList.get(0); assertEquals(LocalTime.parse("15:09:02"), javaTime.getLocalTime()); assertEquals(LocalDate.parse("1988-12-25"), javaTime.getLocalDate()); assertEquals(LocalDateTime.parse("1980-01-01T23:03:20"), javaTime.getLocalDateTime()); return null; }); // insert a new entity into the database BigInteger newId = new BigInteger("2"); LocalTime newTime = LocalTime.now(); LocalDate newDate = LocalDate.now(); LocalDateTime newDateTime = LocalDateTime.now(); this.template.execute((s) -> { JavaTime toInsert = new JavaTime(); toInsert.setId(newId); toInsert.setLocalDate(newDate); toInsert.setLocalTime(newTime); toInsert.setLocalDateTime(newDateTime); entityManager.persist(toInsert); // the transaction should trigger a flush and write to the database return null; }); // validate the new entity inserted into the database this.template.execute((s) -> { JavaTime readBack = entityManager.find(JavaTime.class, newId); assertNotNull(readBack); assertEquals(newId, readBack.getId()); assertEquals(newTime, readBack.getLocalTime()); assertEquals(newDate, readBack.getLocalDate()); assertEquals(newDateTime, readBack.getLocalDateTime()); entityManager.remove(readBack); return null; }); } finally { entityManager.close(); // EntityManagerFactory should be closed by spring. } } }