Use composite primary key for HostHistory and ContactHistory (#809)

* Use composite primary key for HostHistory and ContactHistory

* Update flyway file version

* Make getters private

* Add javadoc

* Rebase on HEAD
This commit is contained in:
Shicong Huang
2020-10-01 11:01:57 -04:00
committed by GitHub
parent 71f86c9970
commit fd40a6a2b9
14 changed files with 248 additions and 89 deletions
@@ -17,8 +17,11 @@ package google.registry.model.contact;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.EntitySubclass;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.contact.ContactHistory.ContactHistoryId;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import java.io.Serializable;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.persistence.Access;
@@ -28,6 +31,7 @@ import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.PostLoad;
/**
@@ -47,13 +51,13 @@ import javax.persistence.PostLoad;
})
@EntitySubclass
@Access(AccessType.FIELD)
@IdClass(ContactHistoryId.class)
public class ContactHistory extends HistoryEntry {
// Store ContactBase instead of ContactResource so we don't pick up its @Id
@Nullable ContactBase contactBase;
@Column(nullable = false)
VKey<ContactResource> contactRepoId;
@Id String contactRepoId;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TempHistorySequenceGenerator")
@@ -76,7 +80,14 @@ public class ContactHistory extends HistoryEntry {
/** The key to the {@link ContactResource} this is based off of. */
public VKey<ContactResource> getContactRepoId() {
return contactRepoId;
return VKey.create(
ContactResource.class, contactRepoId, Key.create(ContactResource.class, contactRepoId));
}
/** Creates a {@link VKey} instance for this entity. */
public VKey<ContactHistory> createVKey() {
return VKey.create(
ContactHistory.class, new ContactHistoryId(contactRepoId, getId()), Key.create(this));
}
@PostLoad
@@ -87,10 +98,65 @@ public class ContactHistory extends HistoryEntry {
contactBase = null;
}
// Fill in the full, symmetric, parent repo ID key
Key<ContactResource> parentKey =
Key.create(ContactResource.class, (String) contactRepoId.getSqlKey());
parent = parentKey;
contactRepoId = VKey.create(ContactResource.class, contactRepoId.getSqlKey(), parentKey);
parent = Key.create(ContactResource.class, contactRepoId);
}
/** Class to represent the composite primary key of {@link ContactHistory} entity. */
static class ContactHistoryId extends ImmutableObject implements Serializable {
private String contactRepoId;
private Long id;
/** Hibernate requires this default constructor. */
private ContactHistoryId() {}
ContactHistoryId(String contactRepoId, long id) {
this.contactRepoId = contactRepoId;
this.id = id;
}
/**
* Returns the contact repository id.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private String getContactRepoId() {
return contactRepoId;
}
/**
* Returns the history revision id.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private long getId() {
return id;
}
/**
* Sets the contact repository id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setContactRepoId(String contactRepoId) {
this.contactRepoId = contactRepoId;
}
/**
* Sets the history revision id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setId(long id) {
this.id = id;
}
}
@Override
@@ -111,9 +177,9 @@ public class ContactHistory extends HistoryEntry {
return this;
}
public Builder setContactRepoId(VKey<ContactResource> contactRepoId) {
public Builder setContactRepoId(String contactRepoId) {
getInstance().contactRepoId = contactRepoId;
contactRepoId.maybeGetOfyKey().ifPresent(parent -> getInstance().parent = parent);
getInstance().parent = Key.create(ContactResource.class, contactRepoId);
return this;
}
@@ -121,8 +187,7 @@ public class ContactHistory extends HistoryEntry {
@Override
public Builder setParent(Key<? extends EppResource> parent) {
super.setParent(parent);
getInstance().contactRepoId =
VKey.create(ContactResource.class, parent.getName(), (Key<ContactResource>) parent);
getInstance().contactRepoId = parent.getName();
return this;
}
}
@@ -51,7 +51,6 @@ public class ContactResource extends ContactBase
@Override
public VKey<ContactResource> createVKey() {
// TODO(mmuller): create symmetric keys if we can ever reload both sides.
return VKey.create(ContactResource.class, getRepoId(), Key.create(this));
}
@@ -156,8 +156,10 @@ public class DomainHistory extends HistoryEntry {
return VKey.create(DomainBase.class, domainRepoId, Key.create(DomainBase.class, domainRepoId));
}
/** Creates a {@link VKey} instance for this entity. */
public VKey<DomainHistory> createVKey() {
return VKey.createSql(DomainHistory.class, new DomainHistoryId(domainRepoId, getId()));
return VKey.create(
DomainHistory.class, new DomainHistoryId(domainRepoId, getId()), Key.create(this));
}
@PostLoad
@@ -188,19 +190,45 @@ public class DomainHistory extends HistoryEntry {
this.id = id;
}
String getDomainRepoId() {
/**
* Returns the domain repository id.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private String getDomainRepoId() {
return domainRepoId;
}
void setDomainRepoId(String domainRepoId) {
this.domainRepoId = domainRepoId;
}
long getId() {
/**
* Returns the history revision id.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private long getId() {
return id;
}
void setId(long id) {
/**
* Sets the domain repository id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setDomainRepoId(String domainRepoId) {
this.domainRepoId = domainRepoId;
}
/**
* Sets the history revision id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setId(long id) {
this.id = id;
}
}
@@ -14,11 +14,15 @@
package google.registry.model.host;
import com.googlecode.objectify.Key;
import com.googlecode.objectify.annotation.EntitySubclass;
import google.registry.model.EppResource;
import google.registry.model.ImmutableObject;
import google.registry.model.host.HostHistory.HostHistoryId;
import google.registry.model.reporting.HistoryEntry;
import google.registry.persistence.VKey;
import java.io.Serializable;
import java.util.Optional;
import javax.annotation.Nullable;
import javax.persistence.Access;
@@ -28,6 +32,7 @@ import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.IdClass;
import javax.persistence.PostLoad;
/**
@@ -48,13 +53,13 @@ import javax.persistence.PostLoad;
})
@EntitySubclass
@Access(AccessType.FIELD)
@IdClass(HostHistoryId.class)
public class HostHistory extends HistoryEntry {
// Store HostBase instead of HostResource so we don't pick up its @Id
@Nullable HostBase hostBase;
@Column(nullable = false)
VKey<HostResource> hostRepoId;
@Id String hostRepoId;
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TempHistorySequenceGenerator")
@@ -77,7 +82,12 @@ public class HostHistory extends HistoryEntry {
/** The key to the {@link google.registry.model.host.HostResource} this is based off of. */
public VKey<HostResource> getHostRepoId() {
return hostRepoId;
return VKey.create(HostResource.class, hostRepoId, Key.create(HostResource.class, hostRepoId));
}
/** Creates a {@link VKey} instance for this entity. */
public VKey<HostHistory> createVKey() {
return VKey.create(HostHistory.class, new HostHistoryId(hostRepoId, getId()), Key.create(this));
}
@PostLoad
@@ -88,9 +98,65 @@ public class HostHistory extends HistoryEntry {
hostBase = null;
}
// Fill in the full, symmetric, parent repo ID key
Key<HostResource> parentKey = Key.create(HostResource.class, (String) hostRepoId.getSqlKey());
parent = parentKey;
hostRepoId = VKey.create(HostResource.class, hostRepoId.getSqlKey(), parentKey);
parent = Key.create(HostResource.class, hostRepoId);
}
/** Class to represent the composite primary key of {@link HostHistory} entity. */
static class HostHistoryId extends ImmutableObject implements Serializable {
private String hostRepoId;
private Long id;
/** Hibernate requires this default constructor. */
private HostHistoryId() {}
HostHistoryId(String hostRepoId, long id) {
this.hostRepoId = hostRepoId;
this.id = id;
}
/**
* Returns the host repository id.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private String getHostRepoId() {
return hostRepoId;
}
/**
* Returns the history revision id.
*
* <p>This method is private because it is only used by Hibernate.
*/
@SuppressWarnings("unused")
private long getId() {
return id;
}
/**
* Sets the host repository id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setHostRepoId(String hostRepoId) {
this.hostRepoId = hostRepoId;
}
/**
* Sets the history revision id.
*
* <p>This method is private because it is only used by Hibernate and should not be used
* externally to keep immutability.
*/
@SuppressWarnings("unused")
private void setId(long id) {
this.id = id;
}
}
@Override
@@ -111,9 +177,9 @@ public class HostHistory extends HistoryEntry {
return this;
}
public Builder setHostRepoId(VKey<HostResource> hostRepoId) {
public Builder setHostRepoId(String hostRepoId) {
getInstance().hostRepoId = hostRepoId;
hostRepoId.maybeGetOfyKey().ifPresent(parent -> getInstance().parent = parent);
getInstance().parent = Key.create(HostResource.class, hostRepoId);
return this;
}
@@ -121,8 +187,7 @@ public class HostHistory extends HistoryEntry {
@Override
public Builder setParent(Key<? extends EppResource> parent) {
super.setParent(parent);
getInstance().hostRepoId =
VKey.create(HostResource.class, parent.getName(), (Key<HostResource>) parent);
getInstance().hostRepoId = parent.getName();
return this;
}
}
@@ -308,19 +308,10 @@ public class HistoryEntry extends ImmutableObject implements Buildable, Datastor
new DomainHistory.Builder().copyFrom(this).setDomainRepoId(parent.getName()).build();
} else if (parentKind.equals(getKind(HostResource.class))) {
resultEntity =
new HostHistory.Builder()
.copyFrom(this)
.setHostRepoId(
VKey.create(HostResource.class, parent.getName(), (Key<HostResource>) parent))
.build();
new HostHistory.Builder().copyFrom(this).setHostRepoId(parent.getName()).build();
} else if (parentKind.equals(getKind(ContactResource.class))) {
resultEntity =
new ContactHistory.Builder()
.copyFrom(this)
.setContactRepoId(
VKey.create(
ContactResource.class, parent.getName(), (Key<ContactResource>) parent))
.build();
new ContactHistory.Builder().copyFrom(this).setContactRepoId(parent.getName()).build();
} else {
throw new IllegalStateException(
String.format("Unknown kind of HistoryEntry parent %s", parentKind));