[#3253] Pull up TableRecord.insert()

This commit is contained in:
Lukas Eder 2014-05-07 18:24:17 +02:00
parent 358fa06035
commit 3cf66dd99d
4 changed files with 199 additions and 174 deletions

View File

@ -61,6 +61,21 @@ public interface TableRecord<R extends TableRecord<R>> extends Record {
@Override
R original();
/**
* Store this record to the database using an <code>INSERT</code>
* statement.
* <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.
*
* @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
*/
int insert() throws DataAccessException;
/**
* Fetch a parent record of this record, given a foreign key
* <p>

View File

@ -241,6 +241,7 @@ public interface UpdatableRecord<R extends UpdatableRecord<R>> extends TableReco
* @throws DataAccessException if something went wrong executing the query
* @see #store()
*/
@Override
int insert() throws DataAccessException;
/**

View File

@ -40,9 +40,25 @@
*/
package org.jooq.impl;
import static java.lang.Boolean.TRUE;
import static org.jooq.impl.RecordDelegate.delegate;
import static org.jooq.impl.RecordDelegate.RecordLifecycleType.INSERT;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.LinkedHashSet;
import org.jooq.DSLContext;
import org.jooq.Field;
import org.jooq.ForeignKey;
import org.jooq.Identity;
import org.jooq.InsertQuery;
import org.jooq.Record;
import org.jooq.Row;
import org.jooq.StoreQuery;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.TableRecord;
import org.jooq.UpdatableRecord;
@ -100,4 +116,171 @@ public class TableRecordImpl<R extends TableRecord<R>> extends AbstractRecord im
public final <O extends UpdatableRecord<O>> O fetchParent(ForeignKey<R, O> key) {
return key.fetchParent((R) this);
}
@Override
public final int insert() {
return storeInsert();
}
final int storeInsert() {
final int[] result = new int[1];
delegate(configuration(), (Record) this, INSERT)
.operate(new RecordOperation<Record, RuntimeException>() {
@Override
public Record operate(Record record) throws RuntimeException {
result[0] = storeInsert0();
return record;
}
});
return result[0];
}
final int storeInsert0() {
DSLContext create = create();
InsertQuery<R> insert = create.insertQuery(getTable());
addChangedValues(insert);
// Don't store records if no value was set by client code
if (!insert.isExecutable()) return 0;
// [#1596] Set timestamp and/or version columns to appropriate values
BigInteger version = addRecordVersion(insert);
Timestamp timestamp = addRecordTimestamp(insert);
// [#814] Refresh identity and/or main unique key values
// [#1002] Consider also identity columns of non-updatable records
// [#1537] Avoid refreshing identity columns on batch inserts
Collection<Field<?>> key = null;
if (!TRUE.equals(create.configuration().data(Utils.DATA_OMIT_RETURNING_CLAUSE))) {
key = getReturning();
insert.setReturning(key);
}
int result = insert.execute();
if (result > 0) {
// [#1596] If insert was successful, update timestamp and/or version columns
setRecordVersionAndTimestamp(version, timestamp);
// If an insert was successful try fetching the generated IDENTITY value
if (key != null && !key.isEmpty()) {
if (insert.getReturnedRecord() != null) {
for (Field<?> field : key) {
setValue(field, new Value<Object>(insert.getReturnedRecord().getValue(field)));
}
}
}
changed(false);
}
return result;
}
/**
* Set a generated version and timestamp value onto this record after
* successfully storing the record.
*/
final void setRecordVersionAndTimestamp(BigInteger version, Timestamp timestamp) {
if (version != null) {
TableField<R, ?> field = getTable().getRecordVersion();
setValue(field, new Value<Object>(field.getDataType().convert(version)));
}
if (timestamp != null) {
TableField<R, ?> field = getTable().getRecordTimestamp();
setValue(field, new Value<Object>(field.getDataType().convert(timestamp)));
}
}
/**
* Set all changed values of this record to a store query
*/
final void addChangedValues(StoreQuery<R> query) {
for (Field<?> field : fields.fields.fields) {
if (getValue0(field).isChanged()) {
addValue(query, field);
}
}
}
/**
* Extracted method to ensure generic type safety.
*/
final <T> void addValue(StoreQuery<?> store, Field<T> field, Object value) {
store.addValue(field, Utils.field(value, field));
}
/**
* Extracted method to ensure generic type safety.
*/
final <T> void addValue(StoreQuery<?> store, Field<T> field) {
addValue(store, field, getValue(field));
}
/**
* Set an updated timestamp value to a store query
*/
final Timestamp addRecordTimestamp(StoreQuery<?> store) {
Timestamp result = null;
if (isTimestampOrVersionAvailable()) {
TableField<R, ? extends java.util.Date> timestamp = getTable().getRecordTimestamp();
if (timestamp != null) {
// Use Timestamp locally, to provide maximum precision
result = new Timestamp(System.currentTimeMillis());
addValue(store, timestamp, result);
}
}
return result;
}
/**
* Set an updated version value to a store query
*/
final BigInteger addRecordVersion(StoreQuery<?> store) {
BigInteger result = null;
if (isTimestampOrVersionAvailable()) {
TableField<R, ? extends Number> version = getTable().getRecordVersion();
if (version != null) {
Number value = getValue(version);
// Use BigInteger locally to avoid arithmetic overflows
if (value == null) {
result = BigInteger.ONE;
}
else {
result = new BigInteger(value.toString()).add(BigInteger.ONE);
}
addValue(store, version, result);
}
}
return result;
}
final boolean isTimestampOrVersionAvailable() {
return getTable().getRecordTimestamp() != null || getTable().getRecordVersion() != null;
}
final Collection<Field<?>> getReturning() {
Collection<Field<?>> result = new LinkedHashSet<Field<?>>();
Identity<R, ?> identity = getTable().getIdentity();
if (identity != null) {
result.add(identity.getField());
}
result.addAll(getPrimaryKey().getFields());
return result;
}
}

View File

@ -47,7 +47,6 @@ import static org.jooq.SQLDialect.SQLITE;
import static org.jooq.conf.SettingsTools.updatablePrimaryKeys;
import static org.jooq.impl.RecordDelegate.delegate;
import static org.jooq.impl.RecordDelegate.RecordLifecycleType.DELETE;
import static org.jooq.impl.RecordDelegate.RecordLifecycleType.INSERT;
import static org.jooq.impl.RecordDelegate.RecordLifecycleType.REFRESH;
import static org.jooq.impl.RecordDelegate.RecordLifecycleType.STORE;
import static org.jooq.impl.RecordDelegate.RecordLifecycleType.UPDATE;
@ -55,21 +54,15 @@ import static org.jooq.impl.Utils.settings;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.List;
import org.jooq.Configuration;
import org.jooq.DSLContext;
import org.jooq.DeleteQuery;
import org.jooq.Field;
import org.jooq.ForeignKey;
import org.jooq.Identity;
import org.jooq.InsertQuery;
import org.jooq.Record;
import org.jooq.Result;
import org.jooq.SelectQuery;
import org.jooq.StoreQuery;
import org.jooq.Table;
import org.jooq.TableField;
import org.jooq.TableRecord;
@ -139,11 +132,6 @@ public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableReco
return result[0];
}
@Override
public final int insert() {
return storeInsert();
}
@Override
public final int update() {
return storeUpdate(getPrimaryKey().getFieldsArray());
@ -190,65 +178,6 @@ public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableReco
return result;
}
private final int storeInsert() {
final int[] result = new int[1];
delegate(configuration(), (Record) this, INSERT)
.operate(new RecordOperation<Record, RuntimeException>() {
@Override
public Record operate(Record record) throws RuntimeException {
result[0] = storeInsert0();
return record;
}
});
return result[0];
}
private final int storeInsert0() {
DSLContext create = create();
InsertQuery<R> insert = create.insertQuery(getTable());
addChangedValues(insert);
// Don't store records if no value was set by client code
if (!insert.isExecutable()) return 0;
// [#1596] Set timestamp and/or version columns to appropriate values
BigInteger version = addRecordVersion(insert);
Timestamp timestamp = addRecordTimestamp(insert);
// [#814] Refresh identity and/or main unique key values
// [#1002] Consider also identity columns of non-updatable records
// [#1537] Avoid refreshing identity columns on batch inserts
Collection<Field<?>> key = null;
if (!TRUE.equals(create.configuration().data(Utils.DATA_OMIT_RETURNING_CLAUSE))) {
key = getReturning();
insert.setReturning(key);
}
int result = insert.execute();
if (result > 0) {
// [#1596] If insert was successful, update timestamp and/or version columns
setRecordVersionAndTimestamp(version, timestamp);
// If an insert was successful try fetching the generated IDENTITY value
if (key != null && !key.isEmpty()) {
if (insert.getReturnedRecord() != null) {
for (Field<?> field : key) {
setValue(field, new Value<Object>(insert.getReturnedRecord().getValue(field)));
}
}
}
changed(false);
}
return result;
}
private final int storeUpdate(final TableField<R, ?>[] keys) {
final int[] result = new int[1];
@ -303,78 +232,6 @@ public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableReco
return result;
}
/**
* Set all changed values of this record to a store query
*/
private final void addChangedValues(StoreQuery<R> query) {
for (Field<?> field : fields.fields.fields) {
if (getValue0(field).isChanged()) {
addValue(query, field);
}
}
}
/**
* Extracted method to ensure generic type safety.
*/
private final <T> void addValue(StoreQuery<?> store, Field<T> field, Object value) {
store.addValue(field, Utils.field(value, field));
}
/**
* Extracted method to ensure generic type safety.
*/
private final <T> void addValue(StoreQuery<?> store, Field<T> field) {
addValue(store, field, getValue(field));
}
/**
* Set an updated timestamp value to a store query
*/
private final Timestamp addRecordTimestamp(StoreQuery<?> store) {
Timestamp result = null;
if (isTimestampOrVersionAvailable()) {
TableField<R, ? extends java.util.Date> timestamp = getTable().getRecordTimestamp();
if (timestamp != null) {
// Use Timestamp locally, to provide maximum precision
result = new Timestamp(System.currentTimeMillis());
addValue(store, timestamp, result);
}
}
return result;
}
/**
* Set an updated version value to a store query
*/
private final BigInteger addRecordVersion(StoreQuery<?> store) {
BigInteger result = null;
if (isTimestampOrVersionAvailable()) {
TableField<R, ? extends Number> version = getTable().getRecordVersion();
if (version != null) {
Number value = getValue(version);
// Use BigInteger locally to avoid arithmetic overflows
if (value == null) {
result = BigInteger.ONE;
}
else {
result = new BigInteger(value.toString()).add(BigInteger.ONE);
}
addValue(store, version, result);
}
}
return result;
}
@Override
public final int delete() {
final int[] result = new int[1];
@ -454,18 +311,6 @@ public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableReco
}
}
private final Collection<Field<?>> getReturning() {
Collection<Field<?>> result = new LinkedHashSet<Field<?>>();
Identity<R, ?> identity = getTable().getIdentity();
if (identity != null) {
result.add(identity.getField());
}
result.addAll(getPrimaryKey().getFields());
return result;
}
@Override
public final R copy() {
return Utils.newRecord(getTable(), configuration())
@ -515,10 +360,6 @@ public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableReco
if (t != null) Utils.addCondition(query, this, t);
}
private final boolean isTimestampOrVersionAvailable() {
return getTable().getRecordTimestamp() != null || getTable().getRecordVersion() != null;
}
/**
* Perform an additional SELECT .. FOR UPDATE to check if the underlying
* database record has been changed compared to this record.
@ -569,19 +410,4 @@ public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableReco
throw new DataChangedException("Database record has been changed or doesn't exist any longer");
}
}
/**
* Set a generated version and timestamp value onto this record after
* successfully storing the record.
*/
private final void setRecordVersionAndTimestamp(BigInteger version, Timestamp timestamp) {
if (version != null) {
TableField<R, ?> field = getTable().getRecordVersion();
setValue(field, new Value<Object>(field.getDataType().convert(version)));
}
if (timestamp != null) {
TableField<R, ?> field = getTable().getRecordTimestamp();
setValue(field, new Value<Object>(field.getDataType().convert(timestamp)));
}
}
}