All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jooq.UpdatableRecord Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2009-2016, Data Geekery GmbH (http://www.datageekery.com)
 * All rights reserved.
 *
 * 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.
 * 

* Any {@link Record} can be updatable, if *

*

    *
  1. it represents a record from a table or view - a {@link TableRecord}
  2. *
  3. its underlying table or view has a "main unique key", i.e. a primary key * or at least one unique key
  4. *
*

* 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 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(Collection> 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 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(Collection> 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; /** * 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(Collection> 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 * @throws NoDataFoundException If 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 * @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. *

* 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 * @throws NoDataFoundException If the record does not exist anymore in the * database */ void refresh(Collection> 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 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 TooManyRowsException 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 TooManyRowsException, 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; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy