Merge a60294cc19
into 3f6d1eb29b
This commit is contained in:
commit
0dbf828567
|
@ -22,6 +22,12 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-jpa</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>ca.uhn.hapi.fhir</groupId>
|
||||
<artifactId>hapi-fhir-caching-testing</artifactId>
|
||||
|
@ -49,6 +55,12 @@
|
|||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.hibernate</groupId>
|
||||
<artifactId>hibernate-testing</artifactId>
|
||||
<version>6.5.2.Final</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package ca.uhn.fhir.jpa.model.pkspike;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
|
||||
public class BasicEntityTestFixture<R extends IRootEntity<J>,J extends IJoinEntity<R>> {
|
||||
|
||||
/** does this scenario support null partition_id? */
|
||||
boolean myNullPartitionSupportFlag = true;
|
||||
|
||||
BasicEntityTestFixture(Class<R> theRootType, Class<J> theJoinType) {
|
||||
myRootType = theRootType;
|
||||
myJoinType = theJoinType;
|
||||
}
|
||||
|
||||
public final Class<R> myRootType;
|
||||
public final Class<J> myJoinType;
|
||||
|
||||
public R buildRootEntity() {
|
||||
return buildInstance(myRootType);
|
||||
}
|
||||
|
||||
public J buildJoinEntity() {
|
||||
return buildInstance(myJoinType);
|
||||
}
|
||||
|
||||
public boolean isSupportNullPartitionId() {
|
||||
return myNullPartitionSupportFlag;
|
||||
}
|
||||
|
||||
public static <R extends IRootEntity<J>,J extends IJoinEntity<R>> BasicEntityTestFixture<R,J> build(Class<R> theRootType, Class<J> theJoinType) {
|
||||
return new BasicEntityTestFixture<>(theRootType, theJoinType);
|
||||
}
|
||||
|
||||
public static <R extends IRootEntity<J>,J extends IJoinEntity<R>> BasicEntityTestFixture<R,J> buildNoNullPartition(Class<R> theRootType, Class<J> theJoinType) {
|
||||
BasicEntityTestFixture<R, J> entityFixture = new BasicEntityTestFixture<>(theRootType, theJoinType);
|
||||
entityFixture.myNullPartitionSupportFlag = false;
|
||||
return entityFixture;
|
||||
}
|
||||
|
||||
|
||||
static <T> @Nonnull T buildInstance(Class<T> theClass) {
|
||||
try {
|
||||
return theClass.getDeclaredConstructor().newInstance();
|
||||
} catch (Exception theE) {
|
||||
throw new RuntimeException(theE);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,238 @@
|
|||
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 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.MethodSource;
|
||||
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.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
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<R extends IRootEntity<J>,J extends IJoinEntity<R>> {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(BasicEntityTestTemplate.class);
|
||||
|
||||
@Autowired
|
||||
EntityManagerFactory myEntityManagerFactory;
|
||||
|
||||
@Autowired
|
||||
TransactionTemplate myTransactionTemplate;
|
||||
|
||||
@Autowired
|
||||
JdbcTemplate myJdbcTemplate;
|
||||
|
||||
@RegisterExtension
|
||||
SchemaCleanerExtension mySchemaCleanerExtension = new SchemaCleanerExtension();
|
||||
|
||||
final BasicEntityTestFixture<R,J> myFixture;
|
||||
|
||||
public BasicEntityTestTemplate(BasicEntityTestFixture<R,J> theFixture) {
|
||||
myFixture = theFixture;
|
||||
}
|
||||
|
||||
static List<Integer> getPartitions(BasicEntityTestFixture<?,?> theFixture) {
|
||||
var result = new ArrayList<Integer>();
|
||||
if (theFixture.isSupportNullPartitionId()) {
|
||||
result.add(null);
|
||||
}
|
||||
result.add(12);
|
||||
return result;
|
||||
}
|
||||
|
||||
@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, myFixture.myRootType);
|
||||
|
||||
assertEquals(1, count);
|
||||
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
|
||||
CriteriaQuery<R> cr = cb.createQuery(myFixture.myRootType);
|
||||
cr.select(cr.from(myFixture.myRootType));
|
||||
R readback = em.createQuery(cr).getSingleResult();
|
||||
|
||||
assertEquals(-1, readback.getResId());
|
||||
assertEquals(-1, readback.getPartitionId());
|
||||
assertEquals("hello!", readback.getString());
|
||||
});
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("getPartitions")
|
||||
void roundTripResourceTable(Integer thePartitionId) {
|
||||
doInTx(em->{
|
||||
R root = myFixture.buildRootEntity();
|
||||
root.setPartitionId(thePartitionId);
|
||||
root.setString("goodbye!");
|
||||
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);
|
||||
assertNotNull(readback);
|
||||
assertEquals(root.getResId(), readback.getResId());
|
||||
assertNotNull(readback.getResId());
|
||||
assertEquals(thePartitionId, readback.getPartitionId());
|
||||
assertEquals("goodbye!", readback.getString());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@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) {
|
||||
doInTx(em->{
|
||||
var root = myFixture.buildRootEntity();
|
||||
root.setPartitionId(thePartitionId);
|
||||
root.setString("parent");
|
||||
|
||||
var join = myFixture.buildJoinEntity();
|
||||
join.setParent(root);
|
||||
join.setString("child");
|
||||
join.setPartitionId(thePartitionId);
|
||||
em.persist(root);
|
||||
em.persist(join);
|
||||
|
||||
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);
|
||||
|
||||
assertNotNull(readback);
|
||||
assertEquals(root.getResId(), readback.getResId());
|
||||
assertNotNull(readback.getResId());
|
||||
assertEquals(thePartitionId, readback.getPartitionId());
|
||||
assertEquals("parent", readback.getString());
|
||||
|
||||
Collection<J> joins = readback.getJoins();
|
||||
assertNotNull(joins);
|
||||
assertEquals(1, joins.size());
|
||||
J joinReadback = joins.iterator().next();
|
||||
assertNotNull(joinReadback);
|
||||
assertNotNull(joinReadback.getResId());
|
||||
assertEquals(root.getResId(), joinReadback.getResId());
|
||||
assertEquals(thePartitionId, joinReadback.getPartitionId());
|
||||
assertEquals("child", joinReadback.getString());
|
||||
});
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("getPartitions")
|
||||
void fetchJoinQuery(Integer thePartitionId) {
|
||||
doInTx(em -> {
|
||||
var root0 = myFixture.buildRootEntity();
|
||||
root0.setPartitionId(thePartitionId);
|
||||
root0.setString("parent");
|
||||
|
||||
var join0 = myFixture.buildJoinEntity();
|
||||
join0.setParent(root0);
|
||||
join0.setString("child");
|
||||
join0.setPartitionId(thePartitionId);
|
||||
|
||||
var join1 = myFixture.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<R> cr = cb.createQuery(myFixture.myRootType);
|
||||
Root<R> from = cr.from(myFixture.myRootType);
|
||||
from.fetch("myJoinEntities");
|
||||
cr.select(from);
|
||||
|
||||
List<R> 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));
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void doInTx(Consumer<EntityManager> theCallback) {
|
||||
myTransactionTemplate.execute(status-> {
|
||||
theCallback.accept(getEntityManagerOrThrow());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private long queryCountAll(EntityManager em, Class<R> rootType) {
|
||||
CriteriaBuilder qb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<Long> cq = qb.createQuery(Long.class);
|
||||
CriteriaQuery<Long> 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));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package ca.uhn.fhir.jpa.model.pkspike;
|
||||
|
||||
public interface IJoinEntity<P> {
|
||||
Long getPid();
|
||||
|
||||
void setString(String theString);
|
||||
|
||||
void setParent(P theRoot);
|
||||
|
||||
String getString();
|
||||
|
||||
void setPartitionId(Integer thePartitionId);
|
||||
|
||||
Integer getPartitionId();
|
||||
|
||||
Long getResId();
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package ca.uhn.fhir.jpa.model.pkspike;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface IRootEntity<J> {
|
||||
Long getResId();
|
||||
|
||||
void setPartitionId(Integer thePartitionId);
|
||||
|
||||
Integer getPartitionId();
|
||||
|
||||
String getString();
|
||||
|
||||
void setString(String theString);
|
||||
|
||||
Collection<J> getJoins();
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
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.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;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
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;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@Configuration
|
||||
public class PKSpikeDefaultJPAConfig {
|
||||
|
||||
@Inject
|
||||
FhirContext myFhirContext;
|
||||
|
||||
@Bean
|
||||
DataSource datasource() {
|
||||
BasicDataSource dataSource = new BasicDataSource();
|
||||
dataSource.setDriver(new org.h2.Driver());
|
||||
dataSource.setUrl("jdbc:h2:mem:testdb_r4" + System.currentTimeMillis());
|
||||
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,
|
||||
PersistenceManagedTypes theManagedTypes,
|
||||
@Autowired(required = false) @Nullable Consumer<HapiFhirLocalContainerEntityManagerFactoryBean> theEntityManagerFactoryCustomizer) {
|
||||
|
||||
HapiFhirLocalContainerEntityManagerFactoryBean entityManagerFactoryBean =
|
||||
new HapiFhirLocalContainerEntityManagerFactoryBean(theConfigurableListableBeanFactory);
|
||||
|
||||
entityManagerFactoryBean.setJpaDialect(new HapiFhirHibernateJpaDialect(myFhirContext.getLocalizer()));
|
||||
HibernatePersistenceProvider persistenceProvider = new HibernatePersistenceProvider();
|
||||
entityManagerFactoryBean.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", "none");
|
||||
jpaProperties.put("hibernate.dialect", HapiFhirH2Dialect.class.getName());
|
||||
entityManagerFactoryBean.setJpaProperties(jpaProperties);
|
||||
|
||||
entityManagerFactoryBean.setPersistenceUnitName("HapiPU");
|
||||
entityManagerFactoryBean.setDataSource(theDataSource);
|
||||
entityManagerFactoryBean.setManagedTypes(theManagedTypes);
|
||||
|
||||
if (theEntityManagerFactoryCustomizer != null) {
|
||||
theEntityManagerFactoryCustomizer.accept(entityManagerFactoryBean);
|
||||
}
|
||||
|
||||
return entityManagerFactoryBean;
|
||||
}
|
||||
|
||||
@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
|
||||
public JdbcTemplate jdbcTemplate(DataSource theDataSource) {
|
||||
return new JdbcTemplate(theDataSource);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@Nonnull
|
||||
JpaStorageSettings storageSettings() {
|
||||
JpaStorageSettings jpaStorageSettings = new JpaStorageSettings();
|
||||
return jpaStorageSettings;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
package ca.uhn.fhir.jpa.model.pkspike;
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
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 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 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)");
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
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<T> implements ParameterResolver {
|
||||
|
||||
private final T myValue;
|
||||
|
||||
public static <T> ValueTypeBasedParameterResolver<T> build(T theValue) {
|
||||
return new ValueTypeBasedParameterResolver<>(theValue);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public T get() {
|
||||
return myValue;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
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.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;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
/**
|
||||
* Spike to assess variable binding against a db.
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = {
|
||||
EmbeddedIdTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class
|
||||
})
|
||||
public class EmbeddedIdPkJpaBindingTest {
|
||||
|
||||
static final BasicEntityTestFixture<ResRootEmbeddedIdEntity, ResJoinEmbeddedIdEntity> ourFixture = BasicEntityTestFixture.buildNoNullPartition(ResRootEmbeddedIdEntity.class, ResJoinEmbeddedIdEntity.class);
|
||||
|
||||
@RegisterExtension
|
||||
static final ParameterResolver ourResolver = ValueTypeBasedParameterResolver.build(ourFixture);
|
||||
|
||||
|
||||
@Nested
|
||||
class Common extends BasicEntityTestTemplate<ResRootEmbeddedIdEntity, ResJoinEmbeddedIdEntity> {
|
||||
Common() {
|
||||
super(ourFixture);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
package ca.uhn.fhir.jpa.model.pkspike.embeddedid;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.pkspike.IJoinEntity;
|
||||
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 IJoinEntity<ResRootEmbeddedIdEntity> {
|
||||
@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 = "RES_ID", nullable = false, updatable = false, insertable = false)
|
||||
Long myResourceId;
|
||||
@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 Long getPid() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
@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() {
|
||||
// fixme keep copy
|
||||
return myResource==null?null:myResource.getResId();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
package ca.uhn.fhir.jpa.model.pkspike.embeddedid;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.pkspike.IRootEntity;
|
||||
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.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* fixme MB IdClass vs embeddable?
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("JpaDataSourceORMInspection")
|
||||
@Entity
|
||||
@Table(name = "RES_ROOT")
|
||||
public class ResRootEmbeddedIdEntity implements IRootEntity<ResJoinEmbeddedIdEntity> {
|
||||
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<ResJoinEmbeddedIdEntity> 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<ResJoinEmbeddedIdEntity> 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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
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 IdClassKeyTypesConfig {
|
||||
@Bean
|
||||
PersistenceManagedTypes getManagedTypes() {
|
||||
return PersistenceManagedTypes.of(
|
||||
ResRootIdClassEntity.class.getName(),
|
||||
ResJoinIdClassEntity.class.getName()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
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;
|
||||
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.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;
|
||||
|
||||
/**
|
||||
* 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 = {
|
||||
IdClassPkCustomXmlJpaBindingTest.Config.class, IdClassKeyTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class
|
||||
})
|
||||
public class IdClassPkCustomXmlJpaBindingTest {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(IdClassPkCustomXmlJpaBindingTest.class);
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
@Bean
|
||||
Consumer<HapiFhirLocalContainerEntityManagerFactoryBean> entityManagerFactoryCustomizer() {
|
||||
return em->{
|
||||
ourLog.info("Injecting custom persistence.xml");
|
||||
em.setMappingResources("/ca/uhn/fhir/jpa/model/pkspike/idclass/ormComposite.xml");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
static final BasicEntityTestFixture<ResRootIdClassEntity, ResJoinIdClassEntity> ourFixture = BasicEntityTestFixture.buildNoNullPartition(ResRootIdClassEntity.class, ResJoinIdClassEntity.class);
|
||||
@RegisterExtension
|
||||
static final ParameterResolver ourFixtureResolver = ValueTypeBasedParameterResolver.build(ourFixture);
|
||||
|
||||
@Nested
|
||||
class Common extends BasicEntityTestTemplate<ResRootIdClassEntity, ResJoinIdClassEntity> {
|
||||
Common() {
|
||||
super(ourFixture);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
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;
|
||||
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.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 = {
|
||||
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<HapiFhirLocalContainerEntityManagerFactoryBean> entityManagerFactoryCustomizer() {
|
||||
return em->{
|
||||
ourLog.info("Injecting custom persistence.xml");
|
||||
em.setMappingResources("/ca/uhn/fhir/jpa/model/pkspike/idclass/ormLong.xml");
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static final BasicEntityTestFixture<ResRootIdClassEntity, ResJoinIdClassEntity> ourFixture = BasicEntityTestFixture.build(ResRootIdClassEntity.class, ResJoinIdClassEntity.class);
|
||||
@RegisterExtension
|
||||
static final ParameterResolver ourFixtureResolver = ValueTypeBasedParameterResolver.build(ourFixture);
|
||||
|
||||
@Nested
|
||||
class Common extends BasicEntityTestTemplate<ResRootIdClassEntity, ResJoinIdClassEntity> {
|
||||
Common() {
|
||||
super(ourFixture);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,137 @@
|
|||
package ca.uhn.fhir.jpa.model.pkspike.idclass;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.pkspike.IJoinEntity;
|
||||
import jakarta.persistence.Column;
|
||||
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<ResRootIdClassEntity> {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.SEQUENCE)
|
||||
@Column(name = "PID")
|
||||
Long myId;
|
||||
|
||||
@PartitionKey
|
||||
@Column(name = "PARTITION_ID", nullable = true, insertable = true, 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")
|
||||
})
|
||||
ResRootIdClassEntity myResource;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getPid() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
@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() {
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
package ca.uhn.fhir.jpa.model.pkspike.idclass;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.pkspike.IRootEntity;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
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.apache.commons.lang3.builder.ToStringStyle;
|
||||
import org.hibernate.annotations.PartitionKey;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* fixme MB IdClass vs embeddable?
|
||||
*
|
||||
*/
|
||||
@SuppressWarnings("JpaDataSourceORMInspection")
|
||||
@Entity
|
||||
@Table(name = "RES_ROOT")
|
||||
@IdClass(ResRootIdClassEntity.ResRootPK.class)
|
||||
public class ResRootIdClassEntity implements IRootEntity<ResJoinIdClassEntity> {
|
||||
private static final Logger ourLog = LoggerFactory.getLogger(ResRootIdClassEntity.class);
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@Column(name = "RES_ID")
|
||||
Long myId;
|
||||
|
||||
// @Id
|
||||
//@PartitionKey
|
||||
@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<ResJoinIdClassEntity> myJoinEntities = new ArrayList<>();
|
||||
|
||||
public ResRootIdClassEntity() {
|
||||
ourLog.info("new ResRootCompositeEntity()");
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
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;
|
||||
}
|
||||
|
||||
public void setString(String theString) {
|
||||
myString = theString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResJoinIdClassEntity> getJoins() {
|
||||
return myJoinEntities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this);
|
||||
}
|
||||
|
||||
static class ResRootPK {
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
@Column(name = "RES_ID")
|
||||
Long myId;
|
||||
|
||||
/** for Hibernate */
|
||||
public ResRootPK() {}
|
||||
|
||||
|
||||
public ResRootPK(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 ResRootCompositePK extends ResRootPK {
|
||||
@Column(name = "PARTITION_ID", nullable = true, insertable = true, updatable = false)
|
||||
Integer myPartitionId;
|
||||
|
||||
/** for Hibernate */
|
||||
public ResRootCompositePK() {}
|
||||
|
||||
public ResRootCompositePK(Long theId, Integer thePartitionId) {
|
||||
super(theId);
|
||||
myPartitionId = thePartitionId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* Various tests of a parent-child JPA relationship exercising configurable pk definition and joins.
|
||||
*<p>
|
||||
* We have a test template that does some basic queries ( {@link ca.uhn.fhir.jpa.model.pkspike.BasicEntityTestTemplate}).
|
||||
* <ul>
|
||||
* <li>See {@link ca.uhn.fhir.jpa.model.pkspike.primitive.SimplePkJpaBindingTest} for the normal case. Supports null partition_id.
|
||||
* <li>See {@link ca.uhn.fhir.jpa.model.pkspike.embeddedid.EmbeddedIdPkJpaBindingTest} for an embedded Id class path. Does not support null partition_id.
|
||||
* <li>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.
|
||||
* <li>See {@link ca.uhn.fhir.jpa.model.pkspike.idclass.IdClassPkJpaBindingTest}. Supports null partition_id.
|
||||
* <li>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.
|
||||
* </ul>
|
||||
* <p>
|
||||
* Things we learned:
|
||||
* <ul>
|
||||
* <li>Hibernate can not fetch any entity with a composite key if any component is null.
|
||||
* <li>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.
|
||||
* <p>
|
||||
* Further hacking Ideas:
|
||||
* <ul>
|
||||
* <li>Try to wrap the partition_id column with a user type (e.g. Optional?) so we can pretend it isn't null.
|
||||
* </p>
|
||||
* <p>
|
||||
* Add this to logback to explore the sql.
|
||||
* <pre>
|
||||
* <logger name="org.hibernate.SQL" level="trace"/>
|
||||
* <logger name="org.hibernate.orm.jdbc.bind" level="trace"/>
|
||||
* </pre>
|
||||
*/
|
||||
package ca.uhn.fhir.jpa.model.pkspike;
|
|
@ -0,0 +1,37 @@
|
|||
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.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;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
/**
|
||||
* 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 = {
|
||||
PartitionTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class
|
||||
})
|
||||
class PartitionJpaBindingTest {
|
||||
|
||||
static final BasicEntityTestFixture<ResRootPartitionEntity, ResJoinPartitionEntity> ourConfig = BasicEntityTestFixture.buildNoNullPartition(ResRootPartitionEntity.class, ResJoinPartitionEntity.class);
|
||||
|
||||
@RegisterExtension
|
||||
static final ParameterResolver ourResolver = ValueTypeBasedParameterResolver.build(ourConfig);
|
||||
|
||||
@Nested
|
||||
class Common extends BasicEntityTestTemplate<ResRootPartitionEntity, ResJoinPartitionEntity> {
|
||||
Common() {
|
||||
super(ourConfig);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package ca.uhn.fhir.jpa.model.pkspike.partitionkey;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.pkspike.IJoinEntity;
|
||||
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.PartitionKey;
|
||||
|
||||
@SuppressWarnings("JpaDataSourceORMInspection")
|
||||
@Entity
|
||||
@Table(
|
||||
name = "RES_JOIN"
|
||||
)
|
||||
public class ResJoinPartitionEntity implements IJoinEntity<ResRootPartitionEntity> {
|
||||
@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 = false, insertable = true, updatable = false)
|
||||
})
|
||||
ResRootPartitionEntity myResource;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getPid() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
package ca.uhn.fhir.jpa.model.pkspike.partitionkey;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.pkspike.IRootEntity;
|
||||
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;
|
||||
|
||||
@SuppressWarnings("JpaDataSourceORMInspection")
|
||||
@Entity
|
||||
@Table(name = "RES_ROOT")
|
||||
public class ResRootPartitionEntity implements IRootEntity<ResJoinPartitionEntity> {
|
||||
@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<ResJoinPartitionEntity> myJoinEntities = new ArrayList<>();
|
||||
|
||||
public Long getId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
public String getString() {
|
||||
return myString;
|
||||
}
|
||||
|
||||
public void setString(String theString) {
|
||||
myString = theString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResJoinPartitionEntity> getJoins() {
|
||||
return myJoinEntities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPartitionId(Integer thePartitionId) {
|
||||
myPartitionId = thePartitionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getPartitionId() {
|
||||
return myPartitionId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getResId() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
package ca.uhn.fhir.jpa.model.pkspike.primitive;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.pkspike.IJoinEntity;
|
||||
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;
|
||||
import org.apache.commons.lang3.builder.ToStringBuilder;
|
||||
import org.apache.commons.lang3.builder.ToStringStyle;
|
||||
|
||||
@SuppressWarnings("JpaDataSourceORMInspection")
|
||||
@Entity
|
||||
@Table(
|
||||
name = "RES_JOIN"
|
||||
)
|
||||
public class ResJoinEntity implements IJoinEntity<ResRootEntity> {
|
||||
@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)
|
||||
Integer myPartitionId;
|
||||
|
||||
@Column(name = "STRING_COL")
|
||||
String myString;
|
||||
|
||||
@Column(name = "RES_ID", nullable = false, insertable = false, updatable = false)
|
||||
Long myResId;
|
||||
|
||||
@ManyToOne(
|
||||
optional = false)
|
||||
@JoinColumn(
|
||||
name = "RES_ID",
|
||||
referencedColumnName = "RES_ID",
|
||||
nullable = false,
|
||||
updatable = false)
|
||||
ResRootEntity myResource;
|
||||
|
||||
@Override
|
||||
public Long getResId() {
|
||||
return myResId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getPid() {
|
||||
return myId;
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
package ca.uhn.fhir.jpa.model.pkspike.primitive;
|
||||
|
||||
import ca.uhn.fhir.jpa.model.pkspike.IRootEntity;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
@SuppressWarnings("JpaDataSourceORMInspection")
|
||||
@Entity
|
||||
@Table(name = "RES_ROOT")
|
||||
public class ResRootEntity implements IRootEntity<ResJoinEntity> {
|
||||
@Id
|
||||
@GeneratedValue()
|
||||
@Column(name = "RES_ID")
|
||||
Long myId;
|
||||
|
||||
@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<ResJoinEntity> myJoinEntities = new ArrayList<>();
|
||||
|
||||
public Long getId() {
|
||||
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;
|
||||
}
|
||||
|
||||
public void setString(String theString) {
|
||||
myString = theString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<ResJoinEntity> getJoins() {
|
||||
return myJoinEntities;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
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.BasicEntityTestFixture;
|
||||
import ca.uhn.fhir.jpa.model.pkspike.PKSpikeDefaultJPAConfig;
|
||||
import ca.uhn.fhir.jpa.model.pkspike.ValueTypeBasedParameterResolver;
|
||||
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;
|
||||
|
||||
/**
|
||||
* The simple scenario - single column keys and joins.
|
||||
*/
|
||||
@ExtendWith(SpringExtension.class)
|
||||
@ContextConfiguration(classes = {
|
||||
SimpleTypesConfig.class, PKSpikeDefaultJPAConfig.class, FhirContextR4Config.class
|
||||
})
|
||||
public class SimplePkJpaBindingTest extends BasicEntityTestTemplate<ResRootEntity, ResJoinEntity> {
|
||||
|
||||
public static final BasicEntityTestFixture<ResRootEntity, ResJoinEntity> ourFixture = BasicEntityTestFixture.build(ResRootEntity.class, ResJoinEntity.class);
|
||||
@RegisterExtension
|
||||
static final ParameterResolver ourFixtureResolver = ValueTypeBasedParameterResolver.build(ourFixture);
|
||||
|
||||
public SimplePkJpaBindingTest() {
|
||||
super(ourFixture);
|
||||
}
|
||||
|
||||
}
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
<entity-mappings
|
||||
xmlns="http://java.sun.com/xml/ns/persistence/orm"
|
||||
version="2.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
|
||||
http://java.sun.com/xml/ns/persistence/orm_2_2.xsd"
|
||||
>
|
||||
<entity class="ca.uhn.fhir.jpa.model.pkspike.idclass.ResRootIdClassEntity" name="ResRootIdClassEntity">
|
||||
<!-- <id-class class="ca.uhn.fhir.jpa.model.pkspike.idclass.ResRootIdClassEntity$ResRootCompositePK"/>-->
|
||||
<attributes>
|
||||
<!-- <id name="myId" access="FIELD">-->
|
||||
<!-- <column name="RES_ID"/>-->
|
||||
<!-- <generated-value strategy="SEQUENCE"/>-->
|
||||
<!-- </id>-->
|
||||
<!-- <id name="myPartitionId" access="FIELD">-->
|
||||
<!-- <column name="PARTITION_ID" insertable="true" updatable="false"/>-->
|
||||
<!-- </id>-->
|
||||
</attributes>
|
||||
</entity>
|
||||
<entity class="ca.uhn.fhir.jpa.model.pkspike.idclass.ResJoinIdClassEntity">
|
||||
<!-- <id-class class="ca.uhn.fhir.jpa.model.pkspike.idclass.ResJoinIdClassEntity$ResJoinCompositePK"/>-->
|
||||
<attributes>
|
||||
<!-- <id name="myId" access="FIELD">-->
|
||||
<!-- <column name="PID"/>-->
|
||||
<!-- <generated-value strategy="SEQUENCE"/>-->
|
||||
<!-- </id>-->
|
||||
<basic name="myPartitionId" access="FIELD">
|
||||
<column name="PARTITION_ID" insertable="false" updatable="false"/>
|
||||
</basic>
|
||||
|
||||
<!-- <basic name="myPartitionId" access="FIELD">-->
|
||||
<!-- <column name="PARTITION_ID" insertable="false" updatable="false"/>-->
|
||||
<!-- </basic>-->
|
||||
<many-to-one name="myResource" access="FIELD">
|
||||
<join-column name="RES_ID" referenced-column-name="RES_ID"/>
|
||||
<join-column name="PARTITION_ID" referenced-column-name="PARTITION_ID"/>
|
||||
</many-to-one>
|
||||
</attributes>
|
||||
</entity>
|
||||
</entity-mappings>
|
|
@ -0,0 +1,25 @@
|
|||
<entity-mappings
|
||||
xmlns="http://java.sun.com/xml/ns/persistence/orm"
|
||||
version="2.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm
|
||||
http://java.sun.com/xml/ns/persistence/orm_2_2.xsd"
|
||||
>
|
||||
<entity class="ca.uhn.fhir.jpa.model.pkspike.idclass.ResRootIdClassEntity" name="ResRootIdClassEntity">
|
||||
<attributes>
|
||||
<basic name="myPartitionId" access="FIELD">
|
||||
<column name="PARTITION_ID" insertable="true" updatable="false"/>
|
||||
</basic>
|
||||
</attributes>
|
||||
</entity>
|
||||
<entity class="ca.uhn.fhir.jpa.model.pkspike.idclass.ResJoinIdClassEntity">
|
||||
<attributes>
|
||||
<basic name="myPartitionId" access="FIELD">
|
||||
<column name="PARTITION_ID" insertable="true" updatable="false"/>
|
||||
</basic>
|
||||
<!-- <many-to-one name="myResource" access="FIELD">-->
|
||||
<!-- <join-column name="RES_ID" referenced-column-name="RES_ID"/>-->
|
||||
<!-- </many-to-one>-->
|
||||
</attributes>
|
||||
</entity>
|
||||
</entity-mappings>
|
|
@ -0,0 +1,24 @@
|
|||
<persistence-unit>
|
||||
<!--
|
||||
name="educationPU"
|
||||
transaction-type="JTA"
|
||||
<provider>org.hibernate.ejb.HibernatePersistence</provider>
|
||||
<class>com.coe.jpa.StudentProfile</class>
|
||||
>
|
||||
<mapping-file>classpath:/ca/uhn/fhir/jpa/model/pkspike/idclass/overrideMappings.xml</mapping-file>
|
||||
-->
|
||||
<mapping-file>./overrideMappings.xml</mapping-file>
|
||||
|
||||
|
||||
<properties>
|
||||
<!--
|
||||
<property name="hibernate.connection.driver_class"
|
||||
value="com.mysql.jdbc.Driver" />
|
||||
<property name="hibernate.connection.url"
|
||||
value="jdbc:mysql://localhost:3306/COE" />
|
||||
<property name="hibernate.connection.username" value="root" />
|
||||
<property name="show_sql" value="true" />
|
||||
<property name="dialect" value="org.hibernate.dialect.MySQLDialect" />
|
||||
-->
|
||||
</properties>
|
||||
</persistence-unit>
|
|
@ -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 <a href="mailto:brian@btmatthews.com">Brian Matthews</a>
|
||||
* @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) {
|
||||
getLoggerForTestClass(context).info("Starting test case [{}]", getMethodName(context));
|
||||
|
||||
}
|
||||
@Override
|
||||
public void afterTestExecution(ExtensionContext context) {
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue