org.jooq.UpdatableRecord Maven / Gradle / Ivy
/**
* Copyright (c) 2009-2014, Data Geekery GmbH (http://www.datageekery.com)
* All rights reserved.
*
* This work is dual-licensed
* - under the Apache Software License 2.0 (the "ASL")
* - under the jOOQ License and Maintenance Agreement (the "jOOQ License")
* =============================================================================
* You may choose which license applies to you:
*
* - If you're using this work with Open Source databases, you may choose
* either ASL or jOOQ License.
* - If you're using this work with at least one commercial database, you must
* choose jOOQ License
*
* For more information, please visit http://www.jooq.org/licenses
*
* Apache Software License 2.0:
* -----------------------------------------------------------------------------
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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.
*
* jOOQ License and Maintenance Agreement:
* -----------------------------------------------------------------------------
* Data Geekery grants the Customer the non-exclusive, timely limited and
* non-transferable license to install and use the Software under the terms of
* the jOOQ License and Maintenance Agreement.
*
* This library is distributed with a LIMITED WARRANTY. See the jOOQ License
* and Maintenance Agreement for more details: http://www.jooq.org/licensing
*/
package org.jooq;
import java.sql.ResultSet;
import java.sql.Statement;
import org.jooq.conf.Settings;
import org.jooq.exception.DataAccessException;
import org.jooq.exception.DataChangedException;
import org.jooq.exception.InvalidResultException;
/**
* A common interface for records that can be stored back to the database again.
*
* Any {@link Record} can be updatable, if
*
*
* - it represents a record from a table or view - a {@link TableRecord}
* - its underlying table or view has a "main unique key", i.e. a primary key
* or at least one unique key
*
*
* The "main unique key" is used by jOOQ to perform the various operations that
* can be performed on an UpdatableRecord
:
*
*
* - {@link #delete()} : Deleting the record
* - {@link #refresh()} : Refreshing the records attributes (or loading it for
* the first time)
* - {@link #store()} : Storing the record to the database. This executes
* either an
INSERT
or an UPDATE
statement
*
*
* UpdatableRecords
are {@link Attachable}, which means that they
* hold an underlying {@link Configuration} that they can be detached from. They
* can also be instantiated without any underlying {@link Configuration}, in
* case of which they have to be attached first, in order to be refreshed,
* stored, or deleted.
*
* @param The record type
* @author Lukas Eder
*/
public interface UpdatableRecord> extends TableRecord {
/**
* A Record copy holding values for the {@link Table#getPrimaryKey()}.
*
* The returned record consists exactly of those fields as returned by the
* table's primary key: {@link UniqueKey#getFields()}.
*
* Generated subtypes may covariantly override this method to add more
* record type information. For instance, they may return {@link Record1},
* {@link Record2}, ...
*/
Record key();
/**
* Store this record back to the database.
*
* Depending on the state of the primary key's value, an {@link #insert()}
* or an {@link #update()} statement is executed.
*
*
Statement type
*
*
* - If this record was created by client code, an
INSERT
* statement is executed
* - If this record was loaded by jOOQ and the primary key value was
* changed, an
INSERT
statement is executed (unless
* {@link Settings#isUpdatablePrimaryKeys()} is set). jOOQ expects that
* primary key values will never change due to the principle of
* normalisation in RDBMS. So if client code changes primary key values,
* this is interpreted by jOOQ as client code wanting to duplicate this
* record.
* - If this record was loaded by jOOQ, and the primary key value was not
* changed, an
UPDATE
statement is executed.
*
*
* In either statement type, only those fields are inserted/updated, which
* had been explicitly set by client code, in order to allow for
* DEFAULT
values to be applied by the underlying RDBMS. If no
* fields were modified, neither an UPDATE
nor an
* INSERT
will be executed.
*
*
Automatic value generation
*
* Use {@link #insert()} or {@link #update()} to explicitly force either
* statement type.
*
*
* - IDENTITY columns
*
* If there is an IDENTITY
column defined on the record's
* underlying table (see {@link Table#getIdentity()}), then the
* auto-generated IDENTITY
value is refreshed automatically on
* INSERT
's. Refreshing is done using
* {@link Statement#getGeneratedKeys()}, where this is supported by the JDBC
* driver. See also {@link InsertQuery#getReturnedRecord()} for more details
*
* - VERSION and TIMESTAMP columns
*
* jOOQ can auto-generate "version" and "timestamp" values that can be used
* for optimistic locking. If this is an {@link UpdatableRecord} and if this
* record returns fields for either {@link Table#getRecordVersion()} or
* {@link Table#getRecordTimestamp()}, then these values are set onto the
* INSERT
or UPDATE
statement being executed. On
* execution success, the generated values are set to this record. Use the
* code-generation configuration to specify naming patterns for
* auto-generated "version" and "timestamp" columns.
*
* Should you want to circumvent jOOQ-generated updates to these columns,
* you can render an INSERT
or UPDATE
statement
* manually using the various {@link DSLContext#insertInto(Table)},
* {@link DSLContext#update(Table)} methods.
*
*
*
Optimistic locking
*
* If an UPDATE
statement is executed and
* {@link Settings#isExecuteWithOptimisticLocking()} is set to
* true
, then this record will first be compared with the
* latest state in the database. There are two modes of operation for
* optimistic locking:
*
* - With VERSION and/or TIMESTAMP columns configured
*
* This is the preferred way of using optimistic locking in jOOQ. If this is
* an {@link UpdatableRecord} and if this record returns fields for either
* {@link Table#getRecordVersion()} or {@link Table#getRecordTimestamp()},
* then these values are compared to the corresponding value in the database
* in the WHERE
clause of the executed DELETE
* statement.
* - Without any specific column configurations
*
* In order to compare this record with the latest state, the database
* record will be locked pessimistically using a
* SELECT .. FOR UPDATE
statement. Not all databases support
* the FOR UPDATE
clause natively. Namely, the following
* databases will show slightly different behaviour:
*
* - {@link SQLDialect#CUBRID} and {@link SQLDialect#SQLSERVER}: jOOQ will
* try to lock the database record using JDBC's
* {@link ResultSet#TYPE_SCROLL_SENSITIVE} and
* {@link ResultSet#CONCUR_UPDATABLE}.
* - {@link SQLDialect#SQLITE}: No pessimistic locking is possible. Client
* code must assure that no race-conditions can occur between jOOQ's
* checking of database record state and the actual
UPDATE
*
*
* See {@link SelectQuery#setForUpdate(boolean)} for more details
*
*
*
Statement examples
*
* Possible statements are
*
* -
*
* INSERT INTO [table] ([modified fields, including keys])
* VALUES ([modified values, including keys])
* -
*
* UPDATE [table]
* SET [modified fields = modified values, excluding keys]
* WHERE [key fields = key values]
* AND [version/timestamp fields = version/timestamp values]
*
*
*
Statement execution enforcement
*
* If you want to enforce statement execution, regardless if the values in
* this record were changed, you can explicitly set the changed flags for
* all values with {@link #changed(boolean)} or for single values with
* {@link #changed(Field, boolean)}, prior to storing.
*
* This is the same as calling record.store(record.fields())
*
* @return 1
if the record was stored to the database. 0
*
if storing was not necessary.
* @throws DataAccessException if something went wrong executing the query
* @throws DataChangedException If optimistic locking is enabled and the
* record has already been changed/deleted in the database
* @see #insert()
* @see #update()
*/
int store() throws DataAccessException, DataChangedException;
/**
* Store parts of this record to the database.
*
* @return 1
if the record was stored to the database. 0
*
if storing was not necessary.
* @throws DataAccessException if something went wrong executing the query
* @throws DataChangedException If optimistic locking is enabled and the
* record has already been changed/deleted in the database
* @see #store()
* @see #insert(Field...)
* @see #update(Field...)
*/
int store(Field>... fields) throws DataAccessException, DataChangedException;
/**
* Store this record back to the database using an INSERT
* statement.
*
* This is the same as {@link #store()}, except that an INSERT
* statement (or no statement) will always be executed.
*
* If you want to enforce statement execution, regardless if the values in
* this record were changed, you can explicitly set the changed flags for
* all values with {@link #changed(boolean)} or for single values with
* {@link #changed(Field, boolean)}, prior to insertion.
*
* This is the same as calling record.insert(record.fields())
*
* @return 1
if the record was stored to the database. 0
*
if storing was not necessary.
* @throws DataAccessException if something went wrong executing the query
* @see #store()
*/
@Override
int insert() throws DataAccessException;
/**
* Store parts of this record to the database using an INSERT
* statement.
*
* @return 1
if the record was stored to the database. 0
*
if storing was not necessary.
* @throws DataAccessException if something went wrong executing the query
* @see #insert()
*/
@Override
int insert(Field>... fields) throws DataAccessException;
/**
* Store this record back to the database using an UPDATE
* statement.
*
* This is the same as {@link #store()}, except that an UPDATE
* statement (or no statement) will always be executed.
*
* If you want to enforce statement execution, regardless if the values in
* this record were changed, you can explicitly set the changed flags for
* all values with {@link #changed(boolean)} or for single values with
* {@link #changed(Field, boolean)}, prior to updating.
*
* This is the same as calling record.update(record.fields())
*
* @return 1
if the record was stored to the database. 0
*
if storing was not necessary.
* @throws DataAccessException if something went wrong executing the query
* @throws DataChangedException If optimistic locking is enabled and the
* record has already been changed/deleted in the database
* @see #store()
*/
int update() throws DataAccessException, DataChangedException;
/**
* Store parts of this record to the database using an UPDATE
* statement.
*
* @return 1
if the record was stored to the database. 0
*
if storing was not necessary.
* @throws DataAccessException if something went wrong executing the query
* @throws DataChangedException If optimistic locking is enabled and the
* record has already been changed/deleted in the database
* @see #update()
*/
int update(Field>... fields) throws DataAccessException, DataChangedException;
/**
* Deletes this record from the database, based on the value of the primary
* key or main unique key.
*
*
Optimistic locking
*
* If a DELETE
statement is executed and
* {@link Settings#isExecuteWithOptimisticLocking()} is set to
* true
, then this record will first be compared with the
* latest state in the database. There are two modes of operation for
* optimistic locking:
*
* - With VERSION and/or TIMESTAMP columns configured
*
* This is the preferred way of using optimistic locking in jOOQ. If this is
* an {@link UpdatableRecord} and if this record returns fields for either
* {@link Table#getRecordVersion()} or
* {@link Table#getRecordTimestamp()}, then these values are
* compared to the corresponding value in the database in the
* WHERE
clause of the executed DELETE
statement.
* - Without any specific column configurations
*
* In order to compare this record with the latest state, the database
* record will be locked pessimistically using a
* SELECT .. FOR UPDATE
statement. Not all databases support
* the FOR UPDATE
clause natively. Namely, the following
* databases will show slightly different behaviour:
*
* - {@link SQLDialect#CUBRID} and {@link SQLDialect#SQLSERVER}: jOOQ will
* try to lock the database record using JDBC's
* {@link ResultSet#TYPE_SCROLL_SENSITIVE} and
* {@link ResultSet#CONCUR_UPDATABLE}.
* - {@link SQLDialect#SQLITE}: No pessimistic locking is possible. Client
* code must assure that no race-conditions can occur between jOOQ's
* checking of database record state and the actual
DELETE
*
*
* See {@link SelectQuery#setForUpdate(boolean)} for more details
*
* Statement examples
*
* The executed statement is
* DELETE FROM [table]
* WHERE [key fields = key values]
* AND [version/timestamp fields = version/timestamp values]
*
* This is in fact the same as calling
* delete(getTable().getPrimaryKey().getFieldsArray())
*
* @return 1
if the record was deleted from the database.
* 0
if deletion was not necessary.
* @throws DataAccessException if something went wrong executing the query
* @throws DataChangedException If optimistic locking is enabled and the
* record has already been changed/deleted in the database
*/
int delete() throws DataAccessException, DataChangedException;
/**
* Refresh this record from the database.
*
* A successful refresh results in the following:
*
* - {@link #valuesRow()} will have been restored to the respective values
* from the database
* - {@link #original()} will match this record
* - {@link #changed()} will be
false
*
*
* Refreshing can trigger any of the following actions:
*
* - Executing a new
SELECT
statement, if this is an
* {@link UpdatableRecord}.
* - Failing, otherwise
*
*
* This is the same as calling record.refresh(record.fields())
*
* @throws DataAccessException This exception is thrown if
*
* - something went wrong executing the refresh
SELECT
*
statement, if this is an {@link UpdatableRecord}.
* - the record does not exist anymore in the database
*
*
*/
void refresh() throws DataAccessException;
/**
* Refresh parts of this record from the database.
*
* A successful refresh results in the following:
*
* - {@link #valuesRow()} will have been restored to the respective values
* from the database
* - {@link #original()} will match this record
* - {@link #changed()} will be
false
*
*
* Refreshing can trigger any of the following actions:
*
* - Executing a new
SELECT
statement, if this is an
* {@link UpdatableRecord}.
* - Failing, otherwise
*
*
* This is the same as calling record.refresh(record.fields())
*
* @throws DataAccessException This exception is thrown if
*
* - something went wrong
* executing the refresh
SELECT
statement, if this
* is an {@link UpdatableRecord}. - the record does not
* exist anymore in the database
*
*/
void refresh(Field>... fields) throws DataAccessException;
/**
* Duplicate this record (in memory) and reset all fields from the primary
* key or main unique key, such that a subsequent call to {@link #store()}
* will result in an INSERT
statement.
*
* @return A new record, distinct from this
record.
*/
R copy();
/**
* Fetch a child record of this record, given a foreign key
*
* This returns a child record referencing this record through a given
* foreign key. If no child record was found, this returns null
*
* @throws DataAccessException if something went wrong executing the query
* @throws InvalidResultException if the query returned more than one record
* @see ForeignKey#fetchChildren(java.util.Collection)
* @see ForeignKey#fetchChildren(Record)
* @see ForeignKey#fetchChildren(Record...)
*/
> O fetchChild(ForeignKey key) throws InvalidResultException, DataAccessException;
/**
* Fetch child records of this record, given a foreign key
*
* This returns childs record referencing this record through a given
* foreign key.
*
* @throws DataAccessException if something went wrong executing the query
* @see ForeignKey#fetchChildren(java.util.Collection)
* @see ForeignKey#fetchChildren(Record)
* @see ForeignKey#fetchChildren(Record...)
*/
> Result fetchChildren(ForeignKey key) throws DataAccessException;
}