Work on multitenancy

This commit is contained in:
jamesagnew 2020-02-09 20:57:50 -05:00
parent ebf395bee9
commit 1d1aadb813
9 changed files with 127 additions and 38 deletions

View File

@ -25,15 +25,13 @@ import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.Collection;
import java.util.Optional;
import java.util.List;
public interface IResourceIndexedCompositeStringUniqueDao extends JpaRepository<ResourceIndexedCompositeStringUnique, Long> {
@Query("SELECT r FROM ResourceIndexedCompositeStringUnique r WHERE r.myIndexString = :str")
ResourceIndexedCompositeStringUnique findByQueryString(@Param("str") String theQueryString);
@Query("SELECT r.myResourceId FROM ResourceIndexedCompositeStringUnique r WHERE r.myIndexString IN :str")
Collection<Long> findResourcePidsByQueryStrings(@Param("str") Collection<String> theQueryString);
@Query("SELECT r FROM ResourceIndexedCompositeStringUnique r WHERE r.myResourceId = :resId")
List<ResourceIndexedCompositeStringUnique> findAllForResourceId(@Param("resId") Long theResourceId);
}

View File

@ -20,9 +20,8 @@ package ca.uhn.fhir.jpa.dao.data;
* #L%
*/
import org.springframework.data.jpa.repository.JpaRepository;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
@ -32,6 +31,9 @@ import java.util.List;
public interface IResourceIndexedSearchParamStringDao extends JpaRepository<ResourceIndexedSearchParamString, Long> {
@Modifying
@Query("delete from ResourceIndexedSearchParamString t WHERE t.myResourcePid = :resid")
void deleteByResourceId(@Param("resid") Long theResourcePid);
@Query("DELETE FROM ResourceIndexedSearchParamString t WHERE t.myResourcePid = :resId")
void deleteByResourceId(@Param("resId") Long theResourcePid);
@Query("SELECT t FROM ResourceIndexedSearchParamString t WHERE t.myResourcePid = :resId")
List<ResourceIndexedSearchParamString> findAllForResourceId(@Param("resId") Long thePatientId);
}

View File

@ -20,16 +20,20 @@ package ca.uhn.fhir.jpa.dao.data;
* #L%
*/
import org.springframework.data.jpa.repository.JpaRepository;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
public interface IResourceLinkDao extends JpaRepository<ResourceLink, Long> {
import java.util.List;
public interface IResourceLinkDao extends JpaRepository<ResourceLink, Long> {
@Modifying
@Query("delete from ResourceLink t WHERE t.mySourceResourcePid = :resid")
void deleteByResourceId(@Param("resid") Long theResourcePid);
@Query("DELETE FROM ResourceLink t WHERE t.mySourceResourcePid = :resId")
void deleteByResourceId(@Param("resId") Long theResourcePid);
@Query("SELECT t FROM ResourceLink t WHERE t.mySourceResourcePid = :resId")
List<ResourceLink> findAllForResourceId(@Param("resId") Long thePatientId);
}

View File

@ -1,7 +1,13 @@
package ca.uhn.fhir.jpa.dao.data;
import java.util.Collection;
import java.util.Date;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
/*
* #%L
@ -23,18 +29,10 @@ import java.util.Date;
* #L%
*/
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
public interface ISearchParamPresentDao extends JpaRepository<SearchParamPresent, Long> {
@Query("SELECT s FROM SearchParamPresent s WHERE s.myResource = :res")
Collection<SearchParamPresent> findAllForResource(@Param("res") ResourceTable theResource);
List<SearchParamPresent> findAllForResource(@Param("res") ResourceTable theResource);
@Modifying
@Query("delete from SearchParamPresent t WHERE t.myResourcePid = :resid")

View File

@ -66,6 +66,7 @@ public class SearchParamPresenceSvcImpl implements ISearchParamPresenceSvc {
present.setResource(theResource);
present.setParamName(paramName);
present.setPresent(next.getValue());
present.setTenantId(theResource.getTenantId());
present.calculateHashes();
newHashToPresence.put(present.getHashPresence(), present);

View File

@ -4,13 +4,22 @@ import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.model.entity.ResourceHistoryTable;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedCompositeStringUnique;
import ca.uhn.fhir.jpa.model.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.model.entity.ResourceLink;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.model.entity.SearchParamPresent;
import ca.uhn.fhir.jpa.model.entity.TenantId;
import ca.uhn.fhir.jpa.model.util.JpaConstants;
import ca.uhn.fhir.jpa.searchparam.SearchParamConstants;
import ca.uhn.fhir.util.TestUtil;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.SearchParameter;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@ -19,6 +28,7 @@ import org.junit.Test;
import javax.servlet.ServletException;
import java.time.LocalDate;
import java.time.Month;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@ -62,30 +72,88 @@ public class MultitenantR4Test extends BaseJpaR4SystemTest {
@Test
public void testCreateResourceWithTenant() {
createUniqueCompositeSp();
int expectId = 3;
LocalDate expectDate = LocalDate.of(2020, Month.JANUARY, 14);
myInterceptorRegistry.registerInterceptor(new MyInterceptor(new TenantId(expectId, expectDate)));
Organization org = new Organization();
org.setName("org");
IIdType orgId = myOrganizationDao.create(org).getId().toUnqualifiedVersionless();
Patient p = new Patient();
p.addName().setFamily("FAM");
p.addIdentifier().setSystem("system").setValue("value");
p.setBirthDate(new Date());
p.getManagingOrganization().setReferenceElement(orgId);
Long patientId = myPatientDao.create(p).getId().getIdPartAsLong();
runInTransaction(() -> {
// HFJ_RESOURCE
ResourceTable resourceTable = myResourceTableDao.findById(patientId).orElseThrow(IllegalArgumentException::new);
assertEquals(expectId, resourceTable.getTenantId().getTenantId().intValue());
assertEquals(expectDate, resourceTable.getTenantId().getTenantDate());
List<ResourceIndexedSearchParamString> strings = myResourceIndexedSearchParamStringDao.findAll();
// HFJ_RES_VER
ResourceHistoryTable version = myResourceHistoryTableDao.findForIdAndVersionAndFetchProvenance(patientId, 1L);
assertEquals(expectId, version.getTenantId().getTenantId().intValue());
assertEquals(expectDate, version.getTenantId().getTenantDate());
// HFJ_SPIDX_STRING
List<ResourceIndexedSearchParamString> strings = myResourceIndexedSearchParamStringDao.findAllForResourceId(patientId);
ourLog.info("\n * {}", strings.stream().map(ResourceIndexedSearchParamString::toString).collect(Collectors.joining("\n * ")));
assertEquals(10, strings.size());
assertEquals(expectId, strings.get(0).getTenantId().getTenantId().intValue());
assertEquals(expectDate, strings.get(0).getTenantId().getTenantDate());
// HFJ_RES_LINK
List<ResourceLink> resourceLinks = myResourceLinkDao.findAllForResourceId(patientId);
assertEquals(1, resourceLinks.size());
assertEquals(expectId, resourceLinks.get(0).getTenantId().getTenantId().intValue());
assertEquals(expectDate, resourceLinks.get(0).getTenantId().getTenantDate());
// HFJ_RES_PARAM_PRESENT
List<SearchParamPresent> presents = mySearchParamPresentDao.findAllForResource(resourceTable);
assertEquals(3, presents.size());
assertEquals(expectId, presents.get(0).getTenantId().getTenantId().intValue());
assertEquals(expectDate, presents.get(0).getTenantId().getTenantDate());
// HFJ_IDX_CMP_STRING_UNIQ
List<ResourceIndexedCompositeStringUnique> uniques = myResourceIndexedCompositeStringUniqueDao.findAllForResourceId(patientId);
assertEquals(3, uniques.size());
assertEquals(expectId, uniques.get(0).getTenantId().getTenantId().intValue());
assertEquals(expectDate, uniques.get(0).getTenantId().getTenantDate());
});
}
private void createUniqueCompositeSp() {
SearchParameter sp = new SearchParameter();
sp.setId("SearchParameter/patient-birthdate");
sp.setType(Enumerations.SearchParamType.DATE);
sp.setCode("birthdate");
sp.setExpression("Patient.birthDate");
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.addBase("Patient");
mySearchParameterDao.update(sp);
sp = new SearchParameter();
sp.setId("SearchParameter/patient-birthdate");
sp.setType(Enumerations.SearchParamType.COMPOSITE);
sp.setStatus(Enumerations.PublicationStatus.ACTIVE);
sp.addBase("Patient");
sp.addComponent()
.setExpression("Patient")
.setDefinition("SearchParameter/patient-birthdate");
sp.addExtension()
.setUrl(SearchParamConstants.EXT_SP_UNIQUE)
.setValue(new BooleanType(true));
mySearchParameterDao.update(sp);
mySearchParamRegistry.forceRefresh();
}
@Interceptor
public static class MyInterceptor {

View File

@ -48,6 +48,8 @@ public class ResourceIndexedCompositeStringUnique implements Comparable<Resource
private Long myResourceId;
@Column(name = "IDX_STRING", nullable = false, length = MAX_STRING_LENGTH)
private String myIndexString;
@Embedded
private TenantId myTenantId;
/**
* Constructor
@ -64,6 +66,14 @@ public class ResourceIndexedCompositeStringUnique implements Comparable<Resource
setIndexString(theIndexString);
}
public TenantId getTenantId() {
return myTenantId;
}
public void setTenantId(TenantId theTenantId) {
myTenantId = theTenantId;
}
@Override
public int compareTo(ResourceIndexedCompositeStringUnique theO) {
CompareToBuilder b = new CompareToBuilder();
@ -116,6 +126,7 @@ public class ResourceIndexedCompositeStringUnique implements Comparable<Resource
.append("id", myId)
.append("resourceId", myResourceId)
.append("indexString", myIndexString)
.append("tenant", myTenantId)
.toString();
}
}

View File

@ -46,12 +46,14 @@ public class SearchParamPresent implements Serializable {
@ManyToOne()
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, foreignKey = @ForeignKey(name = "FK_RESPARMPRES_RESID"))
private ResourceTable myResource;
@Column(name="RES_ID", nullable = false, insertable = false, updatable = false)
@Column(name = "RES_ID", nullable = false, insertable = false, updatable = false)
private Long myResourcePid;
@Transient
private transient String myParamName;
@Column(name = "HASH_PRESENCE")
private Long myHashPresence;
@Embedded
private TenantId myTenantId;
/**
* Constructor
@ -110,9 +112,18 @@ public class SearchParamPresent implements Serializable {
b.append("resPid", myResource.getIdDt().toUnqualifiedVersionless().getValue());
b.append("paramName", myParamName);
b.append("present", myPresent);
b.append("tenant", myTenantId);
return b.build();
}
public TenantId getTenantId() {
return myTenantId;
}
public void setTenantId(TenantId theTenantId) {
myTenantId = theTenantId;
}
public static long calculateHashPresence(String theResourceType, String theParamName, Boolean thePresent) {
String string = thePresent != null ? Boolean.toString(thePresent) : Boolean.toString(false);
return BaseResourceIndexedSearchParam.hash(theResourceType, theParamName, string);

View File

@ -1,18 +1,17 @@
package ca.uhn.fhir.jpa.model.entity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import java.time.LocalDate;
import static org.apache.commons.lang3.ObjectUtils.defaultIfNull;
@Embeddable
public class TenantId implements Cloneable {
@Column(name = "TENANT_ID", nullable = true)
@Column(name = "TENANT_ID", nullable = true, insertable = true, updatable = false)
private Integer myTenantId;
@Column(name = "TENANT_DATE", nullable = true)
@Column(name = "TENANT_DATE", nullable = true, insertable = true, updatable = false)
private LocalDate myTenantDate;
/**
@ -30,7 +29,7 @@ public class TenantId implements Cloneable {
setTenantDate(theTenantDate);
}
public Integer getTenantId() {
public Integer getTenantId() {
return myTenantId;
}
@ -57,9 +56,6 @@ public class TenantId implements Cloneable {
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("id", myTenantId)
.append("date", myTenantDate)
.toString();
return defaultIfNull(myTenantId, "null").toString();
}
}