Finally following up on this, the full PostalAddress class is:
@Entity
@DiscriminatorValue("3001")
@SecondaryTable(schema = "smx3", name = "postal_address", pkJoinColumns = @PrimaryKeyJoinColumn(name = "contact_mech_id"))
@Versioned
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class PostalAddress extends ContactMech {
private String toName;
@Basic
@Column(table = "postal_address", name = "to_name", nullable = true, insertable = true, updatable = true, length = 100)
public String getToName() {
return toName;
}
public void setToName(String toName) {
this.toName = toName;
}
private String attnName;
@Basic
@Column(table = "postal_address", name = "attn_name", nullable = true, insertable = true, updatable = true, length = 100)
public String getAttnName() {
return attnName;
}
public void setAttnName(String attnName) {
this.attnName = attnName;
}
private String address1;
@Basic
@Column(table = "postal_address", name = "address1", nullable = true, insertable = true, updatable = true)
@NotNull(message = "{address1.required}")
public String getAddress1() {
return address1;
}
public void setAddress1(String address1) {
this.address1 = address1;
}
private String address2;
@Basic
@Column(table = "postal_address", name = "address2", nullable = true, insertable = true, updatable = true)
public String getAddress2() {
return address2;
}
public void setAddress2(String address2) {
this.address2 = address2;
}
private String directions;
@Basic
@Column(table = "postal_address", name = "directions", nullable = true, insertable = true, updatable = true)
public String getDirections() {
return directions;
}
public void setDirections(String directions) {
this.directions = directions;
}
private String city;
@Basic
@Column(table = "postal_address", name = "city", nullable = true, insertable = true, updatable = true, length = 100)
@NotNull(message = "{city.required}")
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
private String postalCode;
@Basic
@Column(table = "postal_address", name = "postal_code", nullable = true, insertable = true, updatable = true, length = 60)
public String getPostalCode() {
return postalCode;
}
public void setPostalCode(String postalCode) {
this.postalCode = postalCode;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
PostalAddress that = (PostalAddress) o;
if (address1 != null ? !address1.equals(that.address1) : that.address1 != null) return false;
if (address2 != null ? !address2.equals(that.address2) : that.address2 != null) return false;
if (attnName != null ? !attnName.equals(that.attnName) : that.attnName != null) return false;
if (city != null ? !city.equals(that.city) : that.city != null) return false;
if (directions != null ? !directions.equals(that.directions) : that.directions != null) return false;
if (postalCode != null ? !postalCode.equals(that.postalCode) : that.postalCode != null) return false;
if (toName != null ? !toName.equals(that.toName) : that.toName != null) return false;
return true;
}
@Override
public int hashCode() {
int result;
result = /*31 * result + */(toName != null ? toName.hashCode() : 0);
result = 31 * result + (attnName != null ? attnName.hashCode() : 0);
result = 31 * result + (address1 != null ? address1.hashCode() : 0);
result = 31 * result + (address2 != null ? address2.hashCode() : 0);
result = 31 * result + (directions != null ? directions.hashCode() : 0);
result = 31 * result + (city != null ? city.hashCode() : 0);
result = 31 * result + (postalCode != null ? postalCode.hashCode() : 0);
return result;
}
private StateGeo regionGeo;
@ManyToOne
@JoinColumn(table = "postal_address", name = "state_province_geo_id", referencedColumnName = "geo_id", nullable = true, insertable = true, updatable = true)
public StateGeo getRegionGeo() {
return regionGeo;
}
public void setRegionGeo(StateGeo regionGeo) {
this.regionGeo = regionGeo;
}
private PostalCodeGeo postalCodeGeo;
@ManyToOne
@JoinColumn(table = "postal_address", name = "postal_code_geo_id", referencedColumnName = "geo_id", nullable = true, insertable = true, updatable = true)
public PostalCodeGeo getPostalCodeGeo() {
return postalCodeGeo;
}
public void setPostalCodeGeo(PostalCodeGeo postalCodeGeo) {
this.postalCodeGeo = postalCodeGeo;
}
private CountryGeo countryGeo;
@ManyToOne
@JoinColumn(table = "postal_address", name = "country_geo_id", referencedColumnName = "geo_id", nullable = true, insertable = true, updatable = true)
public CountryGeo getCountryGeo() {
return countryGeo;
}
public void setCountryGeo(CountryGeo countryGeo) {
this.countryGeo = countryGeo;
}
}
and its super class is:
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "contact_mech_type_id", discriminatorType = DiscriminatorType.INTEGER)
@Table(schema = "smx3", name = "contact_mech")
@Versioned
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public abstract class ContactMech implements Identity<Integer> {
private Integer id;
@SequenceGenerator(
name = "CONTACT_MECH_ID_SEQ",
sequenceName = "smx3.contact_mech_id_seq",
allocationSize = 20
)
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "CONTACT_MECH_ID_SEQ")
@Column(name = "contact_mech_id", nullable = false, insertable = true, updatable = true, length = 10)
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
private String infoString;
@Basic
@Column(name = "info_string", nullable = true, insertable = true, updatable = true)
public String getInfoString() {
return infoString;
}
protected void setInfoString(String infoString) {
this.infoString = infoString;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ContactMech that = (ContactMech) o;
if (id != null ? !id.equals(that.id) : that.id != null)
return false;
if (infoString != null ? !infoString.equals(that.infoString) : that.infoString != null) return false;
return true;
}
@Override
public int hashCode() {
int result;
result = (id != null ? id.hashCode() : 0);
result = 31 * result + (infoString != null ? infoString.hashCode() : 0);
return result;
}
private ContactMechType contactMechType;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "contact_mech_type_id", referencedColumnName = "contact_mech_type_id", nullable = false, insertable = false, updatable = false)
public ContactMechType getContactMechType() {
return contactMechType;
}
public void setContactMechType(ContactMechType contactMechType) {
this.contactMechType = contactMechType;
}
private List<ContactMechAttribute> contactMechAttributes;
@OneToMany(mappedBy = "contactMechByContactMechId")
@Cache(usage = READ_WRITE)
public List<ContactMechAttribute> getContactMechAttributes() {
return contactMechAttributes;
}
public void setContactMechAttributes(List<ContactMechAttribute> contactMechAttributes) {
this.contactMechAttributes = contactMechAttributes;
}
private List<ContactMechRelationship> relationshipsTo = new ArrayList<ContactMechRelationship>();
@OneToMany(mappedBy = "to", cascade = {CascadeType.ALL})
@Filter(name = "limitToCurrent")
@Cache(usage = READ_WRITE)
public List<ContactMechRelationship> getRelationshipsTo() {
return relationshipsTo;
}
public void setRelationshipsTo(List<ContactMechRelationship> relationshipsTo) {
this.relationshipsTo = relationshipsTo;
}
private List<ContactMechRelationship> relationshipsFrom = new ArrayList<ContactMechRelationship>();
@OneToMany(mappedBy = "from", fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
@Filter(name = "limitToCurrent")
@Cache(usage = READ_WRITE)
public List<ContactMechRelationship> getRelationshipsFrom() {
return relationshipsFrom;
}
public void setRelationshipsFrom(List<ContactMechRelationship> relationshipsFrom) {
this.relationshipsFrom = relationshipsFrom;
}
private List<ContactMechStatus> contactMechStatuses;
@OneToMany(mappedBy = "contactMech", fetch = FetchType.LAZY)
@Cache(usage = READ_WRITE)
public List<ContactMechStatus> getContactMechStatuses() {
return contactMechStatuses;
}
public void setContactMechStatuses(List<ContactMechStatus> contactMechStatuses) {
this.contactMechStatuses = contactMechStatuses;
}
@Transient
public ContactMechStatus getCurrentStatus() {
List<ContactMechStatus> status = getContactMechStatuses();
return (status != null && status.size() > 0) ?
status.get(status.size() - 1)
: null;
}
private List<InvoiceContactMech> invoiceContactMechs;
@OneToMany(mappedBy = "contactMechByContactMechId", fetch = FetchType.LAZY)
@Cache(usage = READ_WRITE)
public List<InvoiceContactMech> getInvoiceContactMechs() {
return invoiceContactMechs;
}
public void setInvoiceContactMechs(List<InvoiceContactMech> invoiceContactMechs) {
this.invoiceContactMechs = invoiceContactMechs;
}
private List<AgreementContactMech> agreementContactMechs = new ArrayList<AgreementContactMech>();
@OneToMany(mappedBy = "contactMech", fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
@Cache(usage = READ_WRITE)
public List<AgreementContactMech> getAgreementContactMechs() {
return agreementContactMechs;
}
public void setAgreementContactMechs(List<AgreementContactMech> agreementContactMechs) {
this.agreementContactMechs = agreementContactMechs;
}
private List<PartyContactMech> partyContactMechs;
@OneToMany(mappedBy = "contactMech", fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
@Cache(usage = READ_WRITE)
public List<PartyContactMech> getPartyContactMechs() {
return partyContactMechs;
}
public void setPartyContactMechs(List<PartyContactMech> partyContactMechs) {
this.partyContactMechs = partyContactMechs;
}
private List<PolicySetContactMech> policySetContactMechs;
@OneToMany(mappedBy = "contactMech", fetch = FetchType.LAZY, cascade = {CascadeType.ALL})
@Cache(usage = READ_WRITE)
public List<PolicySetContactMech> getPolicySetContactMechs() {
return policySetContactMechs;
}
public void setPolicySetContactMechs(List<PolicySetContactMech> policySetContactMechs) {
this.policySetContactMechs = policySetContactMechs;
}
}
The database is postgresql 8.3, the table DDL is:
CREATE TABLE contact_mech (
contact_mech_id integer NOT NULL,
contact_mech_type_id integer NOT NULL,
info_string character varying(255)
);
CREATE TABLE postal_address (
contact_mech_id integer NOT NULL,
to_name character varying(100),
attn_name character varying(100),
address1 character varying(255) NOT NULL,
address2 character varying(255),
directions character varying(255),
city character varying(100),
postal_code character varying(60),
country_geo_id integer,
state_province_geo_id integer,
postal_code_geo_id integer
);
and my DDL for the relevant version tables is:
CREATE TABLE smx3.revision_details (
revision_id serial,
timestamp bigint,
rest_request_id integer,
primary key (revision_id)
);
CREATE TABLE prototype_versions (
_revision int8 not null,
_revision_type integer
);
CREATE TABLE smx3.contact_mech_versions (
LIKE prototype_versions,
LIKE smx3.contact_mech,
unique(contact_mech_id, _revision)
);
ALTER TABLE smx3.contact_mech_versions ALTER COLUMN contact_mech_id DROP NOT NULL;
ALTER TABLE smx3.contact_mech_versions ALTER COLUMN contact_mech_type_id DROP NOT NULL;
CREATE TABLE smx3.postal_address_versions (
LIKE prototype_versions,
LIKE smx3.postal_address,
unique(contact_mech_id, _revision)
);
ALTER TABLE smx3.postal_address_versions ALTER COLUMN contact_mech_id DROP NOT NULL;
Hopefully there's something in here that helps identify the problem, I'll try and build a simple test case/sample app that can reproduce the problem as well.