diff --git a/core/src/main/java/google/registry/model/EppResource.java b/core/src/main/java/google/registry/model/EppResource.java
index 18241ab8c..845052291 100644
--- a/core/src/main/java/google/registry/model/EppResource.java
+++ b/core/src/main/java/google/registry/model/EppResource.java
@@ -70,9 +70,11 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
/** The ID of the registrar that is currently sponsoring this resource. */
@Index
+ @Column(nullable = false)
String currentSponsorClientId;
/** The ID of the registrar that created this resource. */
+ @Column(nullable = false)
String creationClientId;
/**
@@ -88,7 +90,9 @@ public abstract class EppResource extends BackupGroupRoot implements Buildable {
// Map the method to XML, not the field, because if we map the field (with an adaptor class) it
// will never be omitted from the xml even if the timestamp inside creationTime is null and we
// return null from the adaptor. (Instead it gets written as an empty tag.)
- @Index CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
+ @Column(nullable = false)
+ @Index
+ CreateAutoTimestamp creationTime = CreateAutoTimestamp.create(null);
/**
* The time when this resource was or will be deleted.
diff --git a/core/src/main/java/google/registry/model/contact/ContactAddress.java b/core/src/main/java/google/registry/model/contact/ContactAddress.java
index bacf2e467..7aab6e6d7 100644
--- a/core/src/main/java/google/registry/model/contact/ContactAddress.java
+++ b/core/src/main/java/google/registry/model/contact/ContactAddress.java
@@ -16,13 +16,14 @@ package google.registry.model.contact;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.eppcommon.Address;
+import javax.persistence.Embeddable;
/**
* EPP Contact Address
*
- *
This class is embedded inside the {@link PostalInfo} of an EPP contact to hold its
- * address. The fields are all defined in parent class {@link Address}, but the subclass is still
- * necessary to pick up the contact namespace.
+ *
This class is embedded inside the {@link PostalInfo} of an EPP contact to hold its address.
+ * The fields are all defined in parent class {@link Address}, but the subclass is still necessary
+ * to pick up the contact namespace.
*
*
This does not implement {@code Overlayable} because it is intended to be bulk replaced on
* update.
@@ -30,6 +31,7 @@ import google.registry.model.eppcommon.Address;
* @see PostalInfo
*/
@Embed
+@Embeddable
public class ContactAddress extends Address {
/** Builder for {@link ContactAddress}. */
diff --git a/core/src/main/java/google/registry/model/contact/ContactAuthInfo.java b/core/src/main/java/google/registry/model/contact/ContactAuthInfo.java
index 8d3fa3808..56c4d02d6 100644
--- a/core/src/main/java/google/registry/model/contact/ContactAuthInfo.java
+++ b/core/src/main/java/google/registry/model/contact/ContactAuthInfo.java
@@ -20,6 +20,7 @@ import javax.xml.bind.annotation.XmlType;
/** A version of authInfo specifically for contacts. */
@Embed
+@javax.persistence.Embeddable
@XmlType(namespace = "urn:ietf:params:xml:ns:contact-1.0")
public class ContactAuthInfo extends AuthInfo {
public static ContactAuthInfo create(PasswordAuth pw) {
diff --git a/core/src/main/java/google/registry/model/contact/ContactPhoneNumber.java b/core/src/main/java/google/registry/model/contact/ContactPhoneNumber.java
index dbba4d2b2..750622b86 100644
--- a/core/src/main/java/google/registry/model/contact/ContactPhoneNumber.java
+++ b/core/src/main/java/google/registry/model/contact/ContactPhoneNumber.java
@@ -16,17 +16,19 @@ package google.registry.model.contact;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.eppcommon.PhoneNumber;
+import javax.persistence.Embeddable;
/**
* EPP Contact Phone Number
*
*
This class is embedded inside a {@link ContactResource} hold the phone number of an EPP
- * contact. The fields are all defined in the parent class {@link PhoneNumber}, but the subclass is
+ * contact. The fields are all defined in the parent class {@link PhoneNumber}, but the subclass is
* still necessary to pick up the contact namespace.
*
* @see ContactResource
*/
@Embed
+@Embeddable
public class ContactPhoneNumber extends PhoneNumber {
/** Builder for {@link ContactPhoneNumber}. */
diff --git a/core/src/main/java/google/registry/model/contact/ContactResource.java b/core/src/main/java/google/registry/model/contact/ContactResource.java
index 9c09f165f..f459677ae 100644
--- a/core/src/main/java/google/registry/model/contact/ContactResource.java
+++ b/core/src/main/java/google/registry/model/contact/ContactResource.java
@@ -33,6 +33,11 @@ import google.registry.model.transfer.TransferData;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Stream;
+import javax.persistence.AttributeOverride;
+import javax.persistence.AttributeOverrides;
+import javax.persistence.Column;
+import javax.persistence.Embedded;
+import javax.persistence.Transient;
import javax.xml.bind.annotation.XmlElement;
import org.joda.time.DateTime;
@@ -43,9 +48,19 @@ import org.joda.time.DateTime;
*/
@ReportedOn
@Entity
+@javax.persistence.Entity(name = "Contact")
+@javax.persistence.Table(
+ name = "Contact",
+ indexes = {
+ @javax.persistence.Index(columnList = "creationTime"),
+ @javax.persistence.Index(columnList = "currentSponsorClientId"),
+ @javax.persistence.Index(columnList = "deletionTime"),
+ @javax.persistence.Index(columnList = "contactId", unique = true),
+ @javax.persistence.Index(columnList = "searchName")
+ })
@ExternalMessagingName("contact")
-public class ContactResource extends EppResource implements
- ForeignKeyedEppResource, ResourceWithTransferData {
+public class ContactResource extends EppResource
+ implements ForeignKeyedEppResource, ResourceWithTransferData {
/**
* Unique identifier for this contact.
@@ -61,13 +76,55 @@ public class ContactResource extends EppResource implements
* US-ASCII character set. Personal info; cleared by {@link Builder#wipeOut}.
*/
@IgnoreSave(IfNull.class)
+ @Embedded
+ @AttributeOverrides({
+ @AttributeOverride(name = "name", column = @Column(name = "addr_local_name")),
+ @AttributeOverride(name = "org", column = @Column(name = "addr_local_org")),
+ @AttributeOverride(name = "type", column = @Column(name = "addr_local_type")),
+ @AttributeOverride(
+ name = "address.streetLine1",
+ column = @Column(name = "addr_local_street_line1")),
+ @AttributeOverride(
+ name = "address.streetLine2",
+ column = @Column(name = "addr_local_street_line2")),
+ @AttributeOverride(
+ name = "address.streetLine3",
+ column = @Column(name = "addr_local_street_line3")),
+ @AttributeOverride(name = "address.city", column = @Column(name = "addr_local_city")),
+ @AttributeOverride(name = "address.state", column = @Column(name = "addr_local_state")),
+ @AttributeOverride(name = "address.zip", column = @Column(name = "addr_local_zip")),
+ @AttributeOverride(
+ name = "address.countryCode",
+ column = @Column(name = "addr_local_country_code"))
+ })
PostalInfo localizedPostalInfo;
/**
- * Internationalized postal info for the contact. Personal info; cleared by
- * {@link Builder#wipeOut}.
+ * Internationalized postal info for the contact. Personal info; cleared by {@link
+ * Builder#wipeOut}.
*/
@IgnoreSave(IfNull.class)
+ @Embedded
+ @AttributeOverrides({
+ @AttributeOverride(name = "name", column = @Column(name = "addr_i18n_name")),
+ @AttributeOverride(name = "org", column = @Column(name = "addr_i18n_org")),
+ @AttributeOverride(name = "type", column = @Column(name = "addr_i18n_type")),
+ @AttributeOverride(
+ name = "address.streetLine1",
+ column = @Column(name = "addr_i18n_street_line1")),
+ @AttributeOverride(
+ name = "address.streetLine2",
+ column = @Column(name = "addr_i18n_street_line2")),
+ @AttributeOverride(
+ name = "address.streetLine3",
+ column = @Column(name = "addr_i18n_street_line3")),
+ @AttributeOverride(name = "address.city", column = @Column(name = "addr_i18n_city")),
+ @AttributeOverride(name = "address.state", column = @Column(name = "addr_i18n_state")),
+ @AttributeOverride(name = "address.zip", column = @Column(name = "addr_i18n_zip")),
+ @AttributeOverride(
+ name = "address.countryCode",
+ column = @Column(name = "addr_i18n_country_code"))
+ })
PostalInfo internationalizedPostalInfo;
/**
@@ -80,10 +137,20 @@ public class ContactResource extends EppResource implements
/** Contact’s voice number. Personal info; cleared by {@link Builder#wipeOut}. */
@IgnoreSave(IfNull.class)
+ @Embedded
+ @AttributeOverrides({
+ @AttributeOverride(name = "phoneNumber", column = @Column(name = "voice_phone_number")),
+ @AttributeOverride(name = "extension", column = @Column(name = "voice_phone_extension")),
+ })
ContactPhoneNumber voice;
/** Contact’s fax number. Personal info; cleared by {@link Builder#wipeOut}. */
@IgnoreSave(IfNull.class)
+ @Embedded
+ @AttributeOverrides({
+ @AttributeOverride(name = "phoneNumber", column = @Column(name = "fax_phone_number")),
+ @AttributeOverride(name = "extension", column = @Column(name = "fax_phone_extension")),
+ })
ContactPhoneNumber fax;
/** Contact’s email address. Personal info; cleared by {@link Builder#wipeOut}. */
@@ -91,10 +158,16 @@ public class ContactResource extends EppResource implements
String email;
/** Authorization info (aka transfer secret) of the contact. */
+ @Embedded
+ @AttributeOverrides({
+ @AttributeOverride(name = "pw.value", column = @Column(name = "auth_info_value")),
+ @AttributeOverride(name = "pw.repoId", column = @Column(name = "auth_info_repo_id")),
+ })
ContactAuthInfo authInfo;
/** Data about any pending or past transfers on this contact. */
- TransferData transferData;
+ // TODO(b/153363295): Figure out how to persist transfer data
+ @Transient TransferData transferData;
/**
* The time that this resource was last transferred.
@@ -107,6 +180,16 @@ public class ContactResource extends EppResource implements
// the wipeOut() function, so that data is not kept around for deleted contacts.
/** Disclosure policy. */
+ @Embedded
+ @AttributeOverrides({
+ @AttributeOverride(name = "name", column = @Column(name = "disclose_types_name")),
+ @AttributeOverride(name = "org", column = @Column(name = "disclose_types_org")),
+ @AttributeOverride(name = "addr", column = @Column(name = "disclose_types_addr")),
+ @AttributeOverride(name = "flag", column = @Column(name = "disclose_mode_flag")),
+ @AttributeOverride(name = "voice.marked", column = @Column(name = "disclose_show_voice")),
+ @AttributeOverride(name = "fax.marked", column = @Column(name = "disclose_show_fax")),
+ @AttributeOverride(name = "email.marked", column = @Column(name = "disclose_show_email"))
+ })
Disclose disclose;
public String getContactId() {
diff --git a/core/src/main/java/google/registry/model/contact/Disclose.java b/core/src/main/java/google/registry/model/contact/Disclose.java
index f907b5f05..d0003afd9 100644
--- a/core/src/main/java/google/registry/model/contact/Disclose.java
+++ b/core/src/main/java/google/registry/model/contact/Disclose.java
@@ -22,11 +22,14 @@ import google.registry.model.Buildable;
import google.registry.model.ImmutableObject;
import google.registry.model.eppcommon.PresenceMarker;
import java.util.List;
+import javax.persistence.Embeddable;
+import javax.persistence.Embedded;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlType;
/** The "discloseType" from {@link "http://tools.ietf.org/html/rfc5733"}. */
@Embed
+@Embeddable
@XmlType(propOrder = {"name", "org", "addr", "voice", "fax", "email"})
public class Disclose extends ImmutableObject {
@@ -36,11 +39,11 @@ public class Disclose extends ImmutableObject {
List addr;
- PresenceMarker voice;
+ @Embedded PresenceMarker voice;
- PresenceMarker fax;
+ @Embedded PresenceMarker fax;
- PresenceMarker email;
+ @Embedded PresenceMarker email;
@XmlAttribute
Boolean flag;
diff --git a/core/src/main/java/google/registry/model/contact/PostalInfo.java b/core/src/main/java/google/registry/model/contact/PostalInfo.java
index 6e43dd7e2..d71aed052 100644
--- a/core/src/main/java/google/registry/model/contact/PostalInfo.java
+++ b/core/src/main/java/google/registry/model/contact/PostalInfo.java
@@ -21,6 +21,9 @@ import google.registry.model.Buildable;
import google.registry.model.Buildable.Overlayable;
import google.registry.model.ImmutableObject;
import java.util.Optional;
+import javax.persistence.Embeddable;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlEnumValue;
@@ -29,10 +32,11 @@ import javax.xml.bind.annotation.adapters.NormalizedStringAdapter;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
/**
- * Implementation of both "postalInfoType" and "chgPostalInfoType" from
- * {@link "http://tools.ietf.org/html/rfc5733"}.
+ * Implementation of both "postalInfoType" and "chgPostalInfoType" from {@link
+ * "http://tools.ietf.org/html/rfc5733"}.
*/
@Embed
+@Embeddable
@XmlType(propOrder = {"name", "org", "address", "type"})
public class PostalInfo extends ImmutableObject implements Overlayable {
@@ -53,6 +57,7 @@ public class PostalInfo extends ImmutableObject implements Overlayable
+ *
*
"Contact telephone number structure is derived from structures defined in [ITU.E164.2005].
* Telephone numbers described in this mapping are character strings that MUST begin with a plus
* sign ("+", ASCII value 0x002B), followed by a country code defined in [ITU.E164.2005], followed
* by a dot (".", ASCII value 0x002E), followed by a sequence of digits representing the telephone
* number. An optional "x" attribute is provided to note telephone extension information."
+ *
*
*
* @see google.registry.model.contact.ContactPhoneNumber
* @see google.registry.model.mark.MarkPhoneNumber
*/
@XmlTransient
+@Embeddable
+@MappedSuperclass
public class PhoneNumber extends ImmutableObject {
@XmlValue
diff --git a/core/src/main/java/google/registry/model/eppcommon/PresenceMarker.java b/core/src/main/java/google/registry/model/eppcommon/PresenceMarker.java
index dc65565f9..2eb4f0060 100644
--- a/core/src/main/java/google/registry/model/eppcommon/PresenceMarker.java
+++ b/core/src/main/java/google/registry/model/eppcommon/PresenceMarker.java
@@ -17,6 +17,7 @@ package google.registry.model.eppcommon;
import com.googlecode.objectify.annotation.Embed;
import google.registry.model.ImmutableObject;
import java.io.Serializable;
+import javax.persistence.Embeddable;
import javax.xml.bind.annotation.XmlTransient;
/**
@@ -26,6 +27,7 @@ import javax.xml.bind.annotation.XmlTransient;
* {@code }, and will unmarshal always to {@code }.
*/
@Embed
+@Embeddable
public class PresenceMarker extends ImmutableObject implements Serializable {
@XmlTransient
boolean marked = true;
diff --git a/core/src/main/java/google/registry/persistence/EntityCallbacksListener.java b/core/src/main/java/google/registry/persistence/EntityCallbacksListener.java
index 931b24976..776b1e133 100644
--- a/core/src/main/java/google/registry/persistence/EntityCallbacksListener.java
+++ b/core/src/main/java/google/registry/persistence/EntityCallbacksListener.java
@@ -18,6 +18,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.Objects;
import java.util.stream.Stream;
@@ -31,6 +32,7 @@ import javax.persistence.PostUpdate;
import javax.persistence.PrePersist;
import javax.persistence.PreRemove;
import javax.persistence.PreUpdate;
+import javax.persistence.Transient;
/**
* A listener class to invoke entity callbacks in cases where Hibernate doesn't invoke the callback
@@ -167,10 +169,12 @@ public class EntityCallbacksListener {
private Stream