001package ca.uhn.fhir.jpa.model.entity;
002
003/*
004 * #%L
005 * HAPI FHIR JPA Model
006 * %%
007 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.model.primitive.IdDt;
024import ca.uhn.fhir.rest.api.Constants;
025import ca.uhn.fhir.rest.api.server.storage.ResourcePersistentId;
026import org.apache.commons.lang3.builder.ToStringBuilder;
027import org.apache.commons.lang3.builder.ToStringStyle;
028import org.hibernate.annotations.OptimisticLock;
029
030import javax.persistence.CascadeType;
031import javax.persistence.Column;
032import javax.persistence.Entity;
033import javax.persistence.EnumType;
034import javax.persistence.Enumerated;
035import javax.persistence.FetchType;
036import javax.persistence.ForeignKey;
037import javax.persistence.GeneratedValue;
038import javax.persistence.GenerationType;
039import javax.persistence.Id;
040import javax.persistence.Index;
041import javax.persistence.JoinColumn;
042import javax.persistence.Lob;
043import javax.persistence.ManyToOne;
044import javax.persistence.OneToMany;
045import javax.persistence.OneToOne;
046import javax.persistence.SequenceGenerator;
047import javax.persistence.Table;
048import javax.persistence.UniqueConstraint;
049import java.io.Serializable;
050import java.util.ArrayList;
051import java.util.Collection;
052
053@Entity
054@Table(name = ResourceHistoryTable.HFJ_RES_VER, uniqueConstraints = {
055        @UniqueConstraint(name = ResourceHistoryTable.IDX_RESVER_ID_VER, columnNames = {"RES_ID", "RES_VER"})
056}, indexes = {
057        @Index(name = "IDX_RESVER_TYPE_DATE", columnList = "RES_TYPE,RES_UPDATED"),
058        @Index(name = "IDX_RESVER_ID_DATE", columnList = "RES_ID,RES_UPDATED"),
059        @Index(name = "IDX_RESVER_DATE", columnList = "RES_UPDATED")
060})
061public class ResourceHistoryTable extends BaseHasResource implements Serializable {
062        public static final String IDX_RESVER_ID_VER = "IDX_RESVER_ID_VER";
063        /**
064         * @see ResourceEncodingEnum
065         */
066        // Don't reduce the visibility here, we reference this from Smile
067        @SuppressWarnings("WeakerAccess")
068        public static final int ENCODING_COL_LENGTH = 5;
069        public static final String HFJ_RES_VER = "HFJ_RES_VER";
070        public static final int RES_TEXT_VC_MAX_LENGTH = 4000;
071        private static final long serialVersionUID = 1L;
072        @Id
073        @SequenceGenerator(name = "SEQ_RESOURCE_HISTORY_ID", sequenceName = "SEQ_RESOURCE_HISTORY_ID")
074        @GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_RESOURCE_HISTORY_ID")
075        @Column(name = "PID")
076        private Long myId;
077        @ManyToOne(fetch = FetchType.LAZY)
078        @JoinColumn(name = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_RESOURCE_HISTORY_RESOURCE"))
079        private ResourceTable myResourceTable;
080        @Column(name = "RES_ID", nullable = false, updatable = false, insertable = false)
081        private Long myResourceId;
082        @Column(name = "RES_TYPE", length = ResourceTable.RESTYPE_LEN, nullable = false)
083        private String myResourceType;
084        @Column(name = "RES_VER", nullable = false)
085        private Long myResourceVersion;
086        @OneToMany(mappedBy = "myResourceHistory", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
087        private Collection<ResourceHistoryTag> myTags;
088        @Column(name = "RES_TEXT", length = Integer.MAX_VALUE - 1, nullable = true)
089        @Lob()
090        @OptimisticLock(excluded = true)
091        private byte[] myResource;
092        @Column(name = "RES_TEXT_VC", length = RES_TEXT_VC_MAX_LENGTH, nullable = true)
093        @OptimisticLock(excluded = true)
094        private String myResourceTextVc;
095        @Column(name = "RES_ENCODING", nullable = false, length = ENCODING_COL_LENGTH)
096        @Enumerated(EnumType.STRING)
097        @OptimisticLock(excluded = true)
098        private ResourceEncodingEnum myEncoding;
099        @OneToOne(mappedBy = "myResourceHistoryTable", cascade = {CascadeType.REMOVE})
100        private ResourceHistoryProvenanceEntity myProvenance;
101
102        /**
103         * Constructor
104         */
105        public ResourceHistoryTable() {
106                super();
107        }
108
109        @Override
110        public String toString() {
111                return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
112                        .append("resourceId", myResourceId)
113                        .append("resourceType", myResourceType)
114                        .append("resourceVersion", myResourceVersion)
115                        .append("pid", myId)
116                        .toString();
117        }
118
119        public String getResourceTextVc() {
120                return myResourceTextVc;
121        }
122
123        public void setResourceTextVc(String theResourceTextVc) {
124                myResourceTextVc = theResourceTextVc;
125        }
126
127        public ResourceHistoryProvenanceEntity getProvenance() {
128                return myProvenance;
129        }
130
131        public void addTag(ResourceTag theTag) {
132                ResourceHistoryTag tag = new ResourceHistoryTag(this, theTag.getTag(), getPartitionId());
133                tag.setResourceType(theTag.getResourceType());
134                getTags().add(tag);
135        }
136
137        @Override
138        public ResourceHistoryTag addTag(TagDefinition theTag) {
139                for (ResourceHistoryTag next : getTags()) {
140                        if (next.getTag().equals(theTag)) {
141                                return next;
142                        }
143                }
144                ResourceHistoryTag historyTag = new ResourceHistoryTag(this, theTag, getPartitionId());
145                getTags().add(historyTag);
146                return historyTag;
147        }
148
149        public ResourceEncodingEnum getEncoding() {
150                return myEncoding;
151        }
152
153        public void setEncoding(ResourceEncodingEnum theEncoding) {
154                myEncoding = theEncoding;
155        }
156
157        @Override
158        public Long getId() {
159                return myId;
160        }
161
162        /**
163         * Do not delete, required for java bean introspection
164         */
165        public Long getMyId() {
166                return myId;
167        }
168
169        /**
170         * Do not delete, required for java bean introspection
171         */
172        public void setMyId(Long theId) {
173                myId = theId;
174        }
175
176        public byte[] getResource() {
177                return myResource;
178        }
179
180        public void setResource(byte[] theResource) {
181                myResource = theResource;
182        }
183
184        @Override
185        public Long getResourceId() {
186                return myResourceId;
187        }
188
189        public void setResourceId(Long theResourceId) {
190                myResourceId = theResourceId;
191        }
192
193        @Override
194        public String getResourceType() {
195                return myResourceType;
196        }
197
198        public void setResourceType(String theResourceType) {
199                myResourceType = theResourceType;
200        }
201
202        @Override
203        public Collection<ResourceHistoryTag> getTags() {
204                if (myTags == null) {
205                        myTags = new ArrayList<>();
206                }
207                return myTags;
208        }
209
210        @Override
211        public long getVersion() {
212                return myResourceVersion;
213        }
214
215        public void setVersion(long theVersion) {
216                myResourceVersion = theVersion;
217        }
218
219        @Override
220        public ResourcePersistentId getPersistentId() {
221                return new ResourcePersistentId(myResourceId);
222        }
223
224        public ResourceTable getResourceTable() {
225                return myResourceTable;
226        }
227
228        public void setResourceTable(ResourceTable theResourceTable) {
229                myResourceTable = theResourceTable;
230        }
231
232        @Override
233        public IdDt getIdDt() {
234                // Avoid a join query if possible
235                String resourceIdPart;
236                if (getTransientForcedId() != null) {
237                        resourceIdPart = getTransientForcedId();
238                } else {
239                        if (getResourceTable().getForcedId() == null) {
240                                Long id = getResourceId();
241                                resourceIdPart = id.toString();
242                        } else {
243                                resourceIdPart = getResourceTable().getForcedId().getForcedId();
244                        }
245                }
246                return new IdDt(getResourceType() + '/' + resourceIdPart + '/' + Constants.PARAM_HISTORY + '/' + getVersion());
247        }
248
249        @Override
250        public ForcedId getForcedId() {
251                return getResourceTable().getForcedId();
252        }
253
254        @Override
255        public void setForcedId(ForcedId theForcedId) {
256                getResourceTable().setForcedId(theForcedId);
257        }
258
259        /**
260         * Returns <code>true</code> if there is a populated resource text (i.e.
261         * either {@link #getResource()} or {@link #getResourceTextVc()} return a non null
262         * value.
263         */
264        public boolean hasResource() {
265                return myResource != null || myResourceTextVc != null;
266        }
267}