/* * 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. * * Other licenses: * ----------------------------------------------------------------------------- * Commercial licenses for this work are available. These replace the above * ASL 2.0 and offer limited warranties, support, maintenance, and commercial * database integrations. * * For more information, please visit: http://www.jooq.org/licenses * * * * * * * * * * * * * */ package org.jooq; import java.sql.ResultSet; import java.sql.Statement; import java.util.Collection; import org.jooq.conf.Settings; import org.jooq.exception.DataAccessException; import org.jooq.exception.DataChangedException; import org.jooq.exception.NoDataFoundException; import org.jooq.exception.TooManyRowsException; /** * A common interface for records that can be stored back to the database again. * <p> * Any {@link Record} can be updatable, if * <p> * <ol> * <li>it represents a record from a table or view - a {@link TableRecord}</li> * <li>its underlying table or view has a "main unique key", i.e. a primary key * or at least one unique key</li> * </ol> * <p> * The "main unique key" is used by jOOQ to perform the various operations that * can be performed on an <code>UpdatableRecord</code>: * <p> * <ul> * <li> {@link #delete()} : Deleting the record</li> * <li> {@link #refresh()} : Refreshing the records attributes (or loading it for * the first time)</li> * <li> {@link #store()} : Storing the record to the database. This executes * either an <code>INSERT</code> or an <code>UPDATE</code> statement</li> * </ul> * <p> * <code>UpdatableRecords</code> 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 <R> The record type * @author Lukas Eder */ public interface UpdatableRecord<R extends UpdatableRecord<R>> extends TableRecord<R> { /** * A Record copy holding values for the {@link Table#getPrimaryKey()}. * <p> * The returned record consists exactly of those fields as returned by the * table's primary key: {@link UniqueKey#getFields()}. * <p> * 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. * <p> * Depending on the state of the primary key's value, an {@link #insert()} * or an {@link #update()} statement is executed. * <p> * <h5>Statement type</h5> * <p> * <ul> * <li>If this record was created by client code, an <code>INSERT</code> * statement is executed</li> * <li>If this record was loaded by jOOQ and the primary key value was * changed, an <code>INSERT</code> 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.</li> * <li>If this record was loaded by jOOQ, and the primary key value was not * changed, an <code>UPDATE</code> statement is executed.</li> * </ul> * <p> * In either statement type, only those fields are inserted/updated, which * had been explicitly set by client code, in order to allow for * <code>DEFAULT</code> values to be applied by the underlying RDBMS. If no * fields were modified, neither an <code>UPDATE</code> nor an * <code>INSERT</code> will be executed. * <p> * <h5>Automatic value generation</h5> * <p> * Use {@link #insert()} or {@link #update()} to explicitly force either * statement type. * <p> * <ul> * <li><strong>IDENTITY columns</strong> * <p> * If there is an <code>IDENTITY</code> column defined on the record's * underlying table (see {@link Table#getIdentity()}), then the * auto-generated <code>IDENTITY</code> value is refreshed automatically on * <code>INSERT</code>'s. Refreshing is done using * {@link Statement#getGeneratedKeys()}, where this is supported by the JDBC * driver. See also {@link InsertQuery#getReturnedRecord()} for more details * </li> * <li><strong>VERSION and TIMESTAMP columns</strong> * <p> * 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 * <code>INSERT</code> or <code>UPDATE</code> 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. * <p> * Should you want to circumvent jOOQ-generated updates to these columns, * you can render an <code>INSERT</code> or <code>UPDATE</code> statement * manually using the various {@link DSLContext#insertInto(Table)}, * {@link DSLContext#update(Table)} methods.</li> * </ul> * <p> * <h5>Optimistic locking</h5> * <p> * If an <code>UPDATE</code> statement is executed and * {@link Settings#isExecuteWithOptimisticLocking()} is set to * <code>true</code>, then this record will first be compared with the * latest state in the database. There are two modes of operation for * optimistic locking: * <ul> * <li><strong>With VERSION and/or TIMESTAMP columns configured</strong> * <p> * 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 <code>WHERE</code> clause of the executed <code>DELETE</code> * statement.</li> * <li><strong>Without any specific column configurations</strong> * <p> * In order to compare this record with the latest state, the database * record will be locked pessimistically using a * <code>SELECT .. FOR UPDATE</code> statement. Not all databases support * the <code>FOR UPDATE</code> clause natively. Namely, the following * databases will show slightly different behaviour: * <ul> * <li> {@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}.</li> * <li> {@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 <code>UPDATE</code></li> * </ul> * <p> * See {@link SelectQuery#setForUpdate(boolean)} for more details</li> * </ul> * <p> * <h5>Statement examples</h5> * <p> * Possible statements are * <ul> * <li> * <code><pre> * INSERT INTO [table] ([modified fields, including keys]) * VALUES ([modified values, including keys])</pre></code></li> * <li> * <code><pre> * UPDATE [table] * SET [modified fields = modified values, excluding keys] * WHERE [key fields = key values] * AND [version/timestamp fields = version/timestamp values]</pre></code></li> * </ul> * <p> * <h3>Statement execution enforcement</h3> * <p> * 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. * <p> * This is the same as calling <code>record.store(record.fields())</code> * * @return <code>1</code> if the record was stored to the database. <code>0 * </code> 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 <code>1</code> if the record was stored to the database. <code>0 * </code> 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 parts of this record to the database. * * @return <code>1</code> if the record was stored to the database. <code>0 * </code> 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(Collection<? extends Field<?>> fields) throws DataAccessException, DataChangedException; /** * Store this record back to the database using an <code>INSERT</code> * statement. * <p> * This is the same as {@link #store()}, except that an <code>INSERT</code> * statement (or no statement) will always be executed. * <p> * 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. * <p> * This is the same as calling <code>record.insert(record.fields())</code> * * @return <code>1</code> if the record was stored to the database. <code>0 * </code> 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 <code>INSERT</code> * statement. * * @return <code>1</code> if the record was stored to the database. <code>0 * </code> if storing was not necessary. * @throws DataAccessException if something went wrong executing the query * @see #insert() */ @Override int insert(Field<?>... fields) throws DataAccessException; /** * Store parts of this record to the database using an <code>INSERT</code> * statement. * * @return <code>1</code> if the record was stored to the database. <code>0 * </code> if storing was not necessary. * @throws DataAccessException if something went wrong executing the query * @see #insert() */ @Override int insert(Collection<? extends Field<?>> fields) throws DataAccessException; /** * Store this record back to the database using an <code>UPDATE</code> * statement. * <p> * This is the same as {@link #store()}, except that an <code>UPDATE</code> * statement (or no statement) will always be executed. * <p> * 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. * <p> * This is the same as calling <code>record.update(record.fields())</code> * * @return <code>1</code> if the record was stored to the database. <code>0 * </code> 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 <code>UPDATE</code> * statement. * * @return <code>1</code> if the record was stored to the database. <code>0 * </code> 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; /** * Store parts of this record to the database using an <code>UPDATE</code> * statement. * * @return <code>1</code> if the record was stored to the database. <code>0 * </code> 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(Collection<? extends Field<?>> fields) throws DataAccessException, DataChangedException; /** * Deletes this record from the database, based on the value of the primary * key or main unique key. * <p> * <h5>Optimistic locking</h5> * <p> * If a <code>DELETE</code> statement is executed and * {@link Settings#isExecuteWithOptimisticLocking()} is set to * <code>true</code>, then this record will first be compared with the * latest state in the database. There are two modes of operation for * optimistic locking: * <ul> * <li><strong>With VERSION and/or TIMESTAMP columns configured</strong> * <p> * 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 * <code>WHERE</code> clause of the executed <code>DELETE</code> statement.</li> * <li><strong>Without any specific column configurations</strong> * <p> * In order to compare this record with the latest state, the database * record will be locked pessimistically using a * <code>SELECT .. FOR UPDATE</code> statement. Not all databases support * the <code>FOR UPDATE</code> clause natively. Namely, the following * databases will show slightly different behaviour: * <ul> * <li> {@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}.</li> * <li> {@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 <code>DELETE</code></li> * </ul> * <p> * See {@link SelectQuery#setForUpdate(boolean)} for more details</li> * </ul> * <h5>Statement examples</h5> * <p> * The executed statement is <code><pre> * DELETE FROM [table] * WHERE [key fields = key values] * AND [version/timestamp fields = version/timestamp values]</pre></code> * <p> * This is in fact the same as calling * <code>delete(getTable().getPrimaryKey().getFieldsArray())</code> * * @return <code>1</code> if the record was deleted from the database. * <code>0</code> 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. * <p> * A successful refresh results in the following: * <ul> * <li>{@link #valuesRow()} will have been restored to the respective values * from the database</li> * <li>{@link #original()} will match this record</li> * <li>{@link #changed()} will be <code>false</code></li> * </ul> * <p> * Refreshing can trigger any of the following actions: * <ul> * <li>Executing a new <code>SELECT</code> statement, if this is an * {@link UpdatableRecord}.</li> * <li>Failing, otherwise</li> * </ul> * <p> * This is the same as calling <code>record.refresh(record.fields())</code> * * @throws DataAccessException This exception is thrown if something went * wrong executing the refresh <code>SELECT</code> statement * @throws NoDataFoundException If the record does not exist anymore in the * database */ void refresh() throws DataAccessException; /** * Refresh parts of this record from the database. * <p> * A successful refresh results in the following: * <ul> * <li>{@link #valuesRow()} will have been restored to the respective values * from the database</li> * <li>{@link #original()} will match this record</li> * <li>{@link #changed()} will be <code>false</code></li> * </ul> * <p> * Refreshing can trigger any of the following actions: * <ul> * <li>Executing a new <code>SELECT</code> statement, if this is an * {@link UpdatableRecord}.</li> * <li>Failing, otherwise</li> * </ul> * <p> * This is the same as calling <code>record.refresh(record.fields())</code> * * @throws DataAccessException This exception is thrown if something went * wrong executing the refresh <code>SELECT</code> statement * @throws NoDataFoundException If the record does not exist anymore in the * database */ void refresh(Field<?>... fields) throws DataAccessException, NoDataFoundException; /** * Refresh parts of this record from the database. * <p> * A successful refresh results in the following: * <ul> * <li>{@link #valuesRow()} will have been restored to the respective values * from the database</li> * <li>{@link #original()} will match this record</li> * <li>{@link #changed()} will be <code>false</code></li> * </ul> * <p> * Refreshing can trigger any of the following actions: * <ul> * <li>Executing a new <code>SELECT</code> statement, if this is an * {@link UpdatableRecord}.</li> * <li>Failing, otherwise</li> * </ul> * <p> * This is the same as calling <code>record.refresh(record.fields())</code> * * @throws DataAccessException This exception is thrown if something went * wrong executing the refresh <code>SELECT</code> statement * @throws NoDataFoundException If the record does not exist anymore in the * database */ void refresh(Collection<? extends Field<?>> fields) throws DataAccessException, NoDataFoundException; /** * 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 <code>INSERT</code> statement. * * @return A new record, distinct from <code>this</code> record. */ R copy(); /** * Fetch a child record of this record, given a foreign key. * <p> * This returns a child record referencing this record through a given * foreign key. If no child record was found, this returns <code>null</code> * * @throws DataAccessException if something went wrong executing the query * @throws TooManyRowsException if the query returned more than one record * @see ForeignKey#fetchChildren(java.util.Collection) * @see ForeignKey#fetchChildren(Record) * @see ForeignKey#fetchChildren(Record...) */ <O extends TableRecord<O>> O fetchChild(ForeignKey<O, R> key) throws TooManyRowsException, DataAccessException; /** * Fetch child records of this record, given a foreign key. * <p> * 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...) */ <O extends TableRecord<O>> Result<O> fetchChildren(ForeignKey<O, R> key) throws DataAccessException; }