HHH-11587 - Reordering items in List throws a constraint violation
This commit is contained in:
parent
f8c1417e3c
commit
39f761b250
|
@ -69,6 +69,7 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
|
|||
// collections detect changes made via their public interface and mark
|
||||
// themselves as dirty as a performance optimization
|
||||
private boolean dirty;
|
||||
protected boolean elementRemoved;
|
||||
private Serializable storedSnapshot;
|
||||
|
||||
private String sessionFactoryUuid;
|
||||
|
@ -105,9 +106,15 @@ public abstract class AbstractPersistentCollection implements Serializable, Pers
|
|||
return dirty;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isElementRemoved() {
|
||||
return elementRemoved;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void clearDirty() {
|
||||
dirty = false;
|
||||
elementRemoved = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -307,6 +307,7 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
public boolean remove(Object o) {
|
||||
initialize( true );
|
||||
if ( bag.remove( o ) ) {
|
||||
elementRemoved = true;
|
||||
dirty();
|
||||
return true;
|
||||
}
|
||||
|
@ -346,6 +347,7 @@ public class PersistentBag extends AbstractPersistentCollection implements List
|
|||
if ( c.size()>0 ) {
|
||||
initialize( true );
|
||||
if ( bag.removeAll( c ) ) {
|
||||
elementRemoved = true;
|
||||
dirty();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -150,6 +150,7 @@ public class PersistentIdentifierBag extends AbstractPersistentCollection implem
|
|||
if ( index >= 0 ) {
|
||||
beforeRemove( index );
|
||||
values.remove( index );
|
||||
elementRemoved = true;
|
||||
dirty();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -163,6 +163,7 @@ public class PersistentList extends AbstractPersistentCollection implements List
|
|||
if ( exists == null ) {
|
||||
initialize( true );
|
||||
if ( list.remove( value ) ) {
|
||||
elementRemoved = true;
|
||||
dirty();
|
||||
return true;
|
||||
}
|
||||
|
@ -171,6 +172,7 @@ public class PersistentList extends AbstractPersistentCollection implements List
|
|||
}
|
||||
}
|
||||
else if ( exists ) {
|
||||
elementRemoved = true;
|
||||
queueOperation( new SimpleRemove( value ) );
|
||||
return true;
|
||||
}
|
||||
|
@ -222,6 +224,7 @@ public class PersistentList extends AbstractPersistentCollection implements List
|
|||
if ( coll.size()>0 ) {
|
||||
initialize( true );
|
||||
if ( list.removeAll( coll ) ) {
|
||||
elementRemoved = true;
|
||||
dirty();
|
||||
return true;
|
||||
}
|
||||
|
@ -298,8 +301,10 @@ public class PersistentList extends AbstractPersistentCollection implements List
|
|||
throw new ArrayIndexOutOfBoundsException( "negative index" );
|
||||
}
|
||||
final Object old = isPutQueueEnabled() ? readElementByIndex( index ) : UNKNOWN;
|
||||
elementRemoved = true;
|
||||
if ( old == UNKNOWN ) {
|
||||
write();
|
||||
dirty();
|
||||
return list.remove( index );
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -178,6 +178,7 @@ public class PersistentMap extends AbstractPersistentCollection implements Map {
|
|||
if ( isPutQueueEnabled() ) {
|
||||
final Object old = readElementByIndex( key );
|
||||
if ( old != UNKNOWN ) {
|
||||
elementRemoved = true;
|
||||
queueOperation( new Remove( key, old ) );
|
||||
return old;
|
||||
}
|
||||
|
@ -185,6 +186,7 @@ public class PersistentMap extends AbstractPersistentCollection implements Map {
|
|||
// TODO : safe to interpret "map.remove(key) == null" as non-dirty?
|
||||
initialize( true );
|
||||
if ( map.containsKey( key ) ) {
|
||||
elementRemoved = true;
|
||||
dirty();
|
||||
}
|
||||
return map.remove( key );
|
||||
|
|
|
@ -206,6 +206,7 @@ public class PersistentSet extends AbstractPersistentCollection implements java.
|
|||
if ( exists == null ) {
|
||||
initialize( true );
|
||||
if ( set.remove( value ) ) {
|
||||
elementRemoved = true;
|
||||
dirty();
|
||||
return true;
|
||||
}
|
||||
|
@ -214,6 +215,7 @@ public class PersistentSet extends AbstractPersistentCollection implements java.
|
|||
}
|
||||
}
|
||||
else if ( exists ) {
|
||||
elementRemoved = true;
|
||||
queueOperation( new SimpleRemove( value ) );
|
||||
return true;
|
||||
}
|
||||
|
@ -266,6 +268,7 @@ public class PersistentSet extends AbstractPersistentCollection implements java.
|
|||
if ( coll.size() > 0 ) {
|
||||
initialize( true );
|
||||
if ( set.removeAll( coll ) ) {
|
||||
elementRemoved = true;
|
||||
dirty();
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -396,6 +396,10 @@ public interface PersistentCollection {
|
|||
*/
|
||||
boolean isDirty();
|
||||
|
||||
default boolean isElementRemoved(){
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear the dirty flag, after flushing changes
|
||||
* to the database.
|
||||
|
|
|
@ -9,7 +9,9 @@ package org.hibernate.persister.collection;
|
|||
import java.io.Serializable;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -193,83 +195,51 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
|
|||
}
|
||||
|
||||
try {
|
||||
PreparedStatement st = null;
|
||||
Expectation expectation = Expectations.appropriateExpectation( getUpdateCheckStyle() );
|
||||
boolean callable = isUpdateCallable();
|
||||
final Expectation expectation = Expectations.appropriateExpectation( getUpdateCheckStyle() );
|
||||
final boolean callable = isUpdateCallable();
|
||||
final int jdbcBatchSizeToUse = session.getConfiguredJdbcBatchSize();
|
||||
boolean useBatch = expectation.canBeBatched() && jdbcBatchSizeToUse > 1;
|
||||
Iterator entries = collection.entries( this );
|
||||
String sql = getSQLUpdateRowString();
|
||||
int i = 0;
|
||||
int count = 0;
|
||||
final Iterator entries = collection.entries( this );
|
||||
|
||||
final List elements = new ArrayList();
|
||||
while ( entries.hasNext() ) {
|
||||
Object entry = entries.next();
|
||||
if ( collection.needsUpdating( entry, i, elementType ) ) {
|
||||
int offset = 1;
|
||||
elements.add( entries.next() );
|
||||
}
|
||||
|
||||
if ( useBatch ) {
|
||||
if ( updateBatchKey == null ) {
|
||||
updateBatchKey = new BasicBatchKey(
|
||||
getRole() + "#UPDATE",
|
||||
expectation
|
||||
);
|
||||
}
|
||||
st = session
|
||||
.getJdbcCoordinator()
|
||||
.getBatch( updateBatchKey )
|
||||
.getBatchStatement( sql, callable );
|
||||
}
|
||||
else {
|
||||
st = session
|
||||
.getJdbcCoordinator()
|
||||
.getStatementPreparer()
|
||||
.prepareStatement( sql, callable );
|
||||
}
|
||||
|
||||
try {
|
||||
offset += expectation.prepare( st );
|
||||
int loc = writeElement( st, collection.getElement( entry ), offset, session );
|
||||
if ( hasIdentifier ) {
|
||||
writeIdentifier( st, collection.getIdentifier( entry, i ), loc, session );
|
||||
}
|
||||
else {
|
||||
loc = writeKey( st, id, loc, session );
|
||||
if ( hasIndex && !indexContainsFormula ) {
|
||||
writeIndexToWhere( st, collection.getIndex( entry, i, this ), loc, session );
|
||||
}
|
||||
else {
|
||||
writeElementToWhere( st, collection.getSnapshotElement( entry, i ), loc, session );
|
||||
}
|
||||
}
|
||||
|
||||
if ( useBatch ) {
|
||||
session.getJdbcCoordinator()
|
||||
.getBatch( updateBatchKey )
|
||||
.addToBatch();
|
||||
}
|
||||
else {
|
||||
expectation.verifyOutcome(
|
||||
session.getJdbcCoordinator().getResultSetReturn().executeUpdate(
|
||||
st
|
||||
), st, -1
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (SQLException sqle) {
|
||||
if ( useBatch ) {
|
||||
session.getJdbcCoordinator().abortBatch();
|
||||
}
|
||||
throw sqle;
|
||||
}
|
||||
finally {
|
||||
if ( !useBatch ) {
|
||||
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( st );
|
||||
session.getJdbcCoordinator().afterStatementExecution();
|
||||
}
|
||||
}
|
||||
count++;
|
||||
final String sql = getSQLUpdateRowString();
|
||||
int count = 0;
|
||||
if ( collection.isElementRemoved() ) {
|
||||
// the update should be done starting from the end to the list
|
||||
for ( int i = elements.size() - 1; i >= 0; i-- ) {
|
||||
count = doUpdateRow(
|
||||
id,
|
||||
collection,
|
||||
session,
|
||||
expectation,
|
||||
callable,
|
||||
useBatch,
|
||||
elements,
|
||||
sql,
|
||||
count,
|
||||
i
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for ( int i = 0; i < elements.size(); i++ ) {
|
||||
count = doUpdateRow(
|
||||
id,
|
||||
collection,
|
||||
session,
|
||||
expectation,
|
||||
callable,
|
||||
useBatch,
|
||||
elements,
|
||||
sql,
|
||||
count,
|
||||
i
|
||||
);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
@ -287,6 +257,82 @@ public class BasicCollectionPersister extends AbstractCollectionPersister {
|
|||
}
|
||||
}
|
||||
|
||||
private int doUpdateRow(
|
||||
Serializable id,
|
||||
PersistentCollection collection,
|
||||
SharedSessionContractImplementor session,
|
||||
Expectation expectation, boolean callable, boolean useBatch, List elements, String sql, int count, int i)
|
||||
throws SQLException {
|
||||
PreparedStatement st;
|
||||
Object entry = elements.get( i );
|
||||
if ( collection.needsUpdating( entry, i, elementType ) ) {
|
||||
int offset = 1;
|
||||
|
||||
if ( useBatch ) {
|
||||
if ( updateBatchKey == null ) {
|
||||
updateBatchKey = new BasicBatchKey(
|
||||
getRole() + "#UPDATE",
|
||||
expectation
|
||||
);
|
||||
}
|
||||
st = session
|
||||
.getJdbcCoordinator()
|
||||
.getBatch( updateBatchKey )
|
||||
.getBatchStatement( sql, callable );
|
||||
}
|
||||
else {
|
||||
st = session
|
||||
.getJdbcCoordinator()
|
||||
.getStatementPreparer()
|
||||
.prepareStatement( sql, callable );
|
||||
}
|
||||
|
||||
try {
|
||||
offset += expectation.prepare( st );
|
||||
int loc = writeElement( st, collection.getElement( entry ), offset, session );
|
||||
if ( hasIdentifier ) {
|
||||
writeIdentifier( st, collection.getIdentifier( entry, i ), loc, session );
|
||||
}
|
||||
else {
|
||||
loc = writeKey( st, id, loc, session );
|
||||
if ( hasIndex && !indexContainsFormula ) {
|
||||
writeIndexToWhere( st, collection.getIndex( entry, i, this ), loc, session );
|
||||
}
|
||||
else {
|
||||
writeElementToWhere( st, collection.getSnapshotElement( entry, i ), loc, session );
|
||||
}
|
||||
}
|
||||
|
||||
if ( useBatch ) {
|
||||
session.getJdbcCoordinator()
|
||||
.getBatch( updateBatchKey )
|
||||
.addToBatch();
|
||||
}
|
||||
else {
|
||||
expectation.verifyOutcome(
|
||||
session.getJdbcCoordinator().getResultSetReturn().executeUpdate(
|
||||
st
|
||||
), st, -1
|
||||
);
|
||||
}
|
||||
}
|
||||
catch (SQLException sqle) {
|
||||
if ( useBatch ) {
|
||||
session.getJdbcCoordinator().abortBatch();
|
||||
}
|
||||
throw sqle;
|
||||
}
|
||||
finally {
|
||||
if ( !useBatch ) {
|
||||
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( st );
|
||||
session.getJdbcCoordinator().afterStatementExecution();
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public String selectFragment(
|
||||
Joinable rhs,
|
||||
String rhsAlias,
|
||||
|
|
Loading…
Reference in New Issue