001/* 002 * #%L 003 * HAPI FHIR JPA Model 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.model.entity; 021 022import ca.uhn.fhir.jpa.model.config.PartitionSettings; 023import ca.uhn.fhir.model.api.IQueryParameterType; 024import ca.uhn.fhir.rest.param.NumberParam; 025import jakarta.persistence.Column; 026import jakarta.persistence.Embeddable; 027import jakarta.persistence.Entity; 028import jakarta.persistence.FetchType; 029import jakarta.persistence.ForeignKey; 030import jakarta.persistence.GeneratedValue; 031import jakarta.persistence.GenerationType; 032import jakarta.persistence.Id; 033import jakarta.persistence.Index; 034import jakarta.persistence.JoinColumn; 035import jakarta.persistence.ManyToOne; 036import jakarta.persistence.SequenceGenerator; 037import jakarta.persistence.Table; 038import org.apache.commons.lang3.builder.EqualsBuilder; 039import org.apache.commons.lang3.builder.HashCodeBuilder; 040import org.apache.commons.lang3.builder.ToStringBuilder; 041import org.apache.commons.lang3.builder.ToStringStyle; 042import org.hibernate.annotations.JdbcTypeCode; 043import org.hibernate.search.mapper.pojo.mapping.definition.annotation.ScaledNumberField; 044import org.hibernate.type.SqlTypes; 045 046import java.math.BigDecimal; 047import java.util.Objects; 048 049@Embeddable 050@Entity 051@Table( 052 name = "HFJ_SPIDX_NUMBER", 053 indexes = { 054 // We used to have an index with name IDX_SP_NUMBER - Dont reuse 055 @Index(name = "IDX_SP_NUMBER_HASH_VAL_V2", columnList = "HASH_IDENTITY,SP_VALUE,RES_ID,PARTITION_ID"), 056 @Index(name = "IDX_SP_NUMBER_RESID_V2", columnList = "RES_ID, HASH_IDENTITY, SP_VALUE, PARTITION_ID") 057 }) 058public class ResourceIndexedSearchParamNumber extends BaseResourceIndexedSearchParam { 059 060 private static final long serialVersionUID = 1L; 061 062 @Column(name = "SP_VALUE", nullable = true, precision = 19, scale = 2) 063 @ScaledNumberField 064 @JdbcTypeCode(SqlTypes.DECIMAL) 065 public BigDecimal myValue; 066 067 @Id 068 @SequenceGenerator(name = "SEQ_SPIDX_NUMBER", sequenceName = "SEQ_SPIDX_NUMBER") 069 @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_SPIDX_NUMBER") 070 @Column(name = "SP_ID") 071 private Long myId; 072 /** 073 * @since 3.5.0 - At some point this should be made not-null 074 */ 075 @Column(name = "HASH_IDENTITY", nullable = true) 076 private Long myHashIdentity; 077 078 @ManyToOne( 079 optional = false, 080 fetch = FetchType.LAZY, 081 cascade = {}) 082 @JoinColumn( 083 foreignKey = @ForeignKey(name = "FK_SP_NUMBER_RES"), 084 name = "RES_ID", 085 referencedColumnName = "RES_ID", 086 nullable = false) 087 private ResourceTable myResource; 088 089 public ResourceIndexedSearchParamNumber() {} 090 091 public ResourceIndexedSearchParamNumber( 092 PartitionSettings thePartitionSettings, String theResourceType, String theParamName, BigDecimal theValue) { 093 setPartitionSettings(thePartitionSettings); 094 setResourceType(theResourceType); 095 setParamName(theParamName); 096 setValue(theValue); 097 calculateHashes(); 098 } 099 100 @Override 101 public <T extends BaseResourceIndex> void copyMutableValuesFrom(T theSource) { 102 super.copyMutableValuesFrom(theSource); 103 ResourceIndexedSearchParamNumber source = (ResourceIndexedSearchParamNumber) theSource; 104 myValue = source.myValue; 105 myHashIdentity = source.myHashIdentity; 106 } 107 108 @Override 109 public void clearHashes() { 110 myHashIdentity = null; 111 } 112 113 @Override 114 public void calculateHashes() { 115 if (myHashIdentity != null) { 116 return; 117 } 118 String resourceType = getResourceType(); 119 String paramName = getParamName(); 120 setHashIdentity(calculateHashIdentity(getPartitionSettings(), getPartitionId(), resourceType, paramName)); 121 } 122 123 public Long getHashIdentity() { 124 return myHashIdentity; 125 } 126 127 @Override 128 public boolean equals(Object theObj) { 129 if (this == theObj) { 130 return true; 131 } 132 if (theObj == null) { 133 return false; 134 } 135 if (!(theObj instanceof ResourceIndexedSearchParamNumber)) { 136 return false; 137 } 138 ResourceIndexedSearchParamNumber obj = (ResourceIndexedSearchParamNumber) theObj; 139 EqualsBuilder b = new EqualsBuilder(); 140 b.append(getResourceType(), obj.getResourceType()); 141 b.append(getParamName(), obj.getParamName()); 142 b.append(getHashIdentity(), obj.getHashIdentity()); 143 b.append(normalizeForEqualityComparison(getValue()), normalizeForEqualityComparison(obj.getValue())); 144 b.append(isMissing(), obj.isMissing()); 145 return b.isEquals(); 146 } 147 148 private Double normalizeForEqualityComparison(BigDecimal theValue) { 149 if (theValue == null) { 150 return null; 151 } 152 return theValue.doubleValue(); 153 } 154 155 public void setHashIdentity(Long theHashIdentity) { 156 myHashIdentity = theHashIdentity; 157 } 158 159 @Override 160 public Long getId() { 161 return myId; 162 } 163 164 @Override 165 public void setId(Long theId) { 166 myId = theId; 167 } 168 169 public BigDecimal getValue() { 170 return myValue; 171 } 172 173 public void setValue(BigDecimal theValue) { 174 myValue = theValue; 175 } 176 177 @Override 178 public int hashCode() { 179 HashCodeBuilder b = new HashCodeBuilder(); 180 b.append(getResourceType()); 181 b.append(getParamName()); 182 b.append(getHashIdentity()); 183 b.append(normalizeForEqualityComparison(getValue())); 184 b.append(isMissing()); 185 return b.toHashCode(); 186 } 187 188 @Override 189 public IQueryParameterType toQueryParameterType() { 190 return new NumberParam(myValue.toPlainString()); 191 } 192 193 @Override 194 public String toString() { 195 ToStringBuilder b = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); 196 b.append("paramName", getParamName()); 197 b.append("resourceId", getResource().getId()); // TODO: add a field so we don't need to resolve this 198 b.append("value", getValue()); 199 return b.build(); 200 } 201 202 @Override 203 public boolean matches(IQueryParameterType theParam) { 204 if (!(theParam instanceof NumberParam)) { 205 return false; 206 } 207 NumberParam number = (NumberParam) theParam; 208 return Objects.equals(getValue(), number.getValue()); 209 } 210 211 @Override 212 public ResourceTable getResource() { 213 return myResource; 214 } 215 216 @Override 217 public BaseResourceIndexedSearchParam setResource(ResourceTable theResource) { 218 myResource = theResource; 219 setResourceType(theResource.getResourceType()); 220 return this; 221 } 222}