[#3300] UpdatableRecord.store() executes INSERT instead of UPDATE when nullable primary keys are NULL, and the updatablePrimaryKeys setting is active
This commit is contained in:
parent
5d16d7dc37
commit
29f31bd91b
@ -86,6 +86,7 @@ xxxxxx xxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxx xxxxxxxxxxxxxxx
|
||||
|
||||
xxxxxx xxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxx xxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxx xxxxxxxxxxxxxxxxxx
|
||||
xxxxxx xxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxx xxxxxxxxxxxxxxx
|
||||
@ -1085,7 +1086,60 @@ xxxxxx xxxxx xxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxx xxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxx xxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxx xxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
x
|
||||
|
||||
xxxxx
|
||||
xxxxxx xxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx x
|
||||
xxxxxxxxx xxx xxxx x xxxxxx xxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
|
||||
xxxxxxxxxx xxxxxx x xxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxx xx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxx xx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
|
||||
xx xxxxxxx xxxxx xxxxxxx xx xxxxx
|
||||
xxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxx
|
||||
|
||||
xxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxx
|
||||
|
||||
xxxxxxxxxxxxxxx xxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxx xxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxx xxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxx
|
||||
|
||||
xx xxxxxxx xxxxx xxxxxxx xx xxxxx
|
||||
xxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xx x xxxxxxxxxxxxxxx
|
||||
xx x xxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxx
|
||||
|
||||
xxxxxxxxxxxxxxx xxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxx xxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxx xxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
|
||||
xx xxxxxxx xxxxx xxxxxxx xxxx xxxxx
|
||||
xxxxxxx x xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xx x xxxxxxxxxxxxxxx
|
||||
xx x xxxxxxxxxxxxxxx
|
||||
|
||||
xxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxx
|
||||
xxxxx xxxxxx x xxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxx xxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxx xxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxx xxxx xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
x
|
||||
|
||||
xxxxx
|
||||
|
||||
@ -554,7 +554,7 @@ public abstract class jOOQAbstractTest<
|
||||
if (clean != null && clean.length > 0) {
|
||||
for (Table<?> table : clean) {
|
||||
try {
|
||||
create().delete(table);
|
||||
create().delete(table).execute();
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@ -1,67 +0,0 @@
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* An <code>FunctionalInterface</code> that wraps transactional code.
|
||||
*
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
public interface Transactional<T> {
|
||||
|
||||
/**
|
||||
* Run the transactional code.
|
||||
* <p>
|
||||
* If this method completes normally, and this is not a nested transaction,
|
||||
* then the transaction will be committed. If this method completes with an
|
||||
* exception, then the transaction is rolled back to the beginning of this
|
||||
* <code>Transactional</code>.
|
||||
*
|
||||
* @param configuration The <code>Configuration</code> in whose context the
|
||||
* transaction is run.
|
||||
* @return The outcome of the transaction.
|
||||
* @throws Exception Any exception that will cause a rollback of the code
|
||||
* contained in this transaction. If this is a nested
|
||||
* transaction, the rollback may be performed only to the state
|
||||
* before executing this <code>Transactional</code>.
|
||||
*/
|
||||
T run(Configuration configuration) throws Exception;
|
||||
}
|
||||
@ -91,7 +91,7 @@ abstract class AbstractRecord extends AbstractStore implements Record {
|
||||
final Object[] values;
|
||||
final Object[] originals;
|
||||
final BitSet changed;
|
||||
// final BitSet def;
|
||||
boolean fetched;
|
||||
|
||||
AbstractRecord(Collection<? extends Field<?>> fields) {
|
||||
this(new RowImpl(fields));
|
||||
@ -108,7 +108,6 @@ abstract class AbstractRecord extends AbstractStore implements Record {
|
||||
this.values = new Object[size];
|
||||
this.originals = new Object[size];
|
||||
this.changed = new BitSet(size);
|
||||
// this.def = new BitSet(size);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
@ -299,9 +298,10 @@ abstract class AbstractRecord extends AbstractStore implements Record {
|
||||
|
||||
private final <T> void setValue(int index, Field<T> field, T value) {
|
||||
// Relevant issues documenting this method's behaviour:
|
||||
// [#945]
|
||||
// [#948]
|
||||
// [#979]
|
||||
// [#945] Avoid bugs resulting from setting the same value twice
|
||||
// [#948] To allow for controlling the number of hard-parses
|
||||
// To allow for explicitly overriding default values
|
||||
// [#979] Avoid modifying chnaged flag on unchanged primary key values
|
||||
|
||||
UniqueKey<?> key = getPrimaryKey();
|
||||
|
||||
@ -347,6 +347,8 @@ abstract class AbstractRecord extends AbstractStore implements Record {
|
||||
}
|
||||
|
||||
final void setValues(Field<?>[] fields, AbstractRecord record) {
|
||||
fetched = record.fetched;
|
||||
|
||||
for (Field<?> field : fields) {
|
||||
int targetIndex = indexOrFail(fieldsRow(), field);
|
||||
int sourceIndex = indexOrFail(record.fieldsRow(), field);
|
||||
@ -385,7 +387,7 @@ abstract class AbstractRecord extends AbstractStore implements Record {
|
||||
*/
|
||||
@Override
|
||||
public Record original() {
|
||||
return Utils.newRecord((Class<AbstractRecord>) getClass(), fields.fields.fields, configuration())
|
||||
return Utils.newRecord(fetched, (Class<AbstractRecord>) getClass(), fields.fields.fields, configuration())
|
||||
.operate(new RecordOperation<AbstractRecord, RuntimeException>() {
|
||||
|
||||
@Override
|
||||
@ -544,11 +546,11 @@ abstract class AbstractRecord extends AbstractStore implements Record {
|
||||
|
||||
@Override
|
||||
public final <R extends Record> R into(Table<R> table) {
|
||||
return Utils.newRecord(table, configuration()).operate(new TransferRecordState<R>());
|
||||
return Utils.newRecord(fetched, table, configuration()).operate(new TransferRecordState<R>());
|
||||
}
|
||||
|
||||
final <R extends Record> R intoRecord(Class<R> type) {
|
||||
return Utils.newRecord(type, fields(), configuration()).operate(new TransferRecordState<R>());
|
||||
return Utils.newRecord(fetched, type, fields(), configuration()).operate(new TransferRecordState<R>());
|
||||
}
|
||||
|
||||
private class TransferRecordState<R extends Record> implements RecordOperation<R, MappingException> {
|
||||
|
||||
@ -566,7 +566,7 @@ public abstract class AbstractRoutine<T> extends AbstractQueryPart implements Ro
|
||||
xxxx xxxxxxx x
|
||||
xx xxxxxxxx xx xxxxxxxxxxxxx x
|
||||
xxxxxxxxxxxx xxxxxx x xxxxx
|
||||
xxxxxxxxxxxxxxxxxxx xxxxxxx xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxx xxxxxxxx xxxxxxx xxxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx xxxxxxxxxxxxx xxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
x
|
||||
|
||||
@ -444,7 +444,7 @@ abstract class AbstractStoreQuery<R extends Record> extends AbstractQuery implem
|
||||
if (returning.size() == 1 && new Fields<Record>(returning).field(field) != null) {
|
||||
for (final Number id : ids) {
|
||||
getReturnedRecords().add(
|
||||
Utils.newRecord(into, configuration)
|
||||
Utils.newRecord(true, into, configuration)
|
||||
.operate(new RecordOperation<R, RuntimeException>() {
|
||||
|
||||
@Override
|
||||
|
||||
@ -1408,7 +1408,7 @@ class CursorImpl<R extends Record> implements Cursor<R> {
|
||||
rs.updateRow();
|
||||
}
|
||||
|
||||
record = Utils.newRecord((Class<AbstractRecord>) type, fields, ctx.configuration())
|
||||
record = Utils.newRecord(true, (Class<AbstractRecord>) type, fields, ctx.configuration())
|
||||
.operate(initialiser);
|
||||
|
||||
rows++;
|
||||
|
||||
@ -131,7 +131,7 @@ class DefaultBindContext extends AbstractBindContext {
|
||||
xx [/pro] */
|
||||
// [#1126] Oracle's UDTs need to be bound with their type name
|
||||
if (UDTRecord.class.isAssignableFrom(type)) {
|
||||
String typeName = Utils.newRecord((Class<UDTRecord<?>>) type)
|
||||
String typeName = Utils.newRecord(false, (Class<UDTRecord<?>>) type)
|
||||
.<RuntimeException>operate(null)
|
||||
.getUDT()
|
||||
.getName();
|
||||
|
||||
@ -1673,7 +1673,7 @@ public class DefaultDSLContext implements DSLContext, Serializable {
|
||||
|
||||
@Override
|
||||
public Record newRecord(Field<?>... fields) {
|
||||
return Utils.newRecord(RecordImpl.class, fields, configuration).<RuntimeException>operate(null);
|
||||
return Utils.newRecord(false, RecordImpl.class, fields, configuration).<RuntimeException>operate(null);
|
||||
}
|
||||
|
||||
// [jooq-tools] START [newRecord]
|
||||
@ -1814,17 +1814,17 @@ public class DefaultDSLContext implements DSLContext, Serializable {
|
||||
|
||||
@Override
|
||||
public <R extends UDTRecord<R>> R newRecord(UDT<R> type) {
|
||||
return Utils.newRecord(type, configuration).<RuntimeException>operate(null);
|
||||
return Utils.newRecord(false, type, configuration).<RuntimeException>operate(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends Record> R newRecord(Table<R> table) {
|
||||
return Utils.newRecord(table, configuration).<RuntimeException>operate(null);
|
||||
return Utils.newRecord(false, table, configuration).<RuntimeException>operate(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R extends Record> R newRecord(Table<R> table, final Object source) {
|
||||
return Utils.newRecord(table, configuration)
|
||||
return Utils.newRecord(false, table, configuration)
|
||||
.operate(new RecordOperation<R, RuntimeException>() {
|
||||
|
||||
@Override
|
||||
|
||||
@ -181,6 +181,7 @@ public class TableRecordImpl<R extends TableRecord<R>> extends AbstractRecord im
|
||||
}
|
||||
|
||||
changed(false);
|
||||
fetched = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
@ -141,19 +141,13 @@ public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableReco
|
||||
TableField<R, ?>[] keys = getPrimaryKey().getFieldsArray();
|
||||
boolean executeUpdate = false;
|
||||
|
||||
for (TableField<R, ?> field : keys) {
|
||||
|
||||
// [#2764] If primary key values are allowed to be changed,
|
||||
// inserting is only possible without prior loading of pk values
|
||||
if (updatablePrimaryKeys(settings(this))) {
|
||||
if (original(field) == null) {
|
||||
executeUpdate = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// [#2764] Primary key value changes are interpreted as record copies
|
||||
else {
|
||||
// [#2764] If primary key values are allowed to be changed,
|
||||
// inserting is only possible without prior loading of pk values
|
||||
if (updatablePrimaryKeys(settings(this))) {
|
||||
executeUpdate = fetched;
|
||||
}
|
||||
else {
|
||||
for (TableField<R, ?> field : keys) {
|
||||
|
||||
// If any primary key value is null or changed
|
||||
if (changed(field) ||
|
||||
@ -163,10 +157,10 @@ public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableReco
|
||||
executeUpdate = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, updates are possible
|
||||
executeUpdate = true;
|
||||
// Otherwise, updates are possible
|
||||
executeUpdate = true;
|
||||
}
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
@ -316,7 +310,7 @@ public class UpdatableRecordImpl<R extends UpdatableRecord<R>> extends TableReco
|
||||
|
||||
@Override
|
||||
public final R copy() {
|
||||
return Utils.newRecord(getTable(), configuration())
|
||||
return Utils.newRecord(fetched, getTable(), configuration())
|
||||
.operate(new RecordOperation<R, RuntimeException>() {
|
||||
|
||||
@Override
|
||||
|
||||
@ -342,51 +342,51 @@ final class Utils {
|
||||
/**
|
||||
* Create a new record
|
||||
*/
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(Class<R> type) {
|
||||
return newRecord(type, null);
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(boolean fetched, Class<R> type) {
|
||||
return newRecord(fetched, type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new record
|
||||
*/
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(Class<R> type, Field<?>[] fields) {
|
||||
return newRecord(type, fields, null);
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(boolean fetched, Class<R> type, Field<?>[] fields) {
|
||||
return newRecord(fetched, type, fields, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new record
|
||||
*/
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(Table<R> type) {
|
||||
return newRecord(type, null);
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(boolean fetched, Table<R> type) {
|
||||
return newRecord(fetched, type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new record
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(Table<R> type, Configuration configuration) {
|
||||
return (RecordDelegate<R>) newRecord(type.getRecordType(), type.fields(), configuration);
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(boolean fetched, Table<R> type, Configuration configuration) {
|
||||
return (RecordDelegate<R>) newRecord(fetched, type.getRecordType(), type.fields(), configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new UDT record
|
||||
*/
|
||||
static final <R extends UDTRecord<R>> RecordDelegate<R> newRecord(UDT<R> type) {
|
||||
return newRecord(type, null);
|
||||
static final <R extends UDTRecord<R>> RecordDelegate<R> newRecord(boolean fetched, UDT<R> type) {
|
||||
return newRecord(fetched, type, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new UDT record
|
||||
*/
|
||||
static final <R extends UDTRecord<R>> RecordDelegate<R> newRecord(UDT<R> type, Configuration configuration) {
|
||||
return newRecord(type.getRecordType(), type.fields(), configuration);
|
||||
static final <R extends UDTRecord<R>> RecordDelegate<R> newRecord(boolean fetched, UDT<R> type, Configuration configuration) {
|
||||
return newRecord(fetched, type.getRecordType(), type.fields(), configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new record
|
||||
*/
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(Class<R> type, Field<?>[] fields, Configuration configuration) {
|
||||
static final <R extends Record> RecordDelegate<R> newRecord(boolean fetched, Class<R> type, Field<?>[] fields, Configuration configuration) {
|
||||
try {
|
||||
R record;
|
||||
|
||||
@ -402,6 +402,10 @@ final class Utils {
|
||||
record = Reflect.accessible(type.getDeclaredConstructor()).newInstance();
|
||||
}
|
||||
|
||||
// [#3300] Records that were fetched from the database
|
||||
if (record instanceof AbstractRecord)
|
||||
((AbstractRecord) record).fetched = fetched;
|
||||
|
||||
return new RecordDelegate<R>(configuration, record);
|
||||
}
|
||||
catch (Exception e) {
|
||||
@ -2989,7 +2993,7 @@ final class Utils {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Utils.newRecord((Class<UDTRecord<?>>) type)
|
||||
return Utils.newRecord(true, (Class<UDTRecord<?>>) type)
|
||||
.operate(new RecordOperation<UDTRecord<?>, SQLException>() {
|
||||
|
||||
@Override
|
||||
|
||||
@ -1,183 +0,0 @@
|
||||
/**
|
||||
* 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.impl;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Lukas Eder
|
||||
*/
|
||||
class Value0<T> implements Serializable {
|
||||
|
||||
/**
|
||||
* Generated UID
|
||||
*/
|
||||
private static final long serialVersionUID = -9065797545428164533L;
|
||||
private T original;
|
||||
private T value;
|
||||
private boolean isChanged;
|
||||
|
||||
Value0(T value) {
|
||||
this(value, value, false);
|
||||
}
|
||||
|
||||
Value0(T value, T original, boolean isChanged) {
|
||||
this.value = value;
|
||||
this.original = original;
|
||||
this.isChanged = isChanged;
|
||||
}
|
||||
|
||||
final T getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
final T getValue(T defaultValue) {
|
||||
return value != null ? value : defaultValue;
|
||||
}
|
||||
|
||||
final T getOriginal() {
|
||||
return original;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
final void intern() {
|
||||
|
||||
// [#2177] Future versions of jOOQ may optimise this type check by
|
||||
// performing type-decisions outside of Value
|
||||
if (value instanceof String) {
|
||||
value = (T) ((String) value).intern();
|
||||
}
|
||||
}
|
||||
|
||||
final void setValue(T val) {
|
||||
|
||||
// The flag is always set to false:
|
||||
// [#945] To avoid bugs resulting from setting the same value twice
|
||||
// [#948] To allow for controlling the number of hard-parses
|
||||
// To allow for explicitly overriding default values
|
||||
setValue(val, false);
|
||||
}
|
||||
|
||||
final void setValue(T val, boolean primaryKey) {
|
||||
|
||||
// [#948] Force setting of val in most cases, to allow for controlling
|
||||
// the number of necessary hard-parses, and to allow for explicitly
|
||||
// overriding default values with null
|
||||
if (!primaryKey) {
|
||||
isChanged = true;
|
||||
}
|
||||
|
||||
// [#979] Avoid modifying isChanged on unchanged primary key values
|
||||
else {
|
||||
|
||||
// [#945] Be sure that isChanged is never reset to false
|
||||
if (value == null) {
|
||||
isChanged = isChanged || (val != null);
|
||||
}
|
||||
else {
|
||||
isChanged = isChanged || (!value.equals(val));
|
||||
}
|
||||
}
|
||||
|
||||
value = val;
|
||||
}
|
||||
|
||||
final boolean isChanged() {
|
||||
return isChanged;
|
||||
}
|
||||
|
||||
final void setChanged(boolean isChanged) {
|
||||
this.isChanged = isChanged;
|
||||
|
||||
// [#1995] If a value is meant to be "unchanged", the "original" should
|
||||
// match the supposedly "unchanged" value.
|
||||
if (!isChanged) {
|
||||
original = value;
|
||||
}
|
||||
}
|
||||
|
||||
final void reset() {
|
||||
isChanged = false;
|
||||
value = original;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// XXX: Object API
|
||||
// ------------------------------------------------------------------------
|
||||
//
|
||||
// @Override
|
||||
// public boolean equals(Object obj) {
|
||||
// if (this == obj) {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// if (obj instanceof Value0<?>) {
|
||||
// Value0<?> other = (Value0<?>) obj;
|
||||
//
|
||||
// if (value == null) {
|
||||
// return other.getValue() == null;
|
||||
// }
|
||||
//
|
||||
// return value.equals(other.getValue());
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public int hashCode() {
|
||||
// if (value == null) {
|
||||
// return 0;
|
||||
// }
|
||||
//
|
||||
// return value.hashCode();
|
||||
// }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (isChanged) {
|
||||
return "*" + value;
|
||||
}
|
||||
else {
|
||||
return "" + value;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user