From ffd5e73f12b68a89d0d62c0fff7e9973d3a49489 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Wed, 21 Aug 2024 16:12:44 -0400 Subject: [PATCH 01/17] start hacking --- hapi-fhir-jpaserver-test-r4/pom.xml | 12 ++ .../jpa/model/pkspike/JpaBindingTest.java | 116 ++++++++++++++++++ .../fhir/jpa/test/KeyTypeOverrideTest.java | 43 +++++++ .../fhir/test/utilities/LoggingExtension.java | 40 ++++-- 4 files changed, 200 insertions(+), 11 deletions(-) create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java create mode 100644 hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/KeyTypeOverrideTest.java diff --git a/hapi-fhir-jpaserver-test-r4/pom.xml b/hapi-fhir-jpaserver-test-r4/pom.xml index 548d7257155..f7c4e8cdd3b 100644 --- a/hapi-fhir-jpaserver-test-r4/pom.xml +++ b/hapi-fhir-jpaserver-test-r4/pom.xml @@ -22,6 +22,12 @@ ${project.version} test + + ca.uhn.hapi.fhir + hapi-fhir-jpa + ${project.version} + test + ca.uhn.hapi.fhir hapi-fhir-caching-testing @@ -49,6 +55,12 @@ jakarta.servlet-api test + + org.hibernate + hibernate-testing + 6.5.2.Final + test + diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java new file mode 100644 index 00000000000..42df7a6b607 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java @@ -0,0 +1,116 @@ +package ca.uhn.fhir.jpa.model.pkspike; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; +import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; +import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; +import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect; +import jakarta.annotation.Nonnull; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManagerFactory; +import org.apache.commons.dbcp2.BasicDataSource; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import javax.sql.DataSource; +import java.sql.SQLException; +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Spike to assess variable binding against a db. + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + JpaBindingTest.Config.class, FhirContextR4Config.class +}) +public class JpaBindingTest { + private static final Logger ourLog = LoggerFactory.getLogger(JpaBindingTest.class); + + @Configuration + static class Config { + + @Inject + FhirContext myFhirContext; + + @Bean + DataSource datasource() { + BasicDataSource dataSource = new BasicDataSource(); + dataSource.setDriver(new org.h2.Driver()); + dataSource.setUrl("jdbc:h2:mem:testdb_r4"); + dataSource.setMaxWaitMillis(30000); + dataSource.setUsername(""); + dataSource.setPassword(""); + dataSource.setMaxTotal(10); + + return dataSource; + } + + + @Bean + public HapiFhirLocalContainerEntityManagerFactoryBean entityManagerFactory( +// ModuleMigrationMetadata theModuleMigrationMetadata, + ConfigurableListableBeanFactory theConfigurableListableBeanFactory, + DataSource theDataSource) { + HapiFhirLocalContainerEntityManagerFactoryBean retVal = + new HapiFhirLocalContainerEntityManagerFactoryBean(theConfigurableListableBeanFactory); + + HapiEntityManagerFactoryUtil.configureEntityManagerFactory(retVal, myFhirContext, storageSettings()); + Properties jpaProperties = new Properties(); + jpaProperties.put("hibernate.search.enabled", "false"); + jpaProperties.put("hibernate.format_sql", "false"); + jpaProperties.put("hibernate.show_sql", "false"); + jpaProperties.put("hibernate.hbm2ddl.auto", "update"); + jpaProperties.put("hibernate.dialect", HapiFhirH2Dialect.class.getName()); + + retVal.setPersistenceUnitName("HapiPU"); + retVal.setDataSource(theDataSource); + //retVal.setManagedTypes(PersistenceManagedTypes.of(ResourceTable.class.getName())); + + retVal.setJpaProperties(jpaProperties); + + return retVal; + } + + @Bean + @Nonnull JpaStorageSettings storageSettings() { + JpaStorageSettings jpaStorageSettings = new JpaStorageSettings(); + //jpaStorageSettings.setAdvancedHSearchIndexing(); + return jpaStorageSettings; + } + } + + @Inject + DataSource myDataSource; + + @Inject + EntityManagerFactory myEntityManagerFactory; + + @Test + void createResourceTable_roundTrip() throws SQLException { + ourLog.info("starting"); + // given + + var t = new JdbcTemplate(myDataSource); + + t.execute(" create table table_a (col_a numeric, col_b varchar)"); + int rows = t.update("insert into table_a values (?, ?)", 22, "foo"); + assertEquals(1, rows); + + ourLog.info("done"); + + try(var em = myEntityManagerFactory.createEntityManager()) { + // empty + } + } +} diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/KeyTypeOverrideTest.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/KeyTypeOverrideTest.java new file mode 100644 index 00000000000..b32a9c0872b --- /dev/null +++ b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/KeyTypeOverrideTest.java @@ -0,0 +1,43 @@ +package ca.uhn.fhir.jpa.test; + +import ca.uhn.fhir.jpa.test.config.TestHSearchAddInConfig; +import ca.uhn.fhir.jpa.test.config.TestR4Config; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManagerFactory; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.transaction.support.TransactionTemplate; + +import static org.junit.jupiter.api.Assertions.fail; + +@ContextConfiguration(classes = { + TestHSearchAddInConfig.NoFT.class +}) +public class KeyTypeOverrideTest extends BaseJpaR4Test { + private static final Logger ourLog = LoggerFactory.getLogger(KeyTypeOverrideTest.class); + + @Inject + EntityManagerFactory myEntityManagerFactory; + + @Test + void empty() { + // given + + try (var em = myEntityManagerFactory.createEntityManager()) { + newTxTemplate().execute((status)->{ + em.createQuery("from ResourceTable").getResultStream() + .forEach(e->{ + ourLog.info("found resource"); + }); + return null; + }); + } + + // when + // then + } + +} diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/LoggingExtension.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/LoggingExtension.java index de0630eeb66..532ef62a21e 100644 --- a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/LoggingExtension.java +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/LoggingExtension.java @@ -35,14 +35,15 @@ package ca.uhn.fhir.test.utilities; +import jakarta.annotation.Nonnull; import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.AfterTestExecutionCallback; import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.BeforeTestExecutionCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.text.MessageFormat; - /** * This JUnit rule generates log messages to delineate the start and finish of a JUnit test case and also to note any exceptions * that are thrown. @@ -50,17 +51,34 @@ import java.text.MessageFormat; * @author Brian Matthews * @version 1.0.0 */ -public class LoggingExtension implements BeforeEachCallback, AfterEachCallback { - - @Override - public void afterEach(ExtensionContext context) { - final Logger logger = LoggerFactory.getLogger(context.getTestClass().get()); - logger.info(MessageFormat.format("Finished test case [{0}]", context.getTestMethod().get().getName())); - } +public class LoggingExtension implements BeforeEachCallback, BeforeTestExecutionCallback, AfterEachCallback, AfterTestExecutionCallback { @Override public void beforeEach(ExtensionContext context) { - final Logger logger = LoggerFactory.getLogger(context.getTestClass().get()); - logger.info(MessageFormat.format("Starting test case [{0}]", context.getTestMethod().get().getName())); + getLoggerForTestClass(context).info("Starting setup for test case [{}]", getMethodName(context)); } + + @Override + public void beforeTestExecution(ExtensionContext context) throws Exception { + getLoggerForTestClass(context).info("Starting test case [{}]", getMethodName(context)); + + } + @Override + public void afterTestExecution(ExtensionContext context) throws Exception { + getLoggerForTestClass(context).info("Finished test case [{}]", getMethodName(context)); + } + + @Override + public void afterEach(ExtensionContext context) { + getLoggerForTestClass(context).info("Finished teardown for test case [{}]", getMethodName(context)); + } + + private static Logger getLoggerForTestClass(ExtensionContext context) { + return LoggerFactory.getLogger(context.getTestClass().orElseThrow()); + } + + private static @Nonnull String getMethodName(ExtensionContext context) { + return context.getTestMethod().orElseThrow().getName(); + } + } From dd326c335cfcd0f3c9c63f8f884391d6deaaa598 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Wed, 21 Aug 2024 17:45:22 -0400 Subject: [PATCH 02/17] start hacking --- .../uhn/fhir/jpa/model/pkspike/JPAConfig.java | 100 ++++++++++++ .../jpa/model/pkspike/JpaBindingTest.java | 144 +++++++++--------- .../fhir/jpa/model/pkspike/ResJoinEntity.java | 36 +++++ .../fhir/jpa/model/pkspike/ResRootEntity.java | 69 +++++++++ .../fhir/jpa/model/pkspike/SchemaInit.java | 14 ++ .../fhir/jpa/model/pkspike/package-info.java | 1 + 6 files changed, 288 insertions(+), 76 deletions(-) create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JPAConfig.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResJoinEntity.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaInit.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/package-info.java diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JPAConfig.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JPAConfig.java new file mode 100644 index 00000000000..61cdbf566e2 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JPAConfig.java @@ -0,0 +1,100 @@ +package ca.uhn.fhir.jpa.model.pkspike; + +import ca.uhn.fhir.context.FhirContext; +import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; +import ca.uhn.fhir.jpa.config.HapiFhirHibernateJpaDialect; +import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; +import ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect; +import jakarta.annotation.Nonnull; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManagerFactory; +import org.apache.commons.dbcp2.BasicDataSource; +import org.hibernate.jpa.HibernatePersistenceProvider; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.orm.jpa.JpaTransactionManager; +import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.sql.DataSource; +import java.time.Duration; +import java.util.Properties; + +@Configuration +class JPAConfig { + + @Inject + FhirContext myFhirContext; + + @Bean + DataSource datasource() { + BasicDataSource dataSource = new BasicDataSource(); + dataSource.setDriver(new org.h2.Driver()); + dataSource.setUrl("jdbc:h2:mem:testdb_r4"); + dataSource.setMaxWait(Duration.ofMillis(30000)); + dataSource.setUsername(""); + dataSource.setPassword(""); + dataSource.setMaxTotal(10); + + SchemaInit.initSchema(dataSource); + + return dataSource; + } + + + @Bean + public HapiFhirLocalContainerEntityManagerFactoryBean entityManagerFactory( +// ModuleMigrationMetadata theModuleMigrationMetadata, + ConfigurableListableBeanFactory theConfigurableListableBeanFactory, + DataSource theDataSource) { + HapiFhirLocalContainerEntityManagerFactoryBean retVal = + new HapiFhirLocalContainerEntityManagerFactoryBean(theConfigurableListableBeanFactory); + + retVal.setJpaDialect(new HapiFhirHibernateJpaDialect(myFhirContext.getLocalizer())); +// retVal.setPackagesToScan("ca.uhn.fhir.jpa.model.entity", "ca.uhn.fhir.jpa.entity"); + HibernatePersistenceProvider persistenceProvider = new HibernatePersistenceProvider(); + retVal.setPersistenceProvider(persistenceProvider); + Properties jpaProperties = new Properties(); + jpaProperties.put("hibernate.search.enabled", "false"); + jpaProperties.put("hibernate.format_sql", "false"); + jpaProperties.put("hibernate.show_sql", "false"); + jpaProperties.put("hibernate.integration.envers.enabled=false", "false"); + //jpaProperties.put("hibernate.hbm2ddl.auto", "update"); + jpaProperties.put("hibernate.dialect", HapiFhirH2Dialect.class.getName()); + retVal.setJpaProperties(jpaProperties); + + retVal.setPersistenceUnitName("HapiPU"); + retVal.setDataSource(theDataSource); + retVal.setManagedTypes(PersistenceManagedTypes.of( + ResRootEntity.class.getName(), + ResJoinEntity.class.getName() + )); + + retVal.setJpaProperties(jpaProperties); + + return retVal; + } + + @Bean + public PlatformTransactionManager transactionManager(EntityManagerFactory theEntityManagerFactory) { + JpaTransactionManager retVal = new JpaTransactionManager(); + retVal.setEntityManagerFactory(theEntityManagerFactory); + return retVal; + } + + + @Bean + public TransactionTemplate transactionTemplate(PlatformTransactionManager theTransactionManager) { + return new TransactionTemplate(theTransactionManager); + } + + @Bean + @Nonnull + JpaStorageSettings storageSettings() { + JpaStorageSettings jpaStorageSettings = new JpaStorageSettings(); + //jpaStorageSettings.setAdvancedHSearchIndexing(); + return jpaStorageSettings; + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java index 42df7a6b607..5a0fa489bc0 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java @@ -1,29 +1,24 @@ package ca.uhn.fhir.jpa.model.pkspike; -import ca.uhn.fhir.context.FhirContext; -import ca.uhn.fhir.jpa.api.config.JpaStorageSettings; -import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; -import ca.uhn.fhir.jpa.config.util.HapiEntityManagerFactoryUtil; -import ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect; import jakarta.annotation.Nonnull; import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; -import org.apache.commons.dbcp2.BasicDataSource; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.orm.jpa.EntityManagerFactoryUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.support.TransactionTemplate; import javax.sql.DataSource; -import java.sql.SQLException; -import java.util.Properties; +import java.util.Objects; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -32,85 +27,82 @@ import static org.junit.jupiter.api.Assertions.assertEquals; */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { - JpaBindingTest.Config.class, FhirContextR4Config.class + JPAConfig.class, FhirContextR4Config.class }) public class JpaBindingTest { private static final Logger ourLog = LoggerFactory.getLogger(JpaBindingTest.class); - @Configuration - static class Config { - - @Inject - FhirContext myFhirContext; - - @Bean - DataSource datasource() { - BasicDataSource dataSource = new BasicDataSource(); - dataSource.setDriver(new org.h2.Driver()); - dataSource.setUrl("jdbc:h2:mem:testdb_r4"); - dataSource.setMaxWaitMillis(30000); - dataSource.setUsername(""); - dataSource.setPassword(""); - dataSource.setMaxTotal(10); - - return dataSource; - } - - - @Bean - public HapiFhirLocalContainerEntityManagerFactoryBean entityManagerFactory( -// ModuleMigrationMetadata theModuleMigrationMetadata, - ConfigurableListableBeanFactory theConfigurableListableBeanFactory, - DataSource theDataSource) { - HapiFhirLocalContainerEntityManagerFactoryBean retVal = - new HapiFhirLocalContainerEntityManagerFactoryBean(theConfigurableListableBeanFactory); - - HapiEntityManagerFactoryUtil.configureEntityManagerFactory(retVal, myFhirContext, storageSettings()); - Properties jpaProperties = new Properties(); - jpaProperties.put("hibernate.search.enabled", "false"); - jpaProperties.put("hibernate.format_sql", "false"); - jpaProperties.put("hibernate.show_sql", "false"); - jpaProperties.put("hibernate.hbm2ddl.auto", "update"); - jpaProperties.put("hibernate.dialect", HapiFhirH2Dialect.class.getName()); - - retVal.setPersistenceUnitName("HapiPU"); - retVal.setDataSource(theDataSource); - //retVal.setManagedTypes(PersistenceManagedTypes.of(ResourceTable.class.getName())); - - retVal.setJpaProperties(jpaProperties); - - return retVal; - } - - @Bean - @Nonnull JpaStorageSettings storageSettings() { - JpaStorageSettings jpaStorageSettings = new JpaStorageSettings(); - //jpaStorageSettings.setAdvancedHSearchIndexing(); - return jpaStorageSettings; - } - } - @Inject DataSource myDataSource; @Inject EntityManagerFactory myEntityManagerFactory; + @Inject + TransactionTemplate myTransactionTemplate; + JdbcTemplate myJdbcTemplate; + + @BeforeEach + void setUp() { + myJdbcTemplate = new JdbcTemplate(myDataSource); + } + + @AfterEach + void tearDown() { + myJdbcTemplate.execute("delete from res_join"); + myJdbcTemplate.execute("delete from res_root"); + } + @Test - void createResourceTable_roundTrip() throws SQLException { - ourLog.info("starting"); - // given + void roundTripResourceTable() { + // given + myJdbcTemplate.execute("insert into res_root values (-1, -1, 'hello!')"); - var t = new JdbcTemplate(myDataSource); + myTransactionTemplate.execute(status -> { + var em = getEntityManagerOrThrow(); + long count = em.createQuery("select count(*) from ResRootEntity", Long.class).getSingleResult(); + assertEquals(1, count); - t.execute(" create table table_a (col_a numeric, col_b varchar)"); - int rows = t.update("insert into table_a values (?, ?)", 22, "foo"); - assertEquals(1, rows); + em.createQuery("from ResRootEntity", ResRootEntity.class).getResultStream().forEach(e-> assertEquals("hello!", e.getString())); + return true; + }); + } - ourLog.info("done"); + @Test + void roundTripJoin() { + // given - try(var em = myEntityManagerFactory.createEntityManager()) { - // empty - } + myTransactionTemplate.execute(status -> { + var em = getEntityManagerOrThrow(); + + ResRootEntity resRootEntity = new ResRootEntity(); + resRootEntity.setString("hello world!"); + + ResJoinEntity join = new ResJoinEntity(); + join.myResource = resRootEntity; + join.myString="child"; + + em.persist(resRootEntity); + em.persist(join); + em.flush(); + em.clear(); + + ResRootEntity queryBack = em.find(ResRootEntity.class, resRootEntity.myId); + assertEquals("hello world!", queryBack.myString); + assertEquals(1, queryBack.myJoinEntities.size()); + ResJoinEntity child = queryBack.myJoinEntities.iterator().next(); + assertEquals(resRootEntity.myId, child.myResource.myId); + //assertEquals(resRootEntity.myPartitionId, child.myPartitionId); + assertEquals("child", child.myString); + + long count = em.createQuery("select count(*) from ResRootEntity", Long.class).getSingleResult(); + ourLog.info("found {} roots", count); + assertEquals(1, count); + return true; + }); + } + + @Nonnull EntityManager getEntityManagerOrThrow() { + return Objects.requireNonNull(EntityManagerFactoryUtils.getTransactionalEntityManager(myEntityManagerFactory)); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResJoinEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResJoinEntity.java new file mode 100644 index 00000000000..7f82cd2f9a8 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResJoinEntity.java @@ -0,0 +1,36 @@ +package ca.uhn.fhir.jpa.model.pkspike; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@SuppressWarnings("JpaDataSourceORMInspection") +@Entity +@Table( + name = "RES_JOIN" +) +public class ResJoinEntity { + @Id +// @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "PID") + Long myId; + @Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false) + Long myPartitionId; + + @Column(name = "STRING_COL") + String myString; + + @ManyToOne( + optional = false) + @JoinColumn( + name = "RES_ID", + referencedColumnName = "RES_ID", + nullable = false) + ResRootEntity myResource; +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java new file mode 100644 index 00000000000..63c2b15388e --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java @@ -0,0 +1,69 @@ +package ca.uhn.fhir.jpa.model.pkspike; + +import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.hibernate.annotations.OptimisticLock; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Set; + +@Entity +@Table( + name = "RES_ROOT" +// uniqueConstraints = { +// @UniqueConstraint( +// name = IDX_RES_TYPE_FHIR_ID, +// columnNames = {"RES_TYPE", "FHIR_ID"}) +// }, +// indexes = { +// // Do not reuse previously used index name: IDX_INDEXSTATUS, IDX_RES_TYPE +// @Index(name = "IDX_RES_DATE", columnList = BaseHasResource.RES_UPDATED), +// @Index(name = "IDX_RES_FHIR_ID", columnList = "FHIR_ID"), +// @Index( +// name = "IDX_RES_TYPE_DEL_UPDATED", +// columnList = "RES_TYPE,RES_DELETED_AT,RES_UPDATED,PARTITION_ID,RES_ID"), +// @Index(name = "IDX_RES_RESID_UPDATED", columnList = "RES_ID, RES_UPDATED, PARTITION_ID") +// } + ) +public class ResRootEntity { + @Id +// @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "RES_ID") + Long myId; + + @Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false) + Long myPartitionId; + + @Column(name = "STRING_COL") + String myString; + + @OneToMany(mappedBy = "myResource") + Collection myJoinEntities = new ArrayList<>(); + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + public Long getId() { + return myId; + } + + public String getString() { + return myString; + } + + public void setString(String theString) { + myString = theString; + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaInit.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaInit.java new file mode 100644 index 00000000000..e8b5dfb2ff6 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaInit.java @@ -0,0 +1,14 @@ +package ca.uhn.fhir.jpa.model.pkspike; + +import org.apache.commons.dbcp2.BasicDataSource; +import org.springframework.jdbc.core.JdbcTemplate; + +class SchemaInit { + public static void initSchema(BasicDataSource theDataSource) { + var t = new JdbcTemplate(theDataSource); + + t.execute("create table res_root (RES_ID IDENTITY NOT NULL PRIMARY KEY, PARTITION_ID numeric, STRING_COL varchar)"); + t.execute("create table res_join (PID IDENTITY NOT NULL PRIMARY KEY, RES_ID numeric, PARTITION_ID numeric, STRING_COL varchar)"); + + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/package-info.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/package-info.java new file mode 100644 index 00000000000..dbf1e766a0f --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/package-info.java @@ -0,0 +1 @@ +package ca.uhn.fhir.jpa.model.pkspike; From fb007bea9fad6ebbdf052607dbb16475d96c4d6f Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Wed, 21 Aug 2024 17:48:29 -0400 Subject: [PATCH 03/17] start hacking --- .../jpa/model/pkspike/JpaBindingTest.java | 6 ++- .../fhir/jpa/model/pkspike/ResRootEntity.java | 5 +-- .../fhir/jpa/test/KeyTypeOverrideTest.java | 43 ------------------- .../fhir/test/utilities/LoggingExtension.java | 4 +- 4 files changed, 8 insertions(+), 50 deletions(-) delete mode 100644 hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/KeyTypeOverrideTest.java diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java index 5a0fa489bc0..88e21ab04a0 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java @@ -63,7 +63,11 @@ public class JpaBindingTest { long count = em.createQuery("select count(*) from ResRootEntity", Long.class).getSingleResult(); assertEquals(1, count); - em.createQuery("from ResRootEntity", ResRootEntity.class).getResultStream().forEach(e-> assertEquals("hello!", e.getString())); + em.createQuery("from ResRootEntity", ResRootEntity.class).getResultStream().forEach(e-> { + assertEquals(-1, e.myId); + assertEquals(-1, e.myPartitionId); + assertEquals("hello!", e.getString()); + }); return true; }); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java index 63c2b15388e..0c5da1bfcda 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java @@ -1,21 +1,18 @@ package ca.uhn.fhir.jpa.model.pkspike; -import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamCoords; import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import org.apache.commons.lang3.builder.ToStringBuilder; -import org.hibernate.annotations.OptimisticLock; import java.util.ArrayList; import java.util.Collection; -import java.util.Set; +@SuppressWarnings("JpaDataSourceORMInspection") @Entity @Table( name = "RES_ROOT" diff --git a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/KeyTypeOverrideTest.java b/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/KeyTypeOverrideTest.java deleted file mode 100644 index b32a9c0872b..00000000000 --- a/hapi-fhir-jpaserver-test-utilities/src/main/java/ca/uhn/fhir/jpa/test/KeyTypeOverrideTest.java +++ /dev/null @@ -1,43 +0,0 @@ -package ca.uhn.fhir.jpa.test; - -import ca.uhn.fhir.jpa.test.config.TestHSearchAddInConfig; -import ca.uhn.fhir.jpa.test.config.TestR4Config; -import jakarta.inject.Inject; -import jakarta.persistence.EntityManagerFactory; -import org.junit.jupiter.api.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.annotation.Configuration; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.transaction.support.TransactionTemplate; - -import static org.junit.jupiter.api.Assertions.fail; - -@ContextConfiguration(classes = { - TestHSearchAddInConfig.NoFT.class -}) -public class KeyTypeOverrideTest extends BaseJpaR4Test { - private static final Logger ourLog = LoggerFactory.getLogger(KeyTypeOverrideTest.class); - - @Inject - EntityManagerFactory myEntityManagerFactory; - - @Test - void empty() { - // given - - try (var em = myEntityManagerFactory.createEntityManager()) { - newTxTemplate().execute((status)->{ - em.createQuery("from ResourceTable").getResultStream() - .forEach(e->{ - ourLog.info("found resource"); - }); - return null; - }); - } - - // when - // then - } - -} diff --git a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/LoggingExtension.java b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/LoggingExtension.java index 532ef62a21e..7667b0c9808 100644 --- a/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/LoggingExtension.java +++ b/hapi-fhir-test-utilities/src/main/java/ca/uhn/fhir/test/utilities/LoggingExtension.java @@ -59,12 +59,12 @@ public class LoggingExtension implements BeforeEachCallback, BeforeTestExecution } @Override - public void beforeTestExecution(ExtensionContext context) throws Exception { + public void beforeTestExecution(ExtensionContext context) { getLoggerForTestClass(context).info("Starting test case [{}]", getMethodName(context)); } @Override - public void afterTestExecution(ExtensionContext context) throws Exception { + public void afterTestExecution(ExtensionContext context) { getLoggerForTestClass(context).info("Finished test case [{}]", getMethodName(context)); } From 8eef6dee18ed8ea373fef3d9725c4c55a30eade9 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Wed, 21 Aug 2024 17:56:34 -0400 Subject: [PATCH 04/17] fussing --- .../test/java/ca/uhn/fhir/jpa/model/pkspike/ResJoinEntity.java | 3 ++- .../test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResJoinEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResJoinEntity.java index 7f82cd2f9a8..f9c2f59b333 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResJoinEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResJoinEntity.java @@ -31,6 +31,7 @@ public class ResJoinEntity { @JoinColumn( name = "RES_ID", referencedColumnName = "RES_ID", - nullable = false) + nullable = false, + updatable = false) ResRootEntity myResource; } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java index 0c5da1bfcda..5a99ea30a3b 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java @@ -2,6 +2,7 @@ package ca.uhn.fhir.jpa.model.pkspike; import jakarta.persistence.Column; import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; @@ -44,7 +45,7 @@ public class ResRootEntity { @Column(name = "STRING_COL") String myString; - @OneToMany(mappedBy = "myResource") + @OneToMany(mappedBy = "myResource", fetch = FetchType.EAGER) Collection myJoinEntities = new ArrayList<>(); @Override From 6b41336d7dad51219e9ee487b957b6cdf947ad65 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Wed, 21 Aug 2024 20:54:58 -0400 Subject: [PATCH 05/17] basic composite pk IdClass --- ...nfig.java => PKSpikeDefaultJPAConfig.java} | 12 +- .../model/pkspike/SchemaCleanerExtension.java | 23 ++++ .../fhir/jpa/model/pkspike/SchemaInit.java | 2 + .../composite/CompositeKeyTypesConfig.java | 17 +++ .../composite/CompositePkJpaBindingTest.java | 128 ++++++++++++++++++ .../composite/ResJoinCompositeEntity.java | 43 ++++++ .../composite/ResRootCompositeEntity.java | 88 ++++++++++++ .../{ => primitive}/ResJoinEntity.java | 2 +- .../{ => primitive}/ResRootEntity.java | 23 +--- .../SimplePkJpaBindingTest.java} | 22 +-- .../pkspike/primitive/SimpleTypesConfig.java | 17 +++ 11 files changed, 338 insertions(+), 39 deletions(-) rename hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/{JPAConfig.java => PKSpikeDefaultJPAConfig.java} (93%) create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaCleanerExtension.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositeKeyTypesConfig.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java rename hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/{ => primitive}/ResJoinEntity.java (95%) rename hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/{ => primitive}/ResRootEntity.java (56%) rename hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/{JpaBindingTest.java => primitive/SimplePkJpaBindingTest.java} (83%) create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimpleTypesConfig.java diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JPAConfig.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java similarity index 93% rename from hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JPAConfig.java rename to hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java index 61cdbf566e2..d79b5b2ac02 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JPAConfig.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java @@ -23,7 +23,7 @@ import java.time.Duration; import java.util.Properties; @Configuration -class JPAConfig { +public class PKSpikeDefaultJPAConfig { @Inject FhirContext myFhirContext; @@ -32,7 +32,7 @@ class JPAConfig { DataSource datasource() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setDriver(new org.h2.Driver()); - dataSource.setUrl("jdbc:h2:mem:testdb_r4"); + dataSource.setUrl("jdbc:h2:mem:testdb_r4" + System.currentTimeMillis()); dataSource.setMaxWait(Duration.ofMillis(30000)); dataSource.setUsername(""); dataSource.setPassword(""); @@ -48,7 +48,8 @@ class JPAConfig { public HapiFhirLocalContainerEntityManagerFactoryBean entityManagerFactory( // ModuleMigrationMetadata theModuleMigrationMetadata, ConfigurableListableBeanFactory theConfigurableListableBeanFactory, - DataSource theDataSource) { + DataSource theDataSource, + PersistenceManagedTypes theManagedTypes) { HapiFhirLocalContainerEntityManagerFactoryBean retVal = new HapiFhirLocalContainerEntityManagerFactoryBean(theConfigurableListableBeanFactory); @@ -67,10 +68,7 @@ class JPAConfig { retVal.setPersistenceUnitName("HapiPU"); retVal.setDataSource(theDataSource); - retVal.setManagedTypes(PersistenceManagedTypes.of( - ResRootEntity.class.getName(), - ResJoinEntity.class.getName() - )); + retVal.setManagedTypes(theManagedTypes); retVal.setJpaProperties(jpaProperties); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaCleanerExtension.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaCleanerExtension.java new file mode 100644 index 00000000000..da11dc8e952 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaCleanerExtension.java @@ -0,0 +1,23 @@ +package ca.uhn.fhir.jpa.model.pkspike; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.springframework.context.ApplicationContext; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import javax.sql.DataSource; + +public class SchemaCleanerExtension implements AfterEachCallback { + + @Override + public void afterEach(ExtensionContext theExtensionContext) throws Exception { + ApplicationContext springContext = SpringExtension.getApplicationContext(theExtensionContext); + var dataSource = springContext.getBean(DataSource.class); + + JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); + jdbcTemplate.execute("delete from res_join"); + jdbcTemplate.execute("delete from res_root"); + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaInit.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaInit.java index e8b5dfb2ff6..345bf9d4870 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaInit.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaInit.java @@ -7,6 +7,8 @@ class SchemaInit { public static void initSchema(BasicDataSource theDataSource) { var t = new JdbcTemplate(theDataSource); + t.execute("CREATE SEQUENCE RES_ROOT_SEQ increment by 50"); + t.execute("CREATE SEQUENCE RES_JOIN_SEQ increment by 50"); t.execute("create table res_root (RES_ID IDENTITY NOT NULL PRIMARY KEY, PARTITION_ID numeric, STRING_COL varchar)"); t.execute("create table res_join (PID IDENTITY NOT NULL PRIMARY KEY, RES_ID numeric, PARTITION_ID numeric, STRING_COL varchar)"); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositeKeyTypesConfig.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositeKeyTypesConfig.java new file mode 100644 index 00000000000..8beb84f1956 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositeKeyTypesConfig.java @@ -0,0 +1,17 @@ +package ca.uhn.fhir.jpa.model.pkspike.composite; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes; + +@Configuration +public class CompositeKeyTypesConfig { + @Bean + PersistenceManagedTypes getManagedTypes() { + return PersistenceManagedTypes.of( + ResRootCompositeEntity.class.getName() +// ResJoinCompositeEntity.class.getName() + ); + } + +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java new file mode 100644 index 00000000000..c0b586d08ca --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java @@ -0,0 +1,128 @@ +package ca.uhn.fhir.jpa.model.pkspike.composite; + +import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; +import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; +import ca.uhn.fhir.jpa.model.pkspike.SchemaCleanerExtension; +import ca.uhn.fhir.jpa.model.pkspike.primitive.SimpleTypesConfig; +import jakarta.annotation.Nonnull; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.orm.jpa.EntityManagerFactoryUtils; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.sql.DataSource; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +/** + * Spike to assess variable binding against a db. + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + CompositeKeyTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class +}) +public class CompositePkJpaBindingTest { + private static final Logger ourLog = LoggerFactory.getLogger(CompositePkJpaBindingTest.class); + + @RegisterExtension + SchemaCleanerExtension myCleanerExtension = new SchemaCleanerExtension(); + + @Inject + DataSource myDataSource; + + @Inject + EntityManagerFactory myEntityManagerFactory; + + @Inject + TransactionTemplate myTransactionTemplate; + + JdbcTemplate myJdbcTemplate; + + @BeforeEach + void setUp() { + myJdbcTemplate = new JdbcTemplate(myDataSource); + } + + @Test + void roundTripResourceTable() { + // given + + myTransactionTemplate.execute(status -> { + var em = getEntityManagerOrThrow(); + var root = new ResRootCompositeEntity(); + root.myPartitionId=12; + root.myString="foo"; + + em.persist(root); + em.flush(); + em.clear(); + + Object rawPk = myEntityManagerFactory.getPersistenceUnitUtil().getIdentifier(root); + assertInstanceOf(ResRootCompositeEntity.ResRootPK.class, rawPk); + var pk = (ResRootCompositeEntity.ResRootPK) rawPk; + assertEquals(root.myId, pk.myId); + assertEquals(12, pk.myPartitionId); + + var readback = em.find(ResRootCompositeEntity.class, pk); + assertEquals(12, readback.myPartitionId); + assertEquals("foo", readback.myString); + return true; + }); + } + + + @Test + void roundTripJoin() { + // given + + myTransactionTemplate.execute(status -> { + var em = getEntityManagerOrThrow(); + + ResRootCompositeEntity resRootCompositeEntity = new ResRootCompositeEntity(); + resRootCompositeEntity.setString("hello world!"); + resRootCompositeEntity.myPartitionId = 12; + + ResJoinCompositeEntity join = new ResJoinCompositeEntity(); + join.myResource = resRootCompositeEntity; + join.myString="child"; + + em.persist(resRootCompositeEntity); + em.persist(join); + em.flush(); + em.clear(); + + ResRootCompositeEntity queryBack = em.find(ResRootCompositeEntity.class, getPrimaryKey(resRootCompositeEntity)); + assertEquals("hello world!", queryBack.myString); + //assertEquals(1, queryBack.myJoinEntities.size()); +// ResJoinEntity child = queryBack.myJoinEntities.iterator().next(); +// assertEquals(resRootEntity.myId, child.myResource.myId); +// //assertEquals(resRootEntity.myPartitionId, child.myPartitionId); +// assertEquals("child", child.myString); +// +// long count = em.createQuery("select count(*) from ResRootEntity", Long.class).getSingleResult(); +// ourLog.info("found {} roots", count); +// assertEquals(1, count); + return true; + }); + } + + private static @Nonnull ResRootCompositeEntity.ResRootPK getPrimaryKey(ResRootCompositeEntity resRootCompositeEntity) { + return new ResRootCompositeEntity.ResRootPK(resRootCompositeEntity.myId, resRootCompositeEntity.myPartitionId); + } + + @Nonnull EntityManager getEntityManagerOrThrow() { + return Objects.requireNonNull(EntityManagerFactoryUtils.getTransactionalEntityManager(myEntityManagerFactory)); + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java new file mode 100644 index 00000000000..ae0434441ee --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java @@ -0,0 +1,43 @@ +package ca.uhn.fhir.jpa.model.pkspike.composite; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinColumns; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; + +@SuppressWarnings("JpaDataSourceORMInspection") +@Entity +@Table( + name = "RES_JOIN" +) +public class ResJoinCompositeEntity { + @Id +// @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "PID") + Long myId; + @Column(name = "PARTITION_ID", nullable = true, insertable = false, updatable = false) + Long myPartitionId; + + @Column(name = "STRING_COL") + String myString; + + // fixme mb which side controls vs reads? + @ManyToOne( + optional = false) + @JoinColumns({ + @JoinColumn( + name = "RES_ID", + referencedColumnName = "RES_ID", + nullable = false, + insertable = true, + updatable = false), + @JoinColumn(name = "PARTITION_ID", referencedColumnName = "PARTITION_ID") + }) + ResRootCompositeEntity myResource; +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java new file mode 100644 index 00000000000..636f4567b69 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java @@ -0,0 +1,88 @@ +package ca.uhn.fhir.jpa.model.pkspike.composite; + +import com.google.common.base.Objects; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.IdClass; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import org.apache.commons.lang3.builder.ToStringBuilder; + +import java.util.ArrayList; +import java.util.Collection; + +@SuppressWarnings("JpaDataSourceORMInspection") +@Entity +@Table(name = "RES_ROOT") +@IdClass(ResRootCompositeEntity.ResRootPK.class) +public class ResRootCompositeEntity { + static class ResRootPK { + @GeneratedValue() + @Column(name = "RES_ID") + Long myId; + + @Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false) + Integer myPartitionId; + + /** For Hibernate */ + protected ResRootPK() {} + + public ResRootPK(Long theId, Integer thePartitionId) { + myId = theId; + myPartitionId = thePartitionId; + } + + @Override + public boolean equals(Object theO) { + if (this == theO) return true; + if (theO == null || getClass() != theO.getClass()) return false; + ResRootPK resRootPK = (ResRootPK) theO; + return Objects.equal(myId, resRootPK.myId) && Objects.equal(myPartitionId, resRootPK.myPartitionId); + } + + @Override + public int hashCode() { + return java.util.Objects.hash(myId, myPartitionId); + } + } + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "RES_ID") + Long myId; + + @Id + @Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false) + Integer myPartitionId; + +// ResRootPK getPK() { +// return new ResRootPK(myId, myPartitionId); +// } + + @Column(name = "STRING_COL") + String myString; + +// @OneToMany(mappedBy = "myResource", fetch = FetchType.EAGER) +// Collection myJoinEntities = new ArrayList<>(); + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + public Long getId() { + return myId; + } + + public String getString() { + return myString; + } + + public void setString(String theString) { + myString = theString; + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResJoinEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java similarity index 95% rename from hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResJoinEntity.java rename to hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java index f9c2f59b333..d699bfed3de 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResJoinEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.model.pkspike; +package ca.uhn.fhir.jpa.model.pkspike.primitive; import jakarta.persistence.Column; import jakarta.persistence.Entity; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java similarity index 56% rename from hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java rename to hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java index 5a99ea30a3b..879866ded6b 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ResRootEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java @@ -1,4 +1,4 @@ -package ca.uhn.fhir.jpa.model.pkspike; +package ca.uhn.fhir.jpa.model.pkspike.primitive; import jakarta.persistence.Column; import jakarta.persistence.Entity; @@ -15,27 +15,10 @@ import java.util.Collection; @SuppressWarnings("JpaDataSourceORMInspection") @Entity -@Table( - name = "RES_ROOT" -// uniqueConstraints = { -// @UniqueConstraint( -// name = IDX_RES_TYPE_FHIR_ID, -// columnNames = {"RES_TYPE", "FHIR_ID"}) -// }, -// indexes = { -// // Do not reuse previously used index name: IDX_INDEXSTATUS, IDX_RES_TYPE -// @Index(name = "IDX_RES_DATE", columnList = BaseHasResource.RES_UPDATED), -// @Index(name = "IDX_RES_FHIR_ID", columnList = "FHIR_ID"), -// @Index( -// name = "IDX_RES_TYPE_DEL_UPDATED", -// columnList = "RES_TYPE,RES_DELETED_AT,RES_UPDATED,PARTITION_ID,RES_ID"), -// @Index(name = "IDX_RES_RESID_UPDATED", columnList = "RES_ID, RES_UPDATED, PARTITION_ID") -// } - ) +@Table(name = "RES_ROOT") public class ResRootEntity { @Id -// @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue() @Column(name = "RES_ID") Long myId; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java similarity index 83% rename from hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java rename to hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java index 88e21ab04a0..3b13f6b97f8 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/JpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java @@ -1,6 +1,8 @@ -package ca.uhn.fhir.jpa.model.pkspike; +package ca.uhn.fhir.jpa.model.pkspike.primitive; import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; +import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; +import ca.uhn.fhir.jpa.model.pkspike.SchemaCleanerExtension; import jakarta.annotation.Nonnull; import jakarta.inject.Inject; import jakarta.persistence.EntityManager; @@ -9,6 +11,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; @@ -27,10 +30,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { - JPAConfig.class, FhirContextR4Config.class + SimpleTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class }) -public class JpaBindingTest { - private static final Logger ourLog = LoggerFactory.getLogger(JpaBindingTest.class); +public class SimplePkJpaBindingTest { + private static final Logger ourLog = LoggerFactory.getLogger(SimplePkJpaBindingTest.class); @Inject DataSource myDataSource; @@ -42,17 +45,14 @@ public class JpaBindingTest { TransactionTemplate myTransactionTemplate; JdbcTemplate myJdbcTemplate; + @RegisterExtension + SchemaCleanerExtension mySchemaCleanerExtension = new SchemaCleanerExtension(); + @BeforeEach void setUp() { myJdbcTemplate = new JdbcTemplate(myDataSource); } - @AfterEach - void tearDown() { - myJdbcTemplate.execute("delete from res_join"); - myJdbcTemplate.execute("delete from res_root"); - } - @Test void roundTripResourceTable() { // given @@ -60,7 +60,7 @@ public class JpaBindingTest { myTransactionTemplate.execute(status -> { var em = getEntityManagerOrThrow(); - long count = em.createQuery("select count(*) from ResRootEntity", Long.class).getSingleResult(); + long count = em.createQuery("select count(*) from ResRootEntity ", Long.class).getSingleResult(); assertEquals(1, count); em.createQuery("from ResRootEntity", ResRootEntity.class).getResultStream().forEach(e-> { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimpleTypesConfig.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimpleTypesConfig.java new file mode 100644 index 00000000000..3fc13c708b2 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimpleTypesConfig.java @@ -0,0 +1,17 @@ +package ca.uhn.fhir.jpa.model.pkspike.primitive; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes; + +@Configuration +public class SimpleTypesConfig { + @Bean + PersistenceManagedTypes getManagedTypes() { + return PersistenceManagedTypes.of( + ResRootEntity.class.getName(), + ResJoinEntity.class.getName() + ); + } + +} From 38d820f48be7cb63ddcc2e7eb966a3a43a722929 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Wed, 21 Aug 2024 21:03:58 -0400 Subject: [PATCH 06/17] join via IdClass --- .../composite/CompositeKeyTypesConfig.java | 4 ++-- .../composite/CompositePkJpaBindingTest.java | 19 +++++++++---------- .../composite/ResJoinCompositeEntity.java | 9 ++------- .../composite/ResRootCompositeEntity.java | 15 +++++++++------ 4 files changed, 22 insertions(+), 25 deletions(-) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositeKeyTypesConfig.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositeKeyTypesConfig.java index 8beb84f1956..926a900b0a7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositeKeyTypesConfig.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositeKeyTypesConfig.java @@ -9,8 +9,8 @@ public class CompositeKeyTypesConfig { @Bean PersistenceManagedTypes getManagedTypes() { return PersistenceManagedTypes.of( - ResRootCompositeEntity.class.getName() -// ResJoinCompositeEntity.class.getName() + ResRootCompositeEntity.class.getName(), + ResJoinCompositeEntity.class.getName() ); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java index c0b586d08ca..0ff4e01352a 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java @@ -3,7 +3,6 @@ package ca.uhn.fhir.jpa.model.pkspike.composite; import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; import ca.uhn.fhir.jpa.model.pkspike.SchemaCleanerExtension; -import ca.uhn.fhir.jpa.model.pkspike.primitive.SimpleTypesConfig; import jakarta.annotation.Nonnull; import jakarta.inject.Inject; import jakarta.persistence.EntityManager; @@ -105,15 +104,15 @@ public class CompositePkJpaBindingTest { ResRootCompositeEntity queryBack = em.find(ResRootCompositeEntity.class, getPrimaryKey(resRootCompositeEntity)); assertEquals("hello world!", queryBack.myString); - //assertEquals(1, queryBack.myJoinEntities.size()); -// ResJoinEntity child = queryBack.myJoinEntities.iterator().next(); -// assertEquals(resRootEntity.myId, child.myResource.myId); -// //assertEquals(resRootEntity.myPartitionId, child.myPartitionId); -// assertEquals("child", child.myString); -// -// long count = em.createQuery("select count(*) from ResRootEntity", Long.class).getSingleResult(); -// ourLog.info("found {} roots", count); -// assertEquals(1, count); + assertEquals(1, queryBack.myJoinEntities.size()); + ResJoinCompositeEntity child = queryBack.myJoinEntities.iterator().next(); + assertEquals(queryBack.myId, child.myResource.myId); + assertEquals(queryBack.myPartitionId, child.myPartitionId); + assertEquals("child", child.myString); + + long count = em.createQuery("select count(*) from ResRootCompositeEntity ", Long.class).getSingleResult(); + ourLog.info("found {} roots", count); + assertEquals(1, count); return true; }); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java index ae0434441ee..27fbd5e1281 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java @@ -22,7 +22,7 @@ public class ResJoinCompositeEntity { @Column(name = "PID") Long myId; @Column(name = "PARTITION_ID", nullable = true, insertable = false, updatable = false) - Long myPartitionId; + Integer myPartitionId; @Column(name = "STRING_COL") String myString; @@ -31,12 +31,7 @@ public class ResJoinCompositeEntity { @ManyToOne( optional = false) @JoinColumns({ - @JoinColumn( - name = "RES_ID", - referencedColumnName = "RES_ID", - nullable = false, - insertable = true, - updatable = false), + @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID"), @JoinColumn(name = "PARTITION_ID", referencedColumnName = "PARTITION_ID") }) ResRootCompositeEntity myResource; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java index 636f4567b69..462f00f3d37 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java @@ -2,7 +2,6 @@ package ca.uhn.fhir.jpa.model.pkspike.composite; import com.google.common.base.Objects; import jakarta.persistence.Column; -import jakarta.persistence.Embeddable; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; @@ -16,6 +15,10 @@ import org.apache.commons.lang3.builder.ToStringBuilder; import java.util.ArrayList; import java.util.Collection; +/** + * fixme MB IdClass vs embeddable? + * + */ @SuppressWarnings("JpaDataSourceORMInspection") @Entity @Table(name = "RES_ROOT") @@ -59,15 +62,15 @@ public class ResRootCompositeEntity { @Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false) Integer myPartitionId; -// ResRootPK getPK() { -// return new ResRootPK(myId, myPartitionId); -// } + ResRootPK getPK() { + return new ResRootPK(myId, myPartitionId); + } @Column(name = "STRING_COL") String myString; -// @OneToMany(mappedBy = "myResource", fetch = FetchType.EAGER) -// Collection myJoinEntities = new ArrayList<>(); + @OneToMany(mappedBy = "myResource", fetch = FetchType.EAGER) + Collection myJoinEntities = new ArrayList<>(); @Override public String toString() { From 68d497b767bec47efdf63c2d4bc9596d7330137e Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Thu, 22 Aug 2024 11:16:02 -0400 Subject: [PATCH 07/17] IdClass can't handle Null elements. --- .../pkspike/PKSpikeDefaultJPAConfig.java | 4 +- .../model/pkspike/SchemaCleanerExtension.java | 1 - .../fhir/jpa/model/pkspike/SchemaInit.java | 4 +- .../composite/CompositePkJpaBindingTest.java | 52 +++++++++-- .../composite/ResJoinCompositeEntity.java | 9 +- .../composite/ResRootCompositeEntity.java | 89 ++++++++++--------- .../pkspike/primitive/ResJoinEntity.java | 9 +- .../pkspike/primitive/ResRootEntity.java | 12 +-- .../primitive/SimplePkJpaBindingTest.java | 3 +- .../ca/uhn/fhir/jpa/model/pkspike/readme.md | 2 + 10 files changed, 123 insertions(+), 62 deletions(-) create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/readme.md diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java index d79b5b2ac02..39f1833733b 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java @@ -54,7 +54,6 @@ public class PKSpikeDefaultJPAConfig { new HapiFhirLocalContainerEntityManagerFactoryBean(theConfigurableListableBeanFactory); retVal.setJpaDialect(new HapiFhirHibernateJpaDialect(myFhirContext.getLocalizer())); -// retVal.setPackagesToScan("ca.uhn.fhir.jpa.model.entity", "ca.uhn.fhir.jpa.entity"); HibernatePersistenceProvider persistenceProvider = new HibernatePersistenceProvider(); retVal.setPersistenceProvider(persistenceProvider); Properties jpaProperties = new Properties(); @@ -62,7 +61,7 @@ public class PKSpikeDefaultJPAConfig { jpaProperties.put("hibernate.format_sql", "false"); jpaProperties.put("hibernate.show_sql", "false"); jpaProperties.put("hibernate.integration.envers.enabled=false", "false"); - //jpaProperties.put("hibernate.hbm2ddl.auto", "update"); + jpaProperties.put("hibernate.hbm2ddl.auto", "none"); jpaProperties.put("hibernate.dialect", HapiFhirH2Dialect.class.getName()); retVal.setJpaProperties(jpaProperties); @@ -92,7 +91,6 @@ public class PKSpikeDefaultJPAConfig { @Nonnull JpaStorageSettings storageSettings() { JpaStorageSettings jpaStorageSettings = new JpaStorageSettings(); - //jpaStorageSettings.setAdvancedHSearchIndexing(); return jpaStorageSettings; } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaCleanerExtension.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaCleanerExtension.java index da11dc8e952..69005b5ee2f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaCleanerExtension.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaCleanerExtension.java @@ -1,6 +1,5 @@ package ca.uhn.fhir.jpa.model.pkspike; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.extension.AfterEachCallback; import org.junit.jupiter.api.extension.ExtensionContext; import org.springframework.context.ApplicationContext; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaInit.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaInit.java index 345bf9d4870..feceaf51e99 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaInit.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/SchemaInit.java @@ -9,8 +9,8 @@ class SchemaInit { t.execute("CREATE SEQUENCE RES_ROOT_SEQ increment by 50"); t.execute("CREATE SEQUENCE RES_JOIN_SEQ increment by 50"); - t.execute("create table res_root (RES_ID IDENTITY NOT NULL PRIMARY KEY, PARTITION_ID numeric, STRING_COL varchar)"); - t.execute("create table res_join (PID IDENTITY NOT NULL PRIMARY KEY, RES_ID numeric, PARTITION_ID numeric, STRING_COL varchar)"); + t.execute("create table res_root (RES_ID IDENTITY PRIMARY KEY, PARTITION_ID numeric, STRING_COL varchar)"); + t.execute("create table res_join (PID IDENTITY PRIMARY KEY, RES_ID numeric, PARTITION_ID numeric, STRING_COL varchar)"); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java index 0ff4e01352a..6add049c9f0 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java @@ -11,19 +11,26 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; import org.springframework.orm.jpa.EntityManagerFactoryUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.springframework.transaction.support.TransactionTemplate; import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; import java.util.Objects; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * Spike to assess variable binding against a db. @@ -82,8 +89,10 @@ public class CompositePkJpaBindingTest { } - @Test - void roundTripJoin() { + @ParameterizedTest + @ValueSource(ints = {12}) + @NullSource + void roundTripJoin(Integer thePartitionId) { // given myTransactionTemplate.execute(status -> { @@ -91,7 +100,7 @@ public class CompositePkJpaBindingTest { ResRootCompositeEntity resRootCompositeEntity = new ResRootCompositeEntity(); resRootCompositeEntity.setString("hello world!"); - resRootCompositeEntity.myPartitionId = 12; + resRootCompositeEntity.myPartitionId = thePartitionId; ResJoinCompositeEntity join = new ResJoinCompositeEntity(); join.myResource = resRootCompositeEntity; @@ -102,12 +111,45 @@ public class CompositePkJpaBindingTest { em.flush(); em.clear(); - ResRootCompositeEntity queryBack = em.find(ResRootCompositeEntity.class, getPrimaryKey(resRootCompositeEntity)); + var sqlCount = myJdbcTemplate.queryForObject("select count(1) from RES_ROOT", Integer.class); + assertEquals(1, sqlCount); + + var rootCount = + em.createQuery("select count(*) from ResRootCompositeEntity", Long.class) + .getSingleResult(); + assertEquals(1, rootCount); + + ourLog.info("find rows in res_root:"); + + myJdbcTemplate.query("select res_id, partition_id from RES_ROOT", new RowMapper() { + @Override + public Object mapRow(ResultSet rs, int rowNum) throws SQLException { + ourLog.info("find row in res_root ({} {})", rs.getObject(1, Long.class), rs.getObject(2, Integer.class)); + return null; + } + }); + ourLog.info("end:find rows in res_root:"); + + var readAll = + em.createQuery("from ResRootCompositeEntity", ResRootCompositeEntity.class) + .getResultList(); + assertEquals(1, readAll.size()); + var read2 = readAll.get(0); + assertNotNull(read2); + ourLog.info("readback {}", resRootCompositeEntity); + assertEquals(resRootCompositeEntity.myId, read2.myId); + + ResRootCompositeEntity queryBack = em + .createQuery("from ResRootCompositeEntity where myId = :id", ResRootCompositeEntity.class) + .setParameter("id", resRootCompositeEntity.myId) + .getSingleResult(); + assertEquals("hello world!", queryBack.myString); assertEquals(1, queryBack.myJoinEntities.size()); ResJoinCompositeEntity child = queryBack.myJoinEntities.iterator().next(); assertEquals(queryBack.myId, child.myResource.myId); - assertEquals(queryBack.myPartitionId, child.myPartitionId); + assertEquals(thePartitionId, queryBack.myPartitionId); + assertEquals(thePartitionId, child.myPartitionId); assertEquals("child", child.myString); long count = em.createQuery("select count(*) from ResRootCompositeEntity ", Long.class).getSingleResult(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java index 27fbd5e1281..2bca19affb5 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java @@ -9,6 +9,7 @@ import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumns; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; +import org.apache.commons.lang3.builder.ToStringBuilder; @SuppressWarnings("JpaDataSourceORMInspection") @Entity @@ -18,7 +19,7 @@ import jakarta.persistence.Table; public class ResJoinCompositeEntity { @Id // @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue(strategy = GenerationType.SEQUENCE) @Column(name = "PID") Long myId; @Column(name = "PARTITION_ID", nullable = true, insertable = false, updatable = false) @@ -35,4 +36,10 @@ public class ResJoinCompositeEntity { @JoinColumn(name = "PARTITION_ID", referencedColumnName = "PARTITION_ID") }) ResRootCompositeEntity myResource; + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java index 462f00f3d37..a60acd64de9 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java @@ -1,16 +1,18 @@ package ca.uhn.fhir.jpa.model.pkspike.composite; -import com.google.common.base.Objects; import jakarta.persistence.Column; import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.IdClass; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; @@ -24,6 +26,48 @@ import java.util.Collection; @Table(name = "RES_ROOT") @IdClass(ResRootCompositeEntity.ResRootPK.class) public class ResRootCompositeEntity { + private static final Logger ourLog = LoggerFactory.getLogger(ResRootCompositeEntity.class); + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + @Column(name = "RES_ID") + Long myId; + + @Id + @Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false) + Integer myPartitionId; + + ResRootPK getPK() { + return new ResRootPK(myId, myPartitionId); + } + + @Column(name = "STRING_COL") + String myString; + + @OneToMany(mappedBy = "myResource") + Collection myJoinEntities = new ArrayList<>(); + + public ResRootCompositeEntity() { + ourLog.info("new ResRootCompositeEntity()"); + } + + public Long getId() { + return myId; + } + + public String getString() { + return myString; + } + + public void setString(String theString) { + myString = theString; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + static class ResRootPK { @GeneratedValue() @Column(name = "RES_ID") @@ -42,50 +86,13 @@ public class ResRootCompositeEntity { @Override public boolean equals(Object theO) { - if (this == theO) return true; - if (theO == null || getClass() != theO.getClass()) return false; - ResRootPK resRootPK = (ResRootPK) theO; - return Objects.equal(myId, resRootPK.myId) && Objects.equal(myPartitionId, resRootPK.myPartitionId); + return EqualsBuilder.reflectionEquals(this,theO); } @Override public int hashCode() { - return java.util.Objects.hash(myId, myPartitionId); + return HashCodeBuilder.reflectionHashCode(this); } } - @Id - @GeneratedValue(strategy = GenerationType.AUTO) - @Column(name = "RES_ID") - Long myId; - @Id - @Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false) - Integer myPartitionId; - - ResRootPK getPK() { - return new ResRootPK(myId, myPartitionId); - } - - @Column(name = "STRING_COL") - String myString; - - @OneToMany(mappedBy = "myResource", fetch = FetchType.EAGER) - Collection myJoinEntities = new ArrayList<>(); - - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this); - } - - public Long getId() { - return myId; - } - - public String getString() { - return myString; - } - - public void setString(String theString) { - myString = theString; - } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java index d699bfed3de..71f574c224f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java @@ -8,6 +8,8 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; @SuppressWarnings("JpaDataSourceORMInspection") @Entity @@ -17,7 +19,7 @@ import jakarta.persistence.Table; public class ResJoinEntity { @Id // @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue(strategy = GenerationType.SEQUENCE) @Column(name = "PID") Long myId; @Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false) @@ -34,4 +36,9 @@ public class ResJoinEntity { nullable = false, updatable = false) ResRootEntity myResource; + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE); + } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java index 879866ded6b..2ff855fe42b 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java @@ -4,11 +4,11 @@ import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; import java.util.ArrayList; import java.util.Collection; @@ -31,11 +31,6 @@ public class ResRootEntity { @OneToMany(mappedBy = "myResource", fetch = FetchType.EAGER) Collection myJoinEntities = new ArrayList<>(); - @Override - public String toString() { - return ToStringBuilder.reflectionToString(this); - } - public Long getId() { return myId; } @@ -47,4 +42,9 @@ public class ResRootEntity { public void setString(String theString) { myString = theString; } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE); + } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java index 3b13f6b97f8..3684621ab3d 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java @@ -7,7 +7,6 @@ import jakarta.annotation.Nonnull; import jakarta.inject.Inject; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -96,7 +95,7 @@ public class SimplePkJpaBindingTest { assertEquals(1, queryBack.myJoinEntities.size()); ResJoinEntity child = queryBack.myJoinEntities.iterator().next(); assertEquals(resRootEntity.myId, child.myResource.myId); - //assertEquals(resRootEntity.myPartitionId, child.myPartitionId); + assertEquals(resRootEntity.myPartitionId, child.myPartitionId); assertEquals("child", child.myString); long count = em.createQuery("select count(*) from ResRootEntity", Long.class).getSingleResult(); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/readme.md b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/readme.md new file mode 100644 index 00000000000..795097857ae --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/readme.md @@ -0,0 +1,2 @@ +Hibernate 6 IdClass can have no NULL parts! See AbstractNonAggregatedIdentifierMappingInitializer#233 + From a30b567136cb4a46764fb08b559ca26b1da434a6 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Thu, 22 Aug 2024 15:42:09 -0400 Subject: [PATCH 08/17] Extract common tests. --- .../pkspike/BasicEntityTestTemplate.java | 152 ++++++++++++++++ .../fhir/jpa/model/pkspike/EntityFixture.java | 59 ++++++ .../pkspike/PKSpikeDefaultJPAConfig.java | 6 + .../composite/CompositePkJpaBindingTest.java | 169 ------------------ .../EmbeddedIdPkJpaBindingTest.java | 57 ++++++ .../embeddedid/EmbeddedIdTypesConfig.java | 17 ++ .../embeddedid/ResJoinEmbeddedIdEntity.java | 75 ++++++++ .../embeddedid/ResRootEmbeddedIdEntity.java | 111 ++++++++++++ .../IdClassKeyTypesConfig.java} | 8 +- .../idclass/IdClassPkJpaBindingTest.java | 31 ++++ .../ResJoinIdClassEntity.java} | 36 +++- .../ResRootIdClassEntity.java} | 34 +++- .../partitionkey/PartitionJpaBindingTest.java | 45 +++++ .../partitionkey/PartitionTypesConfig.java | 17 ++ .../partitionkey/ResJoinPartitionEntity.java | 83 +++++++++ .../partitionkey/ResRootPartitionEntity.java | 75 ++++++++ .../pkspike/primitive/ResJoinEntity.java | 38 +++- .../pkspike/primitive/ResRootEntity.java | 26 ++- .../primitive/SimplePkJpaBindingTest.java | 82 +-------- .../ca/uhn/fhir/jpa/model/pkspike/readme.md | 14 +- 20 files changed, 874 insertions(+), 261 deletions(-) create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/EntityFixture.java delete mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdTypesConfig.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResJoinEmbeddedIdEntity.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResRootEmbeddedIdEntity.java rename hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/{composite/CompositeKeyTypesConfig.java => idclass/IdClassKeyTypesConfig.java} (63%) create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java rename hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/{composite/ResJoinCompositeEntity.java => idclass/ResJoinIdClassEntity.java} (62%) rename hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/{composite/ResRootCompositeEntity.java => idclass/ResRootIdClassEntity.java} (73%) create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionTypesConfig.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResRootPartitionEntity.java diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java new file mode 100644 index 00000000000..c14f117d353 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java @@ -0,0 +1,152 @@ +package ca.uhn.fhir.jpa.model.pkspike; + +import jakarta.annotation.Nonnull; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.orm.jpa.EntityManagerFactoryUtils; +import org.springframework.transaction.support.TransactionTemplate; + +import java.util.Objects; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +abstract public class BasicEntityTestTemplate,J extends EntityFixture.IJoinEntity> { + @Autowired + EntityManagerFactory myEntityManagerFactory; + + @Autowired + TransactionTemplate myTransactionTemplate; + + @Autowired + JdbcTemplate myJdbcTemplate; + + @RegisterExtension + SchemaCleanerExtension mySchemaCleanerExtension = new SchemaCleanerExtension(); + + final EntityFixture myEntityFixture; + + public BasicEntityTestTemplate(EntityFixture theEntityFixture) { + myEntityFixture = theEntityFixture; + } + + @Test + void rootEntityBoundToTable() { + // given + myJdbcTemplate.execute("insert into res_root(res_id, partition_id, string_col) values (-1, -1, 'hello!')"); + + doInTx(em->{ + long count = queryCountAll(em, myEntityFixture.myRootType); + + assertEquals(1, count); + + CriteriaBuilder cb = em.getCriteriaBuilder(); + + CriteriaQuery cr = cb.createQuery(myEntityFixture.myRootType); + cr.select(cr.from(myEntityFixture.myRootType)); + R readback = em.createQuery(cr).getSingleResult(); + + assertEquals(-1, readback.getResId()); + assertEquals(-1, readback.getPartitionId()); + assertEquals("hello!", readback.getString()); + }); + } + + @Test + void roundTripResourceTable() { + doInTx(em->{ + R root = myEntityFixture.buildRootEntity(); + root.setPartitionId(12); + root.setString("goodbye!"); + em.persist(root); + + em.flush(); + em.clear(); + + Object id = myEntityManagerFactory.getPersistenceUnitUtil().getIdentifier(root); + R readback = em.find(myEntityFixture.myRootType, id); + assertEquals(root.getResId(), readback.getResId()); + assertNotNull(readback.getResId()); + assertEquals(12, readback.getPartitionId()); + assertEquals("goodbye!", readback.getString()); + }); + } + + + @Test + void roundTripJoin() { + doInTx(em->{ + var root = myEntityFixture.buildRootEntity(); + root.setPartitionId(12); + root.setString("parent"); + + var join = myEntityFixture.buildJoinEntity(); + join.setParent(root); + join.setString("child"); + join.setPartitionId(12); + em.persist(root); + em.persist(join); + + em.flush(); + em.clear(); + + Object id = myEntityManagerFactory.getPersistenceUnitUtil().getIdentifier(root); + R readback = em.find(myEntityFixture.myRootType, id); + + assertEquals(root.getResId(), readback.getResId()); + assertNotNull(readback.getResId()); + assertEquals(12, readback.getPartitionId()); + assertEquals("parent", readback.getString()); + + assertEquals(1, readback.getJoins().size()); + J joinReadback = readback.getJoins().iterator().next(); + assertNotNull(joinReadback); + assertNotNull(joinReadback.getResId()); + assertEquals(root.getResId(), joinReadback.getResId()); + assertEquals(12, joinReadback.getPartitionId()); + assertEquals("child", joinReadback.getString()); + }); + +// +// ResRootEntity queryBack = em.find(ResRootEntity.class, resRootEntity.myId); +// assertEquals("hello world!", queryBack.myString); +// assertEquals(1, queryBack.myJoinEntities.size()); +// ResJoinEntity child = queryBack.myJoinEntities.iterator().next(); +// assertEquals(resRootEntity.myId, child.myResource.myId); +// assertEquals(resRootEntity.myPartitionId, child.myPartitionId); +// assertEquals("child", child.myString); +// +// long count = em.createQuery("select count(*) from ResRootPartitionEntity", Long.class).getSingleResult(); +// ourLog.info("found {} roots", count); +// assertEquals(1, count); +// return true; +// }); + } + private void doInTx(Consumer theCallback) { + myTransactionTemplate.execute(status-> { + theCallback.accept(getEntityManagerOrThrow()); + return null; + }); + } + + private static long queryCountAll(EntityManager em, Class rootType) { + CriteriaBuilder qb = em.getCriteriaBuilder(); + CriteriaQuery cq = qb.createQuery(Long.class); + CriteriaQuery select = cq.select(qb.count(cq.from(rootType))); + long count = em.createQuery(select).getSingleResult(); + return count; + } + + @Nonnull + EntityManager getEntityManagerOrThrow() { + return Objects.requireNonNull(EntityManagerFactoryUtils.getTransactionalEntityManager(myEntityManagerFactory)); + } + +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/EntityFixture.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/EntityFixture.java new file mode 100644 index 00000000000..f960c4f47dc --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/EntityFixture.java @@ -0,0 +1,59 @@ +package ca.uhn.fhir.jpa.model.pkspike; + +import jakarta.annotation.Nonnull; + +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; + +public class EntityFixture { + + public static EntityFixture build(Class theRootType, Class theJoinType) { + return new EntityFixture<>(theRootType, theJoinType); + } + + EntityFixture(Class theRootType, Class theJoinType) { + myRootType = theRootType; + myJoinType = theJoinType; + } + + public J buildJoinEntity() { + return buildInstance(myJoinType); + } + + public interface IRootEntity { + Long getResId(); + void setPartitionId(Integer thePartitionId); + Integer getPartitionId(); + String getString(); + void setString(String theString); + + Collection getJoins(); + } + public interface IJoinEntity

{ + void setString(String theString); + + void setParent(P theRoot); + + String getString(); + + void setPartitionId(Integer thePartitionId); + Integer getPartitionId(); + + Long getResId(); + } + + public final Class myRootType; + public final Class myJoinType; + + public R buildRootEntity() { + return buildInstance(myRootType); + } + + static @Nonnull T buildInstance(Class theClass) { + try { + return theClass.getDeclaredConstructor().newInstance(); + } catch (Exception theE) { + throw new RuntimeException(theE); + } + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java index 39f1833733b..c28791b73d3 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java @@ -13,6 +13,7 @@ import org.hibernate.jpa.HibernatePersistenceProvider; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.orm.jpa.JpaTransactionManager; import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes; import org.springframework.transaction.PlatformTransactionManager; @@ -87,6 +88,11 @@ public class PKSpikeDefaultJPAConfig { return new TransactionTemplate(theTransactionManager); } + @Bean + public JdbcTemplate jdbcTemplate(DataSource theDataSource) { + return new JdbcTemplate(theDataSource); + } + @Bean @Nonnull JpaStorageSettings storageSettings() { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java deleted file mode 100644 index 6add049c9f0..00000000000 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositePkJpaBindingTest.java +++ /dev/null @@ -1,169 +0,0 @@ -package ca.uhn.fhir.jpa.model.pkspike.composite; - -import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; -import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; -import ca.uhn.fhir.jpa.model.pkspike.SchemaCleanerExtension; -import jakarta.annotation.Nonnull; -import jakarta.inject.Inject; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.RegisterExtension; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullSource; -import org.junit.jupiter.params.provider.ValueSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.orm.jpa.EntityManagerFactoryUtils; -import org.springframework.test.context.ContextConfiguration; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.transaction.support.TransactionTemplate; - -import javax.sql.DataSource; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Objects; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertNotNull; - -/** - * Spike to assess variable binding against a db. - */ -@ExtendWith(SpringExtension.class) -@ContextConfiguration(classes = { - CompositeKeyTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class -}) -public class CompositePkJpaBindingTest { - private static final Logger ourLog = LoggerFactory.getLogger(CompositePkJpaBindingTest.class); - - @RegisterExtension - SchemaCleanerExtension myCleanerExtension = new SchemaCleanerExtension(); - - @Inject - DataSource myDataSource; - - @Inject - EntityManagerFactory myEntityManagerFactory; - - @Inject - TransactionTemplate myTransactionTemplate; - - JdbcTemplate myJdbcTemplate; - - @BeforeEach - void setUp() { - myJdbcTemplate = new JdbcTemplate(myDataSource); - } - - @Test - void roundTripResourceTable() { - // given - - myTransactionTemplate.execute(status -> { - var em = getEntityManagerOrThrow(); - var root = new ResRootCompositeEntity(); - root.myPartitionId=12; - root.myString="foo"; - - em.persist(root); - em.flush(); - em.clear(); - - Object rawPk = myEntityManagerFactory.getPersistenceUnitUtil().getIdentifier(root); - assertInstanceOf(ResRootCompositeEntity.ResRootPK.class, rawPk); - var pk = (ResRootCompositeEntity.ResRootPK) rawPk; - assertEquals(root.myId, pk.myId); - assertEquals(12, pk.myPartitionId); - - var readback = em.find(ResRootCompositeEntity.class, pk); - assertEquals(12, readback.myPartitionId); - assertEquals("foo", readback.myString); - return true; - }); - } - - - @ParameterizedTest - @ValueSource(ints = {12}) - @NullSource - void roundTripJoin(Integer thePartitionId) { - // given - - myTransactionTemplate.execute(status -> { - var em = getEntityManagerOrThrow(); - - ResRootCompositeEntity resRootCompositeEntity = new ResRootCompositeEntity(); - resRootCompositeEntity.setString("hello world!"); - resRootCompositeEntity.myPartitionId = thePartitionId; - - ResJoinCompositeEntity join = new ResJoinCompositeEntity(); - join.myResource = resRootCompositeEntity; - join.myString="child"; - - em.persist(resRootCompositeEntity); - em.persist(join); - em.flush(); - em.clear(); - - var sqlCount = myJdbcTemplate.queryForObject("select count(1) from RES_ROOT", Integer.class); - assertEquals(1, sqlCount); - - var rootCount = - em.createQuery("select count(*) from ResRootCompositeEntity", Long.class) - .getSingleResult(); - assertEquals(1, rootCount); - - ourLog.info("find rows in res_root:"); - - myJdbcTemplate.query("select res_id, partition_id from RES_ROOT", new RowMapper() { - @Override - public Object mapRow(ResultSet rs, int rowNum) throws SQLException { - ourLog.info("find row in res_root ({} {})", rs.getObject(1, Long.class), rs.getObject(2, Integer.class)); - return null; - } - }); - ourLog.info("end:find rows in res_root:"); - - var readAll = - em.createQuery("from ResRootCompositeEntity", ResRootCompositeEntity.class) - .getResultList(); - assertEquals(1, readAll.size()); - var read2 = readAll.get(0); - assertNotNull(read2); - ourLog.info("readback {}", resRootCompositeEntity); - assertEquals(resRootCompositeEntity.myId, read2.myId); - - ResRootCompositeEntity queryBack = em - .createQuery("from ResRootCompositeEntity where myId = :id", ResRootCompositeEntity.class) - .setParameter("id", resRootCompositeEntity.myId) - .getSingleResult(); - - assertEquals("hello world!", queryBack.myString); - assertEquals(1, queryBack.myJoinEntities.size()); - ResJoinCompositeEntity child = queryBack.myJoinEntities.iterator().next(); - assertEquals(queryBack.myId, child.myResource.myId); - assertEquals(thePartitionId, queryBack.myPartitionId); - assertEquals(thePartitionId, child.myPartitionId); - assertEquals("child", child.myString); - - long count = em.createQuery("select count(*) from ResRootCompositeEntity ", Long.class).getSingleResult(); - ourLog.info("found {} roots", count); - assertEquals(1, count); - return true; - }); - } - - private static @Nonnull ResRootCompositeEntity.ResRootPK getPrimaryKey(ResRootCompositeEntity resRootCompositeEntity) { - return new ResRootCompositeEntity.ResRootPK(resRootCompositeEntity.myId, resRootCompositeEntity.myPartitionId); - } - - @Nonnull EntityManager getEntityManagerOrThrow() { - return Objects.requireNonNull(EntityManagerFactoryUtils.getTransactionalEntityManager(myEntityManagerFactory)); - } -} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java new file mode 100644 index 00000000000..b2784190bc6 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java @@ -0,0 +1,57 @@ +package ca.uhn.fhir.jpa.model.pkspike.embeddedid; + +import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; +import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate; +import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; +import ca.uhn.fhir.jpa.model.pkspike.SchemaCleanerExtension; +import ca.uhn.fhir.jpa.model.pkspike.idclass.ResJoinIdClassEntity; +import ca.uhn.fhir.jpa.model.pkspike.idclass.ResRootIdClassEntity; +import jakarta.annotation.Nonnull; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.orm.jpa.EntityManagerFactoryUtils; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.sql.DataSource; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * Spike to assess variable binding against a db. + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + EmbeddedIdTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class +}) +public class EmbeddedIdPkJpaBindingTest { + + + @Nested + class Common extends BasicEntityTestTemplate { + Common() { + super(EntityFixture.build(ResRootEmbeddedIdEntity.class, ResJoinEmbeddedIdEntity.class)); + } + } + +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdTypesConfig.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdTypesConfig.java new file mode 100644 index 00000000000..7a290ca3107 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdTypesConfig.java @@ -0,0 +1,17 @@ +package ca.uhn.fhir.jpa.model.pkspike.embeddedid; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes; + +@Configuration +public class EmbeddedIdTypesConfig { + @Bean + PersistenceManagedTypes getManagedTypes() { + return PersistenceManagedTypes.of( + ResRootEmbeddedIdEntity.class.getName(), + ResJoinEmbeddedIdEntity.class.getName() + ); + } + +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResJoinEmbeddedIdEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResJoinEmbeddedIdEntity.java new file mode 100644 index 00000000000..b900464ac83 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResJoinEmbeddedIdEntity.java @@ -0,0 +1,75 @@ +package ca.uhn.fhir.jpa.model.pkspike.embeddedid; + +import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinColumns; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import org.apache.commons.lang3.builder.ToStringBuilder; + +@SuppressWarnings("JpaDataSourceORMInspection") +@Entity +@Table( + name = "RES_JOIN" +) +public class ResJoinEmbeddedIdEntity implements EntityFixture.IJoinEntity { + @Id +// @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) + @GeneratedValue(strategy = GenerationType.SEQUENCE) + @Column(name = "PID") + Long myId; + @Column(name = "PARTITION_ID", nullable = true, insertable = false, updatable = false) + Integer myPartitionId; + + @Column(name = "STRING_COL") + String myString; + + // fixme mb which side controls vs reads? + @ManyToOne( + optional = false) + @JoinColumns({ + @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID"), + @JoinColumn(name = "PARTITION_ID", referencedColumnName = "PARTITION_ID") + }) + ResRootEmbeddedIdEntity myResource; + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + @Override + public void setString(String theString) { + myString = theString; + } + + @Override + public void setParent(ResRootEmbeddedIdEntity theRoot) { + myResource = theRoot; + } + + @Override + public String getString() { + return myString; + } + + @Override + public void setPartitionId(Integer thePartitionId) { + myPartitionId = thePartitionId; + } + + @Override + public Integer getPartitionId() { + return myPartitionId; + } + + @Override + public Long getResId() { + return myResource.getResId(); + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResRootEmbeddedIdEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResRootEmbeddedIdEntity.java new file mode 100644 index 00000000000..43b7436d665 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResRootEmbeddedIdEntity.java @@ -0,0 +1,111 @@ +package ca.uhn.fhir.jpa.model.pkspike.embeddedid; + +import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import jakarta.persistence.EmbeddedId; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * fixme MB IdClass vs embeddable? + * + */ +@SuppressWarnings("JpaDataSourceORMInspection") +@Entity +@Table(name = "RES_ROOT") +public class ResRootEmbeddedIdEntity implements EntityFixture.IRootEntity { + private static final Logger ourLog = LoggerFactory.getLogger(ResRootEmbeddedIdEntity.class); + + @EmbeddedId + ResRootPK myId = new ResRootPK(); + + @Column(name = "PARTITION_ID", nullable = true, insertable = false, updatable = false) + Integer myPartitionId; + + @Column(name = "STRING_COL") + String myString; + + @OneToMany(mappedBy = "myResource") + Collection myJoinEntities = new ArrayList<>(); + + public ResRootEmbeddedIdEntity() { + ourLog.info("new ResRootCompositeEntity()"); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + @Override + public Long getResId() { + return myId==null?null:myId.myId; + } + + @Override + public void setPartitionId(Integer thePartitionId) { + myPartitionId = thePartitionId; + myId.myPartitionId = thePartitionId; + } + + @Override + public Integer getPartitionId() { + return myPartitionId; + } + + @Override + public String getString() { + return myString; + } + + @Override + public void setString(String theString) { + myString = theString; + } + + @Override + public Collection getJoins() { + return myJoinEntities; + } + + @Embeddable + static class ResRootPK { + @GeneratedValue() + @Column(name = "RES_ID") + public Long myId; + + @Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false) + public Integer myPartitionId; + + /** For Hibernate */ + protected ResRootPK() {} + + public ResRootPK(Long theId, Integer thePartitionId) { + myId = theId; + myPartitionId = thePartitionId; + } + + @Override + public boolean equals(Object theO) { + return EqualsBuilder.reflectionEquals(this,theO); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + } + +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositeKeyTypesConfig.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassKeyTypesConfig.java similarity index 63% rename from hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositeKeyTypesConfig.java rename to hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassKeyTypesConfig.java index 926a900b0a7..2ea0f03e60e 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/CompositeKeyTypesConfig.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassKeyTypesConfig.java @@ -1,16 +1,16 @@ -package ca.uhn.fhir.jpa.model.pkspike.composite; +package ca.uhn.fhir.jpa.model.pkspike.idclass; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes; @Configuration -public class CompositeKeyTypesConfig { +public class IdClassKeyTypesConfig { @Bean PersistenceManagedTypes getManagedTypes() { return PersistenceManagedTypes.of( - ResRootCompositeEntity.class.getName(), - ResJoinCompositeEntity.class.getName() + ResRootIdClassEntity.class.getName(), + ResJoinIdClassEntity.class.getName() ); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java new file mode 100644 index 00000000000..b93195994b8 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java @@ -0,0 +1,31 @@ +package ca.uhn.fhir.jpa.model.pkspike.idclass; + +import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; +import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate; +import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +/** + * Spike to assess variable binding against a db. + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + IdClassKeyTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class +}) +public class IdClassPkJpaBindingTest { + + @Nested + class Common extends BasicEntityTestTemplate { + Common() { + super(EntityFixture.build(ResRootIdClassEntity.class, ResJoinIdClassEntity.class)); + } + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java similarity index 62% rename from hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java rename to hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java index 2bca19affb5..f20a9c1880b 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResJoinCompositeEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java @@ -1,5 +1,6 @@ -package ca.uhn.fhir.jpa.model.pkspike.composite; +package ca.uhn.fhir.jpa.model.pkspike.idclass; +import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -16,7 +17,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder; @Table( name = "RES_JOIN" ) -public class ResJoinCompositeEntity { +public class ResJoinIdClassEntity implements EntityFixture.IJoinEntity { @Id // @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.SEQUENCE) @@ -35,11 +36,40 @@ public class ResJoinCompositeEntity { @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID"), @JoinColumn(name = "PARTITION_ID", referencedColumnName = "PARTITION_ID") }) - ResRootCompositeEntity myResource; + ResRootIdClassEntity myResource; @Override public String toString() { return ToStringBuilder.reflectionToString(this); } + @Override + public void setString(String theString) { + myString = theString; + } + + @Override + public void setParent(ResRootIdClassEntity theRoot) { + myResource = theRoot; + } + + @Override + public String getString() { + return myString; + } + + @Override + public void setPartitionId(Integer thePartitionId) { + myPartitionId = thePartitionId; + } + + @Override + public Integer getPartitionId() { + return myPartitionId; + } + + @Override + public Long getResId() { + return myId; + } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResRootIdClassEntity.java similarity index 73% rename from hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java rename to hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResRootIdClassEntity.java index a60acd64de9..fba72353c4b 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/composite/ResRootCompositeEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResRootIdClassEntity.java @@ -1,5 +1,6 @@ -package ca.uhn.fhir.jpa.model.pkspike.composite; +package ca.uhn.fhir.jpa.model.pkspike.idclass; +import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -16,6 +17,7 @@ import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; +import java.util.List; /** * fixme MB IdClass vs embeddable? @@ -24,9 +26,9 @@ import java.util.Collection; @SuppressWarnings("JpaDataSourceORMInspection") @Entity @Table(name = "RES_ROOT") -@IdClass(ResRootCompositeEntity.ResRootPK.class) -public class ResRootCompositeEntity { - private static final Logger ourLog = LoggerFactory.getLogger(ResRootCompositeEntity.class); +@IdClass(ResRootIdClassEntity.ResRootPK.class) +public class ResRootIdClassEntity implements EntityFixture.IRootEntity { + private static final Logger ourLog = LoggerFactory.getLogger(ResRootIdClassEntity.class); @Id @GeneratedValue(strategy = GenerationType.AUTO) @@ -45,9 +47,9 @@ public class ResRootCompositeEntity { String myString; @OneToMany(mappedBy = "myResource") - Collection myJoinEntities = new ArrayList<>(); + Collection myJoinEntities = new ArrayList<>(); - public ResRootCompositeEntity() { + public ResRootIdClassEntity() { ourLog.info("new ResRootCompositeEntity()"); } @@ -55,6 +57,21 @@ public class ResRootCompositeEntity { return myId; } + @Override + public Long getResId() { + return myId; + } + + @Override + public void setPartitionId(Integer thePartitionId) { + myPartitionId = thePartitionId; + } + + @Override + public Integer getPartitionId() { + return myPartitionId; + } + public String getString() { return myString; } @@ -63,6 +80,11 @@ public class ResRootCompositeEntity { myString = theString; } + @Override + public Collection getJoins() { + return myJoinEntities; + } + @Override public String toString() { return ToStringBuilder.reflectionToString(this); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java new file mode 100644 index 00000000000..a54c80be1c3 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java @@ -0,0 +1,45 @@ +package ca.uhn.fhir.jpa.model.pkspike.partitionkey; + +import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; +import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate; +import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; +import ca.uhn.fhir.jpa.model.pkspike.SchemaCleanerExtension; +import jakarta.annotation.Nonnull; +import jakarta.inject.Inject; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.orm.jpa.EntityManagerFactoryUtils; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; +import org.springframework.transaction.support.TransactionTemplate; + +import javax.sql.DataSource; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Spike to assess variable binding against a db. + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + PartitionTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class +}) +class PartitionJpaBindingTest { + @Nested + class Common extends BasicEntityTestTemplate { + Common() { + super(EntityFixture.build(ResRootPartitionEntity.class, ResJoinPartitionEntity.class)); + } + } + +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionTypesConfig.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionTypesConfig.java new file mode 100644 index 00000000000..2d64ee5c8a2 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionTypesConfig.java @@ -0,0 +1,17 @@ +package ca.uhn.fhir.jpa.model.pkspike.partitionkey; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.orm.jpa.persistenceunit.PersistenceManagedTypes; + +@Configuration +public class PartitionTypesConfig { + @Bean + PersistenceManagedTypes getManagedTypes() { + return PersistenceManagedTypes.of( + ResRootPartitionEntity.class.getName(), + ResJoinPartitionEntity.class.getName() + ); + } + +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java new file mode 100644 index 00000000000..60cd300ec64 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java @@ -0,0 +1,83 @@ +package ca.uhn.fhir.jpa.model.pkspike.partitionkey; + +import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.JoinColumns; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.Table; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.JoinColumnOrFormula; +import org.hibernate.annotations.JoinColumnsOrFormulas; +import org.hibernate.annotations.JoinFormula; +import org.hibernate.annotations.PartitionKey; + +@SuppressWarnings("JpaDataSourceORMInspection") +@Entity +@Table( + name = "RES_JOIN" +) +public class ResJoinPartitionEntity implements EntityFixture.IJoinEntity { + @Id +// @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) + @GeneratedValue(strategy = GenerationType.SEQUENCE) + @Column(name = "PID") + Long myId; + @PartitionKey + @Column(name = "PARTITION_ID", nullable = true, insertable = false, updatable = false) + Integer myPartitionId; + + @Column(name = "STRING_COL") + String myString; + + @Column(name = "RES_ID", nullable = false, insertable = false, updatable = false) + Long myResId; + + @ManyToOne( + optional = false) + @JoinColumns({ + @JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, insertable = true, updatable = false), + @JoinColumn(name = "PARTITION_ID", referencedColumnName = "PARTITION_ID", nullable = true, insertable = true, updatable = false) + }) + ResRootPartitionEntity myResource; + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE); + } + + @Override + public void setString(String theString) { + myString = theString; + } + + @Override + public void setParent(ResRootPartitionEntity theRoot) { + myResource = theRoot; + } + + @Override + public String getString() { + return myString; + } + + @Override + public void setPartitionId(Integer thePartitionId) { + myPartitionId = thePartitionId; + } + + @Override + public Integer getPartitionId() { + return myPartitionId; + } + + @Override + public Long getResId() { + return myResId; + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResRootPartitionEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResRootPartitionEntity.java new file mode 100644 index 00000000000..f6244cbc02a --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResRootPartitionEntity.java @@ -0,0 +1,75 @@ +package ca.uhn.fhir.jpa.model.pkspike.partitionkey; + +import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.PartitionKey; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@SuppressWarnings("JpaDataSourceORMInspection") +@Entity +@Table(name = "RES_ROOT") +public class ResRootPartitionEntity implements EntityFixture.IRootEntity { + @Id + @GeneratedValue() + @Column(name = "RES_ID") + Long myId; + + @PartitionKey + @Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false) + Integer myPartitionId; + + @Column(name = "STRING_COL") + String myString; + + @OneToMany(mappedBy = "myResource", fetch = FetchType.EAGER) + Collection myJoinEntities = new ArrayList<>(); + + public Long getId() { + return myId; + } + + public String getString() { + return myString; + } + + public void setString(String theString) { + myString = theString; + } + + @Override + public Collection getJoins() { + return myJoinEntities; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE); + } + + @Override + public void setPartitionId(Integer thePartitionId) { + myPartitionId = thePartitionId; + } + + @Override + public Integer getPartitionId() { + return myPartitionId; + } + + @Override + public Long getResId() { + return myId; + } + +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java index 71f574c224f..78a08de2bfb 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.model.pkspike.primitive; +import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -16,18 +17,21 @@ import org.apache.commons.lang3.builder.ToStringStyle; @Table( name = "RES_JOIN" ) -public class ResJoinEntity { +public class ResJoinEntity implements EntityFixture.IJoinEntity { @Id // @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.SEQUENCE) @Column(name = "PID") Long myId; @Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false) - Long myPartitionId; + Integer myPartitionId; @Column(name = "STRING_COL") String myString; + @Column(name = "RES_ID", nullable = false, insertable = false, updatable = false) + Long myResId; + @ManyToOne( optional = false) @JoinColumn( @@ -37,8 +41,38 @@ public class ResJoinEntity { updatable = false) ResRootEntity myResource; + @Override + public Long getResId() { + return myResId; + } + @Override public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE); } + + @Override + public void setString(String theString) { + myString = theString; + } + + @Override + public void setParent(ResRootEntity theRoot) { + myResource = theRoot; + } + + @Override + public String getString() { + return myString; + } + + @Override + public void setPartitionId(Integer thePartitionId) { + myPartitionId = thePartitionId; + } + + @Override + public Integer getPartitionId() { + return myPartitionId; + } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java index 2ff855fe42b..4c2bee8b089 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.model.pkspike.primitive; +import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -12,18 +13,19 @@ import org.apache.commons.lang3.builder.ToStringStyle; import java.util.ArrayList; import java.util.Collection; +import java.util.List; @SuppressWarnings("JpaDataSourceORMInspection") @Entity @Table(name = "RES_ROOT") -public class ResRootEntity { +public class ResRootEntity implements EntityFixture.IRootEntity { @Id @GeneratedValue() @Column(name = "RES_ID") Long myId; @Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false) - Long myPartitionId; + Integer myPartitionId; @Column(name = "STRING_COL") String myString; @@ -35,6 +37,21 @@ public class ResRootEntity { return myId; } + @Override + public Long getResId() { + return myId; + } + + @Override + public void setPartitionId(Integer thePartitionId) { + myPartitionId = thePartitionId; + } + + @Override + public Integer getPartitionId() { + return myPartitionId; + } + public String getString() { return myString; } @@ -43,6 +60,11 @@ public class ResRootEntity { myString = theString; } + @Override + public Collection getJoins() { + return myJoinEntities; + } + @Override public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java index 3684621ab3d..134f9d37ad9 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java @@ -1,19 +1,19 @@ package ca.uhn.fhir.jpa.model.pkspike.primitive; import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; +import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate; +import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; import ca.uhn.fhir.jpa.model.pkspike.SchemaCleanerExtension; import jakarta.annotation.Nonnull; import jakarta.inject.Inject; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.RegisterExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.orm.jpa.EntityManagerFactoryUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -22,8 +22,6 @@ import org.springframework.transaction.support.TransactionTemplate; import javax.sql.DataSource; import java.util.Objects; -import static org.junit.jupiter.api.Assertions.assertEquals; - /** * Spike to assess variable binding against a db. */ @@ -34,78 +32,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class SimplePkJpaBindingTest { private static final Logger ourLog = LoggerFactory.getLogger(SimplePkJpaBindingTest.class); - @Inject - DataSource myDataSource; - - @Inject - EntityManagerFactory myEntityManagerFactory; - - @Inject - TransactionTemplate myTransactionTemplate; - JdbcTemplate myJdbcTemplate; - @RegisterExtension SchemaCleanerExtension mySchemaCleanerExtension = new SchemaCleanerExtension(); - @BeforeEach - void setUp() { - myJdbcTemplate = new JdbcTemplate(myDataSource); + @Nested + class Common extends BasicEntityTestTemplate { + Common() { + super(EntityFixture.build(ResRootEntity.class, ResJoinEntity.class)); + } } - @Test - void roundTripResourceTable() { - // given - myJdbcTemplate.execute("insert into res_root values (-1, -1, 'hello!')"); - - myTransactionTemplate.execute(status -> { - var em = getEntityManagerOrThrow(); - long count = em.createQuery("select count(*) from ResRootEntity ", Long.class).getSingleResult(); - assertEquals(1, count); - - em.createQuery("from ResRootEntity", ResRootEntity.class).getResultStream().forEach(e-> { - assertEquals(-1, e.myId); - assertEquals(-1, e.myPartitionId); - assertEquals("hello!", e.getString()); - }); - return true; - }); - } - - @Test - void roundTripJoin() { - // given - - myTransactionTemplate.execute(status -> { - var em = getEntityManagerOrThrow(); - - ResRootEntity resRootEntity = new ResRootEntity(); - resRootEntity.setString("hello world!"); - - ResJoinEntity join = new ResJoinEntity(); - join.myResource = resRootEntity; - join.myString="child"; - - em.persist(resRootEntity); - em.persist(join); - em.flush(); - em.clear(); - - ResRootEntity queryBack = em.find(ResRootEntity.class, resRootEntity.myId); - assertEquals("hello world!", queryBack.myString); - assertEquals(1, queryBack.myJoinEntities.size()); - ResJoinEntity child = queryBack.myJoinEntities.iterator().next(); - assertEquals(resRootEntity.myId, child.myResource.myId); - assertEquals(resRootEntity.myPartitionId, child.myPartitionId); - assertEquals("child", child.myString); - - long count = em.createQuery("select count(*) from ResRootEntity", Long.class).getSingleResult(); - ourLog.info("found {} roots", count); - assertEquals(1, count); - return true; - }); - } - - @Nonnull EntityManager getEntityManagerOrThrow() { - return Objects.requireNonNull(EntityManagerFactoryUtils.getTransactionalEntityManager(myEntityManagerFactory)); - } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/readme.md b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/readme.md index 795097857ae..bd85d9b66eb 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/readme.md +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/readme.md @@ -1,2 +1,14 @@ -Hibernate 6 IdClass can have no NULL parts! See AbstractNonAggregatedIdentifierMappingInitializer#233 + +Possible solutions: + +Use IdClass +:Hibernate 6 IdClass can have no NULL parts! See AbstractNonAggregatedIdentifierMappingInitializer#233 + +Use EmbeddedId +:Same problem with EmbeddedId - null partition id means can't query/read! + +What next? +- @PartitionKey !!!! +- try user-type wrapper around nullable partition id? +- Try overriding IdClass with persistence.xml? From 288eac714a219f4c0b16e8227044462c4f2a2211 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Thu, 22 Aug 2024 15:43:38 -0400 Subject: [PATCH 09/17] Add join via null partition id --- .../pkspike/BasicEntityTestTemplate.java | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java index c14f117d353..16c5d6f17b7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java @@ -7,6 +7,9 @@ import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.orm.jpa.EntityManagerFactoryUtils; @@ -80,17 +83,19 @@ abstract public class BasicEntityTestTemplate{ var root = myEntityFixture.buildRootEntity(); - root.setPartitionId(12); + root.setPartitionId(thePartitionId); root.setString("parent"); var join = myEntityFixture.buildJoinEntity(); join.setParent(root); join.setString("child"); - join.setPartitionId(12); + join.setPartitionId(thePartitionId); em.persist(root); em.persist(join); @@ -102,7 +107,7 @@ abstract public class BasicEntityTestTemplate theCallback) { myTransactionTemplate.execute(status-> { theCallback.accept(getEntityManagerOrThrow()); From cfdb661f674ac75f2ee5d1ca4e266c8b88f85c6f Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Thu, 22 Aug 2024 16:12:15 -0400 Subject: [PATCH 10/17] add null cases --- .../pkspike/BasicEntityTestTemplate.java | 30 +++++++++++++++---- .../embeddedid/ResRootEmbeddedIdEntity.java | 7 ++++- .../pkspike/idclass/ResRootIdClassEntity.java | 7 ++++- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java index 16c5d6f17b7..9bc1e58021d 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java @@ -10,11 +10,15 @@ import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.NullSource; import org.junit.jupiter.params.provider.ValueSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.orm.jpa.EntityManagerFactoryUtils; import org.springframework.transaction.support.TransactionTemplate; +import java.util.Collection; +import java.util.List; import java.util.Objects; import java.util.function.Consumer; @@ -22,6 +26,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; abstract public class BasicEntityTestTemplate,J extends EntityFixture.IJoinEntity> { + private static final Logger ourLog = LoggerFactory.getLogger(BasicEntityTestTemplate.class); + @Autowired EntityManagerFactory myEntityManagerFactory; @@ -40,6 +46,10 @@ abstract public class BasicEntityTestTemplate getPartitions() { + return List.of(null, 12); + } + @Test void rootEntityBoundToTable() { // given @@ -62,11 +72,13 @@ abstract public class BasicEntityTestTemplate{ R root = myEntityFixture.buildRootEntity(); - root.setPartitionId(12); + root.setPartitionId(thePartitionId); root.setString("goodbye!"); em.persist(root); @@ -74,10 +86,12 @@ abstract public class BasicEntityTestTemplate joins = readback.getJoins(); + assertNotNull(joins); + assertEquals(1, joins.size()); + J joinReadback = joins.iterator().next(); assertNotNull(joinReadback); assertNotNull(joinReadback.getResId()); assertEquals(root.getResId(), joinReadback.getResId()); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResRootEmbeddedIdEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResRootEmbeddedIdEntity.java index 43b7436d665..9d5f8e26611 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResRootEmbeddedIdEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResRootEmbeddedIdEntity.java @@ -11,12 +11,12 @@ import jakarta.persistence.Table; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.ArrayList; import java.util.Collection; -import java.util.List; /** * fixme MB IdClass vs embeddable? @@ -106,6 +106,11 @@ public class ResRootEmbeddedIdEntity implements EntityFixture.IRootEntity Date: Fri, 23 Aug 2024 12:42:01 -0400 Subject: [PATCH 11/17] add fetch join test --- .../pkspike/BasicEntityTestTemplate.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java index 9bc1e58021d..ff768386388 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java @@ -5,6 +5,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; @@ -138,6 +139,25 @@ abstract public class BasicEntityTestTemplate { + CriteriaBuilder cb = em.getCriteriaBuilder(); + + CriteriaQuery cr = cb.createQuery(myEntityFixture.myRootType); + Root from = cr.from(myEntityFixture.myRootType); + from.fetch("myJoinEntities"); + cr.select(from); + + em.createQuery(cr).getResultStream() + .forEach(Object::toString); + + }); + } + + private void doInTx(Consumer theCallback) { myTransactionTemplate.execute(status-> { theCallback.accept(getEntityManagerOrThrow()); From 38aad43474756fc0260c22cd70dbe81ef49c4be5 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Fri, 23 Aug 2024 12:42:42 -0400 Subject: [PATCH 12/17] orm.xml override --- .../pkspike/PKSpikeDefaultJPAConfig.java | 27 +++++++----- .../IdClassPkCustomXmlJpaBindingTest.java | 42 +++++++++++++++++++ .../pkspike/idclass/overrideMappings.xml | 7 ++++ .../pkspike/idclass/persistenceOverride.xml | 24 +++++++++++ 4 files changed, 90 insertions(+), 10 deletions(-) create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/overrideMappings.xml create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/persistenceOverride.xml diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java index c28791b73d3..a5d1ff202de 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/PKSpikeDefaultJPAConfig.java @@ -6,10 +6,12 @@ import ca.uhn.fhir.jpa.config.HapiFhirHibernateJpaDialect; import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; import ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect; import jakarta.annotation.Nonnull; +import jakarta.annotation.Nullable; import jakarta.inject.Inject; import jakarta.persistence.EntityManagerFactory; import org.apache.commons.dbcp2.BasicDataSource; import org.hibernate.jpa.HibernatePersistenceProvider; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -22,6 +24,7 @@ import org.springframework.transaction.support.TransactionTemplate; import javax.sql.DataSource; import java.time.Duration; import java.util.Properties; +import java.util.function.Consumer; @Configuration public class PKSpikeDefaultJPAConfig { @@ -50,13 +53,15 @@ public class PKSpikeDefaultJPAConfig { // ModuleMigrationMetadata theModuleMigrationMetadata, ConfigurableListableBeanFactory theConfigurableListableBeanFactory, DataSource theDataSource, - PersistenceManagedTypes theManagedTypes) { - HapiFhirLocalContainerEntityManagerFactoryBean retVal = + PersistenceManagedTypes theManagedTypes, + @Autowired(required = false) @Nullable Consumer theEntityManagerFactoryCustomizer) { + + HapiFhirLocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new HapiFhirLocalContainerEntityManagerFactoryBean(theConfigurableListableBeanFactory); - retVal.setJpaDialect(new HapiFhirHibernateJpaDialect(myFhirContext.getLocalizer())); + entityManagerFactoryBean.setJpaDialect(new HapiFhirHibernateJpaDialect(myFhirContext.getLocalizer())); HibernatePersistenceProvider persistenceProvider = new HibernatePersistenceProvider(); - retVal.setPersistenceProvider(persistenceProvider); + entityManagerFactoryBean.setPersistenceProvider(persistenceProvider); Properties jpaProperties = new Properties(); jpaProperties.put("hibernate.search.enabled", "false"); jpaProperties.put("hibernate.format_sql", "false"); @@ -64,15 +69,17 @@ public class PKSpikeDefaultJPAConfig { jpaProperties.put("hibernate.integration.envers.enabled=false", "false"); jpaProperties.put("hibernate.hbm2ddl.auto", "none"); jpaProperties.put("hibernate.dialect", HapiFhirH2Dialect.class.getName()); - retVal.setJpaProperties(jpaProperties); + entityManagerFactoryBean.setJpaProperties(jpaProperties); - retVal.setPersistenceUnitName("HapiPU"); - retVal.setDataSource(theDataSource); - retVal.setManagedTypes(theManagedTypes); + entityManagerFactoryBean.setPersistenceUnitName("HapiPU"); + entityManagerFactoryBean.setDataSource(theDataSource); + entityManagerFactoryBean.setManagedTypes(theManagedTypes); - retVal.setJpaProperties(jpaProperties); + if (theEntityManagerFactoryCustomizer != null) { + theEntityManagerFactoryCustomizer.accept(entityManagerFactoryBean); + } - return retVal; + return entityManagerFactoryBean; } @Bean diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java new file mode 100644 index 00000000000..04ada148995 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java @@ -0,0 +1,42 @@ +package ca.uhn.fhir.jpa.model.pkspike.idclass; + +import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; +import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; +import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate; +import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import java.util.function.Consumer; + +/** + * Spike to assess variable binding against a db. + */ +@ExtendWith(SpringExtension.class) +@ContextConfiguration(classes = { + IdClassPkCustomXmlJpaBindingTest.Config.class, IdClassKeyTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class +}) +public class IdClassPkCustomXmlJpaBindingTest { + @Configuration + static class Config { + @Bean + Consumer entityManagerFactoryCustomizer() { + return em->{ + em.setPersistenceXmlLocation("classpath:/ca/uhn/fhir/jpa/model/pkspike/idclass/persistenceOverride.xml"); + }; + } + + } + + @Nested + class Common extends BasicEntityTestTemplate { + Common() { + super(EntityFixture.build(ResRootIdClassEntity.class, ResJoinIdClassEntity.class)); + } + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/overrideMappings.xml b/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/overrideMappings.xml new file mode 100644 index 00000000000..4dd3b6f2a35 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/overrideMappings.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/persistenceOverride.xml b/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/persistenceOverride.xml new file mode 100644 index 00000000000..b3b19041c32 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/persistenceOverride.xml @@ -0,0 +1,24 @@ + + + ./overrideMappings.xml + + + + + + From dba2b0bc33ce0a43fecf04fe45ba257aff49c7a2 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Fri, 23 Aug 2024 17:09:14 -0400 Subject: [PATCH 13/17] Working example using orm.xml over annotations --- .../pkspike/BasicEntityTestTemplate.java | 49 ++++++++++++++----- .../fhir/jpa/model/pkspike/EntityFixture.java | 20 ++++++-- .../ValueTypeBasedParameterResolver.java | 25 ++++++++++ .../EmbeddedIdPkJpaBindingTest.java | 32 ++---------- .../embeddedid/ResJoinEmbeddedIdEntity.java | 11 ++++- .../embeddedid/ResRootEmbeddedIdEntity.java | 2 +- .../IdClassPkCustomXmlJpaBindingTest.java | 16 +++++- .../idclass/IdClassPkJpaBindingTest.java | 13 +++-- .../pkspike/idclass/ResJoinIdClassEntity.java | 14 ++++-- .../pkspike/idclass/ResRootIdClassEntity.java | 39 ++++++++++----- .../partitionkey/PartitionJpaBindingTest.java | 27 ++++------ .../partitionkey/ResJoinPartitionEntity.java | 10 ++-- .../partitionkey/ResRootPartitionEntity.java | 3 +- .../pkspike/primitive/ResJoinEntity.java | 7 ++- .../pkspike/primitive/ResRootEntity.java | 3 +- .../primitive/SimplePkJpaBindingTest.java | 20 ++------ .../fhir/jpa/model/pkspike/idclass/orm.xml | 38 ++++++++++++++ .../pkspike/idclass/overrideMappings.xml | 7 --- ...ersistenceOverride.xml => persistence.xml} | 0 19 files changed, 217 insertions(+), 119 deletions(-) create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ValueTypeBasedParameterResolver.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/orm.xml delete mode 100644 hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/overrideMappings.xml rename hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/{persistenceOverride.xml => persistence.xml} (100%) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java index ff768386388..dd4a1bf744f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java @@ -9,8 +9,7 @@ import jakarta.persistence.criteria.Root; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullSource; -import org.junit.jupiter.params.provider.ValueSource; +import org.junit.jupiter.params.provider.MethodSource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -18,6 +17,7 @@ import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.orm.jpa.EntityManagerFactoryUtils; import org.springframework.transaction.support.TransactionTemplate; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; @@ -47,8 +47,13 @@ abstract public class BasicEntityTestTemplate getPartitions() { - return List.of(null, 12); + static List getPartitions(EntityFixture theFixture) { + var result = new ArrayList(); + if (theFixture.isSupportNullPartitionId()) { + result.add(null); + } + result.add(12); + return result; } @Test @@ -74,8 +79,7 @@ abstract public class BasicEntityTestTemplate{ R root = myEntityFixture.buildRootEntity(); @@ -99,8 +103,7 @@ abstract public class BasicEntityTestTemplate{ var root = myEntityFixture.buildRootEntity(); @@ -140,19 +143,39 @@ abstract public class BasicEntityTestTemplate { - CriteriaBuilder cb = em.getCriteriaBuilder(); + var root0 = myEntityFixture.buildRootEntity(); + root0.setPartitionId(thePartitionId); + root0.setString("parent"); + var join0 = myEntityFixture.buildJoinEntity(); + join0.setParent(root0); + join0.setString("child"); + join0.setPartitionId(thePartitionId); + + var join1 = myEntityFixture.buildJoinEntity(); + join1.setParent(root0); + join1.setString("child1"); + join1.setPartitionId(thePartitionId); + + em.persist(root0); + em.persist(join0); + em.persist(join1); + + em.flush(); + em.clear(); + + + CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery cr = cb.createQuery(myEntityFixture.myRootType); Root from = cr.from(myEntityFixture.myRootType); from.fetch("myJoinEntities"); cr.select(from); em.createQuery(cr).getResultStream() - .forEach(Object::toString); + .forEach(e-> ourLog.info("e: {}", e)); }); } @@ -165,7 +188,7 @@ abstract public class BasicEntityTestTemplate rootType) { + private long queryCountAll(EntityManager em, Class rootType) { CriteriaBuilder qb = em.getCriteriaBuilder(); CriteriaQuery cq = qb.createQuery(Long.class); CriteriaQuery select = cq.select(qb.count(cq.from(rootType))); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/EntityFixture.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/EntityFixture.java index f960c4f47dc..6c89f7628eb 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/EntityFixture.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/EntityFixture.java @@ -2,15 +2,22 @@ package ca.uhn.fhir.jpa.model.pkspike; import jakarta.annotation.Nonnull; -import java.lang.reflect.InvocationTargetException; import java.util.Collection; -public class EntityFixture { +public class EntityFixture,J extends EntityFixture.IJoinEntity> { - public static EntityFixture build(Class theRootType, Class theJoinType) { + private boolean myNullPartitionSupportFlag = true; + + public static ,J extends EntityFixture.IJoinEntity> EntityFixture build(Class theRootType, Class theJoinType) { return new EntityFixture<>(theRootType, theJoinType); } + public static ,J extends EntityFixture.IJoinEntity> EntityFixture buildNoNullPartition(Class theRootType, Class theJoinType) { + EntityFixture entityFixture = new EntityFixture<>(theRootType, theJoinType); + entityFixture.myNullPartitionSupportFlag = false; + return entityFixture; + } + EntityFixture(Class theRootType, Class theJoinType) { myRootType = theRootType; myJoinType = theJoinType; @@ -20,6 +27,10 @@ public class EntityFixture { Long getResId(); void setPartitionId(Integer thePartitionId); @@ -29,7 +40,8 @@ public class EntityFixture getJoins(); } - public interface IJoinEntity

{ + public interface IJoinEntity

{ + Long getPid(); void setString(String theString); void setParent(P theRoot); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ValueTypeBasedParameterResolver.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ValueTypeBasedParameterResolver.java new file mode 100644 index 00000000000..b164f5976a5 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ValueTypeBasedParameterResolver.java @@ -0,0 +1,25 @@ +package ca.uhn.fhir.jpa.model.pkspike; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; + +public class ValueTypeBasedParameterResolver implements ParameterResolver { + + private final T myValue; + + public ValueTypeBasedParameterResolver(T theValue) { + myValue = theValue; + } + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { + return parameterContext.getParameter().getType().isAssignableFrom(myValue.getClass()); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { + return myValue; + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java index b2784190bc6..4ef31196c43 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java @@ -4,38 +4,13 @@ import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate; import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; -import ca.uhn.fhir.jpa.model.pkspike.SchemaCleanerExtension; -import ca.uhn.fhir.jpa.model.pkspike.idclass.ResJoinIdClassEntity; -import ca.uhn.fhir.jpa.model.pkspike.idclass.ResRootIdClassEntity; -import jakarta.annotation.Nonnull; -import jakarta.inject.Inject; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; -import org.junit.jupiter.api.BeforeEach; +import ca.uhn.fhir.jpa.model.pkspike.ValueTypeBasedParameterResolver; import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ParameterResolver; import org.junit.jupiter.api.extension.RegisterExtension; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.NullSource; -import org.junit.jupiter.params.provider.ValueSource; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.orm.jpa.EntityManagerFactoryUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.transaction.support.TransactionTemplate; - -import javax.sql.DataSource; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.Objects; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertNotNull; /** * Spike to assess variable binding against a db. @@ -46,6 +21,9 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; }) public class EmbeddedIdPkJpaBindingTest { + @RegisterExtension + static final ParameterResolver ourResolver = new ValueTypeBasedParameterResolver<>(EntityFixture.buildNoNullPartition(ResRootEmbeddedIdEntity.class, ResJoinEmbeddedIdEntity.class)); + @Nested class Common extends BasicEntityTestTemplate { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResJoinEmbeddedIdEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResJoinEmbeddedIdEntity.java index b900464ac83..de735c1a87b 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResJoinEmbeddedIdEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResJoinEmbeddedIdEntity.java @@ -25,7 +25,8 @@ public class ResJoinEmbeddedIdEntity implements EntityFixture.IJoinEntity entityManagerFactoryCustomizer() { return em->{ - em.setPersistenceXmlLocation("classpath:/ca/uhn/fhir/jpa/model/pkspike/idclass/persistenceOverride.xml"); + ourLog.info("Injecting custom persistence.xml"); + em.setMappingResources("/ca/uhn/fhir/jpa/model/pkspike/idclass/orm.xml"); }; } } + static final EntityFixture ourFixture = EntityFixture.buildNoNullPartition(ResRootIdClassEntity.class, ResJoinIdClassEntity.class); + @RegisterExtension + static final ParameterResolver ourFixtureResolver = new ValueTypeBasedParameterResolver<>(ourFixture); + @Nested class Common extends BasicEntityTestTemplate { Common() { - super(EntityFixture.build(ResRootIdClassEntity.class, ResJoinIdClassEntity.class)); + super(ourFixture); } } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java index b93195994b8..20b009107b7 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java @@ -4,15 +4,14 @@ import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate; import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; +import ca.uhn.fhir.jpa.model.pkspike.ValueTypeBasedParameterResolver; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertInstanceOf; -import static org.junit.jupiter.api.Assertions.assertNotNull; - /** * Spike to assess variable binding against a db. */ @@ -22,10 +21,14 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; }) public class IdClassPkJpaBindingTest { + public static final EntityFixture ourFixture = EntityFixture.build(ResRootIdClassEntity.class, ResJoinIdClassEntity.class); + @RegisterExtension + static final ParameterResolver ourFixtureResolver = new ValueTypeBasedParameterResolver<>(ourFixture); + @Nested class Common extends BasicEntityTestTemplate { Common() { - super(EntityFixture.build(ResRootIdClassEntity.class, ResJoinIdClassEntity.class)); + super(ourFixture); } } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java index f20a9c1880b..3d6c9c74dae 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java @@ -19,11 +19,11 @@ import org.apache.commons.lang3.builder.ToStringBuilder; ) public class ResJoinIdClassEntity implements EntityFixture.IJoinEntity { @Id -// @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.SEQUENCE) @Column(name = "PID") Long myId; - @Column(name = "PARTITION_ID", nullable = true, insertable = false, updatable = false) + + @Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false) Integer myPartitionId; @Column(name = "STRING_COL") @@ -34,7 +34,7 @@ public class ResJoinIdClassEntity implements EntityFixture.IJoinEntity ourConfig = EntityFixture.buildNoNullPartition(ResRootPartitionEntity.class, ResJoinPartitionEntity.class); + + @RegisterExtension + static final ParameterResolver ourResolver = new ValueTypeBasedParameterResolver(ourConfig); + @Nested class Common extends BasicEntityTestTemplate { Common() { - super(EntityFixture.build(ResRootPartitionEntity.class, ResJoinPartitionEntity.class)); + super(ourConfig); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java index 60cd300ec64..c6af6461eb8 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java @@ -12,9 +12,6 @@ import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; -import org.hibernate.annotations.JoinColumnOrFormula; -import org.hibernate.annotations.JoinColumnsOrFormulas; -import org.hibernate.annotations.JoinFormula; import org.hibernate.annotations.PartitionKey; @SuppressWarnings("JpaDataSourceORMInspection") @@ -48,7 +45,12 @@ public class ResJoinPartitionEntity implements EntityFixture.IJoinEntity { @Override public String toString() { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE); + return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } + + @Override + public Long getPid() { + return myId; } @Override diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java index 4c2bee8b089..d431ddd318c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java @@ -13,7 +13,6 @@ import org.apache.commons.lang3.builder.ToStringStyle; import java.util.ArrayList; import java.util.Collection; -import java.util.List; @SuppressWarnings("JpaDataSourceORMInspection") @Entity @@ -67,6 +66,6 @@ public class ResRootEntity implements EntityFixture.IRootEntity { @Override public String toString() { - return ToStringBuilder.reflectionToString(this, ToStringStyle.SIMPLE_STYLE); + return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java index 134f9d37ad9..5cac33f9d24 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java @@ -4,23 +4,13 @@ import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate; import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; -import ca.uhn.fhir.jpa.model.pkspike.SchemaCleanerExtension; -import jakarta.annotation.Nonnull; -import jakarta.inject.Inject; -import jakarta.persistence.EntityManager; -import jakarta.persistence.EntityManagerFactory; +import ca.uhn.fhir.jpa.model.pkspike.ValueTypeBasedParameterResolver; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ParameterResolver; import org.junit.jupiter.api.extension.RegisterExtension; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.orm.jpa.EntityManagerFactoryUtils; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.transaction.support.TransactionTemplate; - -import javax.sql.DataSource; -import java.util.Objects; /** * Spike to assess variable binding against a db. @@ -30,15 +20,15 @@ import java.util.Objects; SimpleTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class }) public class SimplePkJpaBindingTest { - private static final Logger ourLog = LoggerFactory.getLogger(SimplePkJpaBindingTest.class); + public static final EntityFixture ourFixture = EntityFixture.build(ResRootEntity.class, ResJoinEntity.class); @RegisterExtension - SchemaCleanerExtension mySchemaCleanerExtension = new SchemaCleanerExtension(); + static final ParameterResolver ourFixtureResolver = new ValueTypeBasedParameterResolver<>(ourFixture); @Nested class Common extends BasicEntityTestTemplate { Common() { - super(EntityFixture.build(ResRootEntity.class, ResJoinEntity.class)); + super(ourFixture); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/orm.xml b/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/orm.xml new file mode 100644 index 00000000000..23eb41a375a --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/orm.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/overrideMappings.xml b/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/overrideMappings.xml deleted file mode 100644 index 4dd3b6f2a35..00000000000 --- a/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/overrideMappings.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/persistenceOverride.xml b/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/persistence.xml similarity index 100% rename from hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/persistenceOverride.xml rename to hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/persistence.xml From 227732bcd224332bd9d84b6eb0ee0be5b0b37e93 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Fri, 23 Aug 2024 18:46:29 -0400 Subject: [PATCH 14/17] Notes --- .../pkspike/BasicEntityTestTemplate.java | 2 +- .../ValueTypeBasedParameterResolver.java | 10 ++++++- .../EmbeddedIdPkJpaBindingTest.java | 6 ++-- .../IdClassPkCustomXmlJpaBindingTest.java | 4 +-- .../idclass/IdClassPkJpaBindingTest.java | 5 ++-- .../fhir/jpa/model/pkspike/package-info.java | 28 +++++++++++++++++++ .../partitionkey/PartitionJpaBindingTest.java | 9 +++--- .../partitionkey/ResJoinPartitionEntity.java | 5 ++-- .../primitive/SimplePkJpaBindingTest.java | 4 +-- .../ca/uhn/fhir/jpa/model/pkspike/readme.md | 14 ---------- 10 files changed, 56 insertions(+), 31 deletions(-) delete mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/readme.md diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java index dd4a1bf744f..073358dd5ab 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java @@ -47,7 +47,7 @@ abstract public class BasicEntityTestTemplate getPartitions(EntityFixture theFixture) { + static List getPartitions(EntityFixture theFixture) { var result = new ArrayList(); if (theFixture.isSupportNullPartitionId()) { result.add(null); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ValueTypeBasedParameterResolver.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ValueTypeBasedParameterResolver.java index b164f5976a5..9ab2bada6c0 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ValueTypeBasedParameterResolver.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/ValueTypeBasedParameterResolver.java @@ -9,7 +9,11 @@ public class ValueTypeBasedParameterResolver implements ParameterResolver { private final T myValue; - public ValueTypeBasedParameterResolver(T theValue) { + public static ValueTypeBasedParameterResolver build(T theValue) { + return new ValueTypeBasedParameterResolver<>(theValue); + } + + ValueTypeBasedParameterResolver(T theValue) { myValue = theValue; } @@ -22,4 +26,8 @@ public class ValueTypeBasedParameterResolver implements ParameterResolver { public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { return myValue; } + + public T get() { + return myValue; + } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java index 4ef31196c43..12dfc8d44ea 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java @@ -21,14 +21,16 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; }) public class EmbeddedIdPkJpaBindingTest { + static final EntityFixture ourFixture = EntityFixture.buildNoNullPartition(ResRootEmbeddedIdEntity.class, ResJoinEmbeddedIdEntity.class); + @RegisterExtension - static final ParameterResolver ourResolver = new ValueTypeBasedParameterResolver<>(EntityFixture.buildNoNullPartition(ResRootEmbeddedIdEntity.class, ResJoinEmbeddedIdEntity.class)); + static final ParameterResolver ourResolver = ValueTypeBasedParameterResolver.build(ourFixture); @Nested class Common extends BasicEntityTestTemplate { Common() { - super(EntityFixture.build(ResRootEmbeddedIdEntity.class, ResJoinEmbeddedIdEntity.class)); + super(ourFixture); } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java index d4049bc89bd..8774af27e2b 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java @@ -20,7 +20,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; import java.util.function.Consumer; /** - * Spike to assess variable binding against a db. + * Override the JPA annotations with an orm.xml file to add PARTITION_ID to the root PK, and the join expressions. */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { @@ -43,7 +43,7 @@ public class IdClassPkCustomXmlJpaBindingTest { static final EntityFixture ourFixture = EntityFixture.buildNoNullPartition(ResRootIdClassEntity.class, ResJoinIdClassEntity.class); @RegisterExtension - static final ParameterResolver ourFixtureResolver = new ValueTypeBasedParameterResolver<>(ourFixture); + static final ParameterResolver ourFixtureResolver = ValueTypeBasedParameterResolver.build(ourFixture); @Nested class Common extends BasicEntityTestTemplate { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java index 20b009107b7..c78b4076e93 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java @@ -13,7 +13,8 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; /** - * Spike to assess variable binding against a db. + * Use an IdClass even though the PK is only a single column. + * This allows us to extend the PK next door in the {@link IdClassPkCustomXmlJpaBindingTest}. */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { @@ -23,7 +24,7 @@ public class IdClassPkJpaBindingTest { public static final EntityFixture ourFixture = EntityFixture.build(ResRootIdClassEntity.class, ResJoinIdClassEntity.class); @RegisterExtension - static final ParameterResolver ourFixtureResolver = new ValueTypeBasedParameterResolver<>(ourFixture); + static final ParameterResolver ourFixtureResolver = ValueTypeBasedParameterResolver.build(ourFixture); @Nested class Common extends BasicEntityTestTemplate { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/package-info.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/package-info.java index dbf1e766a0f..ab12ef7e22f 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/package-info.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/package-info.java @@ -1 +1,29 @@ +/** + * Various tests of a parent-child JPA relationship exercising configurable pk definition and joins. + *

+ * We have a test template that does some basic queries ( {@link ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate}). + *

    + *
  • See {@link ca.uhn.fhir.jpa.model.pkspike.primitive.SimplePkJpaBindingTest} for the normal case. Supports null partition_id. + *
  • See {@link ca.uhn.fhir.jpa.model.pkspike.embeddedid.EmbeddedIdPkJpaBindingTest} for an embedded Id class path. Does not support null partition_id. + *
  • See {@link ca.uhn.fhir.jpa.model.pkspike.partitionkey.PartitionJpaBindingTest} for the new Hibernate 6 @{@link org.hibernate.annotations.PartitionKey} annotation. Does support null partition_id. + *
  • See {@link ca.uhn.fhir.jpa.model.pkspike.idclass.IdClassPkJpaBindingTest}. Supports null partition_id. + *
  • See {@link ca.uhn.fhir.jpa.model.pkspike.idclass.IdClassPkCustomXmlJpaBindingTest} which overrides some mappings IdClassPkJpaBindingTest by using an orm.xml file. Does not support null partition_id. + *
+ *

+ * Things we learned: + *

    + *
  • Hibernate can not fetch any entity with a composite key if any component is null. + *
  • It's a real pain to merge orm.xml with annotations when we have duplicate columns. E.g. PARTITION_ID used as a key and join fight over who should be "insert=true" vs false. + *

    + * Further hacking Ideas: + *

      + *
    • Try to wrap the partition_id column with a user type (e.g. Optional?) so we can pretend it isn't null. + *

      + *

      + * Add this to logback to explore the sql. + *

      + *   
      + *  	
      + *  
      + */ package ca.uhn.fhir.jpa.model.pkspike; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java index 2af19d7739c..464fe8a5ec1 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java @@ -13,7 +13,8 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; /** - * Spike to assess variable binding against a db. + * Try out the new @{@link org.hibernate.annotations.PartitionKey} annotation. + * This annotation annotates columns to include in entity update/delete statements, so they can be efficient in a partitioned table. */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { @@ -21,13 +22,13 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; }) class PartitionJpaBindingTest { - static final EntityFixture ourConfig = EntityFixture.buildNoNullPartition(ResRootPartitionEntity.class, ResJoinPartitionEntity.class); + static final EntityFixture ourConfig = EntityFixture.build(ResRootPartitionEntity.class, ResJoinPartitionEntity.class); @RegisterExtension - static final ParameterResolver ourResolver = new ValueTypeBasedParameterResolver(ourConfig); + static final ParameterResolver ourResolver = ValueTypeBasedParameterResolver.build(ourConfig); @Nested - class Common extends BasicEntityTestTemplate { + class Common extends BasicEntityTestTemplate { Common() { super(ourConfig); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java index c6af6461eb8..a51978cdb0e 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java @@ -26,7 +26,7 @@ public class ResJoinPartitionEntity implements EntityFixture.IJoinEntity ourFixture = EntityFixture.build(ResRootEntity.class, ResJoinEntity.class); @RegisterExtension - static final ParameterResolver ourFixtureResolver = new ValueTypeBasedParameterResolver<>(ourFixture); + static final ParameterResolver ourFixtureResolver = ValueTypeBasedParameterResolver.build(ourFixture); @Nested class Common extends BasicEntityTestTemplate { diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/readme.md b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/readme.md deleted file mode 100644 index bd85d9b66eb..00000000000 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/readme.md +++ /dev/null @@ -1,14 +0,0 @@ - -Possible solutions: - -Use IdClass -:Hibernate 6 IdClass can have no NULL parts! See AbstractNonAggregatedIdentifierMappingInitializer#233 - -Use EmbeddedId -:Same problem with EmbeddedId - null partition id means can't query/read! - -What next? -- @PartitionKey !!!! -- try user-type wrapper around nullable partition id? -- Try overriding IdClass with persistence.xml? - From 3c89c6c4ef5e6a9cc7dcf3d46e9a80127da90e12 Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Fri, 23 Aug 2024 19:08:26 -0400 Subject: [PATCH 15/17] Notes --- .../test/java/ca/uhn/fhir/jpa/model/pkspike/package-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/package-info.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/package-info.java index ab12ef7e22f..7a8cd08e981 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/package-info.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/package-info.java @@ -7,7 +7,7 @@ *
    • See {@link ca.uhn.fhir.jpa.model.pkspike.embeddedid.EmbeddedIdPkJpaBindingTest} for an embedded Id class path. Does not support null partition_id. *
    • See {@link ca.uhn.fhir.jpa.model.pkspike.partitionkey.PartitionJpaBindingTest} for the new Hibernate 6 @{@link org.hibernate.annotations.PartitionKey} annotation. Does support null partition_id. *
    • See {@link ca.uhn.fhir.jpa.model.pkspike.idclass.IdClassPkJpaBindingTest}. Supports null partition_id. - *
    • See {@link ca.uhn.fhir.jpa.model.pkspike.idclass.IdClassPkCustomXmlJpaBindingTest} which overrides some mappings IdClassPkJpaBindingTest by using an orm.xml file. Does not support null partition_id. + *
    • See {@link ca.uhn.fhir.jpa.model.pkspike.idclass.IdClassPkCustomXmlJpaBindingTest} which adds PARTITION_ID to the pk and join to the mappings defined in IdClassPkJpaBindingTest by adding an orm.xml file. Does not support null partition_id. *
    *

    * Things we learned: From d4090ae604ab6b454572e11aae57745cbabfe5ee Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Sat, 24 Aug 2024 12:33:05 -0400 Subject: [PATCH 16/17] Cleanup --- .../model/pkspike/BasicEntityTestFixture.java | 48 +++++++++++++ .../pkspike/BasicEntityTestTemplate.java | 36 +++++----- .../fhir/jpa/model/pkspike/EntityFixture.java | 71 ------------------- .../fhir/jpa/model/pkspike/IJoinEntity.java | 17 +++++ .../fhir/jpa/model/pkspike/IRootEntity.java | 17 +++++ .../EmbeddedIdPkJpaBindingTest.java | 4 +- .../embeddedid/ResJoinEmbeddedIdEntity.java | 4 +- .../embeddedid/ResRootEmbeddedIdEntity.java | 4 +- .../IdClassPkCustomXmlJpaBindingTest.java | 4 +- .../idclass/IdClassPkJpaBindingTest.java | 4 +- .../pkspike/idclass/ResJoinIdClassEntity.java | 4 +- .../pkspike/idclass/ResRootIdClassEntity.java | 4 +- .../partitionkey/PartitionJpaBindingTest.java | 4 +- .../partitionkey/ResJoinPartitionEntity.java | 4 +- .../partitionkey/ResRootPartitionEntity.java | 4 +- .../pkspike/primitive/ResJoinEntity.java | 4 +- .../pkspike/primitive/ResRootEntity.java | 4 +- .../primitive/SimplePkJpaBindingTest.java | 14 ++-- 18 files changed, 129 insertions(+), 122 deletions(-) create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestFixture.java delete mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/EntityFixture.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/IJoinEntity.java create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/IRootEntity.java diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestFixture.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestFixture.java new file mode 100644 index 00000000000..5c9e7c51a84 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestFixture.java @@ -0,0 +1,48 @@ +package ca.uhn.fhir.jpa.model.pkspike; + +import jakarta.annotation.Nonnull; + +public class BasicEntityTestFixture,J extends IJoinEntity> { + + /** does this scenario support null partition_id? */ + boolean myNullPartitionSupportFlag = true; + + BasicEntityTestFixture(Class theRootType, Class theJoinType) { + myRootType = theRootType; + myJoinType = theJoinType; + } + + public final Class myRootType; + public final Class myJoinType; + + public R buildRootEntity() { + return buildInstance(myRootType); + } + + public J buildJoinEntity() { + return buildInstance(myJoinType); + } + + public boolean isSupportNullPartitionId() { + return myNullPartitionSupportFlag; + } + + public static ,J extends IJoinEntity> BasicEntityTestFixture build(Class theRootType, Class theJoinType) { + return new BasicEntityTestFixture<>(theRootType, theJoinType); + } + + public static ,J extends IJoinEntity> BasicEntityTestFixture buildNoNullPartition(Class theRootType, Class theJoinType) { + BasicEntityTestFixture entityFixture = new BasicEntityTestFixture<>(theRootType, theJoinType); + entityFixture.myNullPartitionSupportFlag = false; + return entityFixture; + } + + + static @Nonnull T buildInstance(Class theClass) { + try { + return theClass.getDeclaredConstructor().newInstance(); + } catch (Exception theE) { + throw new RuntimeException(theE); + } + } +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java index 073358dd5ab..43ade283bf6 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java @@ -26,7 +26,7 @@ import java.util.function.Consumer; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; -abstract public class BasicEntityTestTemplate,J extends EntityFixture.IJoinEntity> { +abstract public class BasicEntityTestTemplate,J extends IJoinEntity> { private static final Logger ourLog = LoggerFactory.getLogger(BasicEntityTestTemplate.class); @Autowired @@ -41,13 +41,13 @@ abstract public class BasicEntityTestTemplate myEntityFixture; + final BasicEntityTestFixture myFixture; - public BasicEntityTestTemplate(EntityFixture theEntityFixture) { - myEntityFixture = theEntityFixture; + public BasicEntityTestTemplate(BasicEntityTestFixture theFixture) { + myFixture = theFixture; } - static List getPartitions(EntityFixture theFixture) { + static List getPartitions(BasicEntityTestFixture theFixture) { var result = new ArrayList(); if (theFixture.isSupportNullPartitionId()) { result.add(null); @@ -62,14 +62,14 @@ abstract public class BasicEntityTestTemplate{ - long count = queryCountAll(em, myEntityFixture.myRootType); + long count = queryCountAll(em, myFixture.myRootType); assertEquals(1, count); CriteriaBuilder cb = em.getCriteriaBuilder(); - CriteriaQuery cr = cb.createQuery(myEntityFixture.myRootType); - cr.select(cr.from(myEntityFixture.myRootType)); + CriteriaQuery cr = cb.createQuery(myFixture.myRootType); + cr.select(cr.from(myFixture.myRootType)); R readback = em.createQuery(cr).getSingleResult(); assertEquals(-1, readback.getResId()); @@ -82,7 +82,7 @@ abstract public class BasicEntityTestTemplate{ - R root = myEntityFixture.buildRootEntity(); + R root = myFixture.buildRootEntity(); root.setPartitionId(thePartitionId); root.setString("goodbye!"); em.persist(root); @@ -92,7 +92,7 @@ abstract public class BasicEntityTestTemplate{ - var root = myEntityFixture.buildRootEntity(); + var root = myFixture.buildRootEntity(); root.setPartitionId(thePartitionId); root.setString("parent"); - var join = myEntityFixture.buildJoinEntity(); + var join = myFixture.buildJoinEntity(); join.setParent(root); join.setString("child"); join.setPartitionId(thePartitionId); @@ -122,7 +122,7 @@ abstract public class BasicEntityTestTemplate { - var root0 = myEntityFixture.buildRootEntity(); + var root0 = myFixture.buildRootEntity(); root0.setPartitionId(thePartitionId); root0.setString("parent"); - var join0 = myEntityFixture.buildJoinEntity(); + var join0 = myFixture.buildJoinEntity(); join0.setParent(root0); join0.setString("child"); join0.setPartitionId(thePartitionId); - var join1 = myEntityFixture.buildJoinEntity(); + var join1 = myFixture.buildJoinEntity(); join1.setParent(root0); join1.setString("child1"); join1.setPartitionId(thePartitionId); @@ -169,8 +169,8 @@ abstract public class BasicEntityTestTemplate cr = cb.createQuery(myEntityFixture.myRootType); - Root from = cr.from(myEntityFixture.myRootType); + CriteriaQuery cr = cb.createQuery(myFixture.myRootType); + Root from = cr.from(myFixture.myRootType); from.fetch("myJoinEntities"); cr.select(from); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/EntityFixture.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/EntityFixture.java deleted file mode 100644 index 6c89f7628eb..00000000000 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/EntityFixture.java +++ /dev/null @@ -1,71 +0,0 @@ -package ca.uhn.fhir.jpa.model.pkspike; - -import jakarta.annotation.Nonnull; - -import java.util.Collection; - -public class EntityFixture,J extends EntityFixture.IJoinEntity> { - - private boolean myNullPartitionSupportFlag = true; - - public static ,J extends EntityFixture.IJoinEntity> EntityFixture build(Class theRootType, Class theJoinType) { - return new EntityFixture<>(theRootType, theJoinType); - } - - public static ,J extends EntityFixture.IJoinEntity> EntityFixture buildNoNullPartition(Class theRootType, Class theJoinType) { - EntityFixture entityFixture = new EntityFixture<>(theRootType, theJoinType); - entityFixture.myNullPartitionSupportFlag = false; - return entityFixture; - } - - EntityFixture(Class theRootType, Class theJoinType) { - myRootType = theRootType; - myJoinType = theJoinType; - } - - public J buildJoinEntity() { - return buildInstance(myJoinType); - } - - public boolean isSupportNullPartitionId() { - return myNullPartitionSupportFlag; - } - - public interface IRootEntity { - Long getResId(); - void setPartitionId(Integer thePartitionId); - Integer getPartitionId(); - String getString(); - void setString(String theString); - - Collection getJoins(); - } - public interface IJoinEntity

    { - Long getPid(); - void setString(String theString); - - void setParent(P theRoot); - - String getString(); - - void setPartitionId(Integer thePartitionId); - Integer getPartitionId(); - - Long getResId(); - } - - public final Class myRootType; - public final Class myJoinType; - - public R buildRootEntity() { - return buildInstance(myRootType); - } - - static @Nonnull T buildInstance(Class theClass) { - try { - return theClass.getDeclaredConstructor().newInstance(); - } catch (Exception theE) { - throw new RuntimeException(theE); - } - } -} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/IJoinEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/IJoinEntity.java new file mode 100644 index 00000000000..4e0c3f45e83 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/IJoinEntity.java @@ -0,0 +1,17 @@ +package ca.uhn.fhir.jpa.model.pkspike; + +public interface IJoinEntity

    { + Long getPid(); + + void setString(String theString); + + void setParent(P theRoot); + + String getString(); + + void setPartitionId(Integer thePartitionId); + + Integer getPartitionId(); + + Long getResId(); +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/IRootEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/IRootEntity.java new file mode 100644 index 00000000000..4acf4fabb18 --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/IRootEntity.java @@ -0,0 +1,17 @@ +package ca.uhn.fhir.jpa.model.pkspike; + +import java.util.Collection; + +public interface IRootEntity { + Long getResId(); + + void setPartitionId(Integer thePartitionId); + + Integer getPartitionId(); + + String getString(); + + void setString(String theString); + + Collection getJoins(); +} diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java index 12dfc8d44ea..408fe64b947 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/EmbeddedIdPkJpaBindingTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.jpa.model.pkspike.embeddedid; import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate; -import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestFixture; import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; import ca.uhn.fhir.jpa.model.pkspike.ValueTypeBasedParameterResolver; import org.junit.jupiter.api.Nested; @@ -21,7 +21,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; }) public class EmbeddedIdPkJpaBindingTest { - static final EntityFixture ourFixture = EntityFixture.buildNoNullPartition(ResRootEmbeddedIdEntity.class, ResJoinEmbeddedIdEntity.class); + static final BasicEntityTestFixture ourFixture = BasicEntityTestFixture.buildNoNullPartition(ResRootEmbeddedIdEntity.class, ResJoinEmbeddedIdEntity.class); @RegisterExtension static final ParameterResolver ourResolver = ValueTypeBasedParameterResolver.build(ourFixture); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResJoinEmbeddedIdEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResJoinEmbeddedIdEntity.java index de735c1a87b..b31af0d033b 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResJoinEmbeddedIdEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResJoinEmbeddedIdEntity.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.jpa.model.pkspike.embeddedid; -import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.IJoinEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -17,7 +17,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder; @Table( name = "RES_JOIN" ) -public class ResJoinEmbeddedIdEntity implements EntityFixture.IJoinEntity { +public class ResJoinEmbeddedIdEntity implements IJoinEntity { @Id // @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.SEQUENCE) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResRootEmbeddedIdEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResRootEmbeddedIdEntity.java index a9ceb649d33..093e6e22383 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResRootEmbeddedIdEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/embeddedid/ResRootEmbeddedIdEntity.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.jpa.model.pkspike.embeddedid; -import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.IRootEntity; import jakarta.persistence.Column; import jakarta.persistence.Embeddable; import jakarta.persistence.EmbeddedId; @@ -25,7 +25,7 @@ import java.util.Collection; @SuppressWarnings("JpaDataSourceORMInspection") @Entity @Table(name = "RES_ROOT") -public class ResRootEmbeddedIdEntity implements EntityFixture.IRootEntity { +public class ResRootEmbeddedIdEntity implements IRootEntity { private static final Logger ourLog = LoggerFactory.getLogger(ResRootEmbeddedIdEntity.class); @EmbeddedId diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java index 8774af27e2b..5a5e575bf4c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java @@ -3,7 +3,7 @@ package ca.uhn.fhir.jpa.model.pkspike.idclass; import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate; -import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestFixture; import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; import ca.uhn.fhir.jpa.model.pkspike.ValueTypeBasedParameterResolver; import org.junit.jupiter.api.Nested; @@ -41,7 +41,7 @@ public class IdClassPkCustomXmlJpaBindingTest { } - static final EntityFixture ourFixture = EntityFixture.buildNoNullPartition(ResRootIdClassEntity.class, ResJoinIdClassEntity.class); + static final BasicEntityTestFixture ourFixture = BasicEntityTestFixture.buildNoNullPartition(ResRootIdClassEntity.class, ResJoinIdClassEntity.class); @RegisterExtension static final ParameterResolver ourFixtureResolver = ValueTypeBasedParameterResolver.build(ourFixture); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java index c78b4076e93..f6debd270d4 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.jpa.model.pkspike.idclass; import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate; -import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestFixture; import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; import ca.uhn.fhir.jpa.model.pkspike.ValueTypeBasedParameterResolver; import org.junit.jupiter.api.Nested; @@ -22,7 +22,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; }) public class IdClassPkJpaBindingTest { - public static final EntityFixture ourFixture = EntityFixture.build(ResRootIdClassEntity.class, ResJoinIdClassEntity.class); + public static final BasicEntityTestFixture ourFixture = BasicEntityTestFixture.build(ResRootIdClassEntity.class, ResJoinIdClassEntity.class); @RegisterExtension static final ParameterResolver ourFixtureResolver = ValueTypeBasedParameterResolver.build(ourFixture); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java index 3d6c9c74dae..9fc6710a196 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.jpa.model.pkspike.idclass; -import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.IJoinEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -17,7 +17,7 @@ import org.apache.commons.lang3.builder.ToStringBuilder; @Table( name = "RES_JOIN" ) -public class ResJoinIdClassEntity implements EntityFixture.IJoinEntity { +public class ResJoinIdClassEntity implements IJoinEntity { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) @Column(name = "PID") diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResRootIdClassEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResRootIdClassEntity.java index 7526f40b9bf..d89282c39e4 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResRootIdClassEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResRootIdClassEntity.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.jpa.model.pkspike.idclass; -import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.IRootEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -27,7 +27,7 @@ import java.util.Collection; @Entity @Table(name = "RES_ROOT") @IdClass(ResRootIdClassEntity.ResRootPK.class) -public class ResRootIdClassEntity implements EntityFixture.IRootEntity { +public class ResRootIdClassEntity implements IRootEntity { private static final Logger ourLog = LoggerFactory.getLogger(ResRootIdClassEntity.class); @Id diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java index 464fe8a5ec1..58580ca3da0 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java @@ -2,7 +2,7 @@ package ca.uhn.fhir.jpa.model.pkspike.partitionkey; import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate; -import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestFixture; import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; import ca.uhn.fhir.jpa.model.pkspike.ValueTypeBasedParameterResolver; import org.junit.jupiter.api.Nested; @@ -22,7 +22,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; }) class PartitionJpaBindingTest { - static final EntityFixture ourConfig = EntityFixture.build(ResRootPartitionEntity.class, ResJoinPartitionEntity.class); + static final BasicEntityTestFixture ourConfig = BasicEntityTestFixture.build(ResRootPartitionEntity.class, ResJoinPartitionEntity.class); @RegisterExtension static final ParameterResolver ourResolver = ValueTypeBasedParameterResolver.build(ourConfig); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java index a51978cdb0e..b962669439e 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.jpa.model.pkspike.partitionkey; -import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.IJoinEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -19,7 +19,7 @@ import org.hibernate.annotations.PartitionKey; @Table( name = "RES_JOIN" ) -public class ResJoinPartitionEntity implements EntityFixture.IJoinEntity { +public class ResJoinPartitionEntity implements IJoinEntity { @Id // @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.SEQUENCE) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResRootPartitionEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResRootPartitionEntity.java index 78ed07fcc43..aed3ac1d675 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResRootPartitionEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResRootPartitionEntity.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.jpa.model.pkspike.partitionkey; -import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.IRootEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -18,7 +18,7 @@ import java.util.Collection; @SuppressWarnings("JpaDataSourceORMInspection") @Entity @Table(name = "RES_ROOT") -public class ResRootPartitionEntity implements EntityFixture.IRootEntity { +public class ResRootPartitionEntity implements IRootEntity { @Id @GeneratedValue() @Column(name = "RES_ID") diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java index d67e6726cc4..1b023cc7316 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResJoinEntity.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.jpa.model.pkspike.primitive; -import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.IJoinEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; @@ -17,7 +17,7 @@ import org.apache.commons.lang3.builder.ToStringStyle; @Table( name = "RES_JOIN" ) -public class ResJoinEntity implements EntityFixture.IJoinEntity { +public class ResJoinEntity implements IJoinEntity { @Id // @GenericGenerator(name = "SEQ_RESOURCE_ID", type = ca.uhn.fhir.jpa.model.dialect.HapiSequenceStyleGenerator.class) @GeneratedValue(strategy = GenerationType.SEQUENCE) diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java index d431ddd318c..5883f43f64c 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/ResRootEntity.java @@ -1,6 +1,6 @@ package ca.uhn.fhir.jpa.model.pkspike.primitive; -import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.IRootEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.FetchType; @@ -17,7 +17,7 @@ import java.util.Collection; @SuppressWarnings("JpaDataSourceORMInspection") @Entity @Table(name = "RES_ROOT") -public class ResRootEntity implements EntityFixture.IRootEntity { +public class ResRootEntity implements IRootEntity { @Id @GeneratedValue() @Column(name = "RES_ID") diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java index c35c1594b86..3eb47bf7628 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/primitive/SimplePkJpaBindingTest.java @@ -2,10 +2,9 @@ package ca.uhn.fhir.jpa.model.pkspike.primitive; import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate; -import ca.uhn.fhir.jpa.model.pkspike.EntityFixture; +import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestFixture; import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig; import ca.uhn.fhir.jpa.model.pkspike.ValueTypeBasedParameterResolver; -import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ParameterResolver; import org.junit.jupiter.api.extension.RegisterExtension; @@ -19,17 +18,14 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; @ContextConfiguration(classes = { SimpleTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class }) -public class SimplePkJpaBindingTest { +public class SimplePkJpaBindingTest extends BasicEntityTestTemplate { - public static final EntityFixture ourFixture = EntityFixture.build(ResRootEntity.class, ResJoinEntity.class); + public static final BasicEntityTestFixture ourFixture = BasicEntityTestFixture.build(ResRootEntity.class, ResJoinEntity.class); @RegisterExtension static final ParameterResolver ourFixtureResolver = ValueTypeBasedParameterResolver.build(ourFixture); - @Nested - class Common extends BasicEntityTestTemplate { - Common() { - super(ourFixture); - } + public SimplePkJpaBindingTest() { + super(ourFixture); } } From a60294cc1976e2187d57ce086d71c66563175b4e Mon Sep 17 00:00:00 2001 From: Michael Buckley Date: Thu, 29 Aug 2024 11:23:45 -0400 Subject: [PATCH 17/17] Add update path --- .../pkspike/BasicEntityTestTemplate.java | 40 ++++++++++++- .../IdClassPkCustomXmlJpaBindingTest.java | 3 +- .../idclass/IdClassPkJpaBindingTest.java | 21 ++++++- .../pkspike/idclass/ResJoinIdClassEntity.java | 56 +++++++++++++++++++ .../pkspike/idclass/ResRootIdClassEntity.java | 1 + .../partitionkey/PartitionJpaBindingTest.java | 2 +- .../partitionkey/ResJoinPartitionEntity.java | 6 +- .../idclass/{orm.xml => ormComposite.xml} | 32 ++++++----- .../jpa/model/pkspike/idclass/ormLong.xml | 25 +++++++++ 9 files changed, 162 insertions(+), 24 deletions(-) rename hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/{orm.xml => ormComposite.xml} (56%) create mode 100644 hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/ormLong.xml diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java index 43ade283bf6..41e1d95ab7a 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/BasicEntityTestTemplate.java @@ -102,6 +102,32 @@ abstract public class BasicEntityTestTemplate,J extends } + @ParameterizedTest + @MethodSource("getPartitions") + void updateResourceTable(Integer thePartitionId) { + doInTx(em->{ + R root = myFixture.buildRootEntity(); + root.setPartitionId(thePartitionId); + root.setString("hello!"); + em.persist(root); + + em.flush(); + em.clear(); + + Object id = myEntityManagerFactory.getPersistenceUnitUtil().getIdentifier(root); + ourLog.info("flushed root entity. Id is {}", id); + R readback = em.find(myFixture.myRootType, id); + + readback.setString("goodbye!"); + em.flush(); + em.clear(); + + readback = em.find(myFixture.myRootType, id); + assertNotNull(readback); + assertEquals("goodbye!", readback.getString()); + }); + } + @ParameterizedTest @MethodSource("getPartitions") void roundTripJoin(Integer thePartitionId) { @@ -167,15 +193,23 @@ abstract public class BasicEntityTestTemplate,J extends em.flush(); em.clear(); - CriteriaBuilder cb = em.getCriteriaBuilder(); CriteriaQuery cr = cb.createQuery(myFixture.myRootType); Root from = cr.from(myFixture.myRootType); from.fetch("myJoinEntities"); cr.select(from); - em.createQuery(cr).getResultStream() - .forEach(e-> ourLog.info("e: {}", e)); + List resultList = em.createQuery(cr).getResultList(); + assertEquals(1,resultList.size()); + + resultList.forEach(e-> { + ourLog.info("root: {}", e); + assertNotNull(e); + assertNotNull(e.getJoins()); + assertEquals(2, e.getJoins().size()); + assertNotNull(e.getJoins().iterator().next()); + e.getJoins().forEach(j-> ourLog.info("join: {}", j)); + }); }); } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java index 5a5e575bf4c..597a289fe68 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkCustomXmlJpaBindingTest.java @@ -35,10 +35,9 @@ public class IdClassPkCustomXmlJpaBindingTest { Consumer entityManagerFactoryCustomizer() { return em->{ ourLog.info("Injecting custom persistence.xml"); - em.setMappingResources("/ca/uhn/fhir/jpa/model/pkspike/idclass/orm.xml"); + em.setMappingResources("/ca/uhn/fhir/jpa/model/pkspike/idclass/ormComposite.xml"); }; } - } static final BasicEntityTestFixture ourFixture = BasicEntityTestFixture.buildNoNullPartition(ResRootIdClassEntity.class, ResJoinIdClassEntity.class); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java index f6debd270d4..41786f1b91a 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/IdClassPkJpaBindingTest.java @@ -1,5 +1,6 @@ package ca.uhn.fhir.jpa.model.pkspike.idclass; +import ca.uhn.fhir.jpa.config.HapiFhirLocalContainerEntityManagerFactoryBean; import ca.uhn.fhir.jpa.config.r4.FhirContextR4Config; import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate; import ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestFixture; @@ -9,18 +10,36 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ParameterResolver; import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; +import java.util.function.Consumer; + /** * Use an IdClass even though the PK is only a single column. * This allows us to extend the PK next door in the {@link IdClassPkCustomXmlJpaBindingTest}. */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = { - IdClassKeyTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class + IdClassPkJpaBindingTest.Config.class, IdClassKeyTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class }) public class IdClassPkJpaBindingTest { + private static final Logger ourLog = LoggerFactory.getLogger(IdClassPkJpaBindingTest.class); + + @Configuration + static class Config { + @Bean + Consumer entityManagerFactoryCustomizer() { + return em->{ + ourLog.info("Injecting custom persistence.xml"); + em.setMappingResources("/ca/uhn/fhir/jpa/model/pkspike/idclass/ormLong.xml"); + }; + } + } public static final BasicEntityTestFixture ourFixture = BasicEntityTestFixture.build(ResRootIdClassEntity.class, ResJoinIdClassEntity.class); @RegisterExtension diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java index 9fc6710a196..8d32df41d0e 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResJoinIdClassEntity.java @@ -6,23 +6,31 @@ import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import jakarta.persistence.IdClass; import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumns; import jakarta.persistence.ManyToOne; import jakarta.persistence.Table; +import jakarta.persistence.Transient; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.PartitionKey; @SuppressWarnings("JpaDataSourceORMInspection") @Entity @Table( name = "RES_JOIN" ) +@IdClass(ResJoinIdClassEntity.ResJoinPK.class) public class ResJoinIdClassEntity implements IJoinEntity { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) @Column(name = "PID") Long myId; + @PartitionKey @Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false) Integer myPartitionId; @@ -78,4 +86,52 @@ public class ResJoinIdClassEntity implements IJoinEntity { // fixme keep copy return myResource == null? null: myResource.myId; } + + + static class ResJoinPK { + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE) + @Column(name = "PID") + Long myId; + + /** for Hibernate */ + public ResJoinPK() {} + + + public ResJoinPK(Long theId) { + myId = theId; + } + + @Override + public boolean equals(Object theO) { + return EqualsBuilder.reflectionEquals(this,theO); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE); + } + } + + static class ResJoinCompositePK extends ResJoinPK { + @Id + @Column(name = "PARTITION_ID", nullable = false, insertable = false, updatable = false) + Integer myPartitionId; + + /** for Hibernate */ + public ResJoinCompositePK() {} + + public ResJoinCompositePK(Long theId, Integer thePartitionId) { + super(theId); + myPartitionId = thePartitionId; + } + + + + } } diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResRootIdClassEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResRootIdClassEntity.java index d89282c39e4..23d624cad83 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResRootIdClassEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/idclass/ResRootIdClassEntity.java @@ -13,6 +13,7 @@ import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; +import org.hibernate.annotations.PartitionKey; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java index 58580ca3da0..c44595ce4b0 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/PartitionJpaBindingTest.java @@ -22,7 +22,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension; }) class PartitionJpaBindingTest { - static final BasicEntityTestFixture ourConfig = BasicEntityTestFixture.build(ResRootPartitionEntity.class, ResJoinPartitionEntity.class); + static final BasicEntityTestFixture ourConfig = BasicEntityTestFixture.buildNoNullPartition(ResRootPartitionEntity.class, ResJoinPartitionEntity.class); @RegisterExtension static final ParameterResolver ourResolver = ValueTypeBasedParameterResolver.build(ourConfig); diff --git a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java index b962669439e..01a09dd4e3e 100644 --- a/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java +++ b/hapi-fhir-jpaserver-test-r4/src/test/java/ca/uhn/fhir/jpa/model/pkspike/partitionkey/ResJoinPartitionEntity.java @@ -25,8 +25,9 @@ public class ResJoinPartitionEntity implements IJoinEntity - + - - - - - - - + + + + + + + + - - - - - - + + + + + + + - + diff --git a/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/ormLong.xml b/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/ormLong.xml new file mode 100644 index 00000000000..cb11bbb269e --- /dev/null +++ b/hapi-fhir-jpaserver-test-r4/src/test/resources/ca/uhn/fhir/jpa/model/pkspike/idclass/ormLong.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + +