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 jakarta.persistence.Column;
023import jakarta.persistence.EmbeddedId;
024import jakarta.persistence.Entity;
025import jakarta.persistence.FetchType;
026import jakarta.persistence.ForeignKey;
027import jakarta.persistence.Index;
028import jakarta.persistence.JoinColumn;
029import jakarta.persistence.ManyToOne;
030import jakarta.persistence.Table;
031import jakarta.persistence.Temporal;
032import jakarta.persistence.TemporalType;
033
034import java.time.LocalDate;
035import java.util.Date;
036import java.util.Optional;
037
038/**
039 * This entity is used to enforce uniqueness on a given search URL being
040 * used as a conditional operation URL, e.g. a conditional create or a
041 * conditional update. When we perform a conditional operation that is
042 * creating a new resource, we store an entity with the conditional URL
043 * in this table. The URL is the PK of the table, so the database
044 * ensures that two concurrent threads don't accidentally create two
045 * resources with the same conditional URL.
046 */
047@Entity
048@Table(
049                name = "HFJ_RES_SEARCH_URL",
050                indexes = {
051                        @Index(name = "IDX_RESSEARCHURL_RES", columnList = "RES_ID"),
052                        @Index(name = "IDX_RESSEARCHURL_TIME", columnList = "CREATED_TIME")
053                })
054public class ResourceSearchUrlEntity {
055
056        public static final String RES_SEARCH_URL_COLUMN_NAME = "RES_SEARCH_URL";
057        public static final String PARTITION_ID = "PARTITION_ID";
058
059        public static final int RES_SEARCH_URL_LENGTH = 768;
060
061        @EmbeddedId
062        private ResourceSearchUrlEntityPK myPk;
063
064        @ManyToOne(fetch = FetchType.LAZY)
065        @JoinColumn(
066                        name = "RES_ID",
067                        nullable = false,
068                        updatable = false,
069                        foreignKey = @ForeignKey(name = "FK_RES_SEARCH_URL_RESOURCE"))
070        private ResourceTable myResourceTable;
071
072        @Column(name = "RES_ID", updatable = false, nullable = false, insertable = false)
073        private Long myResourcePid;
074
075        @Column(name = "PARTITION_DATE", nullable = true, insertable = true, updatable = false)
076        private LocalDate myPartitionDate;
077
078        @Column(name = "CREATED_TIME", nullable = false)
079        @Temporal(TemporalType.TIMESTAMP)
080        private Date myCreatedTime;
081
082        public static ResourceSearchUrlEntity from(
083                        String theUrl, ResourceTable theResourceTable, boolean theSearchUrlDuplicateAcrossPartitionsEnabled) {
084
085                return new ResourceSearchUrlEntity()
086                                .setPk(ResourceSearchUrlEntityPK.from(
087                                                theUrl, theResourceTable, theSearchUrlDuplicateAcrossPartitionsEnabled))
088                                .setPartitionDate(Optional.ofNullable(theResourceTable.getPartitionId())
089                                                .map(PartitionablePartitionId::getPartitionDate)
090                                                .orElse(null))
091                                .setResourceTable(theResourceTable)
092                                .setCreatedTime(new Date());
093        }
094
095        public ResourceSearchUrlEntityPK getPk() {
096                return myPk;
097        }
098
099        public ResourceSearchUrlEntity setPk(ResourceSearchUrlEntityPK thePk) {
100                myPk = thePk;
101                return this;
102        }
103
104        public Long getResourcePid() {
105                if (myResourcePid != null) {
106                        return myResourcePid;
107                }
108                return myResourceTable.getResourceId();
109        }
110
111        public ResourceSearchUrlEntity setResourcePid(Long theResourcePid) {
112                myResourcePid = theResourcePid;
113                return this;
114        }
115
116        public ResourceTable getResourceTable() {
117                return myResourceTable;
118        }
119
120        public ResourceSearchUrlEntity setResourceTable(ResourceTable myResourceTable) {
121                this.myResourceTable = myResourceTable;
122                return this;
123        }
124
125        public Date getCreatedTime() {
126                return myCreatedTime;
127        }
128
129        public ResourceSearchUrlEntity setCreatedTime(Date theCreatedTime) {
130                myCreatedTime = theCreatedTime;
131                return this;
132        }
133
134        public String getSearchUrl() {
135                return myPk.getSearchUrl();
136        }
137
138        public Integer getPartitionId() {
139                return myPk.getPartitionId();
140        }
141
142        public LocalDate getPartitionDate() {
143                return myPartitionDate;
144        }
145
146        public ResourceSearchUrlEntity setPartitionDate(LocalDate thePartitionDate) {
147                myPartitionDate = thePartitionDate;
148                return this;
149        }
150}