/* * #%L * Nazgul Project: nazgul-core-persistence-model * %% * Copyright (C) 2010 - 2017 jGuru Europe AB * %% * Licensed under the jGuru Europe AB license (the "License"), based * on Apache License, Version 2.0; you may not use this file except * in compliance with the License. * * You may obtain a copy of the License at * * http://www.jguru.se/licenses/jguruCorporateSourceLicense-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * #L% * */ package se.jguru.nazgul.core.persistence.model; import se.jguru.nazgul.core.xmlbinding.api.XmlBinder; import se.jguru.nazgul.tools.validation.api.Validatable; import se.jguru.nazgul.tools.validation.api.exception.InternalStateValidationException; import javax.annotation.PostConstruct; import javax.persistence.Access; import javax.persistence.AccessType; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.MappedSuperclass; import javax.persistence.PrePersist; import javax.persistence.Version; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlType; import java.io.Serializable; import java.util.Objects; /** * Abstract convenience superclass for Entity classes, sporting normally required implementations * for Serializing, Cloning and Validation. Also defines a common way to handle standard JPA properties * for primary key (by defining a synthetic Long as PK) and version (by defining a Long as version identifier). * * @author <a href="mailto:lj@jguru.se">Lennart Jörelid</a>, jGuru Europe AB * @see NazgulMutableIdEntity */ @MappedSuperclass @XmlType(namespace = XmlBinder.CORE_NAMESPACE, propOrder = {"id", "version"}) @XmlAccessorType(XmlAccessType.FIELD) @Access(value = AccessType.FIELD) public abstract class NazgulEntity implements Serializable, Cloneable, Validatable { // Internal state private static final long serialVersionUID = 8829990002L; /** * The Java Persistence API (JPA) identifier (i.e. primary key) value of this {@link NazgulEntity}. * Cannot be null when being written to (or read from) a relational database, but can be absent * when marshalled to XML via JAXB if this NazgulEntity was not yet written to a database. * Note that an entity class should only contain a single Id property (i.e. do not create more * properties annotated with Id in subclasses). */ @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @XmlAttribute(name = "jpaId") private long id; /** * The version of this NazgulEntity, used internally by the Java Persistence API implementation to * ensure consistency when writing back modified entities to the database. * Note that an entity class should only contain a single version property (i.e. do not create more * properties annotated with Version in subclasses). */ @Version @XmlAttribute private long version; /** * JAXB / JPA-friendly constructor. */ public NazgulEntity() { } /** * @return the primary key value (i.e. Database-generated ID) of this NazgulEntity. */ public long getId() { return id; } /** * Assigns the ID property of this NazgulEntity. Note that this is not permitted/recommended according to the JPA * specification, since it is the equivalent of changing the primary key of an Entity. However, since the JPA * specification is insufficient when working with database views (which cannot be mapped to JPA entities in a * standardized, annotation-based, manner) this setId method may be of use in a data access object implementation * to create synthetic NazgulEntity objects corresponding to view rows. * * @param id The new ID of this NazgulEntity. */ protected void setId(final long id) { this.id = id; } /** * @return the Database-generated version/revision of this NazgulEntity. */ public long getVersion() { return version; } /** * Equality comparison definition which, by choice, ignores the * id and version fields in performing the comparison - only the * business fields should be included in performing the equality check. */ @Override @SuppressWarnings("all") public boolean equals(final Object obj) { // Check sanity; fail fast. if (obj == null) { return false; } if (obj == this) { return true; } // Simply validate that the types are equal. return getClass().equals(obj.getClass()); } /** * {@inheritDoc} */ @Override public int hashCode() { return Objects.hash(id, version); } /** * Convenience clone method, which does not require the caller to handle CloneNotSupportedException. * * @param <T> The exact subtype of NazgulEntity used. * @return A clone of this instance, or an IllegalStateException. * @throws IllegalStateException if cloning this instance failed with a {@code CloneNotSupportedException}. */ @SuppressWarnings("unchecked") public <T extends NazgulEntity> T copy() throws IllegalStateException { try { return (T) this.clone(); } catch (CloneNotSupportedException e) { throw new IllegalStateException("Could not clone [" + getClass().getName() + "] instance.", e); } } /** * {@inheritDoc} */ @Override public Object clone() throws CloneNotSupportedException { final NazgulEntity clone = (NazgulEntity) super.clone(); clone.version = this.version; // All Done. return clone; } /** * Performs validation of the internal state of this Validatable. * <strong>Note!</strong> This method should <strong>not</strong> be overridden in subclasses. * Instead, override the {@code validateEntityState} method, and supply your local implementations there. * * @throws InternalStateValidationException if the state of this Validatable was in an incorrect * state (i.e. invalid). */ @PostConstruct @PrePersist @Override public void validateInternalState() throws InternalStateValidationException { // Delegate validateEntityState(); } /** * <p>Override this method to perform validation of the entity internal state of this Validatable. * It is recommended to call {@code super.validateEntityState()} in all subclasses, to ensure that * all validation mechanics from superclasses is guaranteed to run. A typical implementation of the * validateEntityState method is something like:</p> * <pre> * <code> * // For this sample, we can assume that... * // 'aMember' is the variable name of a private member (variable) within this class. * // 'aStringMember' is a private member of type String within this class. * // * InternalStateValidationException.create() * .notNull(aMember, "aMember") * .notNullOrEmpty(aStringMember, "aStringMember") * .endExpressionAndValidate(); * </code> * </pre> * * @throws InternalStateValidationException if the internal state of this Validatable was in an * incorrect state (i.e. invalid). */ protected abstract void validateEntityState() throws InternalStateValidationException; }