Changes to schema to loosen dependencies between Forced ID table and the Resource and Resource History tables.

This commit is contained in:
ianmarshall 2020-01-31 11:16:01 -05:00
parent f38370fbf7
commit 5d5b3d7639
6 changed files with 83 additions and 31 deletions

View File

@ -78,8 +78,6 @@ public class ExpungeEverythingService {
ourLog.info("BEGINNING GLOBAL $expunge"); ourLog.info("BEGINNING GLOBAL $expunge");
myTxTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); myTxTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
myTxTemplate.execute(t -> { myTxTemplate.execute(t -> {
counter.addAndGet(doExpungeEverythingQuery("UPDATE " + ResourceHistoryTable.class.getSimpleName() + " d SET d.myForcedId = null"));
counter.addAndGet(doExpungeEverythingQuery("UPDATE " + ResourceTable.class.getSimpleName() + " d SET d.myForcedId = null"));
counter.addAndGet(doExpungeEverythingQuery("UPDATE " + TermCodeSystem.class.getSimpleName() + " d SET d.myCurrentVersion = null")); counter.addAndGet(doExpungeEverythingQuery("UPDATE " + TermCodeSystem.class.getSimpleName() + " d SET d.myCurrentVersion = null"));
return null; return null;
}); });

View File

@ -30,7 +30,6 @@ import org.junit.Test;
import org.springframework.aop.framework.AopProxyUtils; import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean; import org.springframework.scheduling.concurrent.ThreadPoolExecutorFactoryBean;
import org.springframework.test.context.TestPropertySource;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -163,7 +162,7 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
} }
@Test @Test
public void testFetchTotalAccurateForSlowLoading() throws InterruptedException { public void testFetchTotalAccurateForSlowLoading() {
create200Patients(); create200Patients();
mySearchCoordinatorSvcImpl.setLoadingThrottleForUnitTests(25); mySearchCoordinatorSvcImpl.setLoadingThrottleForUnitTests(25);
@ -836,8 +835,6 @@ public class FhirResourceDaoR4SearchOptimizedTest extends BaseJpaR4Test {
assertEquals(1, myCaptureQueriesListener.countSelectQueriesForCurrentThread()); assertEquals(1, myCaptureQueriesListener.countSelectQueriesForCurrentThread());
assertEquals(4, myCaptureQueriesListener.countInsertQueriesForCurrentThread()); assertEquals(4, myCaptureQueriesListener.countInsertQueriesForCurrentThread());
assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread()); assertEquals(0, myCaptureQueriesListener.countDeleteQueriesForCurrentThread());
// Because of the forced ID's bidirectional link HFJ_RESOURCE <-> HFJ_FORCED_ID
assertEquals(1, myCaptureQueriesListener.countUpdateQueriesForCurrentThread());
runInTransaction(() -> { runInTransaction(() -> {
assertEquals(1, myResourceTableDao.count()); assertEquals(1, myResourceTableDao.count());
assertEquals(1, myResourceHistoryTableDao.count()); assertEquals(1, myResourceHistoryTableDao.count());

View File

@ -61,6 +61,11 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
protected void init420() { // 20191015 - present protected void init420() { // 20191015 - present
Builder version = forVersion(VersionEnum.V4_2_0); Builder version = forVersion(VersionEnum.V4_2_0);
// Eliminate circular dependency.
version.onTable("HFJ_RESOURCE").dropColumn("20200130.1", "FORCED_ID_PID");
version.onTable("HFJ_RES_VER").dropColumn("20200130.2", "FORCED_ID_PID");
version.onTable("HFJ_RES_VER").addForeignKey("20200130.3", "FK_RESOURCE_HISTORY_RESOURCE").toColumn("RES_ID").references("HFJ_RESOURCE", "RES_ID");
} }
protected void init410() { // 20190815 - 20191014 protected void init410() { // 20190815 - 20191014

View File

@ -45,11 +45,6 @@ public abstract class BaseHasResource implements IBaseResourceEntity, IBasePersi
@OptimisticLock(excluded = true) @OptimisticLock(excluded = true)
private FhirVersionEnum myFhirVersion; private FhirVersionEnum myFhirVersion;
@OneToOne(optional = true, fetch = FetchType.LAZY, cascade = {}, orphanRemoval = false)
@JoinColumn(name = "FORCED_ID_PID")
@OptimisticLock(excluded = true)
private ForcedId myForcedId;
@Column(name = "HAS_TAGS", nullable = false) @Column(name = "HAS_TAGS", nullable = false)
@OptimisticLock(excluded = true) @OptimisticLock(excluded = true)
private boolean myHasTags; private boolean myHasTags;
@ -96,29 +91,13 @@ public abstract class BaseHasResource implements IBaseResourceEntity, IBasePersi
myFhirVersion = theFhirVersion; myFhirVersion = theFhirVersion;
} }
public ForcedId getForcedId() { abstract public ForcedId getForcedId();
return myForcedId;
}
public void setForcedId(ForcedId theForcedId) { abstract public void setForcedId(ForcedId theForcedId);
myForcedId = theForcedId;
}
@Override @Override
public abstract Long getId(); public abstract Long getId();
@Override
public IdDt getIdDt() {
if (getForcedId() == null) {
Long id = getResourceId();
return new IdDt(getResourceType() + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + getVersion());
} else {
// Avoid a join query if possible
String forcedId = getTransientForcedId() != null ? getTransientForcedId() : getForcedId().getForcedId();
return new IdDt(getResourceType() + '/' + forcedId + '/' + Constants.PARAM_HISTORY + '/' + getVersion());
}
}
@Override @Override
public boolean isDeleted() { public boolean isDeleted() {
return myDeleted != null; return myDeleted != null;

View File

@ -21,6 +21,8 @@ package ca.uhn.fhir.jpa.model.entity;
*/ */
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId; import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants;
import org.hibernate.annotations.OptimisticLock; import org.hibernate.annotations.OptimisticLock;
import javax.persistence.*; import javax.persistence.*;
@ -54,7 +56,11 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
@Column(name = "PID") @Column(name = "PID")
private Long myId; private Long myId;
@Column(name = "RES_ID") @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_RESOURCE_HISTORY_RESOURCE"))
private ResourceTable myResourceTable;
@Column(name = "RES_ID", nullable = false, updatable = false, insertable = false)
private Long myResourceId; private Long myResourceId;
@Column(name = "RES_TYPE", length = ResourceTable.RESTYPE_LEN, nullable = false) @Column(name = "RES_TYPE", length = ResourceTable.RESTYPE_LEN, nullable = false)
@ -165,4 +171,35 @@ public class ResourceHistoryTable extends BaseHasResource implements Serializabl
public ResourcePersistentId getPersistentId() { public ResourcePersistentId getPersistentId() {
return new ResourcePersistentId(myResourceId); return new ResourcePersistentId(myResourceId);
} }
public ResourceTable getResourceTable() {
return myResourceTable;
}
public void setResourceTable(ResourceTable theResourceTable) {
myResourceTable = theResourceTable;
}
@Override
public IdDt getIdDt() {
if (getResourceTable().getForcedId() == null) {
Long id = getResourceId();
return new IdDt(getResourceType() + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + getVersion());
} else {
// Avoid a join query if possible
String forcedId = getTransientForcedId() != null ? getTransientForcedId() : getResourceTable().getForcedId().getForcedId();
return new IdDt(getResourceType() + '/' + forcedId + '/' + Constants.PARAM_HISTORY + '/' + getVersion());
}
}
@Override
public ForcedId getForcedId() {
return getResourceTable().getForcedId();
}
@Override
public void setForcedId(ForcedId theForcedId) {
getResourceTable().setForcedId(theForcedId);
}
} }

View File

@ -23,6 +23,8 @@ package ca.uhn.fhir.jpa.model.entity;
import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource; import ca.uhn.fhir.jpa.model.cross.IBasePersistedResource;
import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId; import ca.uhn.fhir.jpa.model.cross.ResourcePersistentId;
import ca.uhn.fhir.jpa.model.search.IndexNonDeletedInterceptor; import ca.uhn.fhir.jpa.model.search.IndexNonDeletedInterceptor;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException; import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
@ -199,26 +201,36 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
@OneToMany(mappedBy = "myTargetResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false) @OneToMany(mappedBy = "myTargetResource", cascade = {}, fetch = FetchType.LAZY, orphanRemoval = false)
@OptimisticLock(excluded = true) @OptimisticLock(excluded = true)
private Collection<ResourceLink> myResourceLinksAsTarget; private Collection<ResourceLink> myResourceLinksAsTarget;
@Column(name = "RES_TYPE", length = RESTYPE_LEN, nullable = false) @Column(name = "RES_TYPE", length = RESTYPE_LEN, nullable = false)
@Field @Field
@OptimisticLock(excluded = true) @OptimisticLock(excluded = true)
private String myResourceType; private String myResourceType;
@OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true) @OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
@OptimisticLock(excluded = true) @OptimisticLock(excluded = true)
private Collection<SearchParamPresent> mySearchParamPresents; private Collection<SearchParamPresent> mySearchParamPresents;
@OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true) @OneToMany(mappedBy = "myResource", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
@OptimisticLock(excluded = true) @OptimisticLock(excluded = true)
private Set<ResourceTag> myTags; private Set<ResourceTag> myTags;
@Transient @Transient
private transient boolean myUnchangedInCurrentOperation; private transient boolean myUnchangedInCurrentOperation;
@Version @Version
@Column(name = "RES_VER") @Column(name = "RES_VER")
private long myVersion; private long myVersion;
@OneToMany(mappedBy = "myResourceTable", fetch = FetchType.LAZY) @OneToMany(mappedBy = "myResourceTable", fetch = FetchType.LAZY)
private Collection<ResourceHistoryProvenanceEntity> myProvenance; private Collection<ResourceHistoryProvenanceEntity> myProvenance;
@Transient @Transient
private transient ResourceHistoryTable myCurrentVersionEntity; private transient ResourceHistoryTable myCurrentVersionEntity;
@OneToOne(optional = true, fetch = FetchType.EAGER, cascade = {}, orphanRemoval = false, mappedBy = "myResource")
private ForcedId myForcedId;
@Override @Override
public ResourceTag addTag(TagDefinition theTag) { public ResourceTag addTag(TagDefinition theTag) {
for (ResourceTag next : getTags()) { for (ResourceTag next : getTags()) {
@ -549,7 +561,7 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
retVal.setUpdated(getUpdated()); retVal.setUpdated(getUpdated());
retVal.setFhirVersion(getFhirVersion()); retVal.setFhirVersion(getFhirVersion());
retVal.setDeleted(getDeleted()); retVal.setDeleted(getDeleted());
retVal.setForcedId(getForcedId()); retVal.setResourceTable(this);
retVal.getTags().clear(); retVal.getTags().clear();
@ -606,4 +618,28 @@ public class ResourceTable extends BaseHasResource implements Serializable, IBas
public ResourcePersistentId getPersistentId() { public ResourcePersistentId getPersistentId() {
return new ResourcePersistentId(getId()); return new ResourcePersistentId(getId());
} }
@Override
public ForcedId getForcedId() {
return myForcedId;
}
@Override
public void setForcedId(ForcedId theForcedId) {
myForcedId = theForcedId;
}
@Override
public IdDt getIdDt() {
if (getForcedId() == null) {
Long id = getResourceId();
return new IdDt(getResourceType() + '/' + id + '/' + Constants.PARAM_HISTORY + '/' + getVersion());
} else {
// Avoid a join query if possible
String forcedId = getTransientForcedId() != null ? getTransientForcedId() : getForcedId().getForcedId();
return new IdDt(getResourceType() + '/' + forcedId + '/' + Constants.PARAM_HISTORY + '/' + getVersion());
}
}
} }