org.tentackle.pdo.PersistentObject Maven / Gradle / Ivy
/*
* Tentackle - https://tentackle.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.tentackle.pdo;
import org.tentackle.bind.Bindable;
import org.tentackle.common.Timestamp;
import org.tentackle.misc.IdSerialTuple;
import org.tentackle.misc.Identifiable;
import org.tentackle.misc.IdentifiableMap;
import org.tentackle.misc.Immutable;
import org.tentackle.misc.Modifiable;
import org.tentackle.misc.ScrollableResource;
import org.tentackle.misc.SerialNumbered;
import org.tentackle.misc.Snapshotable;
import org.tentackle.security.Permission;
import org.tentackle.security.SecurityResult;
import org.tentackle.session.PersistenceException;
import org.tentackle.session.Persistent;
import org.tentackle.session.SessionDependable;
import org.tentackle.validate.ScopeConfigurator;
import org.tentackle.validate.Validateable;
import org.tentackle.validate.ValidationFailedException;
import java.util.List;
/**
* Interface for the persistent part of a PDO.
*
* @param the PDO
* @author harald
*/
public interface PersistentObject>
extends PersistenceDelegate, PdoProvider, Snapshotable, Identifiable, Modifiable, SerialNumbered,
Validateable, Immutable, ScopeConfigurator, DomainContextDependable, SessionDependable {
/**
* Gets the unique identification number.
*
* @return the ID, 0 if object is virgin, negative if object is deleted
*/
@Persistent(comment = "object id")
@Override
@Bindable
long getId();
/**
* Gets the serial number.
* Whenever the object is persisted, the serial is incremented by one.
*
* @return the serial number, 0 if not persisted yet
*/
@Persistent(comment = "object version")
@Override
@Bindable
long getSerial();
/**
* Indicates whether this entity's model provides a tableserial column.
*
* @return true if pdo is using the tableSerial column, false if not
*/
boolean isTableSerialProvided();
/**
* Gets the table serial.
*
* @return the table serial
*/
@Persistent(comment = "table version")
@Bindable
long getTableSerial();
/**
* Gets the ID string: "classId:id".
* The ID string describes the PDO by its classId and object-ID.
*
* Example: {@code "1022:1258474"}
*
* @return the id string
*/
String toIdString();
/**
* Returns whether this an abstract PDO.
* Abstract PDOs are real objects but cannot be persisted.
* They may be used to retrieve data for the concrete implementations, however.
*
* @return true if abstract PDO
*/
boolean isAbstract();
/**
* Returns whether this PDO is a composite.
* Composite PDOs consist of other component PDOs, defined via composite relations.
* Notice that a composite root entity is synonym to the aggregate root in DDD lingo.
*
* @return true if object is a composite, false if not.
*/
boolean isComposite();
/**
* Returns whether this object is a root entity.
* All entities that are not a component of another entity, are considered as a root entity.
* They correspond to the so-called aggregate root in DDD,
* with the subtle difference that a root entity may have no components at all.
* Only root entities can be persisted!
*
* Root entities maintain their own entity context in {@link DomainContext}.
*
* @return true if root entity
*/
boolean isRootEntity();
/**
* Returns whether this is an embedded PDO.
* Embedded PDOs are components with their attributes embedded in the parent object's table.
*
* @return true if embedded entity
*/
boolean isEmbedded();
/**
* Gets the parent PDO if this is an embedded PDO.
* Throws a {@link PersistenceException} if this is not an embedded PDO.
*
* @return the parent PDO, never null
* @param the parent's type
*/
> E getEmbeddingParent();
/**
* Returns whether this is a root entity of given component.
*
* @param the type of the component
* @param component the component pdo
* @return true if this is the root entity of given component
*/
> boolean isRootEntityOf(C component);
/**
* Indicates whether this entity provides a root-ID column.
* The column is added in components without a direct parent ID relation
* to provide the ID of the root entity without accessing the database.
*
* @return true if column provided, false if not
* @see #getRootId()
*/
boolean isRootIdProvided();
/**
* Gets the ID of the root entity this component belongs to.
*
* @return the root id
*/
@Persistent(comment = "root id")
@Bindable
long getRootId();
/**
* Indicates whether this entity's model provides a root-class-ID column.
* The column is added in components that belong to different root entity classes.
*
* @return true if column provided, false if not
*/
boolean isRootClassIdProvided();
/**
* Gets the class ID of the root entity this component belongs to.
*
* @return the root class id
*/
@Persistent(comment = "root class id")
@Bindable
int getRootClassId();
/**
* Gets the database table name for the class of this object.
*
* Notice: for MULTI-inheritance entities the tablename is the
* last in the inheritance chain.
*
* @return the table name, null if class does not map to a database table
*/
String getTableName();
/**
* Gets the basename of the class of this object.
* The basename is the class name without the package name.
*
* @return the basename of the Objects class
*/
String getClassBaseName();
/**
* Gets the unique ID of the class.
* Class IDs are provided as a replacement for FQCNs for 2 reasons:
*
* - refactoring: package- and classnames may be changed. The class ID is fixed.
* - performance: comparing integers is faster than comparing strings (in Java and the database)
*
* Notice that abstract entities (all except the leaves within an inheritance hierarchy) don't have a classid.
*
* @return the class id, 0 if abstract
*/
@Persistent(comment = "class id")
@Bindable
int getClassId();
/**
* Creates the {@link SecurityResult} for a given permission.
*
* @param permission the requested permission
* @return the security result (never null)
*/
SecurityResult getSecurityResult(Permission permission);
/**
* Checks if the permission for a given permission is accepted.
*
* @param permission the requested permission
* @return true if permission is accepted
*/
boolean isPermissionAccepted(Permission permission);
/**
* Determines whether the application is allowed to write this PDO.
*
* Notice that there is no {@code isReadAllowed()}, because PDOs without read permission
* won't be loaded at all.
*
* @return true if allowed
*/
boolean isWriteAllowed();
/**
* Determines whether the user is allowed to view this PDO.
* Can be used to determine whether object is shown to the user.
*
* @return true if allowed
*/
boolean isViewAllowed();
/**
* Determines whether the user is allowed to edit this PDO.
* Can be used to determine whether object is editable by the user.
*
* @return true if allowed
*/
boolean isEditAllowed();
/**
* Checks whether this object is already persistent in the database or only residing in memory.
* If an object {@code isNew()}, it means that it can be persisted (inserted).
* This does not mean, that the object has never been stored in the database,
* i.e. it is possible that the object just has been deleted.
*
* @return true if object is not in database, i.e. new (or deleted)
*/
boolean isNew();
/**
* Reserves an ID.
* Reserved IDs are negative.
* A new object with a reserved ID can be distinguished from
* a deleted object by its serial. See also {@link #isVirgin}.
* If the object already has an ID or is deleted (negative ID)
* the ID will _not_ change.
*/
void reserveId();
/**
* Reserves a given ID.
* It doesn't matter whether the ID is negative or positive.
* Reserving 0 clears the reservation.
*
* Throws a {@link PersistenceException} if the object is not new.
*
* @param id the ID to reserve, 0 to clear the reservation
*/
void reserveId(long id);
/**
* Checks whether object is deleted.
*
* @return true if object has been removed from the database
*/
boolean isDeleted();
/**
* Returns whether the modification state is available.
* By default, PDOs are not tracked.
* This is quality measure to ensure that isModified() returns
* false if and only if it hasn't been modified, i.e. the (generated) code
* checks for modification properly.
*
* @return true if tracked, false otherwise (default)
*/
boolean isTracked();
/**
* Determines whether this object got some of its attributes modified.
* It does not check whether some of its components are modified!
* This method can also be used for non-tracked entities.
*
* @return true if this object
*/
boolean attributesModified();
/**
* Returns whether any of the attributes differs from the values persisted in the database.
* This method is only applicable to fulltracked entities and returns false if not fulltracked.
*
* @return true if differs, false if no change or entity isn't fulltracked
*/
boolean differsPersisted();
/**
* Determines whether this object is allowed to be stored in database.
* Notice that once a PDO is persisted, the same object reference is no more persistable
* and must be reloaded. Rule: if the reference is no more used, use {@link #save()},
* otherwise use {@link #persist()} and replace the reference with the return value.
*
* @return true if persistable (default)
* @see #persist()
* @see #save()
*/
boolean isPersistable();
/**
* Checks whether this object is referenced by other objects.
*
* The method can be invoked before operations that may have an impact on the
* referential integrity, for example to enable a delete button in the UI.
* The default implementation returns false.
*
* @return true if referenced
*/
boolean isReferenced();
/**
* Checks whether this object can be removed.
*
* Should be invoked before operations that may have an impact on the
* referential integrity, for example to enable a delete button in the UI.
* The default implementation returns {@code !isNew() && !isReferenced()}.
*
* @return true if removable
*/
boolean isRemovable();
/**
* Removes this object from the database.
*/
void delete();
/**
* Sets this object's expiration flag.
* Expired objects are considered as invalid and must be reloaded before being used any further.
* Used in caches.
*
* @param expired true if object is expired
*/
void setExpired(boolean expired);
/**
* Checks whether object has been marked expired.
* Expired objects will be reloaded from the database by
* the cache when the object is retrieved again.
*
* @return true if object is expired (in cache)
*/
boolean isExpired();
/**
* Gets the current modification count for the whole class.
* The count corresponds to the table serial.
*
* @return the modification count
* @see org.tentackle.session.ModificationTracker
*/
long getModificationCount();
/**
* Gets the expired table serials.
* A physical database query is only done if the requested range is not in the backlog.
* Used to reduce roundtrips for remote sessions.
*
* @param oldSerial non-inclusive lower bound for tableSerial (> oldSerial)
* @param maxSerial inclusive upper bound for tableSerial (≤ maxSerial)
* @return pairs of longs, the first being the ID, the second the tableserial, never null
*/
List getExpiredTableSerials(long oldSerial, long maxSerial);
/**
* Determines the objects with a tableSerial starting at a given serial.
* Useful to clean up caches for example.
*
* @param oldSerial non-inclusive lower bound for tableSerial (> oldSerial)
* @return pairs of longs, the first being the ID, the second the tableserial, never null
*/
List selectExpiredTableSerials(long oldSerial);
/**
* Determines the objects with their tableSerial within a given range.
* Useful to clean up caches.
*
* @param oldSerial non-inclusive lower bound for tableSerial (> oldSerial)
* @param maxSerial inclusive upper bound for tableSerial (≤ maxSerial)
* @return pairs of longs, the first being the ID, the second the tableserial, never null
*/
List selectExpiredTableSerials(long oldSerial, long maxSerial);
/**
* Selects all pdos with a tableSerial starting at a given serial.
* Useful to update expired objects in a batch.
*
* @param oldSerial non-inclusive lower bound for tableSerial (> oldSerial)
* @return the list of objects, never null
*/
List selectAllWithExpiredTableSerials(long oldSerial);
/**
* Selects all id,serial-pairs of this class as a list of {@link IdSerialTuple}.
*
* @return the list of objects
*/
List selectAllIdSerial();
/**
* Selects the serial-number for a given object id from the database.
*
* @param id the object id
* @return the serial for that id, -1 if no such object
*/
long selectSerial(long id);
/**
* Selects the highest id.
*
* @return the highest id, -1 if table is empty
*/
long selectMaxId();
/**
* Selects the highest table serial.
*
* @return the highest table serial, -1 if table is empty
*/
long selectMaxTableSerial();
/**
* Gets the cache.
*
* Notice that abstract classes don't provide their own cache, but
* instead use the caches of their concrete classes.
*
* @return the cache for this PDO, null if none.
*/
PdoCache getCache();
/**
* Gets the last cache access time.
*
* @return the last cache access time
*/
long getCacheAccessTime();
/**
* Gets the cache access count.
*
* @return the access count
*/
long getCacheAccessCount();
/**
* Marks the cache access.
* Increments the access counter and sets the last access time.
*/
void markCacheAccess();
/**
* Returns whether object is cached.
*
* @return true if cached
*/
boolean isCached();
/**
* Determines whether object is cacheable or not.
* The default implementation always returns true, but apps
* can use this as a filter.
*
* @return true if object is cacheable
*/
boolean isCacheable();
/**
* Searches for a "pattern" in this object.
* The default implementation looks for the pattern in the normtext.
*
* @param pattern the pattern to search for
* @return true if this object "contains" the pattern
*/
boolean containsPattern (String pattern);
/**
* Checks whether this object (if saved) would violate any unique constraints.
*
* The method is usually used by the presentation layer to check for duplicates.
* The default implementation invokes findByUniqueDomainKey(getUniqueDomainKey())
* and throws {@link UnsupportedOperationException} if one of those methods are not implemented
* (which is the default).
*
* @return the duplicate object, null if no duplicate
*/
T findDuplicate();
/**
* Indicates whether this entity provides the token lock columns.
*
* @return true if token lock columns present, false if not.
*/
boolean isTokenLockProvided();
/**
* Gets the expiration in milliseconds of the token lock.
* Returns a value greater 0 if {@link #isTokenLockProvided()}, else 0.
*
* @return the timespan in ms, 0 if no token lock
*/
long getTokenLockTimeout();
/**
* Requests a token lock.
*
* Throws a {@link LockException} if locked by another user.
*/
void requestTokenLock();
/**
* Releases a token lock.
* Use this method if a PDO needs to be unlocked without being persisted.
*/
void releaseTokenLock();
/**
* Checks whether this object is token locked and the lock is not expired.
*
* @return true if locked by any user
* @see #isTokenLockedByMe()
*/
boolean isTokenLocked();
/**
* Checks whether this object is token locked by given user.
*
* @param userId the user's ID
* @return true locked
*/
boolean isTokenLockedBy(long userId);
/**
* Checks whether this object is token locked by the current user.
*
* @return true if locked
*/
boolean isTokenLockedByMe();
/**
* Checks whether requesting a token lock for the current user is possible.
*
* @return true if object is not locked at all or locked by the current user
*/
boolean isTokenLockableByMe();
/**
* Transfers the token lock to another user.
* Necessary to hand over a token without releasing it.
* Eliminates the time-gap when object is token-free.
* Allows taking over the token WITHOUT possessing it!
* This method should be used with great care as it does not check
* who owns the token and if there is a token at all.
* The serial will be increased, so the old user gets an error
* if trying to update.
* Furthermore, the current object does *NOT* get the serial increased
* thus preventing any update without reloading the object again!
* You have to use the returned PDO!
*
* The modification table is updated because the serial is increased and thus all caches must be invalidated.
*
* CAUTION: the method is provided for rare corner cases like administration tools.
* Don't use it for regular locking/unlocking!
*
* @param userId is the new user-ID. (special 0 = free token, even if we are not the owner!)
* @return the updated PDO
*/
T transferTokenLock(long userId);
/**
* Loads a PDO from the database by its unique ID.
*
* @param id is the object id
*
* @return object if loaded, null if no such object
*/
T select(long id);
/**
* Loads a PDO from the database by its unique ID and applies a token lock.
* Same as {@link #select(long)} followed by {@link #requestTokenLock()}, but requires only a single roundtrip if remote.
*
* Throws a {@link LockException}, if locked by another user.
*
* @param id is the object id
*
* @return object if loaded, null if no such object
*/
T selectTokenLocked(long id);
/**
* Loads a PDO from the database by its unique ID with a database write-lock.
* Requires that a transaction is running.
*
* @param id is the object id
*
* @return object if loaded, null if no such object
*/
T selectForUpdate(long id);
/**
* Reloads the object.
*
* @return the object if reloaded, null if vanished from the database
*/
T reload();
/**
* Reloads the object and applies a token lock.
* Same as {@link #reload()} followed by {@link #requestTokenLock()}, but requires only a single roundtrip if remote.
*
* Throws a {@link LockException}, if locked by another user.
*
* @return the object if reloaded, null if vanished from the database
*/
T reloadTokenLocked();
/**
* Reloads the object with a database write-lock.
* Requires that a transaction is running.
*
* @return the object if reloaded, else null
*/
T reloadForUpdate();
/**
* Selects all objects of this class.
* Eager relations are loaded as well, but limited to the first level to prevent join hogs.
* If more than one level must be eagerly loaded, use explicit finders.
*
* @return the list of objects
*/
List selectAll();
/**
* Selects all records in current context as a cursor.
*
* @return the cursor
*/
ScrollableResource selectAllAsCursor();
/**
* Selects the latest PDOs.
* Returns an optionally limited list of PDOs sorted by ID descending.
*
* @param greaterId the lower excluding ID
* @param limit the optional limit, 0 if unlimited
* @return the list of objects, never null
*/
List selectLatest(long greaterId, int limit);
/**
* Gets the object via cache.
* If there is no cache, the default implementation just loads from the database.
*
* @param id the unique object ID
* @return the object, null if no such object
* @see #select(long)
*/
T selectCached(long id);
/**
* Gets the object via cache only.
* If there is no cache, the default implementation just loads from the database.
*
* @param id the unique object ID
* @return the object, null if no such object
* @see #select(long)
*/
T selectCachedOnly(long id);
/**
* Gets all objects in context via cache.
* If there is no cache, the default implementation load from the database.
*
* @return the list, never null
* @see #selectAll()
*/
List selectAllCached();
/**
* Selects all objects to be loaded into the cache.
*
* The method is used mainly by {@link PdoCache} as an optimization to retrieve
* the list of PDOs from the remote server cache.
*
* @return the list, never null
* @throws PersistenceException if the session is not remote
*/
List selectAllForCache();
/**
* Selects a PDO to be loaded into the cache.
*
* The method is used mainly by {@link PdoCache} as an optimization to retrieve
* a PDO from the remote server cache.
*
* @param id is the object id
*
* @return object if loaded, null if no such object
*/
T selectForCache(long id);
/**
* Validates this PDO.
* Invokes all field- and object validators.
* If the PDO is a composite, all components will be validated as well.
* Root entities are automatically validated when save()d or persist()ed.
*
* @throws ValidationFailedException if validation fails
*/
void validate();
/**
* Returns whether PDO has been successfully validated.
* A PDO is validated if not modified since the last validation
* or not modified since loaded from the backend.
*
* @return true if validated, false if PDO needs validation before being persisted
*/
boolean isValidated();
/**
* Save this PDO.
* If the object is new, a new ID is obtained and the object inserted.
* Otherwise, the object is updated.
*
* A saved object is no more persistable.
* It must be {@link #reload()}-ed to make it persistable again.
*
* Any token lock will be removed.
* @see #persist()
*/
void save();
/**
* Persist this PDO.
* Same as {@link #save()} but returns the persisted object.
* The returned object is persistable.
* If the session is local, {@code this} is returned. For remote sessions,
* the returned reference is always another instance.
*
* @return the persisted object, never null
* @see #save()
*/
T persist();
/**
* Persist this PDO and renew the token lock.
* Use this method if the application continues to work with the locked PDO
* after being persisted.
*
* @return the persisted object, never null
*/
T persistTokenLocked();
/**
* Gets the id of the user currently editing this object.
*
* @return the id or 0 if not being edited currently.
*/
@Persistent(comment = "userId of token holder")
@Bindable
long getEditedBy();
/**
* Gets the time since when this object is being edited.
*
* @return the time, null if not being edited.
*/
@Persistent(comment = "time since token given to user")
@Bindable
Timestamp getEditedSince();
/**
* Gets the time since when this object is being edited.
*
* @return the time, null if not being edited.
*/
@Persistent(comment = "time when token expires")
@Bindable
Timestamp getEditedExpiry();
/**
* Indicates whether this entity's model provides at least one normtext column.
*
* @return true if pdo is using the normtext column, false if not.
*/
boolean isNormTextProvided();
/**
* Gets the normtext.
* The normtext holds the normalized text of columns and relations.
*
* @return the normtext
* @see org.tentackle.common.StringNormalizer#normalize(String)
*/
@Persistent(comment = "normalized text")
String getNormText();
/**
* Selects all objects containing a normalized text.
* The given text will be normalized via {@link org.tentackle.common.StringNormalizer#normalize(String)}
* and selected via a sql {@code LIKE} expression.
* If the text begins with an exclamation mark or a minus-sign (! or -), {@code NOT LIKE} will be used.
*
* @param text the text to search for
* @return the list of objects, never null
*/
List selectByNormText(String text);
/**
* Same as {@link #selectByNormText(String)} but returns a cursor.
*
* @param text the text to search for
* @return the cursor
*/
ScrollableResource selectByNormTextAsCursor(String text);
/**
* Loads all components.
* The method is used to transfer a copy of an object between tiers including all
* composite object relations recursively.
* Furthermore, it can be used to get a map of all components.
*
* @param onlyLoaded true if return only already loaded components (lazy composite relations)
* @return the map of all components, including this object.
*/
IdentifiableMap extends PersistentDomainObject>> loadComponents(boolean onlyLoaded);
}