HHH-17406 Retrieve arbitrary on-execution generated values efficiently
This commit is contained in:
parent
58173f92ee
commit
d72856fef0
|
@ -45,8 +45,12 @@ import org.hibernate.sql.ast.tree.select.QuerySpec;
|
|||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
import org.hibernate.sql.model.internal.TableInsertStandard;
|
||||
import org.hibernate.sql.model.internal.TableUpdateStandard;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty;
|
||||
|
||||
/**
|
||||
* A SQL AST translator for DB2.
|
||||
*
|
||||
|
@ -419,6 +423,50 @@ public class DB2LegacySqlAstTranslator<T extends JdbcOperation> extends Abstract
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitStandardTableInsert(TableInsertStandard tableInsert) {
|
||||
final List<ColumnReference> returningColumns = tableInsert.getReturningColumns();
|
||||
if ( isNotEmpty( returningColumns ) ) {
|
||||
appendSql( "select " );
|
||||
|
||||
for ( int i = 0; i < returningColumns.size(); i++ ) {
|
||||
if ( i > 0 ) {
|
||||
appendSql( ", " );
|
||||
}
|
||||
appendSql( returningColumns.get( i ).getColumnExpression() );
|
||||
}
|
||||
|
||||
appendSql( " from new table ( " ); // 'from final table' does not seem to play well with triggers
|
||||
super.visitStandardTableInsert( tableInsert );
|
||||
appendSql( ")" );
|
||||
}
|
||||
else {
|
||||
super.visitStandardTableInsert( tableInsert );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitStandardTableUpdate(TableUpdateStandard tableUpdate) {
|
||||
final List<ColumnReference> returningColumns = tableUpdate.getReturningColumns();
|
||||
if ( isNotEmpty( returningColumns ) ) {
|
||||
appendSql( "select " );
|
||||
|
||||
for ( int i = 0; i < returningColumns.size(); i++ ) {
|
||||
if ( i > 0 ) {
|
||||
appendSql( ", " );
|
||||
}
|
||||
appendSql( returningColumns.get( i ).getColumnExpression() );
|
||||
}
|
||||
|
||||
appendSql( " from final table ( " );
|
||||
super.visitStandardTableUpdate( tableUpdate );
|
||||
appendSql( ")" );
|
||||
}
|
||||
else {
|
||||
super.visitStandardTableUpdate( tableUpdate );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||
if ( getDB2Version().isSameOrAfter( 11, 1 ) ) {
|
||||
|
|
|
@ -44,6 +44,9 @@ import org.hibernate.sql.ast.tree.select.SelectClause;
|
|||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
import org.hibernate.sql.model.internal.TableInsertStandard;
|
||||
import org.hibernate.sql.model.internal.TableUpdateStandard;
|
||||
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
|
||||
|
||||
/**
|
||||
* A legacy SQL AST translator for H2.
|
||||
|
@ -60,14 +63,49 @@ public class H2LegacySqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
|||
|
||||
@Override
|
||||
public void visitStandardTableInsert(TableInsertStandard tableInsert) {
|
||||
if ( CollectionHelper.isNotEmpty( tableInsert.getReturningColumns() ) ) {
|
||||
visitReturningInsertStatement( tableInsert );
|
||||
if ( getDialect().getVersion().isSameOrAfter( 2 )
|
||||
|| CollectionHelper.isEmpty( tableInsert.getReturningColumns() ) ) {
|
||||
final boolean closeWrapper = renderReturningClause( tableInsert.getReturningColumns() );
|
||||
super.visitStandardTableInsert( tableInsert );
|
||||
if ( closeWrapper ) {
|
||||
appendSql( ')' );
|
||||
}
|
||||
}
|
||||
else {
|
||||
super.visitStandardTableInsert( tableInsert );
|
||||
visitReturningInsertStatement( tableInsert );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitStandardTableUpdate(TableUpdateStandard tableUpdate) {
|
||||
final boolean closeWrapper = renderReturningClause( tableUpdate.getReturningColumns() );
|
||||
super.visitStandardTableUpdate( tableUpdate );
|
||||
if ( closeWrapper ) {
|
||||
appendSql( ')' );
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean renderReturningClause(List<ColumnReference> returningColumns) {
|
||||
if ( isEmpty( returningColumns ) ) {
|
||||
return false;
|
||||
}
|
||||
appendSql( "select " );
|
||||
for ( int i = 0; i < returningColumns.size(); i++ ) {
|
||||
if ( i > 0 ) {
|
||||
appendSql( ", " );
|
||||
}
|
||||
appendSql( returningColumns.get( i ).getColumnExpression() );
|
||||
}
|
||||
appendSql( " from final table (" );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void visitReturningColumns(List<ColumnReference> returningColumns) {
|
||||
// do nothing - this is handled via `#renderReturningClause`
|
||||
}
|
||||
|
||||
public void visitReturningInsertStatement(TableInsertStandard tableInsert) {
|
||||
assert tableInsert.getReturningColumns() != null
|
||||
&& !tableInsert.getReturningColumns().isEmpty();
|
||||
|
@ -145,11 +183,6 @@ public class H2LegacySqlAstTranslator<T extends JdbcOperation> extends AbstractS
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitReturningColumns(List<ColumnReference> returningColumns) {
|
||||
// do nothing - this is handled via `#visitReturningInsertStatement`
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCteContainer(CteContainer cteContainer) {
|
||||
// H2 has various bugs in different versions that make it impossible to use CTEs with parameters reliably
|
||||
|
|
|
@ -133,11 +133,13 @@ public abstract class AbstractEntityInsertAction extends EntityAction {
|
|||
nullifyTransientReferencesIfNotAlready();
|
||||
final Object version = getVersion( getState(), getPersister() );
|
||||
final PersistenceContext persistenceContextInternal = getSession().getPersistenceContextInternal();
|
||||
persistenceContextInternal.addEntity(
|
||||
persistenceContextInternal.addEntity( getEntityKey(), getInstance() );
|
||||
persistenceContextInternal.addEntry(
|
||||
getInstance(),
|
||||
( getPersister().isMutable() ? Status.MANAGED : Status.READ_ONLY ),
|
||||
getState(),
|
||||
getEntityKey(),
|
||||
getRowId(),
|
||||
getEntityKey().getIdentifier(),
|
||||
version,
|
||||
LockMode.WRITE,
|
||||
isExecuted,
|
||||
|
@ -230,6 +232,8 @@ public abstract class AbstractEntityInsertAction extends EntityAction {
|
|||
*/
|
||||
protected abstract EntityKey getEntityKey();
|
||||
|
||||
protected abstract Object getRowId();
|
||||
|
||||
@Override
|
||||
public void afterDeserialize(EventSource session) {
|
||||
super.afterDeserialize( session );
|
||||
|
|
|
@ -18,9 +18,12 @@ import org.hibernate.event.spi.PostInsertEvent;
|
|||
import org.hibernate.event.spi.PostInsertEventListener;
|
||||
import org.hibernate.event.spi.PreInsertEvent;
|
||||
import org.hibernate.event.spi.PreInsertEventListener;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
||||
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
|
||||
|
||||
/**
|
||||
* The action for performing entity insertions when entity is using IDENTITY column identifier generation
|
||||
*
|
||||
|
@ -32,6 +35,7 @@ public class EntityIdentityInsertAction extends AbstractEntityInsertAction {
|
|||
private final EntityKey delayedEntityKey;
|
||||
private EntityKey entityKey;
|
||||
private Object generatedId;
|
||||
private Object rowId;
|
||||
|
||||
/**
|
||||
* Constructs an EntityIdentityInsertAction
|
||||
|
@ -78,14 +82,25 @@ public class EntityIdentityInsertAction extends AbstractEntityInsertAction {
|
|||
// else inserted the same pk first, the insert would fail
|
||||
|
||||
if ( !isVeto() ) {
|
||||
generatedId = persister.insert( getState(), instance, session );
|
||||
final GeneratedValues generatedValues = persister.getInsertCoordinator().insert(
|
||||
instance,
|
||||
getState(),
|
||||
session
|
||||
);
|
||||
generatedId = castNonNull( generatedValues ).getGeneratedValue( persister.getIdentifierMapping() );
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
if ( persister.getRowIdMapping() != null ) {
|
||||
rowId = generatedValues.getGeneratedValue( persister.getRowIdMapping() );
|
||||
if ( rowId != null && isDelayed ) {
|
||||
persistenceContext.replaceEntityEntryRowId( getInstance(), rowId );
|
||||
}
|
||||
}
|
||||
if ( persister.hasInsertGeneratedProperties() ) {
|
||||
persister.processInsertGeneratedProperties( generatedId, instance, getState(), session );
|
||||
persister.processInsertGeneratedProperties( generatedId, instance, getState(), generatedValues, session );
|
||||
}
|
||||
//need to do that here rather than in the save event listener to let
|
||||
//the post insert events to have a id-filled entity when IDENTITY is used (EJB3)
|
||||
persister.setIdentifier( instance, generatedId, session );
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
persistenceContext.registerInsertedKey( getPersister(), generatedId );
|
||||
entityKey = session.generateEntityKey( generatedId, persister );
|
||||
persistenceContext.checkUniqueness( entityKey, getInstance() );
|
||||
|
@ -212,6 +227,11 @@ public class EntityIdentityInsertAction extends AbstractEntityInsertAction {
|
|||
return entityKey != null ? entityKey : delayedEntityKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getRowId() {
|
||||
return rowId;
|
||||
}
|
||||
|
||||
protected void setEntityKey(EntityKey entityKey) {
|
||||
this.entityKey = entityKey;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.hibernate.event.spi.PostInsertEvent;
|
|||
import org.hibernate.event.spi.PostInsertEventListener;
|
||||
import org.hibernate.event.spi.PreInsertEvent;
|
||||
import org.hibernate.event.spi.PreInsertEventListener;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.stat.internal.StatsHelper;
|
||||
import org.hibernate.stat.spi.StatisticsImplementor;
|
||||
|
@ -83,6 +84,11 @@ public class EntityInsertAction extends AbstractEntityInsertAction {
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object getRowId() {
|
||||
return null ;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EntityKey getEntityKey() {
|
||||
return getSession().generateEntityKey( getId(), getPersister() );
|
||||
|
@ -101,14 +107,14 @@ public class EntityInsertAction extends AbstractEntityInsertAction {
|
|||
if ( !veto ) {
|
||||
final EntityPersister persister = getPersister();
|
||||
final Object instance = getInstance();
|
||||
persister.insert( id, getState(), instance, session );
|
||||
final GeneratedValues generatedValues = persister.insertReturning( id, getState(), instance, session );
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContextInternal();
|
||||
final EntityEntry entry = persistenceContext.getEntry( instance );
|
||||
if ( entry == null ) {
|
||||
throw new AssertionFailure( "possible non-threadsafe access to session" );
|
||||
}
|
||||
entry.postInsert( getState() );
|
||||
handleGeneratedProperties( entry );
|
||||
handleGeneratedProperties( entry, generatedValues, persistenceContext );
|
||||
persistenceContext.registerInsertedKey( persister, getId() );
|
||||
addCollectionsByKeyToPersistenceContext( persistenceContext, getState() );
|
||||
}
|
||||
|
@ -124,11 +130,14 @@ public class EntityInsertAction extends AbstractEntityInsertAction {
|
|||
markExecuted();
|
||||
}
|
||||
|
||||
private void handleGeneratedProperties(EntityEntry entry) {
|
||||
private void handleGeneratedProperties(
|
||||
EntityEntry entry,
|
||||
GeneratedValues generatedValues,
|
||||
PersistenceContext persistenceContext) {
|
||||
final EntityPersister persister = getPersister();
|
||||
if ( persister.hasInsertGeneratedProperties() ) {
|
||||
final Object instance = getInstance();
|
||||
persister.processInsertGeneratedProperties( getId(), instance, getState(), getSession() );
|
||||
persister.processInsertGeneratedProperties( getId(), instance, getState(), generatedValues, getSession() );
|
||||
if ( persister.isVersionPropertyGenerated() ) {
|
||||
version = Versioning.getVersion( getState(), persister );
|
||||
}
|
||||
|
@ -138,6 +147,13 @@ public class EntityInsertAction extends AbstractEntityInsertAction {
|
|||
version = Versioning.getVersion( getState(), persister );
|
||||
entry.postInsert( version );
|
||||
}
|
||||
// Process row-id values when available early by replacing the entity entry
|
||||
if ( generatedValues != null && persister.getRowIdMapping() != null ) {
|
||||
final Object rowId = generatedValues.getGeneratedValue( persister.getRowIdMapping() );
|
||||
if ( rowId != null ) {
|
||||
persistenceContext.replaceEntityEntryRowId( getInstance(), rowId );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void putCacheIfNecessary() {
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.hibernate.event.spi.PostUpdateEvent;
|
|||
import org.hibernate.event.spi.PostUpdateEventListener;
|
||||
import org.hibernate.event.spi.PreUpdateEvent;
|
||||
import org.hibernate.event.spi.PreUpdateEventListener;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.stat.internal.StatsHelper;
|
||||
|
@ -165,12 +166,22 @@ public class EntityUpdateAction extends EntityAction {
|
|||
final Object instance = getInstance();
|
||||
final Object previousVersion = getPreviousVersion();
|
||||
final Object ck = lockCacheItem( previousVersion );
|
||||
persister.update( id, state, dirtyFields, hasDirtyCollection, previousState, previousVersion, instance, rowId, session );
|
||||
final GeneratedValues generatedValues = persister.updateReturning(
|
||||
id,
|
||||
state,
|
||||
dirtyFields,
|
||||
hasDirtyCollection,
|
||||
previousState,
|
||||
previousVersion,
|
||||
instance,
|
||||
rowId,
|
||||
session
|
||||
);
|
||||
final EntityEntry entry = session.getPersistenceContextInternal().getEntry( instance );
|
||||
if ( entry == null ) {
|
||||
throw new AssertionFailure( "possible non thread safe access to session" );
|
||||
}
|
||||
handleGeneratedProperties( entry );
|
||||
handleGeneratedProperties( entry, generatedValues );
|
||||
handleDeleted( entry );
|
||||
updateCacheItem( previousVersion, ck, entry );
|
||||
handleNaturalIdResolutions( persister, session, id );
|
||||
|
@ -228,7 +239,7 @@ public class EntityUpdateAction extends EntityAction {
|
|||
|| session.getCacheMode() == CacheMode.IGNORE;
|
||||
}
|
||||
|
||||
private void handleGeneratedProperties(EntityEntry entry) {
|
||||
private void handleGeneratedProperties(EntityEntry entry, GeneratedValues generatedValues) {
|
||||
final EntityPersister persister = getPersister();
|
||||
if ( entry.getStatus() == Status.MANAGED || persister.isVersionPropertyGenerated() ) {
|
||||
final SharedSessionContractImplementor session = getSession();
|
||||
|
@ -247,7 +258,7 @@ public class EntityUpdateAction extends EntityAction {
|
|||
if ( persister.hasUpdateGeneratedProperties() ) {
|
||||
// this entity defines property generation, so process those generated
|
||||
// values...
|
||||
persister.processUpdateGeneratedProperties( id, instance, state, session );
|
||||
persister.processUpdateGeneratedProperties( id, instance, state, generatedValues, session );
|
||||
}
|
||||
// have the entity entry doAfterTransactionCompletion post-update processing, passing it the
|
||||
// update state and the new version (if one).
|
||||
|
|
|
@ -1055,6 +1055,11 @@ public class DB2Dialect extends Dialect {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsInsertReturningRowId() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsValuesList() {
|
||||
return true;
|
||||
|
|
|
@ -45,6 +45,7 @@ import org.hibernate.sql.ast.tree.select.SelectStatement;
|
|||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
import org.hibernate.sql.model.internal.TableInsertStandard;
|
||||
import org.hibernate.sql.model.internal.TableUpdateStandard;
|
||||
import org.hibernate.type.SqlTypes;
|
||||
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
|
||||
|
@ -434,7 +435,7 @@ public class DB2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAst
|
|||
appendSql( returningColumns.get( i ).getColumnExpression() );
|
||||
}
|
||||
|
||||
appendSql( " from new table ( " ); // 'from final table' does not seem to play well with triggers
|
||||
appendSql( " from new table (" ); // 'from final table' does not seem to play well with triggers
|
||||
super.visitStandardTableInsert( tableInsert );
|
||||
appendSql( ")" );
|
||||
}
|
||||
|
@ -443,6 +444,28 @@ public class DB2SqlAstTranslator<T extends JdbcOperation> extends AbstractSqlAst
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitStandardTableUpdate(TableUpdateStandard tableUpdate) {
|
||||
final List<ColumnReference> returningColumns = tableUpdate.getReturningColumns();
|
||||
if ( isNotEmpty( returningColumns ) ) {
|
||||
appendSql( "select " );
|
||||
|
||||
for ( int i = 0; i < returningColumns.size(); i++ ) {
|
||||
if ( i > 0 ) {
|
||||
appendSql( ", " );
|
||||
}
|
||||
appendSql( returningColumns.get( i ).getColumnExpression() );
|
||||
}
|
||||
|
||||
appendSql( " from final table (" );
|
||||
super.visitStandardTableUpdate( tableUpdate );
|
||||
appendSql( ")" );
|
||||
}
|
||||
else {
|
||||
super.visitStandardTableUpdate( tableUpdate );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void renderComparison(Expression lhs, ComparisonOperator operator, Expression rhs) {
|
||||
if ( getDB2Version().isSameOrAfter( 11, 1 ) ) {
|
||||
|
|
|
@ -4042,7 +4042,6 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
|||
* {@code false} if {@code InsertReturningDelegate} does not work, or only
|
||||
* works for specialized identity/"autoincrement" columns
|
||||
*
|
||||
* @see org.hibernate.generator.OnExecutionGenerator#getGeneratedIdentifierDelegate
|
||||
* @see org.hibernate.id.insert.InsertReturningDelegate
|
||||
*
|
||||
* @since 6.2
|
||||
|
@ -4051,6 +4050,33 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this dialect supports returning the {@link org.hibernate.annotations.RowId} column
|
||||
* after execution of an {@code insert} statement, using native SQL syntax?
|
||||
*
|
||||
* @return {@code true} is the dialect supports returning the rowid column
|
||||
*
|
||||
* @see #supportsInsertReturning()
|
||||
* @since 6.5
|
||||
*/
|
||||
public boolean supportsInsertReturningRowId() {
|
||||
return supportsInsertReturning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this dialect fully support returning arbitrary generated column values
|
||||
* after execution of an {@code update} statement, using native SQL syntax?
|
||||
* <p>
|
||||
* Defaults to the value of {@link #supportsInsertReturning()} but can be overridden
|
||||
* to explicitly disable this for updates.
|
||||
*
|
||||
* @see #supportsInsertReturning()
|
||||
* @since 6.5
|
||||
*/
|
||||
public boolean supportsUpdateReturning() {
|
||||
return supportsInsertReturning();
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this dialect fully support returning arbitrary generated column values
|
||||
* after execution of an {@code insert} statement, using the JDBC method
|
||||
|
@ -4072,6 +4098,17 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
|||
public boolean supportsInsertReturningGeneratedKeys() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this dialect require unquoting identifiers when passing them to the
|
||||
* {@link Connection#prepareStatement(String, String[])} JDBC method.
|
||||
*
|
||||
* @see Dialect#supportsInsertReturningGeneratedKeys()
|
||||
*/
|
||||
public boolean unquoteGetGeneratedKeys() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this dialect support the given {@code FETCH} clause type.
|
||||
*
|
||||
|
|
|
@ -834,6 +834,21 @@ public class H2Dialect extends Dialect {
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsInsertReturningRowId() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsInsertReturningGeneratedKeys() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean unquoteGetGeneratedKeys() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int registerResultSetOutParameter(CallableStatement statement, int position) throws SQLException {
|
||||
return position;
|
||||
|
|
|
@ -17,6 +17,7 @@ import org.hibernate.query.sqm.ComparisonOperator;
|
|||
import org.hibernate.sql.ast.Clause;
|
||||
import org.hibernate.sql.ast.SqlAstNodeRenderingMode;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.MutationStatement;
|
||||
import org.hibernate.sql.ast.tree.Statement;
|
||||
import org.hibernate.sql.ast.tree.cte.CteContainer;
|
||||
import org.hibernate.sql.ast.tree.cte.CteTableGroup;
|
||||
|
@ -40,7 +41,9 @@ import org.hibernate.sql.ast.tree.select.SelectClause;
|
|||
import org.hibernate.sql.ast.tree.update.UpdateStatement;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperation;
|
||||
import org.hibernate.sql.model.internal.TableInsertStandard;
|
||||
import org.hibernate.sql.model.internal.TableUpdateStandard;
|
||||
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.isEmpty;
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty;
|
||||
|
||||
/**
|
||||
|
@ -58,14 +61,44 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends SqlAstTranslato
|
|||
|
||||
@Override
|
||||
public void visitStandardTableInsert(TableInsertStandard tableInsert) {
|
||||
if ( isNotEmpty( tableInsert.getReturningColumns() ) ) {
|
||||
visitReturningInsertStatement( tableInsert );
|
||||
}
|
||||
else {
|
||||
super.visitStandardTableInsert( tableInsert );
|
||||
final boolean closeWrapper = renderReturningClause( tableInsert.getReturningColumns() );
|
||||
super.visitStandardTableInsert( tableInsert );
|
||||
if ( closeWrapper ) {
|
||||
appendSql( ')' );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitStandardTableUpdate(TableUpdateStandard tableUpdate) {
|
||||
final boolean closeWrapper = renderReturningClause( tableUpdate.getReturningColumns() );
|
||||
super.visitStandardTableUpdate( tableUpdate );
|
||||
if ( closeWrapper ) {
|
||||
appendSql( ')' );
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean renderReturningClause(List<ColumnReference> returningColumns) {
|
||||
if ( isEmpty( returningColumns ) ) {
|
||||
return false;
|
||||
}
|
||||
appendSql( "select " );
|
||||
for ( int i = 0; i < returningColumns.size(); i++ ) {
|
||||
if ( i > 0 ) {
|
||||
appendSql( ", " );
|
||||
}
|
||||
appendSql( returningColumns.get( i ).getColumnExpression() );
|
||||
}
|
||||
appendSql( " from final table (" );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void visitReturningColumns(List<ColumnReference> returningColumns) {
|
||||
// do nothing - this is handled via `#renderReturningClause`
|
||||
}
|
||||
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
public void visitReturningInsertStatement(TableInsertStandard tableInsert) {
|
||||
assert tableInsert.getReturningColumns() != null
|
||||
&& !tableInsert.getReturningColumns().isEmpty();
|
||||
|
@ -147,11 +180,6 @@ public class H2SqlAstTranslator<T extends JdbcOperation> extends SqlAstTranslato
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void visitReturningColumns(List<ColumnReference> returningColumns) {
|
||||
// do nothing - this is handled via `#visitReturningInsertStatement`
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitCteContainer(CteContainer cteContainer) {
|
||||
// H2 has various bugs in different versions that make it impossible to use CTEs with parameters reliably
|
||||
|
|
|
@ -12,6 +12,8 @@ import java.sql.SQLException;
|
|||
import org.hibernate.boot.model.FunctionContributions;
|
||||
import org.hibernate.boot.model.TypeContributions;
|
||||
import org.hibernate.dialect.function.CommonFunctionFactory;
|
||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||
import org.hibernate.dialect.identity.MariaDBIdentityColumnSupport;
|
||||
import org.hibernate.dialect.sequence.MariaDBSequenceSupport;
|
||||
import org.hibernate.dialect.sequence.SequenceSupport;
|
||||
import org.hibernate.engine.jdbc.dialect.spi.DialectResolutionInfo;
|
||||
|
@ -248,6 +250,16 @@ public class MariaDBDialect extends MySQLDialect {
|
|||
return getVersion().isSameOrAfter( 10, 5 );
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsUpdateReturning() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityColumnSupport getIdentityColumnSupport() {
|
||||
return MariaDBIdentityColumnSupport.INSTANCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FunctionalDependencyAnalysisSupport getFunctionalDependencyAnalysisSupport() {
|
||||
return FunctionalDependencyAnalysisSupportImpl.TABLE_GROUP_AND_CONSTANTS;
|
||||
|
|
|
@ -10,6 +10,7 @@ import org.hibernate.MappingException;
|
|||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.id.insert.GetGeneratedKeysDelegate;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
* A set of operations providing support for identity columns
|
||||
|
@ -55,7 +56,10 @@ public interface IdentityColumnSupport {
|
|||
*
|
||||
* @return The insert command with any necessary identity select
|
||||
* clause attached.
|
||||
*
|
||||
* @deprecated Use {@link #appendIdentitySelectToInsert(String, String)} instead.
|
||||
*/
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
String appendIdentitySelectToInsert(String insertString);
|
||||
|
||||
/**
|
||||
|
@ -127,8 +131,23 @@ public interface IdentityColumnSupport {
|
|||
* @param dialect The dialect against which to generate the delegate
|
||||
*
|
||||
* @return the dialect specific GetGeneratedKeys delegate
|
||||
*
|
||||
* @deprecated Use {@link #buildGetGeneratedKeysDelegate(EntityPersister, Dialect)} instead.
|
||||
*/
|
||||
GetGeneratedKeysDelegate buildGetGeneratedKeysDelegate(
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
default GetGeneratedKeysDelegate buildGetGeneratedKeysDelegate(
|
||||
PostInsertIdentityPersister persister,
|
||||
Dialect dialect);
|
||||
Dialect dialect) {
|
||||
return buildGetGeneratedKeysDelegate( persister );
|
||||
}
|
||||
|
||||
/**
|
||||
* The Delegate for dealing with IDENTITY columns using JDBC3 getGeneratedKeys
|
||||
*
|
||||
* @param persister The persister
|
||||
* @param dialect The dialect against which to generate the delegate
|
||||
*
|
||||
* @return the dialect specific GetGeneratedKeys delegate
|
||||
*/
|
||||
GetGeneratedKeysDelegate buildGetGeneratedKeysDelegate(EntityPersister persister);
|
||||
}
|
||||
|
|
|
@ -8,8 +8,9 @@ package org.hibernate.dialect.identity;
|
|||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.id.insert.GetGeneratedKeysDelegate;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
|
@ -54,9 +55,7 @@ public class IdentityColumnSupportImpl implements IdentityColumnSupport {
|
|||
}
|
||||
|
||||
@Override
|
||||
public GetGeneratedKeysDelegate buildGetGeneratedKeysDelegate(
|
||||
PostInsertIdentityPersister persister,
|
||||
Dialect dialect) {
|
||||
return new GetGeneratedKeysDelegate( persister, dialect, true );
|
||||
public GetGeneratedKeysDelegate buildGetGeneratedKeysDelegate(EntityPersister persister) {
|
||||
return new GetGeneratedKeysDelegate( persister, true, EventType.INSERT );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.dialect.identity;
|
||||
|
||||
/**
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
public class MariaDBIdentityColumnSupport extends MySQLIdentityColumnSupport {
|
||||
public static final MariaDBIdentityColumnSupport INSTANCE = new MariaDBIdentityColumnSupport();
|
||||
|
||||
@Override
|
||||
public String appendIdentitySelectToInsert(String identityColumnName, String insertString) {
|
||||
return insertString + " returning " + identityColumnName;
|
||||
}
|
||||
}
|
|
@ -8,8 +8,10 @@ package org.hibernate.dialect.identity;
|
|||
|
||||
import org.hibernate.Remove;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.id.insert.GetGeneratedKeysDelegate;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
|
@ -18,7 +20,15 @@ import org.hibernate.id.insert.GetGeneratedKeysDelegate;
|
|||
*/
|
||||
@Deprecated(forRemoval = true) @Remove
|
||||
public class Oracle12cGetGeneratedKeysDelegate extends GetGeneratedKeysDelegate {
|
||||
/**
|
||||
* @deprecated Use {@link #Oracle12cGetGeneratedKeysDelegate(EntityPersister)} instead.
|
||||
*/
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
public Oracle12cGetGeneratedKeysDelegate(PostInsertIdentityPersister persister, Dialect dialect) {
|
||||
super( persister, dialect, false );
|
||||
this( persister );
|
||||
}
|
||||
|
||||
public Oracle12cGetGeneratedKeysDelegate(EntityPersister persister) {
|
||||
super( persister, false, EventType.INSERT );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
package org.hibernate.dialect.identity;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.id.insert.GetGeneratedKeysDelegate;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
/**
|
||||
* @author Andrea Boriero
|
||||
|
@ -28,9 +29,8 @@ public class Oracle12cIdentityColumnSupport extends IdentityColumnSupportImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public GetGeneratedKeysDelegate buildGetGeneratedKeysDelegate(
|
||||
PostInsertIdentityPersister persister, Dialect dialect) {
|
||||
return new GetGeneratedKeysDelegate( persister, dialect, false );
|
||||
public GetGeneratedKeysDelegate buildGetGeneratedKeysDelegate(EntityPersister persister) {
|
||||
return new GetGeneratedKeysDelegate( persister, false, EventType.INSERT );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -10,14 +10,13 @@ import org.hibernate.dialect.Dialect;
|
|||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.id.insert.GetGeneratedKeysDelegate;
|
||||
import org.hibernate.id.insert.SybaseJConnGetGeneratedKeysDelegate;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
public class SybaseJconnIdentityColumnSupport extends AbstractTransactSQLIdentityColumnSupport {
|
||||
public static final SybaseJconnIdentityColumnSupport INSTANCE = new SybaseJconnIdentityColumnSupport();
|
||||
|
||||
@Override
|
||||
public GetGeneratedKeysDelegate buildGetGeneratedKeysDelegate(
|
||||
PostInsertIdentityPersister persister,
|
||||
Dialect dialect) {
|
||||
return new SybaseJConnGetGeneratedKeysDelegate( persister, dialect );
|
||||
public GetGeneratedKeysDelegate buildGetGeneratedKeysDelegate(EntityPersister persister) {
|
||||
return new SybaseJConnGetGeneratedKeysDelegate( persister );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1753,6 +1753,23 @@ public class StatefulPersistenceContext implements PersistenceContext {
|
|||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void replaceEntityEntryRowId(Object entity, Object rowId) {
|
||||
final EntityEntry oldEntry = entityEntryContext.removeEntityEntry( entity );
|
||||
addEntry(
|
||||
entity,
|
||||
oldEntry.getStatus(),
|
||||
oldEntry.getLoadedState(),
|
||||
rowId,
|
||||
oldEntry.getId(),
|
||||
oldEntry.getVersion(),
|
||||
oldEntry.getLockMode(),
|
||||
oldEntry.isExistsInDatabase(),
|
||||
oldEntry.getPersister(),
|
||||
oldEntry.isBeingReplicated()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used by the owning session to explicitly control serialization of the
|
||||
* persistence context.
|
||||
|
|
|
@ -27,7 +27,7 @@ public final class HighlightingFormatter implements Formatter {
|
|||
private static final Set<String> KEYWORDS_LOWERCASED = new HashSet<>( new AnsiSqlKeywords().sql2003() );
|
||||
static {
|
||||
// additional keywords not reserved by ANSI SQL 2003
|
||||
KEYWORDS_LOWERCASED.addAll( Arrays.asList( "key", "sequence", "cascade", "increment", "boolean", "offset", "next" ) );
|
||||
KEYWORDS_LOWERCASED.addAll( Arrays.asList( "key", "sequence", "cascade", "increment", "boolean", "offset", "next", "returning" ) );
|
||||
}
|
||||
|
||||
public static final Formatter INSTANCE =
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.engine.jdbc.mutation;
|
|||
import org.hibernate.Incubating;
|
||||
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.sql.model.ValuesAnalysis;
|
||||
|
||||
/**
|
||||
|
@ -42,7 +43,7 @@ public interface MutationExecutor {
|
|||
* @param resultChecker Custom result checking; pass {@code null} to perform
|
||||
* the standard check using the statement's {@linkplain org.hibernate.jdbc.Expectation expectation}
|
||||
*/
|
||||
Object execute(
|
||||
GeneratedValues execute(
|
||||
Object modelReference,
|
||||
ValuesAnalysis valuesAnalysis,
|
||||
TableInclusionChecker inclusionChecker,
|
||||
|
|
|
@ -12,9 +12,12 @@ import org.hibernate.engine.jdbc.batch.spi.BatchKey;
|
|||
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
|
||||
import org.hibernate.engine.jdbc.mutation.MutationExecutor;
|
||||
import org.hibernate.engine.jdbc.mutation.OperationResultChecker;
|
||||
import org.hibernate.engine.jdbc.mutation.ParameterUsage;
|
||||
import org.hibernate.engine.jdbc.mutation.TableInclusionChecker;
|
||||
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.persister.entity.mutation.EntityTableMapping;
|
||||
import org.hibernate.sql.model.TableMapping;
|
||||
import org.hibernate.sql.model.ValuesAnalysis;
|
||||
|
||||
|
@ -43,23 +46,31 @@ public abstract class AbstractMutationExecutor implements MutationExecutor {
|
|||
* </ol>
|
||||
*/
|
||||
@Override
|
||||
public final Object execute(
|
||||
public final GeneratedValues execute(
|
||||
Object modelReference,
|
||||
ValuesAnalysis valuesAnalysis,
|
||||
TableInclusionChecker inclusionChecker,
|
||||
OperationResultChecker resultChecker,
|
||||
SharedSessionContractImplementor session) {
|
||||
performNonBatchedOperations( valuesAnalysis, inclusionChecker, resultChecker, session );
|
||||
final GeneratedValues generatedValues = performNonBatchedOperations(
|
||||
modelReference,
|
||||
valuesAnalysis,
|
||||
inclusionChecker,
|
||||
resultChecker,
|
||||
session
|
||||
);
|
||||
performSelfExecutingOperations( valuesAnalysis, inclusionChecker, session );
|
||||
performBatchedOperations( valuesAnalysis, inclusionChecker );
|
||||
return null;
|
||||
return generatedValues;
|
||||
}
|
||||
|
||||
protected void performNonBatchedOperations(
|
||||
protected GeneratedValues performNonBatchedOperations(
|
||||
Object modelReference,
|
||||
ValuesAnalysis valuesAnalysis,
|
||||
TableInclusionChecker inclusionChecker,
|
||||
OperationResultChecker resultChecker,
|
||||
SharedSessionContractImplementor session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
protected void performSelfExecutingOperations(
|
||||
|
@ -78,6 +89,7 @@ public abstract class AbstractMutationExecutor implements MutationExecutor {
|
|||
*/
|
||||
protected void performNonBatchedMutation(
|
||||
PreparedStatementDetails statementDetails,
|
||||
Object id,
|
||||
JdbcValueBindings valueBindings,
|
||||
TableInclusionChecker inclusionChecker,
|
||||
OperationResultChecker resultChecker,
|
||||
|
@ -97,6 +109,20 @@ public abstract class AbstractMutationExecutor implements MutationExecutor {
|
|||
return;
|
||||
}
|
||||
|
||||
if ( id != null ) {
|
||||
assert !tableDetails.isIdentifierTable() : "Unsupported identifier table with generated id";
|
||||
( (EntityTableMapping) tableDetails ).getKeyMapping().breakDownKeyJdbcValues(
|
||||
id,
|
||||
(jdbcValue, columnMapping) -> valueBindings.bindValue(
|
||||
jdbcValue,
|
||||
tableDetails.getTableName(),
|
||||
columnMapping.getColumnName(),
|
||||
ParameterUsage.SET
|
||||
),
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
// If we get here the statement is needed - make sure it is resolved
|
||||
session.getJdbcServices().getSqlStatementLogger().logStatement( statementDetails.getSqlString() );
|
||||
valueBindings.beforeStatement( statementDetails );
|
||||
|
|
|
@ -21,8 +21,8 @@ import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
|||
import org.hibernate.engine.jdbc.spi.MutationStatementPreparer;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.jdbc.TooManyRowsAffectedException;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
import org.hibernate.sql.model.MutationTarget;
|
||||
import org.hibernate.sql.model.MutationType;
|
||||
import org.hibernate.sql.model.PreparableMutationOperation;
|
||||
|
@ -97,6 +97,7 @@ public class ModelMutationHelper {
|
|||
public static PreparedStatementGroup toPreparedStatementGroup(
|
||||
MutationType mutationType,
|
||||
MutationTarget<?> mutationTarget,
|
||||
GeneratedValuesMutationDelegate delegate,
|
||||
List<PreparableMutationOperation> mutations,
|
||||
SharedSessionContractImplementor session) {
|
||||
if ( mutations == null || mutations.isEmpty() ) {
|
||||
|
@ -104,37 +105,32 @@ public class ModelMutationHelper {
|
|||
}
|
||||
|
||||
if ( mutations.size() == 1 ) {
|
||||
return new PreparedStatementGroupSingleTable( mutations.get( 0 ), session );
|
||||
return new PreparedStatementGroupSingleTable( mutations.get( 0 ), delegate, session );
|
||||
}
|
||||
|
||||
return new PreparedStatementGroupStandard( mutationType, mutationTarget, mutations, session );
|
||||
return new PreparedStatementGroupStandard( mutationType, mutationTarget, delegate, mutations, session );
|
||||
}
|
||||
|
||||
public static PreparedStatementDetails standardPreparation(
|
||||
PreparableMutationOperation jdbcMutation,
|
||||
GeneratedValuesMutationDelegate delegate,
|
||||
SharedSessionContractImplementor session) {
|
||||
return new PreparedStatementDetailsStandard(
|
||||
jdbcMutation,
|
||||
() -> standardStatementPreparation( jdbcMutation, session ),
|
||||
() -> delegate != null ?
|
||||
delegateStatementPreparation( jdbcMutation, delegate, session ) :
|
||||
standardStatementPreparation( jdbcMutation, session ),
|
||||
session.getJdbcServices()
|
||||
);
|
||||
}
|
||||
|
||||
public static PreparedStatementDetails identityPreparation(
|
||||
public static PreparedStatement delegateStatementPreparation(
|
||||
PreparableMutationOperation jdbcMutation,
|
||||
GeneratedValuesMutationDelegate delegate,
|
||||
SharedSessionContractImplementor session) {
|
||||
return new PreparedStatementDetailsStandard(
|
||||
jdbcMutation,
|
||||
() -> {
|
||||
final EntityMutationTarget target = (EntityMutationTarget) jdbcMutation.getMutationTarget();
|
||||
final PreparedStatement statement = target
|
||||
.getIdentityInsertDelegate()
|
||||
.prepareStatement( jdbcMutation.getSqlString(), session );
|
||||
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().register( null, statement );
|
||||
return statement;
|
||||
},
|
||||
session.getJdbcServices()
|
||||
);
|
||||
final PreparedStatement statement = delegate.prepareStatement( jdbcMutation.getSqlString(), session );
|
||||
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().register( null, statement );
|
||||
return statement;
|
||||
}
|
||||
|
||||
public static PreparedStatement standardStatementPreparation(
|
||||
|
|
|
@ -1,238 +0,0 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
|
||||
*/
|
||||
package org.hibernate.engine.jdbc.mutation.internal;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
|
||||
import org.hibernate.engine.jdbc.mutation.MutationExecutor;
|
||||
import org.hibernate.engine.jdbc.mutation.OperationResultChecker;
|
||||
import org.hibernate.engine.jdbc.mutation.ParameterUsage;
|
||||
import org.hibernate.engine.jdbc.mutation.TableInclusionChecker;
|
||||
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
|
||||
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementGroup;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
import org.hibernate.persister.entity.mutation.EntityTableMapping;
|
||||
import org.hibernate.sql.model.EntityMutationOperationGroup;
|
||||
import org.hibernate.sql.model.MutationOperation;
|
||||
import org.hibernate.sql.model.MutationOperationGroup;
|
||||
import org.hibernate.sql.model.MutationType;
|
||||
import org.hibernate.sql.model.PreparableMutationOperation;
|
||||
import org.hibernate.sql.model.SelfExecutingUpdateOperation;
|
||||
import org.hibernate.sql.model.ValuesAnalysis;
|
||||
import org.hibernate.sql.model.jdbc.JdbcValueDescriptor;
|
||||
|
||||
import static org.hibernate.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER;
|
||||
|
||||
/**
|
||||
* Specialized executor for the case of more than one table operation, with the
|
||||
* root table defining a post-insert id-generation strategy.
|
||||
*
|
||||
* @todo (mutation) : look to consolidate this into/with MutationExecutorStandard
|
||||
* - aside from the special handling for the IDENTITY table insert,
|
||||
* the code below is the same as MutationExecutorStandard.
|
||||
* - consolidating this into MutationExecutorStandard would simplify
|
||||
* creating "single table" variations - i.e. MutationExecutorStandard and
|
||||
* StandardSingleTableExecutor. Otherwise we'd have MutationExecutorStandard,
|
||||
* StandardSingleTableExecutor, MutationExecutorPostInsert and
|
||||
* MutationExecutorPostInsertSingleTable variants
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class MutationExecutorPostInsert implements MutationExecutor, JdbcValueBindingsImpl.JdbcValueDescriptorAccess {
|
||||
protected final EntityMutationTarget mutationTarget;
|
||||
protected final MutationOperationGroup mutationOperationGroup;
|
||||
|
||||
protected final PreparedStatementDetails identityInsertStatementDetails;
|
||||
|
||||
/**
|
||||
* Any non-batched JDBC statements
|
||||
*/
|
||||
protected final PreparedStatementGroup secondaryTablesStatementGroup;
|
||||
|
||||
protected final JdbcValueBindingsImpl valueBindings;
|
||||
|
||||
public MutationExecutorPostInsert(EntityMutationOperationGroup mutationOperationGroup, SharedSessionContractImplementor session) {
|
||||
this.mutationTarget = mutationOperationGroup.getMutationTarget();
|
||||
this.valueBindings = new JdbcValueBindingsImpl(
|
||||
MutationType.INSERT,
|
||||
mutationTarget,
|
||||
this,
|
||||
session
|
||||
);
|
||||
this.mutationOperationGroup = mutationOperationGroup;
|
||||
|
||||
final PreparableMutationOperation identityInsertOperation = (PreparableMutationOperation) mutationOperationGroup.getOperation( mutationTarget.getIdentifierTableName() );
|
||||
this.identityInsertStatementDetails = ModelMutationHelper.identityPreparation(
|
||||
identityInsertOperation,
|
||||
session
|
||||
);
|
||||
|
||||
List<PreparableMutationOperation> secondaryTableMutations = null;
|
||||
|
||||
for ( int i = 0; i < mutationOperationGroup.getNumberOfOperations(); i++ ) {
|
||||
final MutationOperation operation = mutationOperationGroup.getOperation( i );
|
||||
|
||||
if ( operation.getTableDetails().isIdentifierTable() ) {
|
||||
// the identifier table is handled via `identityInsertStatementDetails`
|
||||
continue;
|
||||
}
|
||||
|
||||
// SelfExecutingUpdateOperation are not legal for inserts...
|
||||
assert ! (operation instanceof SelfExecutingUpdateOperation );
|
||||
|
||||
final PreparableMutationOperation preparableMutationOperation = (PreparableMutationOperation) operation;
|
||||
if ( secondaryTableMutations == null ) {
|
||||
secondaryTableMutations = new ArrayList<>();
|
||||
}
|
||||
secondaryTableMutations.add( preparableMutationOperation );
|
||||
}
|
||||
|
||||
|
||||
this.secondaryTablesStatementGroup = ModelMutationHelper.toPreparedStatementGroup(
|
||||
MutationType.INSERT,
|
||||
mutationTarget,
|
||||
secondaryTableMutations,
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcValueBindings getJdbcValueBindings() {
|
||||
return valueBindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcValueDescriptor resolveValueDescriptor(String tableName, String columnName, ParameterUsage usage) {
|
||||
final MutationOperation operation = mutationOperationGroup.getOperation( tableName );
|
||||
if ( operation == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return operation.getJdbcValueDescriptor( columnName, usage );
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatementDetails getPreparedStatementDetails(String tableName) {
|
||||
if ( mutationTarget.getIdentifierTableName().equals( tableName ) ) {
|
||||
return identityInsertStatementDetails;
|
||||
}
|
||||
|
||||
return secondaryTablesStatementGroup.getPreparedStatementDetails( tableName );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(
|
||||
Object modelReference,
|
||||
ValuesAnalysis valuesAnalysis,
|
||||
TableInclusionChecker inclusionChecker,
|
||||
OperationResultChecker resultChecker,
|
||||
SharedSessionContractImplementor session) {
|
||||
final InsertGeneratedIdentifierDelegate identityHandler = mutationTarget.getIdentityInsertDelegate();
|
||||
final Object id = identityHandler.performInsert( identityInsertStatementDetails, valueBindings, modelReference, session );
|
||||
|
||||
if ( MODEL_MUTATION_LOGGER.isTraceEnabled() ) {
|
||||
MODEL_MUTATION_LOGGER.tracef(
|
||||
"Post-insert generated value : `%s` (%s)",
|
||||
id,
|
||||
mutationTarget.getNavigableRole().getFullPath()
|
||||
);
|
||||
}
|
||||
|
||||
if ( secondaryTablesStatementGroup != null ) {
|
||||
secondaryTablesStatementGroup.forEachStatement( (tableName, statementDetails) -> executeWithId(
|
||||
id,
|
||||
tableName,
|
||||
statementDetails,
|
||||
inclusionChecker,
|
||||
resultChecker,
|
||||
session
|
||||
) );
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
private void executeWithId(
|
||||
Object id,
|
||||
String tableName,
|
||||
PreparedStatementDetails statementDetails,
|
||||
TableInclusionChecker inclusionChecker,
|
||||
OperationResultChecker resultChecker,
|
||||
SharedSessionContractImplementor session) {
|
||||
if ( statementDetails == null ) {
|
||||
return;
|
||||
}
|
||||
|
||||
final EntityTableMapping tableDetails = (EntityTableMapping) statementDetails.getMutatingTableDetails();
|
||||
assert !tableDetails.isIdentifierTable();
|
||||
|
||||
if ( inclusionChecker != null && !inclusionChecker.include( tableDetails ) ) {
|
||||
if ( MODEL_MUTATION_LOGGER.isTraceEnabled() ) {
|
||||
MODEL_MUTATION_LOGGER.tracef(
|
||||
"Skipping execution of secondary insert : %s",
|
||||
tableDetails.getTableName()
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If we get here the statement is needed - make sure it is resolved
|
||||
//noinspection resource
|
||||
statementDetails.resolveStatement();
|
||||
|
||||
tableDetails.getKeyMapping().breakDownKeyJdbcValues(
|
||||
id,
|
||||
(jdbcValue, columnMapping) -> {
|
||||
valueBindings.bindValue(
|
||||
jdbcValue,
|
||||
tableName,
|
||||
columnMapping.getColumnName(),
|
||||
ParameterUsage.SET
|
||||
);
|
||||
},
|
||||
session
|
||||
);
|
||||
|
||||
session.getJdbcServices().getSqlStatementLogger().logStatement( statementDetails.getSqlString() );
|
||||
valueBindings.beforeStatement( statementDetails );
|
||||
|
||||
try {
|
||||
final int affectedRowCount = session.getJdbcCoordinator()
|
||||
.getResultSetReturn()
|
||||
.executeUpdate( statementDetails.getStatement(), statementDetails.getSqlString() );
|
||||
|
||||
ModelMutationHelper.checkResults( resultChecker, statementDetails, affectedRowCount, -1 );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
||||
e,
|
||||
"Unable to execute mutation PreparedStatement against table `" + tableName + "`",
|
||||
statementDetails.getSqlString()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
secondaryTablesStatementGroup.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
Locale.ROOT,
|
||||
"MutationExecutorPostInsert(`%s`)",
|
||||
mutationTarget.getNavigableRole().getFullPath()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,126 +0,0 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html.
|
||||
*/
|
||||
package org.hibernate.engine.jdbc.mutation.internal;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
|
||||
import org.hibernate.engine.jdbc.mutation.MutationExecutor;
|
||||
import org.hibernate.engine.jdbc.mutation.OperationResultChecker;
|
||||
import org.hibernate.engine.jdbc.mutation.ParameterUsage;
|
||||
import org.hibernate.engine.jdbc.mutation.TableInclusionChecker;
|
||||
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
import org.hibernate.sql.model.EntityMutationOperationGroup;
|
||||
import org.hibernate.sql.model.MutationType;
|
||||
import org.hibernate.sql.model.PreparableMutationOperation;
|
||||
import org.hibernate.sql.model.ValuesAnalysis;
|
||||
import org.hibernate.sql.model.jdbc.JdbcValueDescriptor;
|
||||
|
||||
import static org.hibernate.engine.jdbc.mutation.internal.ModelMutationHelper.identityPreparation;
|
||||
import static org.hibernate.sql.model.ModelMutationLogging.MODEL_MUTATION_LOGGER;
|
||||
|
||||
/**
|
||||
* Specialized form of {@link MutationExecutorPostInsert} for cases where there
|
||||
* is only the single identity table. Allows us to skip references to things
|
||||
* we won't need (Batch, etc)
|
||||
*
|
||||
* @todo (mutation) : look to consolidate this into/with MutationExecutorStandard
|
||||
* - aside from the special handling for the IDENTITY table insert,
|
||||
* the code below is the same as MutationExecutorStandard.
|
||||
* - consolidating this into MutationExecutorStandard would simplify
|
||||
* creating "single table" variations - i.e. MutationExecutorStandard and
|
||||
* StandardSingleTableExecutor. Otherwise we'd have MutationExecutorStandard,
|
||||
* StandardSingleTableExecutor, MutationExecutorPostInsert and
|
||||
* MutationExecutorPostInsertSingleTable variants
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class MutationExecutorPostInsertSingleTable implements MutationExecutor, JdbcValueBindingsImpl.JdbcValueDescriptorAccess {
|
||||
private final EntityMutationTarget mutationTarget;
|
||||
private final SharedSessionContractImplementor session;
|
||||
private final PreparableMutationOperation operation;
|
||||
private final PreparedStatementDetails identityInsertStatementDetails;
|
||||
|
||||
private final JdbcValueBindingsImpl valueBindings;
|
||||
|
||||
public MutationExecutorPostInsertSingleTable(
|
||||
EntityMutationOperationGroup mutationOperationGroup,
|
||||
SharedSessionContractImplementor session) {
|
||||
this.mutationTarget = mutationOperationGroup.getMutationTarget();
|
||||
this.session = session;
|
||||
|
||||
assert mutationOperationGroup.getNumberOfOperations() == 1;
|
||||
|
||||
this.operation = (PreparableMutationOperation) mutationOperationGroup.getOperation( mutationTarget.getIdentifierTableName() );
|
||||
this.identityInsertStatementDetails = identityPreparation( operation, session );
|
||||
|
||||
this.valueBindings = new JdbcValueBindingsImpl(
|
||||
MutationType.INSERT,
|
||||
mutationTarget,
|
||||
this,
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcValueDescriptor resolveValueDescriptor(String tableName, String columnName, ParameterUsage usage) {
|
||||
assert identityInsertStatementDetails.getMutatingTableDetails().getTableName().equals( tableName );
|
||||
return operation.findValueDescriptor( columnName, usage );
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcValueBindings getJdbcValueBindings() {
|
||||
return valueBindings;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatementDetails getPreparedStatementDetails(String tableName) {
|
||||
if ( mutationTarget.getIdentifierTableName().equals( tableName ) ) {
|
||||
return identityInsertStatementDetails;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(
|
||||
Object modelReference,
|
||||
ValuesAnalysis valuesAnalysis,
|
||||
TableInclusionChecker inclusionChecker,
|
||||
OperationResultChecker resultChecker,
|
||||
SharedSessionContractImplementor session) {
|
||||
final InsertGeneratedIdentifierDelegate identityHandler = mutationTarget.getIdentityInsertDelegate();
|
||||
final Object id = identityHandler.performInsert( identityInsertStatementDetails, valueBindings, modelReference, session );
|
||||
|
||||
if ( MODEL_MUTATION_LOGGER.isTraceEnabled() ) {
|
||||
MODEL_MUTATION_LOGGER.tracef(
|
||||
"Post-insert generated value : `%s` (%s)",
|
||||
id,
|
||||
mutationTarget.getNavigableRole().getFullPath()
|
||||
);
|
||||
}
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
identityInsertStatementDetails.releaseStatement( session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(
|
||||
Locale.ROOT,
|
||||
"MutationExecutorPostInsertSingleTable(`%s`)",
|
||||
mutationTarget.getNavigableRole().getFullPath()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@ package org.hibernate.engine.jdbc.mutation.internal;
|
|||
import org.hibernate.engine.jdbc.mutation.OperationResultChecker;
|
||||
import org.hibernate.engine.jdbc.mutation.TableInclusionChecker;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.sql.model.PreparableMutationOperation;
|
||||
import org.hibernate.sql.model.ValuesAnalysis;
|
||||
|
||||
|
@ -17,12 +19,15 @@ import org.hibernate.sql.model.ValuesAnalysis;
|
|||
*/
|
||||
public class MutationExecutorSingleNonBatched extends AbstractSingleMutationExecutor {
|
||||
private final PreparedStatementGroupSingleTable statementGroup;
|
||||
private final GeneratedValuesMutationDelegate generatedValuesDelegate;
|
||||
|
||||
public MutationExecutorSingleNonBatched(
|
||||
PreparableMutationOperation mutationOperation,
|
||||
GeneratedValuesMutationDelegate generatedValuesDelegate,
|
||||
SharedSessionContractImplementor session) {
|
||||
super( mutationOperation, session );
|
||||
this.statementGroup = new PreparedStatementGroupSingleTable( mutationOperation, session );
|
||||
this.generatedValuesDelegate = generatedValuesDelegate;
|
||||
this.statementGroup = new PreparedStatementGroupSingleTable( mutationOperation, generatedValuesDelegate, session );
|
||||
prepareForNonBatchedWork( null, session );
|
||||
}
|
||||
|
||||
|
@ -32,18 +37,31 @@ public class MutationExecutorSingleNonBatched extends AbstractSingleMutationExec
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void performNonBatchedOperations(
|
||||
protected GeneratedValues performNonBatchedOperations(
|
||||
Object modelReference,
|
||||
ValuesAnalysis valuesAnalysis,
|
||||
TableInclusionChecker inclusionChecker,
|
||||
OperationResultChecker resultChecker,
|
||||
SharedSessionContractImplementor session) {
|
||||
performNonBatchedMutation(
|
||||
statementGroup.getSingleStatementDetails(),
|
||||
getJdbcValueBindings(),
|
||||
inclusionChecker,
|
||||
resultChecker,
|
||||
session
|
||||
);
|
||||
if ( generatedValuesDelegate != null ) {
|
||||
return generatedValuesDelegate.performMutation(
|
||||
statementGroup.getSingleStatementDetails(),
|
||||
getJdbcValueBindings(),
|
||||
modelReference,
|
||||
session
|
||||
);
|
||||
}
|
||||
else {
|
||||
performNonBatchedMutation(
|
||||
statementGroup.getSingleStatementDetails(),
|
||||
null,
|
||||
getJdbcValueBindings(),
|
||||
inclusionChecker,
|
||||
resultChecker,
|
||||
session
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -22,8 +22,13 @@ import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
|
|||
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementGroup;
|
||||
import org.hibernate.engine.jdbc.mutation.spi.BatchKeyAccess;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
import org.hibernate.sql.model.EntityMutationOperationGroup;
|
||||
import org.hibernate.sql.model.MutationOperation;
|
||||
import org.hibernate.sql.model.MutationOperationGroup;
|
||||
import org.hibernate.sql.model.MutationType;
|
||||
import org.hibernate.sql.model.PreparableMutationOperation;
|
||||
import org.hibernate.sql.model.SelfExecutingUpdateOperation;
|
||||
import org.hibernate.sql.model.TableMapping;
|
||||
|
@ -49,6 +54,7 @@ public class MutationExecutorStandard extends AbstractMutationExecutor implement
|
|||
* Any non-batched JDBC statements
|
||||
*/
|
||||
private final PreparedStatementGroup nonBatchedStatementGroup;
|
||||
private final GeneratedValuesMutationDelegate generatedValuesDelegate;
|
||||
|
||||
/**
|
||||
* Operations which handle their own execution
|
||||
|
@ -66,6 +72,9 @@ public class MutationExecutorStandard extends AbstractMutationExecutor implement
|
|||
int batchSize,
|
||||
SharedSessionContractImplementor session) {
|
||||
this.mutationOperationGroup = mutationOperationGroup;
|
||||
this.generatedValuesDelegate = mutationOperationGroup.asEntityMutationOperationGroup() != null ?
|
||||
mutationOperationGroup.asEntityMutationOperationGroup().getMutationDelegate() :
|
||||
null;
|
||||
|
||||
final BatchKey batchKey = batchKeySupplier.getBatchKey();
|
||||
|
||||
|
@ -82,11 +91,10 @@ public class MutationExecutorStandard extends AbstractMutationExecutor implement
|
|||
for ( int i = mutationOperationGroup.getNumberOfOperations() - 1; i >= 0; i-- ) {
|
||||
final MutationOperation operation = mutationOperationGroup.getOperation( i );
|
||||
if ( operation instanceof SelfExecutingUpdateOperation ) {
|
||||
final SelfExecutingUpdateOperation selfExecutingMutation = (SelfExecutingUpdateOperation) operation;
|
||||
if ( selfExecutingMutations == null ) {
|
||||
selfExecutingMutations = new ArrayList<>();
|
||||
}
|
||||
selfExecutingMutations.add( 0, selfExecutingMutation );
|
||||
selfExecutingMutations.add( 0, ( (SelfExecutingUpdateOperation) operation ) );
|
||||
}
|
||||
else {
|
||||
final PreparableMutationOperation preparableMutationOperation = (PreparableMutationOperation) operation;
|
||||
|
@ -126,6 +134,7 @@ public class MutationExecutorStandard extends AbstractMutationExecutor implement
|
|||
this.batch = null;
|
||||
}
|
||||
else {
|
||||
assert generatedValuesDelegate == null : "Unsupported batched mutation for entity target with generated values delegate";
|
||||
final List<PreparableMutationOperation> batchedMutationsRef = batchedJdbcMutations;
|
||||
this.batch = session.getJdbcCoordinator().getBatch(
|
||||
batchKey,
|
||||
|
@ -133,6 +142,7 @@ public class MutationExecutorStandard extends AbstractMutationExecutor implement
|
|||
() -> ModelMutationHelper.toPreparedStatementGroup(
|
||||
mutationOperationGroup.getMutationType(),
|
||||
mutationOperationGroup.getMutationTarget(),
|
||||
null,
|
||||
batchedMutationsRef,
|
||||
session
|
||||
)
|
||||
|
@ -143,6 +153,7 @@ public class MutationExecutorStandard extends AbstractMutationExecutor implement
|
|||
this.nonBatchedStatementGroup = ModelMutationHelper.toPreparedStatementGroup(
|
||||
mutationOperationGroup.getMutationType(),
|
||||
mutationOperationGroup.getMutationTarget(),
|
||||
generatedValuesDelegate,
|
||||
nonBatchedJdbcMutations,
|
||||
session
|
||||
);
|
||||
|
@ -202,22 +213,59 @@ public class MutationExecutorStandard extends AbstractMutationExecutor implement
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void performNonBatchedOperations(
|
||||
protected GeneratedValues performNonBatchedOperations(
|
||||
Object modelReference,
|
||||
ValuesAnalysis valuesAnalysis,
|
||||
TableInclusionChecker inclusionChecker,
|
||||
OperationResultChecker resultChecker,
|
||||
SharedSessionContractImplementor session) {
|
||||
if ( nonBatchedStatementGroup == null || nonBatchedStatementGroup.getNumberOfStatements() <= 0 ) {
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
nonBatchedStatementGroup.forEachStatement( (tableName, statementDetails) -> performNonBatchedMutation(
|
||||
statementDetails,
|
||||
valueBindings,
|
||||
inclusionChecker,
|
||||
resultChecker,
|
||||
session
|
||||
) );
|
||||
final GeneratedValues generatedValues;
|
||||
if ( generatedValuesDelegate != null ) {
|
||||
final EntityMutationOperationGroup entityGroup = mutationOperationGroup.asEntityMutationOperationGroup();
|
||||
final EntityMutationTarget entityTarget = entityGroup.getMutationTarget();
|
||||
final PreparedStatementDetails details = nonBatchedStatementGroup.getPreparedStatementDetails(
|
||||
entityTarget.getIdentifierTableName()
|
||||
);
|
||||
generatedValues = generatedValuesDelegate.performMutation(
|
||||
details,
|
||||
valueBindings,
|
||||
modelReference,
|
||||
session
|
||||
);
|
||||
|
||||
final Object id = entityGroup.getMutationType() == MutationType.INSERT && details.getMutatingTableDetails().isIdentifierTable() ?
|
||||
generatedValues.getGeneratedValue( entityTarget.getTargetPart().getIdentifierMapping() ) :
|
||||
null;
|
||||
nonBatchedStatementGroup.forEachStatement( (tableName, statementDetails) -> {
|
||||
if ( !statementDetails.getMutatingTableDetails().isIdentifierTable() ) {
|
||||
performNonBatchedMutation(
|
||||
statementDetails,
|
||||
id,
|
||||
valueBindings,
|
||||
inclusionChecker,
|
||||
resultChecker,
|
||||
session
|
||||
);
|
||||
}
|
||||
} );
|
||||
}
|
||||
else {
|
||||
generatedValues = null;
|
||||
nonBatchedStatementGroup.forEachStatement( (tableName, statementDetails) -> performNonBatchedMutation(
|
||||
statementDetails,
|
||||
null,
|
||||
valueBindings,
|
||||
inclusionChecker,
|
||||
resultChecker,
|
||||
session
|
||||
) );
|
||||
}
|
||||
|
||||
return generatedValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.util.function.Predicate;
|
|||
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
|
||||
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementGroup;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.sql.model.PreparableMutationOperation;
|
||||
import org.hibernate.sql.model.TableMapping;
|
||||
|
||||
|
@ -30,8 +31,15 @@ public class PreparedStatementGroupSingleTable implements PreparedStatementGroup
|
|||
public PreparedStatementGroupSingleTable(
|
||||
PreparableMutationOperation jdbcMutation,
|
||||
SharedSessionContractImplementor session) {
|
||||
this( jdbcMutation, null, session );
|
||||
}
|
||||
|
||||
public PreparedStatementGroupSingleTable(
|
||||
PreparableMutationOperation jdbcMutation,
|
||||
GeneratedValuesMutationDelegate delegate,
|
||||
SharedSessionContractImplementor session) {
|
||||
this.jdbcMutation = jdbcMutation;
|
||||
this.statementDetails = ModelMutationHelper.standardPreparation( jdbcMutation, session );
|
||||
this.statementDetails = ModelMutationHelper.standardPreparation( jdbcMutation, delegate, session );
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ import org.hibernate.engine.jdbc.mutation.group.PreparedStatementGroup;
|
|||
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
||||
import org.hibernate.engine.jdbc.spi.MutationStatementPreparer;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.sql.model.MutationTarget;
|
||||
import org.hibernate.sql.model.MutationType;
|
||||
import org.hibernate.sql.model.PreparableMutationOperation;
|
||||
|
@ -46,6 +46,7 @@ public class PreparedStatementGroupStandard implements PreparedStatementGroup {
|
|||
public PreparedStatementGroupStandard(
|
||||
MutationType mutationType,
|
||||
MutationTarget<?> mutationTarget,
|
||||
GeneratedValuesMutationDelegate generatedValuesDelegate,
|
||||
List<PreparableMutationOperation> jdbcMutations,
|
||||
SharedSessionContractImplementor session) {
|
||||
this.mutationType = mutationType;
|
||||
|
@ -54,7 +55,7 @@ public class PreparedStatementGroupStandard implements PreparedStatementGroup {
|
|||
|
||||
this.session = session;
|
||||
|
||||
this.statementMap = createStatementDetailsMap( jdbcMutations, mutationType, mutationTarget, session );
|
||||
this.statementMap = createStatementDetailsMap( jdbcMutations, mutationType, generatedValuesDelegate, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -112,8 +113,7 @@ public class PreparedStatementGroupStandard implements PreparedStatementGroup {
|
|||
|
||||
private static PreparedStatementDetails createPreparedStatementDetails(
|
||||
PreparableMutationOperation jdbcMutation,
|
||||
MutationType mutationType,
|
||||
MutationTarget<?> mutationTarget,
|
||||
GeneratedValuesMutationDelegate generatedValuesDelegate,
|
||||
SharedSessionContractImplementor session) {
|
||||
final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
|
||||
final MutationStatementPreparer statementPreparer = jdbcCoordinator.getMutationStatementPreparer();
|
||||
|
@ -121,11 +121,8 @@ public class PreparedStatementGroupStandard implements PreparedStatementGroup {
|
|||
final TableMapping tableDetails = jdbcMutation.getTableDetails();
|
||||
|
||||
final Supplier<PreparedStatement> jdbcStatementCreator;
|
||||
if ( mutationType == MutationType.INSERT
|
||||
&& mutationTarget instanceof EntityMutationTarget
|
||||
&& ( (EntityMutationTarget) mutationTarget ).getIdentityInsertDelegate() != null
|
||||
&& tableDetails.getTableName().equals( mutationTarget.getIdentifierTableName() ) ) {
|
||||
jdbcStatementCreator = () -> ( (EntityMutationTarget) mutationTarget ).getIdentityInsertDelegate().prepareStatement(
|
||||
if ( tableDetails.isIdentifierTable() && generatedValuesDelegate != null ) {
|
||||
jdbcStatementCreator = () -> generatedValuesDelegate.prepareStatement(
|
||||
jdbcMutation.getSqlString(),
|
||||
session
|
||||
);
|
||||
|
@ -152,26 +149,26 @@ public class PreparedStatementGroupStandard implements PreparedStatementGroup {
|
|||
}
|
||||
|
||||
|
||||
private SortedMap<String, PreparedStatementDetails> createStatementDetailsMap(
|
||||
private static SortedMap<String, PreparedStatementDetails> createStatementDetailsMap(
|
||||
List<PreparableMutationOperation> jdbcMutations,
|
||||
MutationType mutationType,
|
||||
MutationTarget<?> mutationTarget,
|
||||
GeneratedValuesMutationDelegate generatedValuesDelegate,
|
||||
SharedSessionContractImplementor session) {
|
||||
final Comparator<String> comparator;
|
||||
|
||||
if ( mutationType == MutationType.DELETE ) {
|
||||
// reverse order
|
||||
comparator = Comparator.comparingInt( (tableName) -> {
|
||||
final TableMapping tableMapping = locateTableMapping( tableName );
|
||||
final TableMapping tableMapping = locateTableMapping( jdbcMutations, tableName );
|
||||
if ( tableMapping == null ) {
|
||||
return -1;
|
||||
}
|
||||
return this.jdbcMutations.size() - tableMapping.getRelativePosition();
|
||||
return jdbcMutations.size() - tableMapping.getRelativePosition();
|
||||
} );
|
||||
}
|
||||
else {
|
||||
comparator = Comparator.comparingInt( (tableName) -> {
|
||||
final TableMapping tableMapping = locateTableMapping( tableName );
|
||||
final TableMapping tableMapping = locateTableMapping( jdbcMutations, tableName );
|
||||
if ( tableMapping == null ) {
|
||||
return -1;
|
||||
}
|
||||
|
@ -181,20 +178,19 @@ public class PreparedStatementGroupStandard implements PreparedStatementGroup {
|
|||
|
||||
final TreeMap<String, PreparedStatementDetails> map = new TreeMap<>( comparator );
|
||||
|
||||
for ( int i = 0; i < jdbcMutations.size(); i++ ) {
|
||||
final PreparableMutationOperation jdbcMutation = jdbcMutations.get( i );
|
||||
for ( final PreparableMutationOperation jdbcMutation : jdbcMutations ) {
|
||||
map.put(
|
||||
jdbcMutation.getTableDetails().getTableName(),
|
||||
createPreparedStatementDetails( jdbcMutation, mutationType, mutationTarget, session )
|
||||
createPreparedStatementDetails( jdbcMutation, generatedValuesDelegate, session )
|
||||
);
|
||||
}
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
private TableMapping locateTableMapping(String name) {
|
||||
for ( int i = 0; i < jdbcMutations.size(); i++ ) {
|
||||
final TableMapping tableMapping = jdbcMutations.get( i ).getTableDetails();
|
||||
private static TableMapping locateTableMapping(List<PreparableMutationOperation> jdbcMutations, String name) {
|
||||
for ( final PreparableMutationOperation jdbcMutation : jdbcMutations ) {
|
||||
final TableMapping tableMapping = jdbcMutation.getTableDetails();
|
||||
if ( tableMapping.getTableName().equals( name ) ) {
|
||||
return tableMapping;
|
||||
}
|
||||
|
|
|
@ -15,10 +15,8 @@ import org.hibernate.engine.jdbc.mutation.spi.BatchKeyAccess;
|
|||
import org.hibernate.engine.jdbc.mutation.spi.MutationExecutorService;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.util.config.ConfigurationHelper;
|
||||
import org.hibernate.sql.model.EntityMutationOperationGroup;
|
||||
import org.hibernate.sql.model.MutationOperation;
|
||||
import org.hibernate.sql.model.MutationOperationGroup;
|
||||
import org.hibernate.sql.model.MutationType;
|
||||
import org.hibernate.sql.model.PreparableMutationOperation;
|
||||
import org.hibernate.sql.model.SelfExecutingUpdateOperation;
|
||||
|
||||
|
@ -53,20 +51,7 @@ public class StandardMutationExecutorService implements MutationExecutorService
|
|||
? globalBatchSize
|
||||
: sessionBatchSize;
|
||||
|
||||
final int numberOfOperations = operationGroup.getNumberOfOperations();
|
||||
final MutationType mutationType = operationGroup.getMutationType();
|
||||
final EntityMutationOperationGroup entityMutationOperationGroup = operationGroup.asEntityMutationOperationGroup();
|
||||
|
||||
if ( mutationType == MutationType.INSERT
|
||||
&& entityMutationOperationGroup != null
|
||||
&& entityMutationOperationGroup.getMutationTarget().getIdentityInsertDelegate() != null ) {
|
||||
if ( numberOfOperations > 1 ) {
|
||||
return new MutationExecutorPostInsert( entityMutationOperationGroup, session );
|
||||
}
|
||||
return new MutationExecutorPostInsertSingleTable( entityMutationOperationGroup, session );
|
||||
}
|
||||
|
||||
if ( numberOfOperations == 1 ) {
|
||||
if ( operationGroup.getNumberOfOperations() == 1 ) {
|
||||
final MutationOperation singleOperation = operationGroup.getSingleOperation();
|
||||
if ( singleOperation instanceof SelfExecutingUpdateOperation ) {
|
||||
return new MutationExecutorSingleSelfExecuting( (SelfExecutingUpdateOperation) singleOperation, session );
|
||||
|
@ -78,7 +63,13 @@ public class StandardMutationExecutorService implements MutationExecutorService
|
|||
return new MutationExecutorSingleBatched( jdbcOperation, batchKey, batchSizeToUse, session );
|
||||
}
|
||||
|
||||
return new MutationExecutorSingleNonBatched( jdbcOperation, session );
|
||||
return new MutationExecutorSingleNonBatched(
|
||||
jdbcOperation,
|
||||
operationGroup.asEntityMutationOperationGroup() != null ?
|
||||
operationGroup.asEntityMutationOperationGroup().getMutationDelegate() :
|
||||
null,
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
return new MutationExecutorStandard( operationGroup, batchKeySupplier, batchSizeToUse, session );
|
||||
|
|
|
@ -744,6 +744,9 @@ public interface PersistenceContext {
|
|||
|
||||
void replaceDelayedEntityIdentityInsertKeys(EntityKey oldKey, Object generatedId);
|
||||
|
||||
@Internal
|
||||
void replaceEntityEntryRowId(Object entity, Object rowId);
|
||||
|
||||
/**
|
||||
* Add a child/parent relation to cache for cascading op
|
||||
*
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.hibernate.id.insert.UniqueKeySelectingDelegate;
|
|||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
import static org.hibernate.generator.internal.NaturalIdHelper.getNaturalIdPropertyNames;
|
||||
import static org.hibernate.generator.values.internal.GeneratedValuesHelper.noCustomSql;
|
||||
|
||||
/**
|
||||
* A generator which produces a new value by actually going ahead and writing a row to the
|
||||
|
@ -116,16 +117,21 @@ public interface OnExecutionGenerator extends Generator {
|
|||
*/
|
||||
@Incubating
|
||||
default InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate(PostInsertIdentityPersister persister) {
|
||||
Dialect dialect = persister.getFactory().getJdbcServices().getDialect();
|
||||
if ( dialect.supportsInsertReturningGeneratedKeys() ) {
|
||||
return new GetGeneratedKeysDelegate( persister, dialect, false );
|
||||
final Dialect dialect = persister.getFactory().getJdbcServices().getDialect();
|
||||
if ( dialect.supportsInsertReturningGeneratedKeys()
|
||||
&& persister.getFactory().getSessionFactoryOptions().isGetGeneratedKeysEnabled() ) {
|
||||
return new GetGeneratedKeysDelegate( persister, false, EventType.INSERT );
|
||||
}
|
||||
else if ( dialect.supportsInsertReturning() ) {
|
||||
return new InsertReturningDelegate( persister, dialect );
|
||||
else if ( dialect.supportsInsertReturning() && noCustomSql( persister, EventType.INSERT ) ) {
|
||||
return new InsertReturningDelegate( persister, EventType.INSERT );
|
||||
}
|
||||
else {
|
||||
// let's just hope the entity has a @NaturalId!
|
||||
return new UniqueKeySelectingDelegate( persister, dialect, getUniqueKeyPropertyNames( persister ) );
|
||||
return new UniqueKeySelectingDelegate(
|
||||
persister,
|
||||
getUniqueKeyPropertyNames( persister ),
|
||||
EventType.INSERT
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.generator.values;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.generator.values.internal.GeneratedValuesHelper;
|
||||
import org.hibernate.generator.values.internal.GeneratedValuesMappingProducer;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
|
||||
|
||||
/**
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
public abstract class AbstractGeneratedValuesMutationDelegate implements GeneratedValuesMutationDelegate {
|
||||
protected final EntityPersister persister;
|
||||
private final EventType timing;
|
||||
private final boolean supportsArbitraryValues;
|
||||
private final boolean supportsRowId;
|
||||
protected final GeneratedValuesMappingProducer jdbcValuesMappingProducer;
|
||||
|
||||
public AbstractGeneratedValuesMutationDelegate(EntityPersister persister, EventType timing) {
|
||||
this( persister, timing, true, true );
|
||||
}
|
||||
|
||||
public AbstractGeneratedValuesMutationDelegate(
|
||||
EntityPersister persister,
|
||||
EventType timing,
|
||||
boolean supportsArbitraryValues,
|
||||
boolean supportsRowId) {
|
||||
this.persister = persister;
|
||||
this.timing = timing;
|
||||
this.supportsArbitraryValues = supportsArbitraryValues;
|
||||
this.supportsRowId = supportsRowId;
|
||||
this.jdbcValuesMappingProducer = GeneratedValuesHelper.createMappingProducer(
|
||||
persister,
|
||||
timing,
|
||||
supportsArbitraryValues,
|
||||
supportsRowId
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventType getTiming() {
|
||||
return timing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean supportsArbitraryValues() {
|
||||
return supportsArbitraryValues;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean supportsRowId() {
|
||||
return supportsRowId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JdbcValuesMappingProducer getGeneratedValuesMappingProducer() {
|
||||
return jdbcValuesMappingProducer;
|
||||
}
|
||||
|
||||
protected Dialect dialect() {
|
||||
return persister.getFactory().getJdbcServices().getDialect();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.generator.values;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||
import org.hibernate.query.results.DomainResultCreationStateImpl;
|
||||
import org.hibernate.query.results.ResultBuilder;
|
||||
import org.hibernate.query.results.ResultsHelper;
|
||||
import org.hibernate.query.results.dynamic.DynamicFetchBuilderLegacy;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.basic.BasicResult;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
|
||||
|
||||
import static org.hibernate.generator.values.internal.GeneratedValuesHelper.getActualGeneratedModelPart;
|
||||
import static org.hibernate.query.results.ResultsHelper.impl;
|
||||
import static org.hibernate.query.results.ResultsHelper.jdbcPositionToValuesArrayPosition;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link ResultBuilder} for retrieving generated basic values.
|
||||
*
|
||||
* @author Marco Belladelli
|
||||
* @see GeneratedValuesMutationDelegate
|
||||
*/
|
||||
public class GeneratedValueBasicResultBuilder implements ResultBuilder {
|
||||
private final NavigablePath navigablePath;
|
||||
private final BasicValuedModelPart modelPart;
|
||||
private final Integer valuesArrayPosition;
|
||||
private final TableGroup tableGroup;
|
||||
|
||||
public GeneratedValueBasicResultBuilder(
|
||||
NavigablePath navigablePath,
|
||||
BasicValuedModelPart modelPart,
|
||||
TableGroup tableGroup,
|
||||
Integer valuesArrayPosition) {
|
||||
this.navigablePath = navigablePath;
|
||||
this.modelPart = modelPart;
|
||||
this.valuesArrayPosition = valuesArrayPosition;
|
||||
this.tableGroup = tableGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getJavaType() {
|
||||
return modelPart.getExpressibleJavaType().getJavaTypeClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultBuilder cacheKeyInstance() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicResult<?> buildResult(
|
||||
JdbcValuesMetadata jdbcResultsMetadata,
|
||||
int resultPosition,
|
||||
BiFunction<String, String, DynamicFetchBuilderLegacy> legacyFetchResolver,
|
||||
DomainResultCreationState domainResultCreationState) {
|
||||
final DomainResultCreationStateImpl creationStateImpl = impl( domainResultCreationState );
|
||||
|
||||
final TableGroup tableGroup = creationStateImpl.getFromClauseAccess().resolveTableGroup(
|
||||
navigablePath.getParent(),
|
||||
path -> this.tableGroup
|
||||
);
|
||||
final TableReference tableReference = tableGroup.resolveTableReference(
|
||||
navigablePath,
|
||||
modelPart,
|
||||
"t"
|
||||
);
|
||||
|
||||
final int position = valuesArrayPosition == null ?
|
||||
columnIndex( jdbcResultsMetadata, modelPart ) :
|
||||
valuesArrayPosition;
|
||||
final SqlSelection sqlSelection = creationStateImpl.resolveSqlSelection(
|
||||
ResultsHelper.resolveSqlExpression(
|
||||
creationStateImpl,
|
||||
tableReference,
|
||||
modelPart,
|
||||
position
|
||||
),
|
||||
modelPart.getJdbcMapping().getJdbcJavaType(),
|
||||
null,
|
||||
creationStateImpl.getSessionFactory().getTypeConfiguration()
|
||||
);
|
||||
|
||||
return new BasicResult<>(
|
||||
sqlSelection.getValuesArrayPosition(),
|
||||
null,
|
||||
modelPart.getJdbcMapping()
|
||||
);
|
||||
}
|
||||
|
||||
public BasicValuedModelPart getModelPart() {
|
||||
return modelPart;
|
||||
}
|
||||
|
||||
private static int columnIndex(JdbcValuesMetadata jdbcResultsMetadata, BasicValuedModelPart modelPart) {
|
||||
try {
|
||||
return jdbcPositionToValuesArrayPosition( jdbcResultsMetadata.resolveColumnPosition(
|
||||
getActualGeneratedModelPart( modelPart ).getSelectionExpression()
|
||||
) );
|
||||
}
|
||||
catch (Exception e) {
|
||||
if ( modelPart.isEntityIdentifierMapping() ) {
|
||||
// Default to the first position for entity identifiers
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.generator.values;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
|
||||
/**
|
||||
* A container for {@linkplain org.hibernate.generator.OnExecutionGenerator database generated values}
|
||||
* retrieved by the mutation operation. The values are stored relative to the {@linkplain ModelPart property}
|
||||
* they represent.
|
||||
*
|
||||
* @author Marco Belladelli
|
||||
* @see org.hibernate.generator.OnExecutionGenerator
|
||||
*/
|
||||
public interface GeneratedValues {
|
||||
/**
|
||||
* Register a generated value for the corresponding {@link ModelPart}
|
||||
*/
|
||||
void addGeneratedValue(ModelPart modelPart, Object value);
|
||||
|
||||
/**
|
||||
* Retrieve a generated value for the requested {@link ModelPart}.
|
||||
*/
|
||||
Object getGeneratedValue(ModelPart modelPart);
|
||||
|
||||
/**
|
||||
* Retrieves a list of generated values corresponding to the list of requested {@link ModelPart}s.
|
||||
* Ensures the order of the values in the returned list corresponds to the input properties.
|
||||
*/
|
||||
List<Object> getGeneratedValues(List<? extends ModelPart> modelParts);
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.generator.values;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
|
||||
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
|
||||
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.jdbc.Expectation;
|
||||
import org.hibernate.sql.model.ast.builder.TableMutationBuilder;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
|
||||
|
||||
/**
|
||||
* Each implementation defines a strategy for retrieving values
|
||||
* {@linkplain org.hibernate.generator.OnExecutionGenerator generated by
|
||||
* the database} after execution of a mutation statement.
|
||||
* <p>
|
||||
* An implementation controls:
|
||||
* <ul>
|
||||
* <li>building the SQL mutation statement, and
|
||||
* <li>retrieving the generated values using JDBC.
|
||||
* </ul>
|
||||
*
|
||||
* @author Marco Belladelli
|
||||
* @see org.hibernate.generator.OnExecutionGenerator
|
||||
* @since 6.5
|
||||
*/
|
||||
public interface GeneratedValuesMutationDelegate {
|
||||
/**
|
||||
* Create a {@link TableMutationBuilder} instance used to build table mutations for this delegate.
|
||||
*/
|
||||
TableMutationBuilder<?> createTableMutationBuilder(Expectation expectation, SessionFactoryImplementor sessionFactory);
|
||||
|
||||
/**
|
||||
* Create a {@link PreparedStatement} from the provided {@code sql} based on the delegate needs.
|
||||
*/
|
||||
PreparedStatement prepareStatement(String sql, SharedSessionContractImplementor session);
|
||||
|
||||
/**
|
||||
* Perform the {@code mutation} and extract the database-generated values.
|
||||
*
|
||||
* @see #createTableMutationBuilder
|
||||
*/
|
||||
GeneratedValues performMutation(
|
||||
PreparedStatementDetails statementDetails,
|
||||
JdbcValueBindings valueBindings,
|
||||
Object entity,
|
||||
SharedSessionContractImplementor session);
|
||||
|
||||
/**
|
||||
* Returns the timing this generated values delegate handles.
|
||||
*/
|
||||
EventType getTiming();
|
||||
|
||||
/**
|
||||
* Returns {@code true} when this delegate supports retrieving arbitrary generated values,
|
||||
* or {@code false} when it only supports identifiers.
|
||||
*/
|
||||
boolean supportsArbitraryValues();
|
||||
|
||||
/**
|
||||
* Returns {@code true} when this delegate supports retrieving the {@link org.hibernate.annotations.RowId} value.
|
||||
*/
|
||||
boolean supportsRowId();
|
||||
|
||||
/**
|
||||
* Retrieve the {@linkplain JdbcValuesMappingProducer mapping producer} used to read the generated values.
|
||||
*/
|
||||
JdbcValuesMappingProducer getGeneratedValuesMappingProducer();
|
||||
}
|
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.generator.values.internal;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Internal;
|
||||
import org.hibernate.LockOptions;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.generator.values.GeneratedValueBasicResultBuilder;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.id.IdentifierGeneratorHelper;
|
||||
import org.hibernate.id.insert.GetGeneratedKeysDelegate;
|
||||
import org.hibernate.id.insert.InsertReturningDelegate;
|
||||
import org.hibernate.id.insert.UniqueKeySelectingDelegate;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.mutation.EntityTableMapping;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.query.results.TableGroupImpl;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.spi.NavigablePath;
|
||||
import org.hibernate.sql.ast.tree.from.NamedTableReference;
|
||||
import org.hibernate.sql.ast.tree.from.TableGroup;
|
||||
import org.hibernate.sql.exec.internal.BaseExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.model.MutationType;
|
||||
import org.hibernate.sql.model.TableMapping;
|
||||
import org.hibernate.sql.results.internal.ResultsHelper;
|
||||
import org.hibernate.sql.results.internal.RowProcessingStateStandardImpl;
|
||||
import org.hibernate.sql.results.internal.RowTransformerArrayImpl;
|
||||
import org.hibernate.sql.results.jdbc.internal.DirectResultSetAccess;
|
||||
import org.hibernate.sql.results.jdbc.internal.JdbcValuesResultSetImpl;
|
||||
import org.hibernate.sql.results.jdbc.internal.JdbcValuesSourceProcessingStateStandardImpl;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValues;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesSourceProcessingOptions;
|
||||
import org.hibernate.sql.results.spi.ListResultsConsumer;
|
||||
import org.hibernate.sql.results.spi.RowReader;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
|
||||
import static org.hibernate.generator.internal.NaturalIdHelper.getNaturalIdPropertyNames;
|
||||
|
||||
/**
|
||||
* Factory and helper methods for {@link GeneratedValuesMutationDelegate} framework.
|
||||
*
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
@Internal
|
||||
public class GeneratedValuesHelper {
|
||||
private static final CoreMessageLogger LOG = CoreLogging.messageLogger( IdentifierGeneratorHelper.class );
|
||||
|
||||
/**
|
||||
* Reads the {@link EntityPersister#getGeneratedProperties(EventType) generated values}
|
||||
* for the specified {@link ResultSet}.
|
||||
*
|
||||
* @param resultSet The result set from which to extract the generated values
|
||||
* @param persister The entity type which we're reading the generated values for
|
||||
* @param wrapperOptions The session
|
||||
*
|
||||
* @return The generated values
|
||||
*
|
||||
* @throws SQLException Can be thrown while accessing the result set
|
||||
* @throws HibernateException Indicates a problem reading back a generated value
|
||||
*/
|
||||
public static GeneratedValues getGeneratedValues(
|
||||
ResultSet resultSet,
|
||||
EntityPersister persister,
|
||||
EventType timing,
|
||||
WrapperOptions wrapperOptions) throws SQLException {
|
||||
if ( resultSet == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final GeneratedValuesMutationDelegate delegate = persister.getMutationDelegate(
|
||||
timing == EventType.INSERT ? MutationType.INSERT : MutationType.UPDATE
|
||||
);
|
||||
final GeneratedValuesMappingProducer mappingProducer =
|
||||
(GeneratedValuesMappingProducer) delegate.getGeneratedValuesMappingProducer();
|
||||
final List<GeneratedValueBasicResultBuilder> resultBuilders = mappingProducer.getResultBuilders();
|
||||
final List<ModelPart> generatedProperties = new ArrayList<>( resultBuilders.size() );
|
||||
for ( GeneratedValueBasicResultBuilder resultBuilder : resultBuilders ) {
|
||||
generatedProperties.add( resultBuilder.getModelPart() );
|
||||
}
|
||||
|
||||
final GeneratedValuesImpl generatedValues = new GeneratedValuesImpl( generatedProperties );
|
||||
final Object[] results = readGeneratedValues(
|
||||
resultSet,
|
||||
persister,
|
||||
mappingProducer,
|
||||
wrapperOptions.getSession()
|
||||
);
|
||||
|
||||
if ( LOG.isDebugEnabled() ) {
|
||||
LOG.debugf(
|
||||
"Extracted generated values %s: %s",
|
||||
MessageHelper.infoString( persister ),
|
||||
results
|
||||
);
|
||||
}
|
||||
|
||||
for ( int i = 0; i < results.length; i++ ) {
|
||||
generatedValues.addGeneratedValue( generatedProperties.get( i ), results[i] );
|
||||
}
|
||||
|
||||
return generatedValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that reads the generated values from the specified {@link ResultSet}
|
||||
* using the {@link JdbcValuesMappingProducer} provided in input.
|
||||
*
|
||||
* @param resultSet the result set containing the generated values
|
||||
* @param persister the current entity persister
|
||||
* @param mappingProducer the mapping producer to use when reading generated values
|
||||
* @param session the current session
|
||||
*
|
||||
* @return an object array containing the generated values, order is consistent with the generated model parts list
|
||||
*/
|
||||
private static Object[] readGeneratedValues(
|
||||
ResultSet resultSet,
|
||||
EntityPersister persister,
|
||||
JdbcValuesMappingProducer mappingProducer,
|
||||
SharedSessionContractImplementor session) {
|
||||
final ExecutionContext executionContext = new BaseExecutionContext( session );
|
||||
|
||||
final DirectResultSetAccess directResultSetAccess;
|
||||
try {
|
||||
directResultSetAccess = new DirectResultSetAccess(
|
||||
session,
|
||||
(PreparedStatement) resultSet.getStatement(),
|
||||
resultSet
|
||||
);
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw new HibernateException( "Could not retrieve statement from generated values result set", e );
|
||||
}
|
||||
|
||||
final JdbcValues jdbcValues = new JdbcValuesResultSetImpl(
|
||||
directResultSetAccess,
|
||||
null,
|
||||
null,
|
||||
QueryOptions.NONE,
|
||||
mappingProducer.resolve(
|
||||
directResultSetAccess,
|
||||
session.getLoadQueryInfluencers(),
|
||||
session.getSessionFactory()
|
||||
),
|
||||
null,
|
||||
executionContext
|
||||
);
|
||||
|
||||
final JdbcValuesSourceProcessingOptions processingOptions = new JdbcValuesSourceProcessingOptions() {
|
||||
@Override
|
||||
public Object getEffectiveOptionalObject() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEffectiveOptionalEntityName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getEffectiveOptionalId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldReturnProxies() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
final JdbcValuesSourceProcessingStateStandardImpl valuesProcessingState = new JdbcValuesSourceProcessingStateStandardImpl(
|
||||
executionContext,
|
||||
processingOptions
|
||||
);
|
||||
|
||||
final RowReader<Object[]> rowReader = ResultsHelper.createRowReader(
|
||||
executionContext,
|
||||
LockOptions.NONE,
|
||||
RowTransformerArrayImpl.instance(),
|
||||
Object[].class,
|
||||
jdbcValues
|
||||
);
|
||||
|
||||
final RowProcessingStateStandardImpl rowProcessingState = new RowProcessingStateStandardImpl(
|
||||
valuesProcessingState,
|
||||
executionContext,
|
||||
rowReader,
|
||||
jdbcValues
|
||||
);
|
||||
|
||||
final List<Object[]> results = ListResultsConsumer.<Object[]>instance( ListResultsConsumer.UniqueSemantic.NONE )
|
||||
.consume(
|
||||
jdbcValues,
|
||||
session,
|
||||
processingOptions,
|
||||
valuesProcessingState,
|
||||
rowProcessingState,
|
||||
rowReader
|
||||
);
|
||||
|
||||
if ( results.isEmpty() ) {
|
||||
throw new HibernateException(
|
||||
"The database returned no natively generated values : " + persister.getNavigableRole().getFullPath()
|
||||
);
|
||||
}
|
||||
|
||||
return results.get( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method that instantiates a {@link JdbcValuesMappingProducer} so it can be cached by the
|
||||
* {@link GeneratedValuesMutationDelegate delegates} when they are instantiated.
|
||||
*
|
||||
* @param persister the current entity persister
|
||||
* @param timing the timing of the mutation operation
|
||||
* @param supportsArbitraryValues if we should process arbitrary (non-identifier) generated values
|
||||
* @param supportsRowId if we should process {@link org.hibernate.metamodel.mapping.EntityRowIdMapping rowid}s
|
||||
* {@code false} if we should retrieve the index through the column expression
|
||||
*
|
||||
* @return the instantiated jdbc values mapping producer
|
||||
*/
|
||||
public static GeneratedValuesMappingProducer createMappingProducer(
|
||||
EntityPersister persister,
|
||||
EventType timing,
|
||||
boolean supportsArbitraryValues,
|
||||
boolean supportsRowId) {
|
||||
// This is just a mock table group needed to correctly resolve expressions
|
||||
final NavigablePath parentNavigablePath = new NavigablePath( persister.getEntityName() );
|
||||
final TableGroup tableGroup = new TableGroupImpl(
|
||||
parentNavigablePath,
|
||||
null,
|
||||
new NamedTableReference( "t", "t" ),
|
||||
persister
|
||||
);
|
||||
// Create the mapping producer and add all result builders to it
|
||||
final List<? extends ModelPart> generatedProperties = getActualGeneratedModelParts(
|
||||
persister,
|
||||
timing,
|
||||
supportsArbitraryValues,
|
||||
supportsRowId
|
||||
);
|
||||
final GeneratedValuesMappingProducer mappingProducer = new GeneratedValuesMappingProducer();
|
||||
for ( int i = 0; i < generatedProperties.size(); i++ ) {
|
||||
final ModelPart modelPart = generatedProperties.get( i );
|
||||
final BasicValuedModelPart basicModelPart = modelPart.asBasicValuedModelPart();
|
||||
if ( basicModelPart != null ) {
|
||||
final GeneratedValueBasicResultBuilder resultBuilder = new GeneratedValueBasicResultBuilder(
|
||||
parentNavigablePath.append( basicModelPart.getSelectableName() ),
|
||||
basicModelPart,
|
||||
tableGroup,
|
||||
supportsArbitraryValues ? i : null
|
||||
);
|
||||
mappingProducer.addResultBuilder( resultBuilder );
|
||||
}
|
||||
else {
|
||||
throw new UnsupportedOperationException( "Unsupported generated ModelPart: " + modelPart.getPartName() );
|
||||
}
|
||||
}
|
||||
return mappingProducer;
|
||||
}
|
||||
|
||||
public static BasicValuedModelPart getActualGeneratedModelPart(BasicValuedModelPart modelPart) {
|
||||
// Use the root entity descriptor's identifier mapping to get the correct selection
|
||||
// expression since we always retrieve generated values for the root table only
|
||||
return modelPart.isEntityIdentifierMapping() ?
|
||||
modelPart.findContainingEntityMapping()
|
||||
.getRootEntityDescriptor()
|
||||
.getIdentifierMapping()
|
||||
.asBasicValuedModelPart() :
|
||||
modelPart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of {@link ModelPart}s that represent the actual generated values
|
||||
* based on timing and the support flags passed in input.
|
||||
*/
|
||||
private static List<? extends ModelPart> getActualGeneratedModelParts(
|
||||
EntityPersister persister,
|
||||
EventType timing,
|
||||
boolean supportsArbitraryValues,
|
||||
boolean supportsRowId) {
|
||||
if ( timing == EventType.INSERT ) {
|
||||
final List<? extends ModelPart> generatedProperties = supportsArbitraryValues ?
|
||||
persister.getInsertGeneratedProperties() :
|
||||
List.of( persister.getIdentifierMapping() );
|
||||
if ( persister.getRowIdMapping() != null && supportsRowId ) {
|
||||
final List<ModelPart> newList = new ArrayList<>( generatedProperties.size() + 1 );
|
||||
newList.addAll( generatedProperties );
|
||||
newList.add( persister.getRowIdMapping() );
|
||||
return Collections.unmodifiableList( newList );
|
||||
}
|
||||
else {
|
||||
return generatedProperties;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return persister.getUpdateGeneratedProperties();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link GeneratedValuesMutationDelegate delegate} used to retrieve
|
||||
* {@linkplain org.hibernate.generator.OnExecutionGenerator database generated values} on
|
||||
* mutation execution through e.g. {@link Dialect#supportsInsertReturning() insert ... returning}
|
||||
* syntax or the JDBC {@link Dialect#supportsInsertReturningGeneratedKeys() getGeneratedKeys()} API.
|
||||
* <p>
|
||||
* If the current {@link Dialect} doesn't support any of the available delegates this method returns {@code null}.
|
||||
*/
|
||||
public static GeneratedValuesMutationDelegate getGeneratedValuesDelegate(
|
||||
EntityPersister persister,
|
||||
EventType timing) {
|
||||
final boolean hasGeneratedProperties = !persister.getGeneratedProperties( timing ).isEmpty();
|
||||
final boolean hasRowId = timing == EventType.INSERT && persister.getRowIdMapping() != null;
|
||||
final Dialect dialect = persister.getFactory().getJdbcServices().getDialect();
|
||||
|
||||
if ( hasRowId && dialect.supportsInsertReturning() && dialect.supportsInsertReturningRowId()
|
||||
&& noCustomSql( persister, timing ) ) {
|
||||
// Special case for RowId on INSERT, since GetGeneratedKeysDelegate doesn't support it
|
||||
// make InsertReturningDelegate the preferred method if the dialect supports it
|
||||
return new InsertReturningDelegate( persister, timing );
|
||||
}
|
||||
|
||||
if ( !hasGeneratedProperties ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( dialect.supportsInsertReturningGeneratedKeys()
|
||||
&& persister.getFactory().getSessionFactoryOptions().isGetGeneratedKeysEnabled() ) {
|
||||
return new GetGeneratedKeysDelegate( persister, false, timing );
|
||||
}
|
||||
else if ( supportsReturning( dialect, timing ) && noCustomSql( persister, timing ) ) {
|
||||
return new InsertReturningDelegate( persister, timing );
|
||||
}
|
||||
else if ( timing == EventType.INSERT && persister.getNaturalIdentifierProperties() != null
|
||||
&& !persister.getEntityMetamodel().isNaturalIdentifierInsertGenerated() ) {
|
||||
return new UniqueKeySelectingDelegate(
|
||||
persister,
|
||||
getNaturalIdPropertyNames( persister ),
|
||||
timing
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean supportsReturning(Dialect dialect, EventType timing) {
|
||||
return timing == EventType.INSERT ? dialect.supportsInsertReturning() : dialect.supportsUpdateReturning();
|
||||
}
|
||||
|
||||
public static boolean noCustomSql(EntityPersister persister, EventType timing) {
|
||||
final EntityTableMapping identifierTable = persister.getIdentifierTableMapping();
|
||||
final TableMapping.MutationDetails mutationDetails = timing == EventType.INSERT ?
|
||||
identifierTable.getInsertDetails() :
|
||||
identifierTable.getUpdateDetails();
|
||||
return mutationDetails.getCustomSql() == null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.generator.values.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
|
||||
/**
|
||||
* Standard implementation for {@link GeneratedValues} using {@link IdentityHashMap}.
|
||||
*
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
public class GeneratedValuesImpl implements GeneratedValues {
|
||||
private final Map<ModelPart, Object> generatedValuesMap;
|
||||
|
||||
public GeneratedValuesImpl(List<? extends ModelPart> generatedProperties) {
|
||||
this.generatedValuesMap = new IdentityHashMap<>( generatedProperties.size() );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addGeneratedValue(ModelPart modelPart, Object value) {
|
||||
generatedValuesMap.put( modelPart, value );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGeneratedValue(ModelPart modelPart) {
|
||||
return generatedValuesMap.get( modelPart );
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Object> getGeneratedValues(List<? extends ModelPart> modelParts) {
|
||||
if ( CollectionHelper.isEmpty( modelParts ) ) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
final List<Object> generatedValues = new ArrayList<>( modelParts.size() );
|
||||
for ( ModelPart modelPart : modelParts ) {
|
||||
assert generatedValuesMap.containsKey( modelPart );
|
||||
generatedValues.add( generatedValuesMap.get( modelPart ) );
|
||||
}
|
||||
|
||||
return generatedValues;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.generator.values.internal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.engine.spi.LoadQueryInfluencers;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.generator.values.GeneratedValueBasicResultBuilder;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.query.results.DomainResultCreationStateImpl;
|
||||
import org.hibernate.query.results.JdbcValuesMappingImpl;
|
||||
import org.hibernate.query.results.ResultBuilder;
|
||||
import org.hibernate.sql.ast.spi.SqlSelection;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMapping;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMappingProducer;
|
||||
import org.hibernate.sql.results.jdbc.spi.JdbcValuesMetadata;
|
||||
|
||||
/**
|
||||
* Simple implementation of {@link JdbcValuesMappingProducer} used when reading
|
||||
* generated values from a mutation statement.
|
||||
*
|
||||
* @author Marco Belladelli
|
||||
* @see GeneratedValuesMutationDelegate
|
||||
* @see GeneratedValuesHelper#getGeneratedValues
|
||||
*/
|
||||
public class GeneratedValuesMappingProducer implements JdbcValuesMappingProducer {
|
||||
private final List<GeneratedValueBasicResultBuilder> resultBuilders = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public JdbcValuesMapping resolve(
|
||||
JdbcValuesMetadata jdbcResultsMetadata,
|
||||
LoadQueryInfluencers loadQueryInfluencers,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
final int numberOfResults = resultBuilders.size();
|
||||
final int rowSize = jdbcResultsMetadata.getColumnCount();
|
||||
|
||||
final List<SqlSelection> sqlSelections = new ArrayList<>( rowSize );
|
||||
final List<DomainResult<?>> domainResults = new ArrayList<>( numberOfResults );
|
||||
|
||||
final DomainResultCreationStateImpl creationState = new DomainResultCreationStateImpl(
|
||||
null,
|
||||
jdbcResultsMetadata,
|
||||
null,
|
||||
sqlSelections::add,
|
||||
loadQueryInfluencers,
|
||||
sessionFactory
|
||||
);
|
||||
|
||||
for ( int i = 0; i < numberOfResults; i++ ) {
|
||||
final ResultBuilder resultBuilder = resultBuilders.get( i );
|
||||
final DomainResult<?> domainResult = resultBuilder.buildResult(
|
||||
jdbcResultsMetadata,
|
||||
domainResults.size(),
|
||||
creationState.getLegacyFetchResolver()::resolve,
|
||||
creationState
|
||||
);
|
||||
|
||||
if ( domainResult.containsAnyNonScalarResults() ) {
|
||||
creationState.disallowPositionalSelections();
|
||||
}
|
||||
|
||||
domainResults.add( domainResult );
|
||||
}
|
||||
|
||||
return new JdbcValuesMappingImpl(
|
||||
sqlSelections,
|
||||
domainResults,
|
||||
rowSize,
|
||||
creationState.getRegisteredLockModes()
|
||||
);
|
||||
}
|
||||
|
||||
public void addResultBuilder(GeneratedValueBasicResultBuilder resultBuilder) {
|
||||
resultBuilders.add( resultBuilder );
|
||||
}
|
||||
|
||||
public List<GeneratedValueBasicResultBuilder> getResultBuilders() {
|
||||
return resultBuilders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAffectedTableNames(Set<String> affectedTableNames, SessionFactoryImplementor sessionFactory) {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.generator.values.internal;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.model.MutationOperation;
|
||||
import org.hibernate.sql.model.ast.MutatingTableReference;
|
||||
import org.hibernate.sql.model.ast.RestrictedTableMutation;
|
||||
import org.hibernate.sql.model.ast.builder.AbstractTableUpdateBuilder;
|
||||
import org.hibernate.sql.model.internal.TableUpdateStandard;
|
||||
|
||||
/**
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
public class TableUpdateReturningBuilder<O extends MutationOperation> extends AbstractTableUpdateBuilder<O> {
|
||||
final List<ColumnReference> generatedColumns;
|
||||
|
||||
public TableUpdateReturningBuilder(
|
||||
EntityPersister mutationTarget,
|
||||
MutatingTableReference tableReference,
|
||||
List<ColumnReference> generatedColumns,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
super( mutationTarget, tableReference, sessionFactory );
|
||||
this.generatedColumns = generatedColumns;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EntityPersister getMutationTarget() {
|
||||
return (EntityPersister) super.getMutationTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings( "unchecked" )
|
||||
public RestrictedTableMutation<O> buildMutation() {
|
||||
return (RestrictedTableMutation<O>) new TableUpdateStandard(
|
||||
getMutatingTable(),
|
||||
getMutationTarget(),
|
||||
getSqlComment(),
|
||||
combine( getValueBindings(), getKeyBindings(), getLobValueBindings() ),
|
||||
getKeyRestrictionBindings(),
|
||||
getOptimisticLockBindings(),
|
||||
null,
|
||||
null,
|
||||
generatedColumns
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
|
||||
/**
|
||||
* Contains a framework of strategies for efficient retrieval of
|
||||
* {@linkplain org.hibernate.generator.OnExecutionGenerator database-generated values}.
|
||||
*
|
||||
* @see org.hibernate.generator.values.GeneratedValuesMutationDelegate
|
||||
*/
|
||||
package org.hibernate.generator.values;
|
|
@ -6,17 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.id;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Internal;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
|
@ -27,6 +16,18 @@ import java.sql.ResultSetMetaData;
|
|||
import java.sql.SQLException;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Internal;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.generator.values.internal.GeneratedValuesHelper;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.StringHelper;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.SqlTypedMapping;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.type.descriptor.WrapperOptions;
|
||||
|
||||
/**
|
||||
* Factory and helper methods for {@link IdentifierGenerator} framework.
|
||||
*
|
||||
|
@ -76,7 +77,10 @@ public final class IdentifierGeneratorHelper {
|
|||
* @return The generated identity value
|
||||
* @throws SQLException Can be thrown while accessing the result set
|
||||
* @throws HibernateException Indicates a problem reading back a generated identity value.
|
||||
*
|
||||
* @deprecated Use {@link GeneratedValuesHelper#getGeneratedValues} instead
|
||||
*/
|
||||
@Deprecated( since = "6.5", forRemoval = true )
|
||||
public static Object getGeneratedIdentity(
|
||||
String path,
|
||||
ResultSet resultSet,
|
||||
|
@ -112,7 +116,7 @@ public final class IdentifierGeneratorHelper {
|
|||
|
||||
private static boolean equal(String keyColumnName, String alias, Dialect dialect) {
|
||||
return alias.equalsIgnoreCase( keyColumnName )
|
||||
|| alias.equalsIgnoreCase( StringHelper.unquote( keyColumnName, dialect ) );
|
||||
|| alias.equalsIgnoreCase( StringHelper.unquote( keyColumnName, dialect ) );
|
||||
}
|
||||
|
||||
public static IntegralDataTypeHolder getIntegralDataTypeHolder(Class<?> integralType) {
|
||||
|
|
|
@ -7,11 +7,17 @@
|
|||
package org.hibernate.id;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.id.factory.spi.StandardGenerator;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.generator.OnExecutionGenerator;
|
||||
import org.hibernate.id.factory.spi.StandardGenerator;
|
||||
import org.hibernate.id.insert.BasicSelectingDelegate;
|
||||
import org.hibernate.id.insert.GetGeneratedKeysDelegate;
|
||||
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
|
||||
import org.hibernate.id.insert.InsertReturningDelegate;
|
||||
import org.hibernate.id.insert.UniqueKeySelectingDelegate;
|
||||
|
||||
import static org.hibernate.generator.internal.NaturalIdHelper.getNaturalIdPropertyNames;
|
||||
import static org.hibernate.generator.values.internal.GeneratedValuesHelper.noCustomSql;
|
||||
|
||||
/**
|
||||
* An {@link OnExecutionGenerator} that handles {@code IDENTITY}/"autoincrement"
|
||||
|
@ -21,16 +27,11 @@ import org.hibernate.id.insert.InsertReturningDelegate;
|
|||
* provided by the {@linkplain Dialect#getIdentityColumnSupport() dialect}.
|
||||
* <p>
|
||||
* The actual work involved in retrieving the primary key value is the job of a
|
||||
* {@link org.hibernate.id.insert.InsertGeneratedIdentifierDelegate}, either:
|
||||
* <ul>
|
||||
* <li>a {@link org.hibernate.id.insert.GetGeneratedKeysDelegate},
|
||||
* <li>an {@link org.hibernate.id.insert.InsertReturningDelegate}, or a
|
||||
* <li>a {@link org.hibernate.id.insert.BasicSelectingDelegate}.
|
||||
* </ul>
|
||||
* {@link org.hibernate.generator.values.GeneratedValuesMutationDelegate}.
|
||||
*
|
||||
* @see jakarta.persistence.GenerationType#IDENTITY
|
||||
* @see org.hibernate.dialect.identity.IdentityColumnSupport
|
||||
* @see org.hibernate.id.insert.InsertGeneratedIdentifierDelegate
|
||||
* @see org.hibernate.generator.values.GeneratedValuesMutationDelegate
|
||||
*
|
||||
* @author Christoph Sturm
|
||||
*
|
||||
|
@ -52,14 +53,28 @@ public class IdentityGenerator
|
|||
@Override
|
||||
public InsertGeneratedIdentifierDelegate getGeneratedIdentifierDelegate(PostInsertIdentityPersister persister) {
|
||||
final Dialect dialect = persister.getFactory().getJdbcServices().getDialect();
|
||||
if ( persister.getFactory().getSessionFactoryOptions().isGetGeneratedKeysEnabled() ) {
|
||||
// Try to use generic delegates if the dialects supports them
|
||||
if ( dialect.supportsInsertReturningGeneratedKeys()
|
||||
&& persister.getFactory().getSessionFactoryOptions().isGetGeneratedKeysEnabled() ) {
|
||||
return new GetGeneratedKeysDelegate( persister, false, EventType.INSERT );
|
||||
}
|
||||
else if ( dialect.supportsInsertReturning() && noCustomSql( persister, EventType.INSERT ) ) {
|
||||
return new InsertReturningDelegate( persister, EventType.INSERT );
|
||||
}
|
||||
// Fall back to delegates which only handle identifiers
|
||||
else if ( persister.getFactory().getSessionFactoryOptions().isGetGeneratedKeysEnabled() ) {
|
||||
return dialect.getIdentityColumnSupport().buildGetGeneratedKeysDelegate( persister, dialect );
|
||||
}
|
||||
else if ( dialect.getIdentityColumnSupport().supportsInsertSelectIdentity() ) {
|
||||
return new InsertReturningDelegate( persister, dialect );
|
||||
else if ( persister.getNaturalIdentifierProperties() != null
|
||||
&& !persister.getEntityMetamodel().isNaturalIdentifierInsertGenerated() ) {
|
||||
return new UniqueKeySelectingDelegate(
|
||||
persister,
|
||||
getNaturalIdPropertyNames( persister ),
|
||||
EventType.INSERT
|
||||
);
|
||||
}
|
||||
else {
|
||||
return new BasicSelectingDelegate( persister, dialect );
|
||||
return new BasicSelectingDelegate( persister );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,55 +7,30 @@
|
|||
package org.hibernate.id;
|
||||
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
|
||||
/**
|
||||
* A persister that may have an identity assigned by execution of
|
||||
* A persister that may have an identity assigned by execution of
|
||||
* a SQL {@code INSERT}.
|
||||
*
|
||||
* @author Gavin King
|
||||
* @deprecated Use {@link EntityPersister} instead.
|
||||
*/
|
||||
public interface PostInsertIdentityPersister extends EntityPersister, EntityMutationTarget {
|
||||
/**
|
||||
* Get a SQL select string that performs a select based on a unique
|
||||
* key determined by the given property name.
|
||||
*
|
||||
* @param propertyName The name of the property which maps to the
|
||||
* column(s) to use in the select statement restriction.
|
||||
* @return The SQL select string
|
||||
*/
|
||||
String getSelectByUniqueKeyString(String propertyName);
|
||||
|
||||
/**
|
||||
* Get a SQL select string that performs a select based on a unique
|
||||
* key determined by the given property names.
|
||||
*
|
||||
* @param propertyNames The names of the properties which maps to the
|
||||
* column(s) to use in the select statement restriction.
|
||||
* @return The SQL select string
|
||||
*/
|
||||
default String getSelectByUniqueKeyString(String[] propertyNames) {
|
||||
// default impl only for backward compatibility
|
||||
if ( propertyNames.length > 1 ) {
|
||||
throw new IllegalArgumentException( "support for multiple properties not implemented" );
|
||||
}
|
||||
return getSelectByUniqueKeyString( propertyNames[0] );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the database-specific SQL command to retrieve the last
|
||||
* generated IDENTITY value.
|
||||
*
|
||||
* @return The SQL command string
|
||||
*/
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
public interface PostInsertIdentityPersister extends EntityPersister {
|
||||
@Override
|
||||
String getIdentitySelectString();
|
||||
|
||||
@Override
|
||||
String[] getIdentifierColumnNames();
|
||||
|
||||
/**
|
||||
* The names of the primary key columns in the root table.
|
||||
*
|
||||
* @return The primary key column names.
|
||||
*/
|
||||
@Override
|
||||
String getSelectByUniqueKeyString(String propertyName);
|
||||
|
||||
@Override
|
||||
default String getSelectByUniqueKeyString(String[] propertyNames) {
|
||||
return EntityPersister.super.getSelectByUniqueKeyString( propertyNames );
|
||||
}
|
||||
|
||||
@Override
|
||||
String[] getRootTableKeyColumnNames();
|
||||
}
|
||||
|
|
|
@ -13,43 +13,58 @@ import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
|
|||
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.generator.values.AbstractGeneratedValuesMutationDelegate;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
|
||||
/**
|
||||
* Abstract {@link InsertGeneratedIdentifierDelegate} implementation where
|
||||
* Abstract {@link org.hibernate.generator.values.GeneratedValuesMutationDelegate} implementation where
|
||||
* the underlying strategy causes the generated identifier to be returned as
|
||||
* an effect of performing the insert statement. Thus, there is no need for
|
||||
* an additional sql statement to determine the generated identifier.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractReturningDelegate implements InsertGeneratedIdentifierDelegate {
|
||||
private final PostInsertIdentityPersister persister;
|
||||
|
||||
public abstract class AbstractReturningDelegate extends AbstractGeneratedValuesMutationDelegate
|
||||
implements InsertGeneratedIdentifierDelegate {
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractReturningDelegate(EntityPersister, EventType, boolean, boolean)} instead.
|
||||
*/
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
public AbstractReturningDelegate(PostInsertIdentityPersister persister) {
|
||||
this.persister = persister;
|
||||
super( persister, EventType.INSERT );
|
||||
}
|
||||
|
||||
public AbstractReturningDelegate(
|
||||
EntityPersister persister,
|
||||
EventType timing,
|
||||
boolean supportsArbitraryValues,
|
||||
boolean supportsRowId) {
|
||||
super( persister, timing, supportsArbitraryValues, supportsRowId );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object performInsert(
|
||||
PreparedStatementDetails insertStatementDetails,
|
||||
public GeneratedValues performMutation(
|
||||
PreparedStatementDetails statementDetails,
|
||||
JdbcValueBindings valueBindings,
|
||||
Object entity,
|
||||
SharedSessionContractImplementor session) {
|
||||
session.getJdbcServices().getSqlStatementLogger().logStatement( insertStatementDetails.getSqlString() );
|
||||
valueBindings.beforeStatement( insertStatementDetails );
|
||||
return executeAndExtract( insertStatementDetails.getSqlString(), insertStatementDetails.getStatement(), session );
|
||||
session.getJdbcServices().getSqlStatementLogger().logStatement( statementDetails.getSqlString() );
|
||||
valueBindings.beforeStatement( statementDetails );
|
||||
return executeAndExtractReturning( statementDetails.getSqlString(), statementDetails.getStatement(), session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Object performInsert(String insertSql, SharedSessionContractImplementor session, Binder binder) {
|
||||
public final GeneratedValues performInsertReturning(String sql, SharedSessionContractImplementor session, Binder binder) {
|
||||
try {
|
||||
// prepare and execute the insert
|
||||
PreparedStatement insert = prepareStatement( insertSql, session );
|
||||
PreparedStatement insert = prepareStatement( sql, session );
|
||||
try {
|
||||
binder.bindValues( insert );
|
||||
return executeAndExtract( insertSql, insert, session );
|
||||
return executeAndExtractReturning( sql, insert, session );
|
||||
}
|
||||
finally {
|
||||
releaseStatement( insert, session );
|
||||
|
@ -59,23 +74,31 @@ public abstract class AbstractReturningDelegate implements InsertGeneratedIdenti
|
|||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
||||
sqle,
|
||||
"could not insert: " + MessageHelper.infoString( persister ),
|
||||
insertSql
|
||||
sql
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected PostInsertIdentityPersister getPersister() {
|
||||
return persister;
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
protected Object executeAndExtract(
|
||||
String sql,
|
||||
PreparedStatement preparedStatement,
|
||||
SharedSessionContractImplementor session) {
|
||||
final GeneratedValues generatedValues = executeAndExtractReturning( sql, preparedStatement, session );
|
||||
return generatedValues.getGeneratedValue( persister.getIdentifierMapping() );
|
||||
}
|
||||
|
||||
protected abstract Object executeAndExtract(
|
||||
String insertSql,
|
||||
PreparedStatement insertStatement,
|
||||
protected abstract GeneratedValues executeAndExtractReturning(
|
||||
String sql,
|
||||
PreparedStatement preparedStatement,
|
||||
SharedSessionContractImplementor session);
|
||||
|
||||
protected void releaseStatement(PreparedStatement insert, SharedSessionContractImplementor session) {
|
||||
protected void releaseStatement(PreparedStatement preparedStatement, SharedSessionContractImplementor session) {
|
||||
final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
|
||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( insert );
|
||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( preparedStatement );
|
||||
jdbcCoordinator.afterStatementExecution();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,24 +16,39 @@ import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
|||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.jdbc.spi.StatementPreparer;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.generator.values.AbstractGeneratedValuesMutationDelegate;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
|
||||
import static java.sql.Statement.NO_GENERATED_KEYS;
|
||||
import static org.hibernate.id.IdentifierGeneratorHelper.getGeneratedIdentity;
|
||||
import static org.hibernate.generator.values.internal.GeneratedValuesHelper.getGeneratedValues;
|
||||
|
||||
/**
|
||||
* Abstract {@link InsertGeneratedIdentifierDelegate} implementation where
|
||||
* Abstract {@link org.hibernate.generator.values.GeneratedValuesMutationDelegate} implementation where
|
||||
* the underlying strategy requires a subsequent {@code select} after the
|
||||
* {@code insert} to determine the generated identifier.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractSelectingDelegate implements InsertGeneratedIdentifierDelegate {
|
||||
private final PostInsertIdentityPersister persister;
|
||||
|
||||
public abstract class AbstractSelectingDelegate extends AbstractGeneratedValuesMutationDelegate
|
||||
implements InsertGeneratedIdentifierDelegate {
|
||||
/**
|
||||
* @deprecated Use {@link #AbstractSelectingDelegate(EntityPersister, EventType, boolean, boolean)} instead.
|
||||
*/
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
protected AbstractSelectingDelegate(PostInsertIdentityPersister persister) {
|
||||
this.persister = persister;
|
||||
super( persister, EventType.INSERT );
|
||||
}
|
||||
|
||||
protected AbstractSelectingDelegate(
|
||||
EntityPersister persister,
|
||||
EventType timing,
|
||||
boolean supportsArbitraryValues,
|
||||
boolean supportsRowId) {
|
||||
super( persister, timing, supportsArbitraryValues, supportsRowId );
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -47,12 +62,22 @@ public abstract class AbstractSelectingDelegate implements InsertGeneratedIdenti
|
|||
throws SQLException {
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated No substitute.
|
||||
*/
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
protected Object extractGeneratedValues(ResultSet resultSet, SharedSessionContractImplementor session)
|
||||
throws SQLException {
|
||||
final GeneratedValues generatedValues = extractReturningValues( resultSet, session );
|
||||
return generatedValues.getGeneratedValue( persister.getIdentifierMapping() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the generated key value from the given result set after execution of {@link #getSelectSQL()}.
|
||||
*/
|
||||
protected Object extractGeneratedValue(ResultSet resultSet, SharedSessionContractImplementor session)
|
||||
private GeneratedValues extractReturningValues(ResultSet resultSet, SharedSessionContractImplementor session)
|
||||
throws SQLException {
|
||||
return getGeneratedIdentity( persister.getNavigableRole().getFullPath(), resultSet, persister, session );
|
||||
return getGeneratedValues( resultSet, persister, getTiming(), session );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -61,19 +86,19 @@ public abstract class AbstractSelectingDelegate implements InsertGeneratedIdenti
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object performInsert(
|
||||
PreparedStatementDetails insertStatementDetails,
|
||||
public GeneratedValues performMutation(
|
||||
PreparedStatementDetails statementDetails,
|
||||
JdbcValueBindings jdbcValueBindings,
|
||||
Object entity,
|
||||
SharedSessionContractImplementor session) {
|
||||
final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
|
||||
final JdbcServices jdbcServices = session.getJdbcServices();
|
||||
|
||||
jdbcServices.getSqlStatementLogger().logStatement( insertStatementDetails.getSqlString() );
|
||||
jdbcValueBindings.beforeStatement( insertStatementDetails );
|
||||
jdbcServices.getSqlStatementLogger().logStatement( statementDetails.getSqlString() );
|
||||
jdbcValueBindings.beforeStatement( statementDetails );
|
||||
|
||||
jdbcCoordinator.getResultSetReturn()
|
||||
.executeUpdate( insertStatementDetails.resolveStatement(), insertStatementDetails.getSqlString() );
|
||||
.executeUpdate( statementDetails.resolveStatement(), statementDetails.getSqlString() );
|
||||
|
||||
// the insert is complete, select the generated id...
|
||||
|
||||
|
@ -84,7 +109,7 @@ public abstract class AbstractSelectingDelegate implements InsertGeneratedIdenti
|
|||
|
||||
final ResultSet resultSet = session.getJdbcCoordinator().getResultSetReturn().extract( idSelect, idSelectSql );
|
||||
try {
|
||||
return extractGeneratedValue( resultSet, session );
|
||||
return extractReturningValues( resultSet, session );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw jdbcServices.getSqlExceptionHelper().convert(
|
||||
|
@ -108,15 +133,15 @@ public abstract class AbstractSelectingDelegate implements InsertGeneratedIdenti
|
|||
}
|
||||
|
||||
@Override
|
||||
public final Object performInsert(String insertSQL, SharedSessionContractImplementor session, Binder binder) {
|
||||
public final GeneratedValues performInsertReturning(String sql, SharedSessionContractImplementor session, Binder binder) {
|
||||
JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
|
||||
StatementPreparer statementPreparer = jdbcCoordinator.getStatementPreparer();
|
||||
try {
|
||||
// prepare and execute the insert
|
||||
PreparedStatement insert = statementPreparer.prepareStatement( insertSQL, NO_GENERATED_KEYS );
|
||||
PreparedStatement insert = statementPreparer.prepareStatement( sql, NO_GENERATED_KEYS );
|
||||
try {
|
||||
binder.bindValues( insert );
|
||||
jdbcCoordinator.getResultSetReturn().executeUpdate( insert, insertSQL );
|
||||
jdbcCoordinator.getResultSetReturn().executeUpdate( insert, sql );
|
||||
}
|
||||
finally {
|
||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( insert );
|
||||
|
@ -127,7 +152,7 @@ public abstract class AbstractSelectingDelegate implements InsertGeneratedIdenti
|
|||
throw session.getJdbcServices().getSqlExceptionHelper().convert(
|
||||
sqle,
|
||||
"could not insert: " + MessageHelper.infoString( persister ),
|
||||
insertSQL
|
||||
sql
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -140,7 +165,7 @@ public abstract class AbstractSelectingDelegate implements InsertGeneratedIdenti
|
|||
bindParameters( binder.getEntity(), idSelect, session );
|
||||
ResultSet resultSet = jdbcCoordinator.getResultSetReturn().extract( idSelect, selectSQL );
|
||||
try {
|
||||
return extractGeneratedValue( resultSet, session );
|
||||
return extractReturningValues( resultSet, session );
|
||||
}
|
||||
finally {
|
||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( resultSet, idSelect );
|
||||
|
|
|
@ -6,66 +6,46 @@
|
|||
*/
|
||||
package org.hibernate.id.insert;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import org.hibernate.boot.registry.classloading.internal.ClassLoaderServiceImpl;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.jdbc.Expectation;
|
||||
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
|
||||
import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.sql.model.ast.builder.TableInsertBuilderStandard;
|
||||
import org.hibernate.generator.OnExecutionGenerator;
|
||||
import org.hibernate.sql.model.ast.builder.TableMutationBuilder;
|
||||
|
||||
/**
|
||||
* Delegate for dealing with {@code IDENTITY} columns where the dialect requires an
|
||||
* additional command execution to retrieve the generated {@code IDENTITY} value
|
||||
*/
|
||||
public class BasicSelectingDelegate extends AbstractSelectingDelegate {
|
||||
private final PostInsertIdentityPersister persister;
|
||||
private final Dialect dialect;
|
||||
final private EntityPersister persister;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #BasicSelectingDelegate(EntityPersister)} instead.
|
||||
*/
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
public BasicSelectingDelegate(PostInsertIdentityPersister persister, Dialect dialect) {
|
||||
super( persister );
|
||||
this.persister = persister;
|
||||
this.dialect = dialect;
|
||||
this( persister );
|
||||
}
|
||||
|
||||
@Override @Deprecated
|
||||
public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert(SqlStringGenerationContext context) {
|
||||
IdentifierGeneratingInsert insert = new IdentifierGeneratingInsert( persister.getFactory() );
|
||||
insert.addGeneratedColumns( persister.getRootTableKeyColumnNames(), (OnExecutionGenerator) persister.getGenerator() );
|
||||
return insert;
|
||||
public BasicSelectingDelegate(EntityPersister persister) {
|
||||
super( persister, EventType.INSERT, false, false );
|
||||
this.persister = persister;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableInsertBuilder createTableInsertBuilder(
|
||||
BasicEntityIdentifierMapping identifierMapping,
|
||||
public TableMutationBuilder<?> createTableMutationBuilder(
|
||||
Expectation expectation,
|
||||
SessionFactoryImplementor factory) {
|
||||
final TableInsertBuilder builder =
|
||||
new TableInsertBuilderStandard( persister, persister.getIdentifierTableMapping(), factory );
|
||||
|
||||
final OnExecutionGenerator generator = (OnExecutionGenerator) persister.getGenerator();
|
||||
if ( generator.referenceColumnsInSql( dialect ) ) {
|
||||
final String[] columnNames = persister.getRootTableKeyColumnNames();
|
||||
final String[] columnValues = generator.getReferencedColumnValues( dialect );
|
||||
if ( columnValues.length != columnNames.length ) {
|
||||
throw new MappingException("wrong number of generated columns");
|
||||
}
|
||||
for ( int i = 0; i < columnValues.length; i++ ) {
|
||||
builder.addKeyColumn( columnNames[i], columnValues[i], identifierMapping.getJdbcMapping() );
|
||||
}
|
||||
}
|
||||
|
||||
return builder;
|
||||
return new TableInsertBuilderStandard( persister, persister.getIdentifierTableMapping(), factory );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSelectSQL() {
|
||||
if ( persister.getIdentitySelectString() == null && !dialect.getIdentityColumnSupport().supportsInsertSelectIdentity() ) {
|
||||
if ( persister.getIdentitySelectString() == null && !dialect().getIdentityColumnSupport().supportsInsertSelectIdentity() ) {
|
||||
throw CoreLogging.messageLogger( BasicSelectingDelegate.class ).nullIdentitySelectString();
|
||||
}
|
||||
return persister.getIdentitySelectString();
|
||||
|
|
|
@ -9,10 +9,10 @@ package org.hibernate.id.insert;
|
|||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
|
||||
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
|
||||
|
@ -21,95 +21,104 @@ import org.hibernate.engine.jdbc.spi.JdbcServices;
|
|||
import org.hibernate.engine.jdbc.spi.MutationStatementPreparer;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.generator.values.GeneratedValueBasicResultBuilder;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.jdbc.Expectation;
|
||||
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
|
||||
import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.sql.model.ast.builder.TableInsertBuilderStandard;
|
||||
import org.hibernate.generator.OnExecutionGenerator;
|
||||
import org.hibernate.sql.model.ast.builder.TableMutationBuilder;
|
||||
import org.hibernate.sql.model.ast.builder.TableUpdateBuilderStandard;
|
||||
|
||||
import static java.sql.Statement.RETURN_GENERATED_KEYS;
|
||||
import static org.hibernate.id.IdentifierGeneratorHelper.getGeneratedIdentity;
|
||||
import static org.hibernate.generator.values.internal.GeneratedValuesHelper.getActualGeneratedModelPart;
|
||||
import static org.hibernate.generator.values.internal.GeneratedValuesHelper.getGeneratedValues;
|
||||
import static org.hibernate.internal.util.StringHelper.unquote;
|
||||
|
||||
/**
|
||||
* Delegate for dealing with {@code IDENTITY} columns using the JDBC3 method
|
||||
* Delegate for dealing with generated values using the JDBC3 method
|
||||
* {@link PreparedStatement#getGeneratedKeys()}.
|
||||
* <p>
|
||||
* Supports both {@link EventType#INSERT insert} and {@link EventType#UPDATE update} statements.
|
||||
*
|
||||
* @author Andrea Boriero
|
||||
*/
|
||||
public class GetGeneratedKeysDelegate extends AbstractReturningDelegate {
|
||||
private final PostInsertIdentityPersister persister;
|
||||
private final Dialect dialect;
|
||||
private final boolean inferredKeys;
|
||||
private final String[] columnNames;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #GetGeneratedKeysDelegate(EntityPersister, boolean, EventType)} instead.
|
||||
*/
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
public GetGeneratedKeysDelegate(PostInsertIdentityPersister persister, Dialect dialect, boolean inferredKeys) {
|
||||
super( persister );
|
||||
this.persister = persister;
|
||||
this.dialect = dialect;
|
||||
this.inferredKeys = inferredKeys;
|
||||
this( persister, inferredKeys, EventType.INSERT );
|
||||
}
|
||||
|
||||
@Override @Deprecated
|
||||
public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert(SqlStringGenerationContext context) {
|
||||
IdentifierGeneratingInsert insert = new IdentifierGeneratingInsert( persister.getFactory() );
|
||||
insert.addGeneratedColumns( persister.getRootTableKeyColumnNames(), (OnExecutionGenerator) persister.getGenerator() );
|
||||
return insert;
|
||||
public GetGeneratedKeysDelegate(
|
||||
EntityPersister persister,
|
||||
boolean inferredKeys,
|
||||
EventType timing) {
|
||||
super( persister, timing, !inferredKeys, false );
|
||||
|
||||
if ( inferredKeys ) {
|
||||
columnNames = null;
|
||||
}
|
||||
else {
|
||||
final List<GeneratedValueBasicResultBuilder> resultBuilders = jdbcValuesMappingProducer.getResultBuilders();
|
||||
final List<String> columnNamesList = new ArrayList<>( resultBuilders.size() );
|
||||
final boolean unquote = dialect().unquoteGetGeneratedKeys();
|
||||
for ( GeneratedValueBasicResultBuilder resultBuilder : resultBuilders ) {
|
||||
final String col = getActualGeneratedModelPart( resultBuilder.getModelPart() ).getSelectionExpression();
|
||||
columnNamesList.add( unquote ? unquote( col, dialect() ) : col );
|
||||
}
|
||||
columnNames = columnNamesList.toArray( new String[0] );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableInsertBuilder createTableInsertBuilder(
|
||||
BasicEntityIdentifierMapping identifierMapping,
|
||||
public TableMutationBuilder<?> createTableMutationBuilder(
|
||||
Expectation expectation,
|
||||
SessionFactoryImplementor factory) {
|
||||
final TableInsertBuilder builder =
|
||||
new TableInsertBuilderStandard( persister, persister.getIdentifierTableMapping(), factory );
|
||||
|
||||
final OnExecutionGenerator generator = (OnExecutionGenerator) persister.getGenerator();
|
||||
if ( generator.referenceColumnsInSql( dialect ) ) {
|
||||
final String[] columnNames = persister.getRootTableKeyColumnNames();
|
||||
final String[] columnValues = generator.getReferencedColumnValues( dialect );
|
||||
if ( columnValues.length != columnNames.length ) {
|
||||
throw new MappingException("wrong number of generated columns");
|
||||
}
|
||||
for ( int i = 0; i < columnValues.length; i++ ) {
|
||||
builder.addKeyColumn( columnNames[i], columnValues[i], identifierMapping.getJdbcMapping() );
|
||||
}
|
||||
if ( getTiming() == EventType.INSERT ) {
|
||||
return new TableInsertBuilderStandard( persister, persister.getIdentifierTableMapping(), factory );
|
||||
}
|
||||
else {
|
||||
return new TableUpdateBuilderStandard<>( persister, persister.getIdentifierTableMapping(), factory );
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String insertSql, SharedSessionContractImplementor session) {
|
||||
public PreparedStatement prepareStatement(String sql, SharedSessionContractImplementor session) {
|
||||
MutationStatementPreparer preparer = session.getJdbcCoordinator().getMutationStatementPreparer();
|
||||
return inferredKeys
|
||||
? preparer.prepareStatement( insertSql, RETURN_GENERATED_KEYS )
|
||||
: preparer.prepareStatement( insertSql, persister.getRootTableKeyColumnNames() );
|
||||
return columnNames == null
|
||||
? preparer.prepareStatement( sql, RETURN_GENERATED_KEYS )
|
||||
: preparer.prepareStatement( sql, columnNames );
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object performInsert(
|
||||
PreparedStatementDetails insertStatementDetails,
|
||||
public GeneratedValues performMutation(
|
||||
PreparedStatementDetails statementDetails,
|
||||
JdbcValueBindings jdbcValueBindings,
|
||||
Object entity,
|
||||
SharedSessionContractImplementor session) {
|
||||
final JdbcServices jdbcServices = session.getJdbcServices();
|
||||
final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
|
||||
|
||||
final String insertSql = insertStatementDetails.getSqlString();
|
||||
final String sql = statementDetails.getSqlString();
|
||||
|
||||
jdbcServices.getSqlStatementLogger().logStatement( insertSql );
|
||||
jdbcServices.getSqlStatementLogger().logStatement( sql );
|
||||
|
||||
final PreparedStatement insertStatement = insertStatementDetails.resolveStatement();
|
||||
jdbcValueBindings.beforeStatement( insertStatementDetails );
|
||||
final PreparedStatement preparedStatement = statementDetails.resolveStatement();
|
||||
jdbcValueBindings.beforeStatement( statementDetails );
|
||||
|
||||
try {
|
||||
jdbcCoordinator.getResultSetReturn().executeUpdate( insertStatement, insertSql );
|
||||
jdbcCoordinator.getResultSetReturn().executeUpdate( preparedStatement, sql );
|
||||
|
||||
try {
|
||||
final ResultSet resultSet = insertStatement.getGeneratedKeys();
|
||||
final ResultSet resultSet = preparedStatement.getGeneratedKeys();
|
||||
try {
|
||||
return getGeneratedIdentity( persister.getNavigableRole().getFullPath(), resultSet, persister, session );
|
||||
return getGeneratedValues( resultSet, persister, getTiming(), session );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw jdbcServices.getSqlExceptionHelper().convert(
|
||||
|
@ -119,7 +128,7 @@ public class GetGeneratedKeysDelegate extends AbstractReturningDelegate {
|
|||
"Unable to extract generated key from generated-key for `%s`",
|
||||
persister.getNavigableRole().getFullPath()
|
||||
),
|
||||
insertSql
|
||||
sql
|
||||
);
|
||||
}
|
||||
finally {
|
||||
|
@ -127,48 +136,51 @@ public class GetGeneratedKeysDelegate extends AbstractReturningDelegate {
|
|||
jdbcCoordinator
|
||||
.getLogicalConnection()
|
||||
.getResourceRegistry()
|
||||
.release( resultSet, insertStatement );
|
||||
.release( resultSet, preparedStatement );
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( insertStatement );
|
||||
if ( statementDetails.getStatement() != null ) {
|
||||
statementDetails.releaseStatement( session );
|
||||
}
|
||||
jdbcValueBindings.afterStatement( statementDetails.getMutatingTableDetails() );
|
||||
}
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw jdbcServices.getSqlExceptionHelper().convert(
|
||||
e,
|
||||
"Unable to extract generated-keys ResultSet",
|
||||
insertSql
|
||||
sql
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeAndExtract(
|
||||
String insertSql,
|
||||
PreparedStatement insertStatement,
|
||||
public GeneratedValues executeAndExtractReturning(
|
||||
String sql,
|
||||
PreparedStatement preparedStatement,
|
||||
SharedSessionContractImplementor session) {
|
||||
final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
|
||||
final JdbcServices jdbcServices = session.getJdbcServices();
|
||||
|
||||
jdbcCoordinator.getResultSetReturn().executeUpdate( insertStatement, insertSql );
|
||||
jdbcCoordinator.getResultSetReturn().executeUpdate( preparedStatement, sql );
|
||||
|
||||
try {
|
||||
final ResultSet resultSet = insertStatement.getGeneratedKeys();
|
||||
final ResultSet resultSet = preparedStatement.getGeneratedKeys();
|
||||
try {
|
||||
return getGeneratedIdentity( persister.getNavigableRole().getFullPath(), resultSet, persister, session );
|
||||
return getGeneratedValues( resultSet, persister, getTiming(), session );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw jdbcServices.getSqlExceptionHelper().convert(
|
||||
e,
|
||||
"Unable to extract generated key(s) from generated-keys ResultSet",
|
||||
insertSql
|
||||
sql
|
||||
);
|
||||
}
|
||||
finally {
|
||||
if ( resultSet != null ) {
|
||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( resultSet, insertStatement );
|
||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( resultSet, preparedStatement );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -176,7 +188,7 @@ public class GetGeneratedKeysDelegate extends AbstractReturningDelegate {
|
|||
throw jdbcServices.getSqlExceptionHelper().convert(
|
||||
e,
|
||||
"Unable to extract generated-keys ResultSet",
|
||||
insertSql
|
||||
sql
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,13 +8,15 @@ package org.hibernate.id.insert;
|
|||
|
||||
import java.sql.PreparedStatement;
|
||||
|
||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
|
||||
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.jdbc.Expectation;
|
||||
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
|
||||
|
||||
/**
|
||||
|
@ -37,17 +39,23 @@ import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
|
|||
* @see org.hibernate.generator.OnExecutionGenerator
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @deprecated Use {@link GeneratedValuesMutationDelegate} instead.
|
||||
*/
|
||||
public interface InsertGeneratedIdentifierDelegate {
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
public interface InsertGeneratedIdentifierDelegate extends GeneratedValuesMutationDelegate {
|
||||
/**
|
||||
* Create a {@link TableInsertBuilder} with any specific identity
|
||||
* handling already built in.
|
||||
*/
|
||||
TableInsertBuilder createTableInsertBuilder(
|
||||
default TableInsertBuilder createTableInsertBuilder(
|
||||
BasicEntityIdentifierMapping identifierMapping,
|
||||
Expectation expectation,
|
||||
SessionFactoryImplementor sessionFactory);
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
return (TableInsertBuilder) createTableMutationBuilder( expectation, sessionFactory );
|
||||
}
|
||||
|
||||
@Override
|
||||
PreparedStatement prepareStatement(String insertSql, SharedSessionContractImplementor session);
|
||||
|
||||
/**
|
||||
|
@ -56,23 +64,20 @@ public interface InsertGeneratedIdentifierDelegate {
|
|||
*
|
||||
* @see #createTableInsertBuilder
|
||||
*/
|
||||
Object performInsert(
|
||||
default Object performInsert(
|
||||
PreparedStatementDetails insertStatementDetails,
|
||||
JdbcValueBindings valueBindings,
|
||||
Object entity,
|
||||
SharedSessionContractImplementor session);
|
||||
|
||||
/**
|
||||
* Build an {@linkplain org.hibernate.sql.Insert insert statement}
|
||||
* specific to the delegate's mode of handling generated key values.
|
||||
*
|
||||
* @param context A context to help generate SQL strings
|
||||
* @return An {@link IdentifierGeneratingInsert}
|
||||
*
|
||||
* @deprecated this is no longer called
|
||||
*/
|
||||
@Deprecated(since = "6.2")
|
||||
IdentifierGeneratingInsert prepareIdentifierGeneratingInsert(SqlStringGenerationContext context);
|
||||
SharedSessionContractImplementor session) {
|
||||
final EntityPersister entityPersister = session.getEntityPersister( null, entity );
|
||||
final GeneratedValues generatedValues = performMutation(
|
||||
insertStatementDetails,
|
||||
valueBindings,
|
||||
entity,
|
||||
session
|
||||
);
|
||||
return generatedValues.getGeneratedValue( entityPersister.getIdentifierMapping() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Append SQL specific to this delegate's mode of handling generated
|
||||
|
@ -84,6 +89,22 @@ public interface InsertGeneratedIdentifierDelegate {
|
|||
return insertSQL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@code insert} statement and return the generated
|
||||
* key value.
|
||||
*
|
||||
* @param insertSQL The {@code insert} statement string
|
||||
* @param session The session in which we are operating
|
||||
* @param binder The parameter binder
|
||||
*
|
||||
* @return The generated identifier value
|
||||
*/
|
||||
default Object performInsert(String insertSQL, SharedSessionContractImplementor session, Binder binder) {
|
||||
final EntityPersister entityPersister = session.getEntityPersister( null, binder.getEntity() );
|
||||
final GeneratedValues generatedValues = performInsertReturning( insertSQL, session, binder );
|
||||
return generatedValues.getGeneratedValue( entityPersister.getIdentifierMapping() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given {@code insert} statement and return the generated
|
||||
* key value.
|
||||
|
@ -94,6 +115,5 @@ public interface InsertGeneratedIdentifierDelegate {
|
|||
*
|
||||
* @return The generated identifier value
|
||||
*/
|
||||
Object performInsert(String insertSQL, SharedSessionContractImplementor session, Binder binder);
|
||||
|
||||
GeneratedValues performInsertReturning(String insertSQL, SharedSessionContractImplementor session, Binder binder);
|
||||
}
|
||||
|
|
|
@ -9,86 +9,116 @@ package org.hibernate.id.insert;
|
|||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.dialect.identity.IdentityColumnSupport;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.generator.values.GeneratedValueBasicResultBuilder;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.generator.values.internal.TableUpdateReturningBuilder;
|
||||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.jdbc.Expectation;
|
||||
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
|
||||
import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
|
||||
import org.hibernate.generator.OnExecutionGenerator;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.model.ast.MutatingTableReference;
|
||||
import org.hibernate.sql.model.ast.builder.TableMutationBuilder;
|
||||
|
||||
import static java.sql.Statement.NO_GENERATED_KEYS;
|
||||
import static org.hibernate.id.IdentifierGeneratorHelper.getGeneratedIdentity;
|
||||
import static org.hibernate.generator.values.internal.GeneratedValuesHelper.getActualGeneratedModelPart;
|
||||
import static org.hibernate.generator.values.internal.GeneratedValuesHelper.getGeneratedValues;
|
||||
|
||||
/**
|
||||
* Delegate for dealing with {@code IDENTITY} columns where the dialect supports
|
||||
* returning the generated {@code IDENTITY} value directly from the insert statement.
|
||||
* Delegate for dealing with generated values where the dialect supports
|
||||
* returning the generated column directly from the mutation statement.
|
||||
* <p>
|
||||
* Supports both {@link EventType#INSERT insert} and {@link EventType#UPDATE update} statements.
|
||||
*
|
||||
* @see org.hibernate.id.IdentityGenerator
|
||||
* @see IdentityColumnSupport#supportsInsertSelectIdentity()
|
||||
* @see org.hibernate.generator.OnExecutionGenerator
|
||||
* @see GeneratedValuesMutationDelegate
|
||||
*/
|
||||
public class InsertReturningDelegate extends AbstractReturningDelegate {
|
||||
private final PostInsertIdentityPersister persister;
|
||||
private final Dialect dialect;
|
||||
private final MutatingTableReference tableReference;
|
||||
private final List<ColumnReference> generatedColumns;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #InsertReturningDelegate(EntityPersister, EventType)} instead.
|
||||
*/
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
public InsertReturningDelegate(PostInsertIdentityPersister persister, Dialect dialect) {
|
||||
super( persister );
|
||||
this.persister = persister;
|
||||
this.dialect = dialect;
|
||||
this( persister, EventType.INSERT );
|
||||
}
|
||||
|
||||
@Override @Deprecated
|
||||
public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert(SqlStringGenerationContext context) {
|
||||
InsertSelectIdentityInsert insert = new InsertSelectIdentityInsert( persister.getFactory() );
|
||||
insert.addGeneratedColumns( persister.getRootTableKeyColumnNames(), (OnExecutionGenerator) persister.getGenerator() );
|
||||
return insert;
|
||||
public InsertReturningDelegate(EntityPersister persister, EventType timing) {
|
||||
super(
|
||||
persister,
|
||||
timing,
|
||||
true,
|
||||
persister.getFactory().getJdbcServices().getDialect().supportsInsertReturningRowId()
|
||||
);
|
||||
this.tableReference = new MutatingTableReference( persister.getIdentifierTableMapping() );
|
||||
final List<GeneratedValueBasicResultBuilder> resultBuilders = jdbcValuesMappingProducer.getResultBuilders();
|
||||
this.generatedColumns = new ArrayList<>( resultBuilders.size() );
|
||||
for ( GeneratedValueBasicResultBuilder resultBuilder : resultBuilders ) {
|
||||
generatedColumns.add( new ColumnReference(
|
||||
tableReference,
|
||||
getActualGeneratedModelPart( resultBuilder.getModelPart() )
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableInsertBuilder createTableInsertBuilder(
|
||||
BasicEntityIdentifierMapping identifierMapping,
|
||||
public TableMutationBuilder<?> createTableMutationBuilder(
|
||||
Expectation expectation,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
return new TableInsertReturningBuilder( persister, sessionFactory );
|
||||
if ( getTiming() == EventType.INSERT ) {
|
||||
return new TableInsertReturningBuilder( persister, tableReference, generatedColumns, sessionFactory );
|
||||
}
|
||||
else {
|
||||
return new TableUpdateReturningBuilder<>( persister, tableReference, generatedColumns, sessionFactory );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object executeAndExtract(
|
||||
String insertSql,
|
||||
PreparedStatement insertStatement,
|
||||
protected GeneratedValues executeAndExtractReturning(
|
||||
String sql,
|
||||
PreparedStatement preparedStatement,
|
||||
SharedSessionContractImplementor session) {
|
||||
final JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
|
||||
final JdbcServices jdbcServices = session.getJdbcServices();
|
||||
|
||||
final ResultSet resultSet = jdbcCoordinator.getResultSetReturn().execute( insertStatement, insertSql );
|
||||
final ResultSet resultSet = jdbcCoordinator.getResultSetReturn().execute( preparedStatement, sql );
|
||||
try {
|
||||
return getGeneratedIdentity( persister.getNavigableRole().getFullPath(), resultSet, persister, session );
|
||||
return getGeneratedValues( resultSet, persister, getTiming(), session );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw jdbcServices.getSqlExceptionHelper().convert(
|
||||
e,
|
||||
"Unable to extract generated key(s) from generated-keys ResultSet",
|
||||
insertSql
|
||||
sql
|
||||
);
|
||||
}
|
||||
finally {
|
||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( resultSet, insertStatement );
|
||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( resultSet, preparedStatement );
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prepareIdentifierGeneratingInsert(String insertSQL) {
|
||||
return dialect.getIdentityColumnSupport().appendIdentitySelectToInsert( insertSQL );
|
||||
return dialect().getIdentityColumnSupport().appendIdentitySelectToInsert(
|
||||
( (BasicEntityIdentifierMapping) persister.getRootEntityDescriptor().getIdentifierMapping() ).getSelectionExpression(),
|
||||
insertSQL
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreparedStatement prepareStatement(String insertSql, SharedSessionContractImplementor session) {
|
||||
return session.getJdbcCoordinator().getMutationStatementPreparer().prepareStatement( insertSql, NO_GENERATED_KEYS );
|
||||
public PreparedStatement prepareStatement(String sql, SharedSessionContractImplementor session) {
|
||||
return session.getJdbcCoordinator().getMutationStatementPreparer().prepareStatement( sql, NO_GENERATED_KEYS );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
*/
|
||||
package org.hibernate.id.insert;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.sql.Insert;
|
||||
import org.hibernate.generator.OnExecutionGenerator;
|
||||
|
@ -17,7 +16,11 @@ import org.hibernate.generator.OnExecutionGenerator;
|
|||
* to the end of the insert statement.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*
|
||||
* @deprecated This is not used anymore in any of the
|
||||
* {@link org.hibernate.generator.values.GeneratedValuesMutationDelegate} implementations.
|
||||
*/
|
||||
@Deprecated( since = "6.5" )
|
||||
public class InsertSelectIdentityInsert extends IdentifierGeneratingInsert {
|
||||
protected String identityColumnName;
|
||||
|
||||
|
|
|
@ -10,27 +10,17 @@ import java.sql.PreparedStatement;
|
|||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.jdbc.mutation.JdbcValueBindings;
|
||||
import org.hibernate.engine.jdbc.mutation.group.PreparedStatementDetails;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcCoordinator;
|
||||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.jdbc.spi.StatementPreparer;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.OnExecutionGenerator;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.jdbc.Expectation;
|
||||
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
|
||||
import org.hibernate.sql.model.ast.builder.TableInsertBuilderStandard;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
import static java.sql.Statement.NO_GENERATED_KEYS;
|
||||
import static org.hibernate.id.IdentifierGeneratorHelper.getGeneratedIdentity;
|
||||
import static org.hibernate.generator.values.internal.GeneratedValuesHelper.getGeneratedValues;
|
||||
|
||||
/**
|
||||
* Specialized {@link IdentifierGeneratingInsert} which appends the database
|
||||
|
@ -40,41 +30,47 @@ import static org.hibernate.id.IdentifierGeneratorHelper.getGeneratedIdentity;
|
|||
* @author Christian Beikov
|
||||
*/
|
||||
public class SybaseJConnGetGeneratedKeysDelegate extends GetGeneratedKeysDelegate {
|
||||
private final PostInsertIdentityPersister persister;
|
||||
private final Dialect dialect;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #SybaseJConnGetGeneratedKeysDelegate(EntityPersister)} instead.
|
||||
*/
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
public SybaseJConnGetGeneratedKeysDelegate(PostInsertIdentityPersister persister, Dialect dialect) {
|
||||
super( persister, dialect, true );
|
||||
this.persister = persister;
|
||||
this.dialect = dialect;
|
||||
this( persister );
|
||||
}
|
||||
|
||||
public SybaseJConnGetGeneratedKeysDelegate(EntityPersister persister) {
|
||||
super( persister, true, EventType.INSERT );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String prepareIdentifierGeneratingInsert(String insertSQL) {
|
||||
return dialect.getIdentityColumnSupport().appendIdentitySelectToInsert( insertSQL );
|
||||
return dialect().getIdentityColumnSupport().appendIdentitySelectToInsert(
|
||||
( (BasicEntityIdentifierMapping) persister.getRootEntityDescriptor().getIdentifierMapping() ).getSelectionExpression(),
|
||||
insertSQL
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeAndExtract(
|
||||
String insertSql,
|
||||
PreparedStatement insertStatement,
|
||||
public GeneratedValues executeAndExtractReturning(
|
||||
String sql,
|
||||
PreparedStatement preparedStatement,
|
||||
SharedSessionContractImplementor session) {
|
||||
JdbcCoordinator jdbcCoordinator = session.getJdbcCoordinator();
|
||||
final JdbcServices jdbcServices = session.getJdbcServices();
|
||||
|
||||
ResultSet resultSet = jdbcCoordinator.getResultSetReturn().execute( insertStatement, insertSql );
|
||||
ResultSet resultSet = jdbcCoordinator.getResultSetReturn().execute( preparedStatement, sql );
|
||||
try {
|
||||
return getGeneratedIdentity( persister.getNavigableRole().getFullPath(), resultSet, persister, session );
|
||||
return getGeneratedValues( resultSet, persister, getTiming(), session );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
throw jdbcServices.getSqlExceptionHelper().convert(
|
||||
e,
|
||||
"Unable to extract generated-keys ResultSet",
|
||||
insertSql
|
||||
sql
|
||||
);
|
||||
}
|
||||
finally {
|
||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( resultSet, insertStatement );
|
||||
jdbcCoordinator.getLogicalConnection().getResourceRegistry().release( resultSet, preparedStatement );
|
||||
jdbcCoordinator.afterStatementExecution();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,40 +6,77 @@
|
|||
*/
|
||||
package org.hibernate.id.insert;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityRowIdMapping;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.model.ast.MutatingTableReference;
|
||||
import org.hibernate.sql.model.ast.TableInsert;
|
||||
import org.hibernate.sql.model.ast.builder.AbstractTableInsertBuilder;
|
||||
import org.hibernate.sql.model.internal.TableInsertStandard;
|
||||
|
||||
import static org.hibernate.generator.values.internal.GeneratedValuesHelper.getActualGeneratedModelPart;
|
||||
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class TableInsertReturningBuilder extends AbstractTableInsertBuilder {
|
||||
private final List<ColumnReference> generatedColumns;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #TableInsertReturningBuilder(EntityPersister, MutatingTableReference, List, SessionFactoryImplementor)} instead.
|
||||
*/
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
public TableInsertReturningBuilder(
|
||||
PostInsertIdentityPersister mutationTarget,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
super( mutationTarget, mutationTarget.getIdentifierTableMapping(), sessionFactory );
|
||||
this(
|
||||
mutationTarget,
|
||||
new MutatingTableReference( mutationTarget.getIdentifierTableMapping() ),
|
||||
new ArrayList<>(),
|
||||
sessionFactory
|
||||
);
|
||||
final List<? extends ModelPart> insertGeneratedProperties = getMutationTarget().getInsertGeneratedProperties();
|
||||
for ( final ModelPart prop : insertGeneratedProperties ) {
|
||||
generatedColumns.add( new ColumnReference(
|
||||
getMutatingTable(),
|
||||
getActualGeneratedModelPart( castNonNull( prop.asBasicValuedModelPart() ) )
|
||||
) );
|
||||
}
|
||||
// special case for rowid when the dialect supports it
|
||||
final EntityRowIdMapping rowIdMapping = getMutationTarget().getRowIdMapping();
|
||||
if ( rowIdMapping != null && getJdbcServices().getDialect().supportsInsertReturningRowId() ) {
|
||||
generatedColumns.add( new ColumnReference( getMutatingTable(), rowIdMapping ) );
|
||||
}
|
||||
}
|
||||
|
||||
public TableInsertReturningBuilder(
|
||||
EntityPersister mutationTarget,
|
||||
MutatingTableReference tableReference,
|
||||
List<ColumnReference> generatedColumns,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
super( mutationTarget, tableReference, sessionFactory );
|
||||
this.generatedColumns = generatedColumns;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PostInsertIdentityPersister getMutationTarget() {
|
||||
return (PostInsertIdentityPersister) super.getMutationTarget();
|
||||
protected EntityPersister getMutationTarget() {
|
||||
return (EntityPersister) super.getMutationTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableInsert buildMutation() {
|
||||
final BasicEntityIdentifierMapping identifierMapping =
|
||||
(BasicEntityIdentifierMapping) getMutationTarget().getIdentifierMapping();
|
||||
return new TableInsertStandard(
|
||||
getMutatingTable(),
|
||||
getMutationTarget(),
|
||||
combine( getValueBindingList(), getKeyBindingList(), getLobValueBindingList() ),
|
||||
Collections.singletonList( new ColumnReference( getMutatingTable(), identifierMapping ) ),
|
||||
generatedColumns,
|
||||
getParameters()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,19 +6,25 @@
|
|||
*/
|
||||
package org.hibernate.id.insert;
|
||||
|
||||
import org.hibernate.boot.model.relational.SqlStringGenerationContext;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.generator.values.GeneratedValueBasicResultBuilder;
|
||||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.jdbc.Expectation;
|
||||
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
|
||||
import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
|
||||
import org.hibernate.metamodel.mapping.EntityRowIdMapping;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.sql.model.ast.builder.TableInsertBuilderStandard;
|
||||
import org.hibernate.sql.model.ast.builder.TableMutationBuilder;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import static org.hibernate.generator.values.internal.GeneratedValuesHelper.getActualGeneratedModelPart;
|
||||
|
||||
/**
|
||||
* Uses a unique key of the inserted entity to locate the newly inserted row.
|
||||
|
@ -26,40 +32,57 @@ import java.sql.SQLException;
|
|||
* @author Gavin King
|
||||
*/
|
||||
public class UniqueKeySelectingDelegate extends AbstractSelectingDelegate {
|
||||
private final PostInsertIdentityPersister persister;
|
||||
private final Dialect dialect;
|
||||
|
||||
private final String[] uniqueKeyPropertyNames;
|
||||
private final Type[] uniqueKeyTypes;
|
||||
|
||||
private final String idSelectString;
|
||||
private final String selectString;
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #UniqueKeySelectingDelegate(EntityPersister, String[], EventType)} instead.
|
||||
*/
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
public UniqueKeySelectingDelegate(PostInsertIdentityPersister persister, Dialect dialect, String[] uniqueKeyPropertyNames) {
|
||||
super( persister );
|
||||
this( persister, uniqueKeyPropertyNames, EventType.INSERT );
|
||||
}
|
||||
|
||||
public UniqueKeySelectingDelegate(
|
||||
EntityPersister persister,
|
||||
String[] uniqueKeyPropertyNames,
|
||||
EventType timing) {
|
||||
super( persister, timing, true, true );
|
||||
|
||||
this.persister = persister;
|
||||
this.dialect = dialect;
|
||||
this.uniqueKeyPropertyNames = uniqueKeyPropertyNames;
|
||||
|
||||
idSelectString = persister.getSelectByUniqueKeyString( uniqueKeyPropertyNames );
|
||||
uniqueKeyTypes = new Type[ uniqueKeyPropertyNames.length ];
|
||||
for (int i = 0; i < uniqueKeyPropertyNames.length; i++ ) {
|
||||
for ( int i = 0; i < uniqueKeyPropertyNames.length; i++ ) {
|
||||
uniqueKeyTypes[i] = persister.getPropertyType( uniqueKeyPropertyNames[i] );
|
||||
}
|
||||
|
||||
final EntityRowIdMapping rowIdMapping = persister.getRowIdMapping();
|
||||
if ( !persister.isIdentifierAssignedByInsert()
|
||||
|| persister.getInsertGeneratedProperties().size() > 1
|
||||
|| rowIdMapping != null ) {
|
||||
final List<GeneratedValueBasicResultBuilder> resultBuilders = jdbcValuesMappingProducer.getResultBuilders();
|
||||
final List<String> columnNames = new ArrayList<>( resultBuilders.size() );
|
||||
for ( GeneratedValueBasicResultBuilder resultBuilder : resultBuilders ) {
|
||||
columnNames.add( getActualGeneratedModelPart( resultBuilder.getModelPart() ).getSelectionExpression() );
|
||||
}
|
||||
selectString = persister.getSelectByUniqueKeyString(
|
||||
uniqueKeyPropertyNames,
|
||||
columnNames.toArray( new String[0] )
|
||||
);
|
||||
}
|
||||
else {
|
||||
selectString = persister.getSelectByUniqueKeyString( uniqueKeyPropertyNames );
|
||||
}
|
||||
}
|
||||
|
||||
protected String getSelectSQL() {
|
||||
return idSelectString;
|
||||
}
|
||||
|
||||
@Override @Deprecated
|
||||
public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert(SqlStringGenerationContext context) {
|
||||
return new IdentifierGeneratingInsert( persister.getFactory() );
|
||||
return selectString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TableInsertBuilder createTableInsertBuilder(
|
||||
BasicEntityIdentifierMapping identifierMapping,
|
||||
public TableMutationBuilder<?> createTableMutationBuilder(
|
||||
Expectation expectation,
|
||||
SessionFactoryImplementor factory) {
|
||||
return new TableInsertBuilderStandard( persister, persister.getIdentifierTableMapping(), factory );
|
||||
|
|
|
@ -8,7 +8,6 @@ package org.hibernate.internal;
|
|||
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.persistence.EntityGraph;
|
||||
import org.hibernate.CacheMode;
|
||||
import org.hibernate.FlushMode;
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -31,16 +30,18 @@ import org.hibernate.engine.spi.PersistentAttributeInterceptable;
|
|||
import org.hibernate.engine.spi.PersistentAttributeInterceptor;
|
||||
import org.hibernate.engine.transaction.internal.jta.JtaStatusHelper;
|
||||
import org.hibernate.engine.transaction.jta.platform.spi.JtaPlatform;
|
||||
import org.hibernate.generator.BeforeExecutionGenerator;
|
||||
import org.hibernate.generator.Generator;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.graph.GraphSemantic;
|
||||
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||
import org.hibernate.loader.ast.spi.CascadingFetchProfile;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
import org.hibernate.generator.Generator;
|
||||
import org.hibernate.generator.BeforeExecutionGenerator;
|
||||
import org.hibernate.tuple.entity.EntityMetamodel;
|
||||
|
||||
import jakarta.persistence.EntityGraph;
|
||||
import jakarta.transaction.SystemException;
|
||||
|
||||
import static org.hibernate.engine.internal.ManagedTypeHelper.asPersistentAttributeInterceptable;
|
||||
|
@ -107,10 +108,11 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
persister.setValues( entity, state );
|
||||
}
|
||||
}
|
||||
persister.insert( id, state, entity, this );
|
||||
persister.insertReturning( id, state, entity, this );
|
||||
}
|
||||
else {
|
||||
id = persister.insert( state, entity, this );
|
||||
final GeneratedValues generatedValues = persister.insertReturning( state, entity, this );
|
||||
id = generatedValues.getGeneratedValue( persister.getIdentifierMapping() );
|
||||
}
|
||||
persister.setIdentifier( entity, id, this );
|
||||
return id;
|
||||
|
@ -165,7 +167,7 @@ public class StatelessSessionImpl extends AbstractSharedSessionContract implemen
|
|||
else {
|
||||
oldVersion = null;
|
||||
}
|
||||
persister.update( id, state, null, false, null, oldVersion, entity, null, this );
|
||||
persister.updateReturning( id, state, null, false, null, oldVersion, entity, null, this );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -59,4 +59,9 @@ public interface BasicValuedModelPart extends BasicValuedMapping, ValuedModelPar
|
|||
default boolean hasPartitionedSelectionMapping() {
|
||||
return isPartitioned();
|
||||
}
|
||||
|
||||
@Override
|
||||
default BasicValuedModelPart asBasicValuedModelPart() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,6 @@ package org.hibernate.metamodel.mapping;
|
|||
*
|
||||
* @see org.hibernate.annotations.RowId
|
||||
*/
|
||||
public interface EntityRowIdMapping extends VirtualModelPart, SelectableMapping {
|
||||
public interface EntityRowIdMapping extends BasicValuedModelPart, VirtualModelPart, SelectableMapping {
|
||||
String getRowIdName();
|
||||
}
|
||||
|
|
|
@ -148,6 +148,10 @@ public interface ModelPart extends MappingModelExpressible {
|
|||
return null;
|
||||
}
|
||||
|
||||
@Nullable default BasicValuedModelPart asBasicValuedModelPart() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A short hand form of {@link #breakDownJdbcValues(Object, int, Object, Object, JdbcValueBiConsumer, SharedSessionContractImplementor)},
|
||||
* that passes 0 as offset and null for the two values {@code X} and {@code Y}.
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.metamodel.mapping.internal;
|
|||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.cache.MutableCacheKeyBuilder;
|
||||
import org.hibernate.engine.FetchTiming;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.util.IndexedConsumer;
|
||||
|
@ -25,6 +26,9 @@ import org.hibernate.sql.ast.tree.from.TableGroup;
|
|||
import org.hibernate.sql.ast.tree.from.TableReference;
|
||||
import org.hibernate.sql.results.graph.DomainResult;
|
||||
import org.hibernate.sql.results.graph.DomainResultCreationState;
|
||||
import org.hibernate.sql.results.graph.Fetch;
|
||||
import org.hibernate.sql.results.graph.FetchOptions;
|
||||
import org.hibernate.sql.results.graph.FetchParent;
|
||||
import org.hibernate.sql.results.graph.basic.BasicResult;
|
||||
import org.hibernate.type.BasicType;
|
||||
import org.hibernate.type.descriptor.java.JavaType;
|
||||
|
@ -252,4 +256,35 @@ public class EntityRowIdMappingImpl implements EntityRowIdMapping {
|
|||
public JdbcMapping getJdbcMapping() {
|
||||
return rowIdType.getJdbcMapping();
|
||||
}
|
||||
|
||||
@Override
|
||||
public MappingType getMappedType() {
|
||||
return rowIdType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFetchableName() {
|
||||
return rowIdName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFetchableKey() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public FetchOptions getMappedFetchOptions() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Fetch generateFetch(
|
||||
FetchParent fetchParent,
|
||||
NavigablePath fetchablePath,
|
||||
FetchTiming fetchTiming,
|
||||
boolean selected,
|
||||
String resultVariable,
|
||||
DomainResultCreationState creationState) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,13 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.generator.Generator;
|
||||
import org.hibernate.generator.OnExecutionGenerator;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.loader.ast.internal.LoaderSelectBuilder;
|
||||
import org.hibernate.loader.ast.internal.NoCallbackExecutionContext;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.query.spi.QueryOptions;
|
||||
import org.hibernate.sql.ast.tree.select.SelectStatement;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
|
||||
|
@ -28,19 +31,24 @@ import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
|
|||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.exec.spi.JdbcParametersList;
|
||||
|
||||
import static org.hibernate.internal.util.NullnessUtil.castNonNull;
|
||||
import static org.hibernate.sql.results.spi.ListResultsConsumer.UniqueSemantic.FILTER;
|
||||
|
||||
/**
|
||||
* Responsible for retrieving {@linkplain OnExecutionGenerator database-generated}
|
||||
* attribute values after an {@code insert} or {@code update} statement is executed.
|
||||
* <p>
|
||||
* Note that this class has responsibility for regular attributes of the entity. The
|
||||
* primary key / id attribute is handled separately, being the responsibility of an
|
||||
* instance of {@link org.hibernate.id.insert.InsertGeneratedIdentifierDelegate}.
|
||||
* The values might have been retrieved early by an instance of {@link GeneratedValuesMutationDelegate},
|
||||
* which case the {@link GeneratedValues generatedValues} parameter of {@link #processGeneratedValues}
|
||||
* will already contain the values we need and this processor handles only the
|
||||
* {@link #setEntityAttributes setting of entity attributes}.
|
||||
* <p>
|
||||
* Note that the primary key / id attribute is always handled by the delegate.
|
||||
*
|
||||
* @see OnExecutionGenerator
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
@Incubating
|
||||
public class GeneratedValuesProcessor {
|
||||
|
@ -49,24 +57,23 @@ public class GeneratedValuesProcessor {
|
|||
private final List<AttributeMapping> generatedValuesToSelect;
|
||||
private final JdbcParametersList jdbcParameters;
|
||||
|
||||
private final EntityMappingType entityDescriptor;
|
||||
private final SessionFactoryImplementor sessionFactory;
|
||||
private final EntityPersister entityDescriptor;
|
||||
|
||||
public GeneratedValuesProcessor(
|
||||
EntityMappingType entityDescriptor,
|
||||
EntityPersister entityDescriptor,
|
||||
List<AttributeMapping> generatedAttributes,
|
||||
EventType timing,
|
||||
SessionFactoryImplementor sessionFactory) {
|
||||
this.entityDescriptor = entityDescriptor;
|
||||
this.sessionFactory = sessionFactory;
|
||||
|
||||
generatedValuesToSelect = getGeneratedAttributes( entityDescriptor, timing );
|
||||
if ( generatedValuesToSelect.isEmpty() ) {
|
||||
generatedValuesToSelect = generatedAttributes;
|
||||
if ( generatedValuesToSelect.isEmpty() || !needsSubsequentSelect( timing, generatedAttributes ) ) {
|
||||
selectStatement = null;
|
||||
jdbcSelect = null;
|
||||
this.jdbcParameters = JdbcParametersList.empty();
|
||||
jdbcParameters = null;
|
||||
}
|
||||
else {
|
||||
JdbcParametersList.Builder builder = JdbcParametersList.newBuilder();
|
||||
final JdbcParametersList.Builder builder = JdbcParametersList.newBuilder();
|
||||
|
||||
selectStatement = LoaderSelectBuilder.createSelect(
|
||||
entityDescriptor,
|
||||
|
@ -82,7 +89,30 @@ public class GeneratedValuesProcessor {
|
|||
jdbcSelect = sessionFactory.getJdbcServices().getJdbcEnvironment().getSqlAstTranslatorFactory()
|
||||
.buildSelectTranslator( sessionFactory, selectStatement )
|
||||
.translate( JdbcParameterBindings.NO_BINDINGS, QueryOptions.NONE );
|
||||
this.jdbcParameters = builder.build();
|
||||
jdbcParameters = builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean needsSubsequentSelect(EventType timing, List<AttributeMapping> generatedAttributes) {
|
||||
if ( timing == EventType.INSERT ) {
|
||||
return entityDescriptor.getInsertDelegate() == null
|
||||
|| !entityDescriptor.getInsertDelegate().supportsArbitraryValues()
|
||||
// Check if we need to select more properties than what is processed by the identity delegate.
|
||||
// This can happen for on-execution generated values on non-identifier tables
|
||||
|| generatedAttributes.size() > numberOfGeneratedNonIdentifierProperties( timing );
|
||||
}
|
||||
else {
|
||||
return entityDescriptor.getUpdateDelegate() == null;
|
||||
}
|
||||
}
|
||||
|
||||
private int numberOfGeneratedNonIdentifierProperties(EventType timing) {
|
||||
if ( timing == EventType.INSERT) {
|
||||
return entityDescriptor.getInsertGeneratedProperties().size()
|
||||
- ( entityDescriptor.isIdentifierAssignedByInsert() ? 1 : 0 );
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -91,7 +121,7 @@ public class GeneratedValuesProcessor {
|
|||
*
|
||||
* @return a list of {@link AttributeMapping}s.
|
||||
*/
|
||||
private static List<AttributeMapping> getGeneratedAttributes(EntityMappingType entityDescriptor, EventType timing) {
|
||||
public static List<AttributeMapping> getGeneratedAttributes(EntityMappingType entityDescriptor, EventType timing) {
|
||||
// todo (6.0): For now, we rely on the entity metamodel as composite attributes report
|
||||
// GenerationTiming.NEVER even if they have attributes that would need generation
|
||||
final Generator[] generators = entityDescriptor.getEntityPersister().getEntityMetamodel().getGenerators();
|
||||
|
@ -110,11 +140,23 @@ public class GeneratedValuesProcessor {
|
|||
/**
|
||||
* Obtain the generated values, and populate the snapshot and the fields of the entity instance.
|
||||
*/
|
||||
public void processGeneratedValues(Object entity, Object id, Object[] state, SharedSessionContractImplementor session) {
|
||||
if ( selectStatement != null && hasActualGeneratedValuesToSelect( session, entity ) ) {
|
||||
final List<Object[]> results = executeSelect( id, session );
|
||||
assert results.size() == 1;
|
||||
setEntityAttributes( entity, state, results.get(0) );
|
||||
public void processGeneratedValues(
|
||||
Object entity,
|
||||
Object id,
|
||||
Object[] state,
|
||||
GeneratedValues generatedValues,
|
||||
SharedSessionContractImplementor session) {
|
||||
if ( hasActualGeneratedValuesToSelect( session, entity ) ) {
|
||||
if ( selectStatement != null ) {
|
||||
final List<Object[]> results = executeSelect( id, session );
|
||||
assert results.size() == 1;
|
||||
setEntityAttributes( entity, state, results.get( 0 ) );
|
||||
}
|
||||
else {
|
||||
castNonNull( generatedValues );
|
||||
final List<Object> results = generatedValues.getGeneratedValues( generatedValuesToSelect );
|
||||
setEntityAttributes( entity, state, results.toArray( new Object[0] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,8 +211,4 @@ public class GeneratedValuesProcessor {
|
|||
public EntityMappingType getEntityDescriptor() {
|
||||
return entityDescriptor;
|
||||
}
|
||||
|
||||
public SessionFactoryImplementor getSessionFactory() {
|
||||
return sessionFactory;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,6 +104,9 @@ import org.hibernate.generator.EventType;
|
|||
import org.hibernate.generator.Generator;
|
||||
import org.hibernate.generator.OnExecutionGenerator;
|
||||
import org.hibernate.generator.internal.VersionGeneration;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.generator.values.internal.GeneratedValuesHelper;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.graph.spi.RootGraphImplementor;
|
||||
import org.hibernate.id.Assigned;
|
||||
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
|
||||
|
@ -112,7 +115,6 @@ import org.hibernate.id.IdentifierGenerator;
|
|||
import org.hibernate.id.OptimizableGenerator;
|
||||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.id.enhanced.Optimizer;
|
||||
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.FilterAliasGenerator;
|
||||
|
@ -162,7 +164,6 @@ import org.hibernate.metamodel.mapping.AttributeMapping;
|
|||
import org.hibernate.metamodel.mapping.AttributeMappingsList;
|
||||
import org.hibernate.metamodel.mapping.AttributeMappingsMap;
|
||||
import org.hibernate.metamodel.mapping.AttributeMetadata;
|
||||
import org.hibernate.metamodel.mapping.BasicValuedModelPart;
|
||||
import org.hibernate.metamodel.mapping.DiscriminatedAssociationModelPart;
|
||||
import org.hibernate.metamodel.mapping.DiscriminatorConverter;
|
||||
import org.hibernate.metamodel.mapping.DiscriminatorType;
|
||||
|
@ -316,6 +317,7 @@ import static org.hibernate.internal.util.collections.ArrayHelper.toIntArray;
|
|||
import static org.hibernate.internal.util.collections.ArrayHelper.toObjectArray;
|
||||
import static org.hibernate.internal.util.collections.ArrayHelper.toStringArray;
|
||||
import static org.hibernate.internal.util.collections.ArrayHelper.toTypeArray;
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.combine;
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.isNotEmpty;
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.setOfSize;
|
||||
import static org.hibernate.internal.util.collections.CollectionHelper.toSmallList;
|
||||
|
@ -426,10 +428,13 @@ public abstract class AbstractEntityPersister
|
|||
private final FilterHelper filterHelper;
|
||||
private volatile Set<String> affectingFetchProfileNames;
|
||||
|
||||
protected List<? extends ModelPart> insertGeneratedProperties;
|
||||
protected List<? extends ModelPart> updateGeneratedProperties;
|
||||
private GeneratedValuesProcessor insertGeneratedValuesProcessor;
|
||||
private GeneratedValuesProcessor updateGeneratedValuesProcessor;
|
||||
|
||||
private InsertGeneratedIdentifierDelegate identityDelegate;
|
||||
private GeneratedValuesMutationDelegate insertDelegate;
|
||||
private GeneratedValuesMutationDelegate updateDelegate;
|
||||
private String identitySelectString;
|
||||
|
||||
private boolean[] tableHasColumns;
|
||||
|
@ -2008,8 +2013,10 @@ public abstract class AbstractEntityPersister
|
|||
return select.addRestriction( rootTableKeyColumnNames ).toStatementString();
|
||||
}
|
||||
|
||||
private GeneratedValuesProcessor createGeneratedValuesProcessor(EventType timing) {
|
||||
return new GeneratedValuesProcessor( this, timing, getFactory() );
|
||||
private GeneratedValuesProcessor createGeneratedValuesProcessor(
|
||||
EventType timing,
|
||||
List<AttributeMapping> generatedAttributes) {
|
||||
return new GeneratedValuesProcessor( this, generatedAttributes, timing, getFactory() );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -2795,11 +2802,22 @@ public abstract class AbstractEntityPersister
|
|||
return select.toStatementString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectByUniqueKeyString(String[] propertyNames, String[] columnNames) {
|
||||
final SimpleSelect select = new SimpleSelect( getFactory() )
|
||||
.setTableName( getTableName( 0 ) )
|
||||
.addColumns( columnNames );
|
||||
for ( final String propertyName : propertyNames ) {
|
||||
select.addRestriction( getPropertyColumnNames( propertyName ) );
|
||||
}
|
||||
return select.toStatementString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an object
|
||||
*/
|
||||
@Override
|
||||
public void update(
|
||||
public GeneratedValues updateReturning(
|
||||
final Object id,
|
||||
final Object[] values,
|
||||
int[] dirtyAttributeIndexes,
|
||||
|
@ -2809,7 +2827,7 @@ public abstract class AbstractEntityPersister
|
|||
final Object object,
|
||||
final Object rowId,
|
||||
final SharedSessionContractImplementor session) throws HibernateException {
|
||||
updateCoordinator.coordinateUpdate(
|
||||
return updateCoordinator.coordinateUpdate(
|
||||
object,
|
||||
id,
|
||||
rowId,
|
||||
|
@ -2861,18 +2879,23 @@ public abstract class AbstractEntityPersister
|
|||
}
|
||||
|
||||
@Override
|
||||
public InsertGeneratedIdentifierDelegate getIdentityInsertDelegate() {
|
||||
return identityDelegate;
|
||||
public GeneratedValuesMutationDelegate getInsertDelegate() {
|
||||
return insertDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object insert(Object[] fields, Object object, SharedSessionContractImplementor session) {
|
||||
public GeneratedValuesMutationDelegate getUpdateDelegate() {
|
||||
return updateDelegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneratedValues insertReturning(Object[] fields, Object object, SharedSessionContractImplementor session) {
|
||||
return insertCoordinator.coordinateInsert( null, fields, object, session );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void insert(Object id, Object[] fields, Object object, SharedSessionContractImplementor session) {
|
||||
insertCoordinator.coordinateInsert( id, fields, object, session );
|
||||
public GeneratedValues insertReturning(Object id, Object[] fields, Object object, SharedSessionContractImplementor session) {
|
||||
return insertCoordinator.coordinateInsert( id, fields, object, session );
|
||||
}
|
||||
|
||||
protected EntityTableMapping[] getTableMappings() {
|
||||
|
@ -3404,13 +3427,36 @@ public abstract class AbstractEntityPersister
|
|||
}
|
||||
|
||||
private void doLateInit() {
|
||||
tableMappings = buildTableMappings();
|
||||
|
||||
final List<AttributeMapping> insertGeneratedAttributes = hasInsertGeneratedProperties() ?
|
||||
GeneratedValuesProcessor.getGeneratedAttributes( this, INSERT )
|
||||
: Collections.emptyList();
|
||||
final List<AttributeMapping> updateGeneratedAttributes = hasUpdateGeneratedProperties() ?
|
||||
GeneratedValuesProcessor.getGeneratedAttributes( this, UPDATE )
|
||||
: Collections.emptyList();
|
||||
|
||||
insertGeneratedProperties = initInsertGeneratedProperties( insertGeneratedAttributes );
|
||||
updateGeneratedProperties = initUpdateGeneratedProperties( updateGeneratedAttributes );
|
||||
|
||||
if ( isIdentifierAssignedByInsert() ) {
|
||||
final OnExecutionGenerator generator = (OnExecutionGenerator) getGenerator();
|
||||
identityDelegate = generator.getGeneratedIdentifierDelegate( this );
|
||||
insertDelegate = generator.getGeneratedIdentifierDelegate( this );
|
||||
identitySelectString = getIdentitySelectString( factory.getJdbcServices().getDialect() );
|
||||
}
|
||||
else {
|
||||
insertDelegate = GeneratedValuesHelper.getGeneratedValuesDelegate( this, INSERT );
|
||||
}
|
||||
|
||||
updateDelegate = GeneratedValuesHelper.getGeneratedValuesDelegate( this, UPDATE );
|
||||
|
||||
if ( hasInsertGeneratedProperties() ) {
|
||||
insertGeneratedValuesProcessor = createGeneratedValuesProcessor( INSERT, insertGeneratedAttributes );
|
||||
}
|
||||
if ( hasUpdateGeneratedProperties() ) {
|
||||
updateGeneratedValuesProcessor = createGeneratedValuesProcessor( UPDATE, updateGeneratedAttributes );
|
||||
}
|
||||
|
||||
tableMappings = buildTableMappings();
|
||||
insertCoordinator = buildInsertCoordinator();
|
||||
updateCoordinator = buildUpdateCoordinator();
|
||||
deleteCoordinator = buildDeleteCoordinator();
|
||||
|
@ -4666,11 +4712,39 @@ public abstract class AbstractEntityPersister
|
|||
Object id,
|
||||
Object entity,
|
||||
Object[] state,
|
||||
GeneratedValues generatedValues,
|
||||
SharedSessionContractImplementor session) {
|
||||
if ( insertGeneratedValuesProcessor == null ) {
|
||||
throw new UnsupportedOperationException( "Entity has no insert-generated properties - `" + getEntityName() + "`" );
|
||||
}
|
||||
insertGeneratedValuesProcessor.processGeneratedValues( entity, id, state, session );
|
||||
insertGeneratedValuesProcessor.processGeneratedValues( entity, id, state, generatedValues, session );
|
||||
}
|
||||
|
||||
protected List<? extends ModelPart> initInsertGeneratedProperties(List<AttributeMapping> generatedAttributes) {
|
||||
final int originalSize = generatedAttributes.size();
|
||||
final List<ModelPart> generatedBasicAttributes = new ArrayList<>( originalSize );
|
||||
for ( AttributeMapping generatedAttribute : generatedAttributes ) {
|
||||
// todo (7.0) : support non selectable mappings? Component, ToOneAttributeMapping, ...
|
||||
if ( generatedAttribute instanceof BasicValuedModelPart
|
||||
&& generatedAttribute.getContainingTableExpression().equals( getSubclassTableName( 0 ) ) ) {
|
||||
generatedBasicAttributes.add( generatedAttribute );
|
||||
}
|
||||
}
|
||||
|
||||
final List<ModelPart> identifierList = isIdentifierAssignedByInsert() ?
|
||||
List.of( getIdentifierMapping() ) :
|
||||
Collections.emptyList();
|
||||
if ( originalSize > 0 && generatedBasicAttributes.size() == originalSize ) {
|
||||
return Collections.unmodifiableList( combine( identifierList, generatedBasicAttributes ) );
|
||||
}
|
||||
else {
|
||||
return identifierList;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends ModelPart> getInsertGeneratedProperties() {
|
||||
return insertGeneratedProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -4678,11 +4752,35 @@ public abstract class AbstractEntityPersister
|
|||
Object id,
|
||||
Object entity,
|
||||
Object[] state,
|
||||
GeneratedValues generatedValues,
|
||||
SharedSessionContractImplementor session) {
|
||||
if ( updateGeneratedValuesProcessor == null ) {
|
||||
throw new AssertionFailure( "Entity has no update-generated properties - `" + getEntityName() + "`" );
|
||||
}
|
||||
updateGeneratedValuesProcessor.processGeneratedValues( entity, id, state, session );
|
||||
updateGeneratedValuesProcessor.processGeneratedValues( entity, id, state, generatedValues, session );
|
||||
}
|
||||
|
||||
protected List<? extends ModelPart> initUpdateGeneratedProperties(List<AttributeMapping> generatedAttributes) {
|
||||
final int originalSize = generatedAttributes.size();
|
||||
final List<ModelPart> generatedBasicAttributes = new ArrayList<>( originalSize );
|
||||
for ( AttributeMapping generatedAttribute : generatedAttributes ) {
|
||||
if ( generatedAttribute instanceof SelectableMapping
|
||||
&& ( (SelectableMapping) generatedAttribute ).getContainingTableExpression().equals( getSubclassTableName( 0 ) ) ) {
|
||||
generatedBasicAttributes.add( generatedAttribute );
|
||||
}
|
||||
}
|
||||
|
||||
if ( generatedBasicAttributes.size() == originalSize ) {
|
||||
return Collections.unmodifiableList( generatedBasicAttributes );
|
||||
}
|
||||
else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<? extends ModelPart> getUpdateGeneratedProperties() {
|
||||
return updateGeneratedProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -4998,12 +5096,6 @@ public abstract class AbstractEntityPersister
|
|||
visitSubTypeAttributeMappings( builder::add );
|
||||
assert superMappingType != null || builder.assertFetchableIndexes();
|
||||
staticFetchableList = builder.build();
|
||||
if ( hasInsertGeneratedProperties() ) {
|
||||
insertGeneratedValuesProcessor = createGeneratedValuesProcessor( INSERT );
|
||||
}
|
||||
if ( hasUpdateGeneratedProperties() ) {
|
||||
updateGeneratedValuesProcessor = createGeneratedValuesProcessor( UPDATE );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
package org.hibernate.persister.entity;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
@ -29,9 +30,11 @@ import org.hibernate.engine.spi.SessionFactoryImplementor;
|
|||
import org.hibernate.engine.spi.SessionImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.generator.Generator;
|
||||
import org.hibernate.generator.BeforeExecutionGenerator;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.generator.Generator;
|
||||
import org.hibernate.generator.internal.VersionGeneration;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.id.IdentifierGenerator;
|
||||
import org.hibernate.internal.FilterAliasGenerator;
|
||||
import org.hibernate.internal.TableGroupFilterAliasGenerator;
|
||||
|
@ -41,9 +44,10 @@ import org.hibernate.loader.ast.spi.NaturalIdLoader;
|
|||
import org.hibernate.metadata.ClassMetadata;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.internal.InFlightEntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.internal.MappingModelCreationProcess;
|
||||
import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
import org.hibernate.persister.walking.spi.AttributeSource;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
|
@ -104,7 +108,7 @@ import org.hibernate.type.descriptor.java.VersionJavaType;
|
|||
* @see org.hibernate.persister.spi.PersisterFactory
|
||||
* @see org.hibernate.persister.spi.PersisterClassResolver
|
||||
*/
|
||||
public interface EntityPersister extends EntityMappingType, RootTableGroupProducer, AttributeSource {
|
||||
public interface EntityPersister extends EntityMappingType, EntityMutationTarget, RootTableGroupProducer, AttributeSource {
|
||||
|
||||
/**
|
||||
* Finish the initialization of this object.
|
||||
|
@ -624,22 +628,50 @@ public interface EntityPersister extends EntityMappingType, RootTableGroupProduc
|
|||
/**
|
||||
* Persist an instance
|
||||
*/
|
||||
void insert(Object id, Object[] fields, Object object, SharedSessionContractImplementor session);
|
||||
default void insert(Object id, Object[] fields, Object object, SharedSessionContractImplementor session) {
|
||||
insertReturning( id, fields, object, session );
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist an instance
|
||||
*/
|
||||
GeneratedValues insertReturning(Object id, Object[] fields, Object object, SharedSessionContractImplementor session);
|
||||
|
||||
/**
|
||||
* Persist an instance
|
||||
*/
|
||||
default Object insert(Object[] fields, Object object, SharedSessionContractImplementor session) {
|
||||
final GeneratedValues generatedValues = insertReturning( fields, object, session );
|
||||
return generatedValues.getGeneratedValue( getIdentifierMapping() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist an instance, using a natively generated identifier (optional operation)
|
||||
*/
|
||||
Object insert(Object[] fields, Object object, SharedSessionContractImplementor session);
|
||||
GeneratedValues insertReturning(Object[] fields, Object object, SharedSessionContractImplementor session);
|
||||
|
||||
/**
|
||||
* Delete a persistent instance
|
||||
*/
|
||||
void delete(Object id, Object version, Object object, SharedSessionContractImplementor session);
|
||||
|
||||
default void update(
|
||||
Object id,
|
||||
Object[] fields,
|
||||
int[] dirtyFields,
|
||||
boolean hasDirtyCollection,
|
||||
Object[] oldFields,
|
||||
Object oldVersion,
|
||||
Object object,
|
||||
Object rowId,
|
||||
SharedSessionContractImplementor session) {
|
||||
updateReturning( id, fields, dirtyFields, hasDirtyCollection, oldFields, oldVersion, object, rowId, session );
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a persistent instance
|
||||
*/
|
||||
void update(
|
||||
GeneratedValues updateReturning(
|
||||
Object id,
|
||||
Object[] fields,
|
||||
int[] dirtyFields,
|
||||
|
@ -896,8 +928,39 @@ public interface EntityPersister extends EntityMappingType, RootTableGroupProduc
|
|||
* Note, that because we update the PersistenceContext here, callers
|
||||
* need to take care that they have already written the initial snapshot
|
||||
* to the PersistenceContext before calling this method.
|
||||
* @deprecated Use {@link #processInsertGeneratedProperties(Object, Object, Object[], GeneratedValues, SharedSessionContractImplementor)} instead.
|
||||
*/
|
||||
void processInsertGeneratedProperties(Object id, Object entity, Object[] state, SharedSessionContractImplementor session);
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
default void processInsertGeneratedProperties(Object id, Object entity, Object[] state, SharedSessionContractImplementor session) {
|
||||
processInsertGeneratedProperties( id, entity, state, null, session );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the values of any insert generated properties through the provided
|
||||
* {@link GeneratedValues} or, when that's not available, by selecting them
|
||||
* back from the database, injecting these generated values into the
|
||||
* given entity as well as writing this state to the
|
||||
* {@link org.hibernate.engine.spi.PersistenceContext}.
|
||||
* <p>
|
||||
* Note, that because we update the PersistenceContext here, callers
|
||||
* need to take care that they have already written the initial snapshot
|
||||
* to the PersistenceContext before calling this method.
|
||||
*/
|
||||
default void processInsertGeneratedProperties(
|
||||
Object id,
|
||||
Object entity,
|
||||
Object[] state,
|
||||
GeneratedValues generatedValues,
|
||||
SharedSessionContractImplementor session) {
|
||||
}
|
||||
|
||||
default List<? extends ModelPart> getGeneratedProperties(EventType timing) {
|
||||
return timing == EventType.INSERT ? getInsertGeneratedProperties() : getUpdateGeneratedProperties();
|
||||
}
|
||||
|
||||
default List<? extends ModelPart> getInsertGeneratedProperties() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a select to retrieve the values of any generated properties
|
||||
|
@ -908,8 +971,34 @@ public interface EntityPersister extends EntityMappingType, RootTableGroupProduc
|
|||
* Note, that because we update the PersistenceContext here, callers
|
||||
* need to take care that they have already written the initial snapshot
|
||||
* to the PersistenceContext before calling this method.
|
||||
* @deprecated Use {@link #processUpdateGeneratedProperties(Object, Object, Object[], GeneratedValues, SharedSessionContractImplementor)} instead.
|
||||
*/
|
||||
void processUpdateGeneratedProperties(Object id, Object entity, Object[] state, SharedSessionContractImplementor session);
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
default void processUpdateGeneratedProperties(Object id, Object entity, Object[] state, SharedSessionContractImplementor session) {
|
||||
processUpdateGeneratedProperties( id, entity, state, null, session );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the values of any update generated properties through the provided
|
||||
* {@link GeneratedValues} or, when that's not available, by selecting them
|
||||
* back from the database, injecting these generated values into the
|
||||
* given entity as well as writing this state to the
|
||||
* {@link org.hibernate.engine.spi.PersistenceContext}.
|
||||
* <p>
|
||||
* Note, that because we update the PersistenceContext here, callers
|
||||
* need to take care that they have already written the initial snapshot
|
||||
* to the PersistenceContext before calling this method.
|
||||
*/
|
||||
void processUpdateGeneratedProperties(
|
||||
Object id,
|
||||
Object entity,
|
||||
Object[] state,
|
||||
GeneratedValues generatedValues,
|
||||
SharedSessionContractImplementor session);
|
||||
|
||||
default List<? extends ModelPart> getUpdateGeneratedProperties() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -1122,4 +1211,50 @@ public interface EntityPersister extends EntityMappingType, RootTableGroupProduc
|
|||
*/
|
||||
@Incubating
|
||||
Iterable<UniqueKeyEntry> uniqueKeyEntries();
|
||||
|
||||
/**
|
||||
* Get a SQL select string that performs a select based on a unique
|
||||
* key determined by the given property name.
|
||||
*
|
||||
* @param propertyName The name of the property which maps to the
|
||||
* column(s) to use in the select statement restriction.
|
||||
* @return The SQL select string
|
||||
*/
|
||||
String getSelectByUniqueKeyString(String propertyName);
|
||||
|
||||
/**
|
||||
* Get a SQL select string that performs a select based on a unique
|
||||
* key determined by the given property names.
|
||||
*
|
||||
* @param propertyNames The names of the properties which maps to the
|
||||
* column(s) to use in the select statement restriction.
|
||||
* @return The SQL select string
|
||||
*/
|
||||
default String getSelectByUniqueKeyString(String[] propertyNames) {
|
||||
// default impl only for backward compatibility
|
||||
if ( propertyNames.length > 1 ) {
|
||||
throw new IllegalArgumentException( "support for multiple properties not implemented" );
|
||||
}
|
||||
return getSelectByUniqueKeyString( propertyNames[0] );
|
||||
}
|
||||
|
||||
String getSelectByUniqueKeyString(String[] propertyNames, String[] columnNames);
|
||||
|
||||
|
||||
/**
|
||||
* The names of the primary key columns in the root table.
|
||||
*
|
||||
* @return The primary key column names.
|
||||
*/
|
||||
String[] getRootTableKeyColumnNames();
|
||||
|
||||
/**
|
||||
* Get the database-specific SQL command to retrieve the last
|
||||
* generated IDENTITY value.
|
||||
*
|
||||
* @return The SQL command string
|
||||
*/
|
||||
String getIdentitySelectString();
|
||||
|
||||
String[] getIdentifierColumnNames();
|
||||
}
|
||||
|
|
|
@ -43,7 +43,6 @@ import org.hibernate.metamodel.mapping.EntityDiscriminatorMapping;
|
|||
import org.hibernate.metamodel.mapping.EntityIdentifierMapping;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.EntityVersionMapping;
|
||||
import org.hibernate.metamodel.mapping.JdbcMapping;
|
||||
import org.hibernate.metamodel.mapping.TableDetails;
|
||||
import org.hibernate.metamodel.mapping.internal.BasicEntityIdentifierMappingImpl;
|
||||
import org.hibernate.metamodel.mapping.internal.CaseStatementDiscriminatorMappingImpl;
|
||||
|
@ -75,7 +74,6 @@ import org.hibernate.type.spi.TypeConfiguration;
|
|||
import org.jboss.logging.Logger;
|
||||
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static org.hibernate.internal.util.collections.ArrayHelper.indexOf;
|
||||
import static org.hibernate.internal.util.collections.ArrayHelper.to2DStringArray;
|
||||
import static org.hibernate.internal.util.collections.ArrayHelper.toIntArray;
|
||||
import static org.hibernate.internal.util.collections.ArrayHelper.toStringArray;
|
||||
|
|
|
@ -9,10 +9,12 @@ package org.hibernate.persister.entity.mutation;
|
|||
import org.hibernate.Incubating;
|
||||
import org.hibernate.annotations.Table;
|
||||
import org.hibernate.engine.jdbc.mutation.MutationExecutor;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
|
||||
import org.hibernate.metamodel.mapping.EntityMappingType;
|
||||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.sql.model.MutationTarget;
|
||||
import org.hibernate.sql.model.MutationType;
|
||||
|
||||
/**
|
||||
* Anything that can be the target of {@linkplain MutationExecutor mutations}
|
||||
|
@ -47,6 +49,30 @@ public interface EntityMutationTarget extends MutationTarget<EntityTableMapping>
|
|||
/**
|
||||
* The delegate for executing inserts against the root table for
|
||||
* targets defined using post-insert id generation
|
||||
*
|
||||
* @deprecated use {@link #getInsertDelegate()} instead
|
||||
*/
|
||||
InsertGeneratedIdentifierDelegate getIdentityInsertDelegate();
|
||||
@Deprecated( forRemoval = true, since = "6.5" )
|
||||
default InsertGeneratedIdentifierDelegate getIdentityInsertDelegate() {
|
||||
final GeneratedValuesMutationDelegate insertDelegate = getInsertDelegate();
|
||||
if ( insertDelegate instanceof InsertGeneratedIdentifierDelegate ) {
|
||||
return (InsertGeneratedIdentifierDelegate) insertDelegate;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
GeneratedValuesMutationDelegate getInsertDelegate();
|
||||
|
||||
GeneratedValuesMutationDelegate getUpdateDelegate();
|
||||
|
||||
default GeneratedValuesMutationDelegate getMutationDelegate(MutationType mutationType) {
|
||||
switch ( mutationType ) {
|
||||
case INSERT:
|
||||
return getInsertDelegate();
|
||||
case UPDATE:
|
||||
return getUpdateDelegate();
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,8 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|||
import org.hibernate.generator.BeforeExecutionGenerator;
|
||||
import org.hibernate.generator.Generator;
|
||||
import org.hibernate.generator.OnExecutionGenerator;
|
||||
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.metamodel.mapping.AttributeMapping;
|
||||
import org.hibernate.metamodel.mapping.AttributeMappingsList;
|
||||
import org.hibernate.metamodel.mapping.BasicEntityIdentifierMapping;
|
||||
|
@ -36,6 +37,7 @@ import org.hibernate.sql.model.ValuesAnalysis;
|
|||
import org.hibernate.sql.model.ast.builder.MutationGroupBuilder;
|
||||
import org.hibernate.sql.model.ast.builder.TableInsertBuilder;
|
||||
import org.hibernate.sql.model.ast.builder.TableInsertBuilderStandard;
|
||||
import org.hibernate.sql.model.ast.builder.TableMutationBuilder;
|
||||
import org.hibernate.tuple.entity.EntityMetamodel;
|
||||
|
||||
import static org.hibernate.generator.EventType.INSERT;
|
||||
|
@ -55,8 +57,8 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
|
|||
public InsertCoordinator(AbstractEntityPersister entityPersister, SessionFactoryImplementor factory) {
|
||||
super( entityPersister, factory );
|
||||
|
||||
if ( entityPersister.hasInsertGeneratedProperties() ) {
|
||||
// disable batching in case of insert generated properties
|
||||
if ( entityPersister.isIdentifierAssignedByInsert() || entityPersister.hasInsertGeneratedProperties() ) {
|
||||
// disable batching in case of insert generated identifier or properties
|
||||
batchKey = null;
|
||||
}
|
||||
else {
|
||||
|
@ -96,7 +98,7 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
|
|||
*
|
||||
* @return The id
|
||||
*/
|
||||
public Object coordinateInsert(
|
||||
public GeneratedValues coordinateInsert(
|
||||
Object id,
|
||||
Object[] values,
|
||||
Object entity,
|
||||
|
@ -154,7 +156,7 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
|
|||
}
|
||||
}
|
||||
|
||||
protected Object doStaticInserts(Object id, Object[] values, Object object, SharedSessionContractImplementor session) {
|
||||
protected GeneratedValues doStaticInserts(Object id, Object[] values, Object object, SharedSessionContractImplementor session) {
|
||||
final InsertValuesAnalysis insertValuesAnalysis = new InsertValuesAnalysis( entityPersister(), values );
|
||||
|
||||
final TableInclusionChecker tableInclusionChecker = getTableInclusionChecker( insertValuesAnalysis );
|
||||
|
@ -220,7 +222,7 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
|
|||
}
|
||||
|
||||
if ( id == null ) {
|
||||
assert entityPersister().getIdentityInsertDelegate() != null;
|
||||
assert entityPersister().getInsertDelegate() != null;
|
||||
}
|
||||
else {
|
||||
for ( int position = 0; position < mutationGroup.getNumberOfOperations(); position++ ) {
|
||||
|
@ -277,7 +279,7 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
|
|||
}
|
||||
}
|
||||
|
||||
protected Object doDynamicInserts(
|
||||
protected GeneratedValues doDynamicInserts(
|
||||
Object id,
|
||||
Object[] values,
|
||||
Object object,
|
||||
|
@ -361,12 +363,10 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
|
|||
return createOperationGroup( null, insertGroupBuilder.buildMutationGroup() );
|
||||
}
|
||||
|
||||
private TableInsertBuilder createTableInsertBuilder(EntityTableMapping tableMapping, boolean forceIdentifierBinding) {
|
||||
final InsertGeneratedIdentifierDelegate identityDelegate = entityPersister().getIdentityInsertDelegate();
|
||||
if ( tableMapping.isIdentifierTable() && identityDelegate != null && !forceIdentifierBinding ) {
|
||||
final BasicEntityIdentifierMapping mapping =
|
||||
(BasicEntityIdentifierMapping) entityPersister().getIdentifierMapping();
|
||||
return identityDelegate.createTableInsertBuilder( mapping, tableMapping.getInsertExpectation(), factory() );
|
||||
private TableMutationBuilder<?> createTableInsertBuilder(EntityTableMapping tableMapping, boolean forceIdentifierBinding) {
|
||||
final GeneratedValuesMutationDelegate delegate = entityPersister().getInsertDelegate();
|
||||
if ( tableMapping.isIdentifierTable() && delegate != null && !forceIdentifierBinding ) {
|
||||
return delegate.createTableMutationBuilder( tableMapping.getInsertExpectation(), factory() );
|
||||
}
|
||||
else {
|
||||
return new TableInsertBuilderStandard( entityPersister(), tableMapping, factory() );
|
||||
|
@ -414,13 +414,21 @@ public class InsertCoordinator extends AbstractMutationCoordinator {
|
|||
entityPersister().addSoftDeleteToInsertGroup( insertGroupBuilder );
|
||||
|
||||
// add the keys
|
||||
final InsertGeneratedIdentifierDelegate identityDelegate = entityPersister().getIdentityInsertDelegate();
|
||||
insertGroupBuilder.forEachTableMutationBuilder( (tableMutationBuilder) -> {
|
||||
final TableInsertBuilder tableInsertBuilder = (TableInsertBuilder) tableMutationBuilder;
|
||||
final EntityTableMapping tableMapping = (EntityTableMapping) tableInsertBuilder.getMutatingTable().getTableMapping();
|
||||
//noinspection StatementWithEmptyBody
|
||||
if ( tableMapping.isIdentifierTable() && identityDelegate != null && !forceIdentifierBinding ) {
|
||||
// nothing to do - the builder already includes the identity handling
|
||||
if ( tableMapping.isIdentifierTable() && entityPersister().isIdentifierAssignedByInsert() && !forceIdentifierBinding ) {
|
||||
assert entityPersister().getInsertDelegate() != null;
|
||||
final OnExecutionGenerator generator = (OnExecutionGenerator) entityPersister().getGenerator();
|
||||
if ( generator.referenceColumnsInSql( dialect() ) ) {
|
||||
final BasicEntityIdentifierMapping identifierMapping = (BasicEntityIdentifierMapping) entityPersister().getIdentifierMapping();
|
||||
final String[] columnValues = generator.getReferencedColumnValues( dialect );
|
||||
tableMapping.getKeyMapping().forEachKeyColumn( (i, column) -> tableInsertBuilder.addKeyColumn(
|
||||
column.getColumnName(),
|
||||
columnValues[i],
|
||||
identifierMapping.getJdbcMapping()
|
||||
) );
|
||||
}
|
||||
}
|
||||
else {
|
||||
tableMapping.getKeyMapping().forEachKeyColumn( tableInsertBuilder::addKeyColumn );
|
||||
|
|
|
@ -11,6 +11,7 @@ import org.hibernate.persister.entity.AbstractEntityPersister;
|
|||
import org.hibernate.sql.model.MutationOperation;
|
||||
import org.hibernate.sql.model.ast.builder.AbstractTableUpdateBuilder;
|
||||
import org.hibernate.sql.model.ast.builder.TableMergeBuilder;
|
||||
import org.hibernate.sql.model.ast.builder.TableUpdateBuilder;
|
||||
|
||||
/**
|
||||
* Specialized {@link UpdateCoordinator} for {@code merge into}.
|
||||
|
|
|
@ -8,6 +8,7 @@ package org.hibernate.persister.entity.mutation;
|
|||
|
||||
import org.hibernate.Internal;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.sql.model.MutationOperationGroup;
|
||||
|
||||
/**
|
||||
|
@ -21,7 +22,7 @@ import org.hibernate.sql.model.MutationOperationGroup;
|
|||
public interface UpdateCoordinator {
|
||||
MutationOperationGroup getStaticUpdateGroup();
|
||||
|
||||
void coordinateUpdate(
|
||||
GeneratedValues coordinateUpdate(
|
||||
Object entity,
|
||||
Object id,
|
||||
Object rowId,
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.persister.entity.mutation;
|
||||
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.sql.model.internal.MutationOperationGroupFactory;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||
|
@ -28,8 +29,9 @@ public class UpdateCoordinatorNoOp implements UpdateCoordinator {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void coordinateUpdate(Object entity, Object id, Object rowId, Object[] values, Object oldVersion, Object[] incomingOldValues, int[] dirtyAttributeIndexes, boolean hasDirtyCollection, SharedSessionContractImplementor session) {
|
||||
public GeneratedValues coordinateUpdate(Object entity, Object id, Object rowId, Object[] values, Object oldVersion, Object[] incomingOldValues, int[] dirtyAttributeIndexes, boolean hasDirtyCollection, SharedSessionContractImplementor session) {
|
||||
// nothing to do
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -9,6 +9,7 @@ package org.hibernate.persister.entity.mutation;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -29,6 +30,8 @@ import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
|||
import org.hibernate.generator.BeforeExecutionGenerator;
|
||||
import org.hibernate.generator.Generator;
|
||||
import org.hibernate.generator.OnExecutionGenerator;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.internal.CoreLogging;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
|
@ -45,7 +48,7 @@ import org.hibernate.sql.model.MutationType;
|
|||
import org.hibernate.sql.model.ast.MutatingTableReference;
|
||||
import org.hibernate.sql.model.ast.builder.AbstractTableUpdateBuilder;
|
||||
import org.hibernate.sql.model.ast.builder.MutationGroupBuilder;
|
||||
import org.hibernate.sql.model.ast.builder.RestrictedTableMutationBuilder;
|
||||
import org.hibernate.sql.model.ast.builder.TableMutationBuilder;
|
||||
import org.hibernate.sql.model.ast.builder.TableUpdateBuilder;
|
||||
import org.hibernate.sql.model.ast.builder.TableUpdateBuilderSkipped;
|
||||
import org.hibernate.sql.model.ast.builder.TableUpdateBuilderStandard;
|
||||
|
@ -164,7 +167,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
}
|
||||
|
||||
@Override
|
||||
public void coordinateUpdate(
|
||||
public GeneratedValues coordinateUpdate(
|
||||
Object entity,
|
||||
Object id,
|
||||
Object rowId,
|
||||
|
@ -176,7 +179,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
SharedSessionContractImplementor session) {
|
||||
final EntityVersionMapping versionMapping = entityPersister().getVersionMapping();
|
||||
if ( versionMapping != null ) {
|
||||
final boolean isForcedVersionIncrement = handlePotentialImplicitForcedVersionIncrement(
|
||||
final Supplier<GeneratedValues> generatedValuesAccess = handlePotentialImplicitForcedVersionIncrement(
|
||||
entity,
|
||||
id,
|
||||
values,
|
||||
|
@ -185,8 +188,8 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
session,
|
||||
versionMapping
|
||||
);
|
||||
if ( isForcedVersionIncrement ) {
|
||||
return;
|
||||
if ( generatedValuesAccess != null ) {
|
||||
return generatedValuesAccess.get();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -239,7 +242,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
}
|
||||
|
||||
|
||||
performUpdate(
|
||||
return performUpdate(
|
||||
entity,
|
||||
id,
|
||||
rowId,
|
||||
|
@ -255,7 +258,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
);
|
||||
}
|
||||
|
||||
protected void performUpdate(
|
||||
protected GeneratedValues performUpdate(
|
||||
Object entity,
|
||||
Object id,
|
||||
Object rowId,
|
||||
|
@ -307,9 +310,10 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
//noinspection StatementWithEmptyBody
|
||||
if ( valuesAnalysis.tablesNeedingUpdate.isEmpty() ) {
|
||||
// nothing to do
|
||||
return null;
|
||||
}
|
||||
else if ( valuesAnalysis.needsDynamicUpdate() ) {
|
||||
doDynamicUpdate(
|
||||
return doDynamicUpdate(
|
||||
entity,
|
||||
id,
|
||||
rowId,
|
||||
|
@ -321,7 +325,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
);
|
||||
}
|
||||
else {
|
||||
doStaticUpdate(
|
||||
return doStaticUpdate(
|
||||
entity,
|
||||
id,
|
||||
rowId,
|
||||
|
@ -395,7 +399,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
}
|
||||
}
|
||||
|
||||
protected boolean handlePotentialImplicitForcedVersionIncrement(
|
||||
protected Supplier<GeneratedValues> handlePotentialImplicitForcedVersionIncrement(
|
||||
Object entity,
|
||||
Object id,
|
||||
Object[] values,
|
||||
|
@ -437,11 +441,11 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
if ( isSimpleVersionUpdate ) {
|
||||
// we have just the version being updated - use the special handling
|
||||
assert newVersion != null;
|
||||
doVersionUpdate( entity, id, newVersion, oldVersion, session );
|
||||
return true;
|
||||
final GeneratedValues generatedValues = doVersionUpdate( entity, id, newVersion, oldVersion, session );
|
||||
return () -> generatedValues;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -466,16 +470,16 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
: entityPersister().getPropertyUpdateability();
|
||||
}
|
||||
|
||||
protected void doVersionUpdate(
|
||||
protected GeneratedValues doVersionUpdate(
|
||||
Object entity,
|
||||
Object id,
|
||||
Object version,
|
||||
Object oldVersion,
|
||||
SharedSessionContractImplementor session) {
|
||||
doVersionUpdate( entity, id, version, oldVersion, true, session );
|
||||
return doVersionUpdate( entity, id, version, oldVersion, true, session );
|
||||
}
|
||||
|
||||
protected void doVersionUpdate(
|
||||
protected GeneratedValues doVersionUpdate(
|
||||
Object entity,
|
||||
Object id,
|
||||
Object version,
|
||||
|
@ -521,7 +525,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
);
|
||||
|
||||
try {
|
||||
mutationExecutor.execute(
|
||||
return mutationExecutor.execute(
|
||||
entity,
|
||||
null,
|
||||
(tableMapping) -> tableMapping.getTableName().equals( entityPersister().getIdentifierTableName() ),
|
||||
|
@ -749,7 +753,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
);
|
||||
}
|
||||
|
||||
protected void doStaticUpdate(
|
||||
protected GeneratedValues doStaticUpdate(
|
||||
Object entity,
|
||||
Object id,
|
||||
Object rowId,
|
||||
|
@ -774,8 +778,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
bindPartitionColumnValueBindings( oldValues, session, mutationExecutor.getJdbcValueBindings() );
|
||||
|
||||
try {
|
||||
//noinspection SuspiciousMethodCalls
|
||||
mutationExecutor.execute(
|
||||
return mutationExecutor.execute(
|
||||
entity,
|
||||
valuesAnalysis,
|
||||
valuesAnalysis.tablesNeedingUpdate::contains,
|
||||
|
@ -926,7 +929,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
}
|
||||
}
|
||||
|
||||
protected void doDynamicUpdate(
|
||||
protected GeneratedValues doDynamicUpdate(
|
||||
Object entity,
|
||||
Object id,
|
||||
Object rowId,
|
||||
|
@ -962,7 +965,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
bindPartitionColumnValueBindings( oldValues, session, mutationExecutor.getJdbcValueBindings() );
|
||||
|
||||
try {
|
||||
mutationExecutor.execute(
|
||||
return mutationExecutor.execute(
|
||||
entity,
|
||||
valuesAnalysis,
|
||||
(tableMapping) -> {
|
||||
|
@ -1040,14 +1043,14 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
|
||||
entityPersister().forEachMutableTable( (tableMapping) -> {
|
||||
final MutatingTableReference tableReference = new MutatingTableReference( tableMapping );
|
||||
final RestrictedTableMutationBuilder<?,?> tableUpdateBuilder;
|
||||
final TableMutationBuilder<?> tableUpdateBuilder;
|
||||
//noinspection SuspiciousMethodCalls
|
||||
if ( ! valuesAnalysis.tablesNeedingUpdate.contains( tableReference.getTableMapping() ) ) {
|
||||
// this table does not need updating
|
||||
tableUpdateBuilder = new TableUpdateBuilderSkipped( tableReference );
|
||||
}
|
||||
else {
|
||||
tableUpdateBuilder = newTableUpdateBuilder( tableMapping );
|
||||
tableUpdateBuilder = createTableUpdateBuilder( tableMapping );
|
||||
}
|
||||
updateGroupBuilder.addTableDetailsBuilder( tableUpdateBuilder );
|
||||
} );
|
||||
|
@ -1065,6 +1068,18 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
return createOperationGroup( valuesAnalysis, updateGroupBuilder.buildMutationGroup() );
|
||||
}
|
||||
|
||||
private TableMutationBuilder<?> createTableUpdateBuilder(EntityTableMapping tableMapping) {
|
||||
final GeneratedValuesMutationDelegate delegate = tableMapping.isIdentifierTable() ?
|
||||
entityPersister().getUpdateDelegate() :
|
||||
null;
|
||||
if ( delegate != null ) {
|
||||
return delegate.createTableMutationBuilder( tableMapping.getInsertExpectation(), factory() );
|
||||
}
|
||||
else {
|
||||
return newTableUpdateBuilder( tableMapping );
|
||||
}
|
||||
}
|
||||
|
||||
protected <O extends MutationOperation> AbstractTableUpdateBuilder<O> newTableUpdateBuilder(EntityTableMapping tableMapping) {
|
||||
return new TableUpdateBuilderStandard<>( entityPersister(), tableMapping, factory() );
|
||||
}
|
||||
|
@ -1624,7 +1639,7 @@ public class UpdateCoordinatorStandard extends AbstractMutationCoordinator imple
|
|||
|
||||
entityPersister().forEachMutableTable( (tableMapping) -> {
|
||||
// NOTE : TableUpdateBuilderStandard handles custom sql-update mappings
|
||||
updateGroupBuilder.addTableDetailsBuilder( newTableUpdateBuilder( tableMapping ) );
|
||||
updateGroupBuilder.addTableDetailsBuilder( createTableUpdateBuilder( tableMapping ) );
|
||||
} );
|
||||
|
||||
// next, iterate each attribute and build the SET and WHERE clauses
|
||||
|
|
|
@ -67,6 +67,24 @@ public class ResultsHelper {
|
|||
);
|
||||
}
|
||||
|
||||
public static Expression resolveSqlExpression(
|
||||
DomainResultCreationStateImpl resolver,
|
||||
TableReference tableReference,
|
||||
SelectableMapping selectableMapping,
|
||||
int valuesArrayPosition) {
|
||||
return resolver.resolveSqlExpression(
|
||||
createColumnReferenceKey(
|
||||
tableReference,
|
||||
selectableMapping.getSelectablePath(),
|
||||
selectableMapping.getJdbcMapping()
|
||||
),
|
||||
processingState -> new ResultSetMappingSqlSelection(
|
||||
valuesArrayPosition,
|
||||
selectableMapping.getJdbcMapping()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private ResultsHelper() {
|
||||
}
|
||||
|
||||
|
|
|
@ -22,9 +22,12 @@ import org.hibernate.engine.FetchTiming;
|
|||
import org.hibernate.engine.jdbc.spi.JdbcServices;
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.generator.BeforeExecutionGenerator;
|
||||
import org.hibernate.generator.Generator;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.id.BulkInsertionCapableIdentifierGenerator;
|
||||
import org.hibernate.id.OptimizableGenerator;
|
||||
import org.hibernate.id.PostInsertIdentityPersister;
|
||||
import org.hibernate.id.enhanced.Optimizer;
|
||||
import org.hibernate.id.insert.Binder;
|
||||
import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
|
||||
|
@ -37,6 +40,7 @@ import org.hibernate.metamodel.mapping.MappingModelExpressible;
|
|||
import org.hibernate.metamodel.mapping.ModelPartContainer;
|
||||
import org.hibernate.persister.entity.AbstractEntityPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
import org.hibernate.query.SemanticException;
|
||||
import org.hibernate.query.SortDirection;
|
||||
import org.hibernate.query.results.TableGroupImpl;
|
||||
|
@ -70,17 +74,12 @@ import org.hibernate.sql.exec.internal.JdbcParameterBindingImpl;
|
|||
import org.hibernate.sql.exec.internal.JdbcParameterBindingsImpl;
|
||||
import org.hibernate.sql.exec.internal.JdbcParameterImpl;
|
||||
import org.hibernate.sql.exec.spi.ExecutionContext;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperationQueryInsert;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperationQueryMutation;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperationQuerySelect;
|
||||
import org.hibernate.sql.exec.spi.JdbcOperationQueryUpdate;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBindings;
|
||||
import org.hibernate.sql.results.graph.basic.BasicFetch;
|
||||
import org.hibernate.sql.results.internal.SqlSelectionImpl;
|
||||
import org.hibernate.sql.results.spi.ListResultsConsumer;
|
||||
import org.hibernate.generator.Generator;
|
||||
import org.hibernate.generator.OnExecutionGenerator;
|
||||
import org.hibernate.generator.BeforeExecutionGenerator;
|
||||
import org.hibernate.type.descriptor.ValueBinder;
|
||||
|
||||
import static org.hibernate.generator.EventType.INSERT;
|
||||
|
@ -564,15 +563,16 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
|
|||
.translate( null, executionContext.getQueryOptions() );
|
||||
|
||||
if ( generator.generatedOnExecution() ) {
|
||||
final OnExecutionGenerator databaseGenerator = (OnExecutionGenerator) generator;
|
||||
final InsertGeneratedIdentifierDelegate identifierDelegate =
|
||||
databaseGenerator.getGeneratedIdentifierDelegate( (PostInsertIdentityPersister) entityPersister );
|
||||
final GeneratedValuesMutationDelegate insertDelegate = ( (EntityMutationTarget) entityDescriptor.getEntityPersister() ).getInsertDelegate();
|
||||
// todo 7.0 : InsertGeneratedIdentifierDelegate will be removed once we're going to handle
|
||||
// generated values within the jdbc insert operaetion itself
|
||||
final InsertGeneratedIdentifierDelegate identifierDelegate = (InsertGeneratedIdentifierDelegate) insertDelegate;
|
||||
final String finalSql = identifierDelegate.prepareIdentifierGeneratingInsert( jdbcInsert.getSqlString() );
|
||||
final BasicEntityIdentifierMapping identifierMapping =
|
||||
(BasicEntityIdentifierMapping) entityDescriptor.getIdentifierMapping();
|
||||
final ValueBinder jdbcValueBinder = identifierMapping.getJdbcMapping().getJdbcValueBinder();
|
||||
for ( Map.Entry<Object, Object> entry : entityTableToRootIdentity.entrySet() ) {
|
||||
final Object rootIdentity = identifierDelegate.performInsert(
|
||||
final GeneratedValues generatedValues = identifierDelegate.performInsertReturning(
|
||||
finalSql,
|
||||
session,
|
||||
new Binder() {
|
||||
|
@ -586,6 +586,7 @@ public class InsertExecutionDelegate implements TableBasedInsertHandler.Executio
|
|||
}
|
||||
}
|
||||
);
|
||||
final Object rootIdentity = generatedValues.getGeneratedValue( identifierMapping );
|
||||
entry.setValue( rootIdentity );
|
||||
}
|
||||
|
||||
|
|
|
@ -8737,6 +8737,10 @@ public abstract class AbstractSqlAstTranslator<T extends JdbcOperation> implemen
|
|||
if ( tableUpdate.getWhereFragment() != null ) {
|
||||
sqlBuffer.append( " and (" ).append( tableUpdate.getWhereFragment() ).append( ")" );
|
||||
}
|
||||
|
||||
if ( tableUpdate.getNumberOfReturningColumns() > 0 ) {
|
||||
visitReturningColumns( tableUpdate::getReturningColumns );
|
||||
}
|
||||
}
|
||||
finally {
|
||||
getCurrentClauseStack().pop();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.sql.model;
|
||||
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.persister.entity.mutation.EntityMutationTarget;
|
||||
|
||||
public interface EntityMutationOperationGroup extends MutationOperationGroup {
|
||||
|
@ -22,4 +23,7 @@ public interface EntityMutationOperationGroup extends MutationOperationGroup {
|
|||
return this;
|
||||
}
|
||||
|
||||
default GeneratedValuesMutationDelegate getMutationDelegate() {
|
||||
return getMutationTarget().getMutationDelegate( getMutationType() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,15 +57,12 @@ public interface PreparableMutationOperation extends MutationOperation {
|
|||
return false;
|
||||
}
|
||||
|
||||
if ( getMutationType() == MutationType.INSERT ) {
|
||||
// we cannot batch inserts which generate id values post-insert (IDENTITY, TRIGGER, etc)
|
||||
if ( getTableDetails().isIdentifierTable()
|
||||
&& getMutationTarget() instanceof EntityMutationTarget
|
||||
&& ( (EntityMutationTarget) getMutationTarget() ).getIdentityInsertDelegate() != null ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if ( getMutationType() == MutationType.UPDATE ) {
|
||||
// This should already be guaranteed by the batchKey being null
|
||||
assert !getTableDetails().isIdentifierTable() ||
|
||||
!( getMutationTarget() instanceof EntityMutationTarget
|
||||
&& ( (EntityMutationTarget) getMutationTarget() ).getMutationDelegate( getMutationType() ) != null );
|
||||
|
||||
if ( getMutationType() == MutationType.UPDATE ) {
|
||||
// we cannot batch updates against optional tables
|
||||
if ( getTableDetails().isOptional() ) {
|
||||
return false;
|
||||
|
|
|
@ -43,8 +43,6 @@ public interface TableInsert extends TableMutation<JdbcInsertMutation> {
|
|||
|
||||
/**
|
||||
* The columns to return from the insert.
|
||||
*
|
||||
* @see java.sql.Connection#prepareStatement(String, String[])
|
||||
*/
|
||||
List<ColumnReference> getReturningColumns();
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@ package org.hibernate.sql.model.ast;
|
|||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.internal.util.collections.CollectionHelper;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.model.MutationOperation;
|
||||
|
||||
/**
|
||||
|
@ -44,4 +46,26 @@ public interface TableUpdate<O extends MutationOperation>
|
|||
* @see #getValueBindings()
|
||||
*/
|
||||
void forEachValueBinding(BiConsumer<Integer, ColumnValueBinding> consumer);
|
||||
|
||||
/**
|
||||
* The columns to return from the insert.
|
||||
*/
|
||||
List<ColumnReference> getReturningColumns();
|
||||
|
||||
/**
|
||||
* The number of columns being returned
|
||||
*
|
||||
* @see #getReturningColumns
|
||||
*/
|
||||
default int getNumberOfReturningColumns() {
|
||||
final List<ColumnReference> returningColumns = getReturningColumns();
|
||||
return CollectionHelper.size( returningColumns );
|
||||
}
|
||||
|
||||
/**
|
||||
* Visit each return-column
|
||||
*
|
||||
* @see #getReturningColumns
|
||||
*/
|
||||
void forEachReturningColumn(BiConsumer<Integer,ColumnReference> consumer);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.sql.model.ast.builder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
|
@ -76,7 +77,8 @@ public class CollectionRowDeleteByUpdateSetNullBuilder<O extends MutationOperati
|
|||
getKeyRestrictionBindings(),
|
||||
getOptimisticLockBindings(),
|
||||
getWhereFragment(),
|
||||
null
|
||||
null,
|
||||
Collections.emptyList()
|
||||
) {
|
||||
@Override
|
||||
public Expectation getExpectation() {
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
package org.hibernate.sql.model.ast.builder;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
@ -96,7 +97,8 @@ public class TableUpdateBuilderStandard<O extends MutationOperation> extends Abs
|
|||
getKeyRestrictionBindings(),
|
||||
getOptimisticLockBindings(),
|
||||
whereFragment,
|
||||
null
|
||||
null,
|
||||
Collections.emptyList()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,12 @@
|
|||
*/
|
||||
package org.hibernate.sql.model.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.sql.ast.SqlAstWalker;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.model.MutationTarget;
|
||||
import org.hibernate.sql.model.ast.AbstractTableUpdate;
|
||||
import org.hibernate.sql.model.ast.ColumnValueBinding;
|
||||
|
@ -63,6 +66,16 @@ public class TableUpdateCustomSql
|
|||
return getMutatingTable().getTableMapping().getUpdateDetails().isCallable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ColumnReference> getReturningColumns() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachReturningColumn(BiConsumer<Integer, ColumnReference> consumer) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(SqlAstWalker walker) {
|
||||
walker.visitCustomTableUpdate( this );
|
||||
|
|
|
@ -13,6 +13,7 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.hibernate.jdbc.Expectation;
|
||||
import org.hibernate.sql.ast.SqlAstWalker;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.exec.spi.JdbcParameterBinder;
|
||||
import org.hibernate.sql.model.MutationOperation;
|
||||
import org.hibernate.sql.model.MutationTarget;
|
||||
|
@ -86,4 +87,14 @@ public class TableUpdateNoSet
|
|||
@Override
|
||||
public void forEachParameter(Consumer<ColumnValueParameter> consumer) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ColumnReference> getReturningColumns() {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachReturningColumn(BiConsumer<Integer, ColumnReference> consumer) {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,13 @@
|
|||
*/
|
||||
package org.hibernate.sql.model.internal;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
import org.hibernate.jdbc.Expectation;
|
||||
import org.hibernate.sql.ast.SqlAstWalker;
|
||||
import org.hibernate.sql.ast.tree.expression.ColumnReference;
|
||||
import org.hibernate.sql.model.MutationTarget;
|
||||
import org.hibernate.sql.model.ast.AbstractTableUpdate;
|
||||
import org.hibernate.sql.model.ast.ColumnValueBinding;
|
||||
|
@ -22,8 +25,8 @@ import org.hibernate.sql.model.jdbc.JdbcMutationOperation;
|
|||
*/
|
||||
public class TableUpdateStandard extends AbstractTableUpdate<JdbcMutationOperation> {
|
||||
private final String whereFragment;
|
||||
|
||||
private final Expectation expectation;
|
||||
private final List<ColumnReference> returningColumns;
|
||||
|
||||
public TableUpdateStandard(
|
||||
MutatingTableReference mutatingTable,
|
||||
|
@ -32,7 +35,17 @@ public class TableUpdateStandard extends AbstractTableUpdate<JdbcMutationOperati
|
|||
List<ColumnValueBinding> valueBindings,
|
||||
List<ColumnValueBinding> keyRestrictionBindings,
|
||||
List<ColumnValueBinding> optLockRestrictionBindings) {
|
||||
this( mutatingTable, mutationTarget, sqlComment, valueBindings, keyRestrictionBindings, optLockRestrictionBindings, null, null );
|
||||
this(
|
||||
mutatingTable,
|
||||
mutationTarget,
|
||||
sqlComment,
|
||||
valueBindings,
|
||||
keyRestrictionBindings,
|
||||
optLockRestrictionBindings,
|
||||
null,
|
||||
null,
|
||||
Collections.emptyList()
|
||||
);
|
||||
}
|
||||
|
||||
public TableUpdateStandard(
|
||||
|
@ -43,10 +56,12 @@ public class TableUpdateStandard extends AbstractTableUpdate<JdbcMutationOperati
|
|||
List<ColumnValueBinding> keyRestrictionBindings,
|
||||
List<ColumnValueBinding> optLockRestrictionBindings,
|
||||
String whereFragment,
|
||||
Expectation expectation) {
|
||||
Expectation expectation,
|
||||
List<ColumnReference> returningColumns) {
|
||||
super( mutatingTable, mutationTarget, sqlComment, valueBindings, keyRestrictionBindings, optLockRestrictionBindings );
|
||||
this.whereFragment = whereFragment;
|
||||
this.expectation = expectation;
|
||||
this.returningColumns = returningColumns;
|
||||
}
|
||||
|
||||
public TableUpdateStandard(
|
||||
|
@ -57,7 +72,17 @@ public class TableUpdateStandard extends AbstractTableUpdate<JdbcMutationOperati
|
|||
List<ColumnValueBinding> keyRestrictionBindings,
|
||||
List<ColumnValueBinding> optLockRestrictionBindings,
|
||||
List<ColumnValueParameter> parameters) {
|
||||
this( tableReference, mutationTarget, sqlComment, valueBindings, keyRestrictionBindings, optLockRestrictionBindings, parameters, null, null );
|
||||
this(
|
||||
tableReference,
|
||||
mutationTarget,
|
||||
sqlComment,
|
||||
valueBindings,
|
||||
keyRestrictionBindings,
|
||||
optLockRestrictionBindings,
|
||||
parameters,
|
||||
null,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
public TableUpdateStandard(
|
||||
|
@ -73,6 +98,7 @@ public class TableUpdateStandard extends AbstractTableUpdate<JdbcMutationOperati
|
|||
super( tableReference, mutationTarget, sqlComment, valueBindings, keyRestrictionBindings, optLockRestrictionBindings, parameters );
|
||||
this.whereFragment = whereFragment;
|
||||
this.expectation = expectation;
|
||||
this.returningColumns = Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -101,4 +127,14 @@ public class TableUpdateStandard extends AbstractTableUpdate<JdbcMutationOperati
|
|||
}
|
||||
return super.getExpectation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ColumnReference> getReturningColumns() {
|
||||
return returningColumns;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachReturningColumn(BiConsumer<Integer,ColumnReference> consumer) {
|
||||
forEachThing( returningColumns, consumer );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,8 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.id.IdentifierGenerator;
|
||||
import org.hibernate.internal.FilterAliasGenerator;
|
||||
import org.hibernate.internal.util.IndexedConsumer;
|
||||
|
@ -56,11 +58,13 @@ import org.hibernate.metamodel.mapping.JdbcMapping;
|
|||
import org.hibernate.metamodel.mapping.ModelPart;
|
||||
import org.hibernate.metamodel.mapping.NaturalIdMapping;
|
||||
import org.hibernate.metamodel.mapping.TableDetails;
|
||||
import org.hibernate.metamodel.mapping.ValuedModelPart;
|
||||
import org.hibernate.metamodel.model.domain.NavigableRole;
|
||||
import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.UniqueKeyEntry;
|
||||
import org.hibernate.persister.entity.mutation.EntityTableMapping;
|
||||
import org.hibernate.persister.spi.PersisterClassResolver;
|
||||
import org.hibernate.persister.spi.PersisterCreationContext;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
|
||||
|
@ -399,11 +403,12 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void insert(Object id, Object[] fields, Object object, SharedSessionContractImplementor session) {
|
||||
public GeneratedValues insertReturning(Object id, Object[] fields, Object object, SharedSessionContractImplementor session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable insert(Object[] fields, Object object, SharedSessionContractImplementor session) {
|
||||
public GeneratedValues insertReturning(Object[] fields, Object object, SharedSessionContractImplementor session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -412,7 +417,7 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void update(
|
||||
public GeneratedValues updateReturning(
|
||||
Object id,
|
||||
Object[] fields,
|
||||
int[] dirtyFields,
|
||||
|
@ -422,6 +427,7 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
|
|||
Object object,
|
||||
Object rowId,
|
||||
SharedSessionContractImplementor session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -605,11 +611,11 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void processInsertGeneratedProperties(Object id, Object entity, Object[] state, SharedSessionContractImplementor session) {
|
||||
public void processInsertGeneratedProperties(Object id, Object entity, Object[] state, GeneratedValues generatedValues, SharedSessionContractImplementor session) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processUpdateGeneratedProperties(Object id, Object entity, Object[] state, SharedSessionContractImplementor session) {
|
||||
public void processUpdateGeneratedProperties(Object id, Object entity, Object[] state, GeneratedValues generatedValues, SharedSessionContractImplementor session) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -759,6 +765,31 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
|
|||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectByUniqueKeyString(String propertyName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectByUniqueKeyString(String[] propertyNames, String[] columnNames) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getRootTableKeyColumnNames() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentitySelectString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getIdentifierColumnNames() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAffectedByEnabledFilters(LoadQueryInfluencers influencers) {
|
||||
return false;
|
||||
|
@ -788,6 +819,51 @@ public class GoofyPersisterClassProvider implements PersisterClassResolver {
|
|||
public JavaType getMappedJavaType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType getTargetPart() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachMutableTable(Consumer<EntityTableMapping> consumer) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachMutableTableReverse(Consumer<EntityTableMapping> consumer) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifierTableName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityTableMapping getIdentifierTableMapping() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPart getIdentifierDescriptor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSkippableTables() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneratedValuesMutationDelegate getInsertDelegate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneratedValuesMutationDelegate getUpdateDelegate() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NoopCollectionPersister implements CollectionPersister {
|
||||
|
|
|
@ -33,6 +33,8 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.id.IdentifierGenerator;
|
||||
import org.hibernate.internal.FilterAliasGenerator;
|
||||
import org.hibernate.internal.util.IndexedConsumer;
|
||||
|
@ -59,6 +61,7 @@ import org.hibernate.orm.test.jpa.SettingsGenerator;
|
|||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.UniqueKeyEntry;
|
||||
import org.hibernate.persister.entity.mutation.EntityTableMapping;
|
||||
import org.hibernate.persister.internal.PersisterClassResolverInitiator;
|
||||
import org.hibernate.persister.spi.PersisterClassResolver;
|
||||
import org.hibernate.persister.spi.PersisterCreationContext;
|
||||
|
@ -442,11 +445,12 @@ public class PersisterClassProviderTest {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void insert(Object id, Object[] fields, Object object, SharedSessionContractImplementor session) {
|
||||
public GeneratedValues insertReturning(Object id, Object[] fields, Object object, SharedSessionContractImplementor session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable insert(Object[] fields, Object object, SharedSessionContractImplementor session) {
|
||||
public GeneratedValues insertReturning(Object[] fields, Object object, SharedSessionContractImplementor session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -455,7 +459,8 @@ public class PersisterClassProviderTest {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void update(Object id, Object[] fields, int[] dirtyFields, boolean hasDirtyCollection, Object[] oldFields, Object oldVersion, Object object, Object rowId, SharedSessionContractImplementor session) {
|
||||
public GeneratedValues updateReturning(Object id, Object[] fields, int[] dirtyFields, boolean hasDirtyCollection, Object[] oldFields, Object oldVersion, Object object, Object rowId, SharedSessionContractImplementor session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -622,11 +627,11 @@ public class PersisterClassProviderTest {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void processInsertGeneratedProperties(Object id, Object entity, Object[] state, SharedSessionContractImplementor session) {
|
||||
public void processInsertGeneratedProperties(Object id, Object entity, Object[] state, GeneratedValues generatedValues, SharedSessionContractImplementor session) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processUpdateGeneratedProperties(Object id, Object entity, Object[] state, SharedSessionContractImplementor session) {
|
||||
public void processUpdateGeneratedProperties(Object id, Object entity, Object[] state, GeneratedValues generatedValues, SharedSessionContractImplementor session) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -730,6 +735,31 @@ public class PersisterClassProviderTest {
|
|||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectByUniqueKeyString(String propertyName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectByUniqueKeyString(String[] propertyNames, String[] columnNames) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getRootTableKeyColumnNames() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentitySelectString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getIdentifierColumnNames() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public CacheEntry buildCacheEntry(Object entity, Object[] state, Object version, SharedSessionContractImplementor session) {
|
||||
return null;
|
||||
|
@ -809,6 +839,51 @@ public class PersisterClassProviderTest {
|
|||
public JavaType getMappedJavaType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType getTargetPart() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachMutableTable(Consumer<EntityTableMapping> consumer) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachMutableTableReverse(Consumer<EntityTableMapping> consumer) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifierTableName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityTableMapping getIdentifierTableMapping() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPart getIdentifierDescriptor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSkippableTables() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneratedValuesMutationDelegate getInsertDelegate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneratedValuesMutationDelegate getUpdateDelegate() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static class GoofyException extends RuntimeException {
|
||||
|
|
|
@ -34,6 +34,8 @@ import org.hibernate.engine.spi.LoadQueryInfluencers;
|
|||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.event.spi.EventSource;
|
||||
import org.hibernate.generator.values.GeneratedValues;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.id.IdentifierGenerator;
|
||||
import org.hibernate.id.UUIDHexGenerator;
|
||||
import org.hibernate.internal.FilterAliasGenerator;
|
||||
|
@ -58,6 +60,7 @@ import org.hibernate.metamodel.model.domain.NavigableRole;
|
|||
import org.hibernate.metamodel.spi.EntityRepresentationStrategy;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.UniqueKeyEntry;
|
||||
import org.hibernate.persister.entity.mutation.EntityTableMapping;
|
||||
import org.hibernate.persister.spi.PersisterCreationContext;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableInsertStrategy;
|
||||
import org.hibernate.query.sqm.mutation.spi.SqmMultiTableMutationStrategy;
|
||||
|
@ -264,10 +267,10 @@ public class CustomPersister implements EntityPersister {
|
|||
return getPropertyValues( object );
|
||||
}
|
||||
|
||||
public void processInsertGeneratedProperties(Object id, Object entity, Object[] state, SharedSessionContractImplementor session) {
|
||||
public void processInsertGeneratedProperties(Object id, Object entity, Object[] state, GeneratedValues generatedValues, SharedSessionContractImplementor session) {
|
||||
}
|
||||
|
||||
public void processUpdateGeneratedProperties(Object id, Object entity, Object[] state, SharedSessionContractImplementor session) {
|
||||
public void processUpdateGeneratedProperties(Object id, Object entity, Object[] state, GeneratedValues generatedValues, SharedSessionContractImplementor session) {
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -500,7 +503,7 @@ public class CustomPersister implements EntityPersister {
|
|||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public void insert(
|
||||
public GeneratedValues insertReturning(
|
||||
Object id,
|
||||
Object[] fields,
|
||||
Object object,
|
||||
|
@ -508,9 +511,11 @@ public class CustomPersister implements EntityPersister {
|
|||
) throws HibernateException {
|
||||
|
||||
INSTANCES.put(id, ( (Custom) object ).clone() );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public Serializable insert(Object[] fields, Object object, SharedSessionContractImplementor session)
|
||||
public GeneratedValues insertReturning(Object[] fields, Object object, SharedSessionContractImplementor session)
|
||||
throws HibernateException {
|
||||
|
||||
throw new UnsupportedOperationException();
|
||||
|
@ -529,7 +534,7 @@ public class CustomPersister implements EntityPersister {
|
|||
/**
|
||||
* @see EntityPersister
|
||||
*/
|
||||
public void update(
|
||||
public GeneratedValues updateReturning(
|
||||
Object id,
|
||||
Object[] fields,
|
||||
int[] dirtyFields,
|
||||
|
@ -543,6 +548,7 @@ public class CustomPersister implements EntityPersister {
|
|||
|
||||
INSTANCES.put( id, ( (Custom) object ).clone() );
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static final BasicType<String> STRING_TYPE = new BasicTypeImpl<>(
|
||||
|
@ -851,6 +857,31 @@ public class CustomPersister implements EntityPersister {
|
|||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectByUniqueKeyString(String propertyName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSelectByUniqueKeyString(String[] propertyNames, String[] columnNames) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentitySelectString() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getIdentifierColumnNames() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getRootTableKeyColumnNames() {
|
||||
return new String[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAffectedByEntityGraph(LoadQueryInfluencers loadQueryInfluencers) {
|
||||
return loadQueryInfluencers.getEffectiveEntityGraph().getGraph() != null;
|
||||
|
@ -880,4 +911,49 @@ public class CustomPersister implements EntityPersister {
|
|||
public JavaType getMappedJavaType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityMappingType getTargetPart() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachMutableTable(Consumer<EntityTableMapping> consumer) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachMutableTableReverse(Consumer<EntityTableMapping> consumer) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIdentifierTableName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityTableMapping getIdentifierTableMapping() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelPart getIdentifierDescriptor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSkippableTables() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneratedValuesMutationDelegate getInsertDelegate() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneratedValuesMutationDelegate getUpdateDelegate() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,353 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.generated.delegate;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
import org.hibernate.annotations.Generated;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.annotations.RowId;
|
||||
import org.hibernate.annotations.SourceType;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.id.insert.UniqueKeySelectingDelegate;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.sql.model.MutationType;
|
||||
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.junit.DialectFeatureChecks;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.RequiresDialectFeature;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests {@link GeneratedValuesMutationDelegate efficient generated values retrieval}
|
||||
* when {@link GenerationType#IDENTITY identity} generated identifiers are involved.
|
||||
*
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
@DomainModel( annotatedClasses = {
|
||||
MutationDelegateIdentityTest.IdentityOnly.class,
|
||||
MutationDelegateIdentityTest.IdentityAndValues.class,
|
||||
MutationDelegateIdentityTest.IdentityAndValuesAndRowId.class,
|
||||
MutationDelegateIdentityTest.IdentityAndValuesAndRowIdAndNaturalId.class,
|
||||
} )
|
||||
@SessionFactory( useCollectingStatementInspector = true )
|
||||
@RequiresDialectFeature( feature = DialectFeatureChecks.SupportsIdentityColumns.class )
|
||||
// Batch size is only enabled to make sure it's ignored when using mutation delegates
|
||||
@ServiceRegistry( settings = @Setting( name = AvailableSettings.STATEMENT_BATCH_SIZE, value = "5" ) )
|
||||
public class MutationDelegateIdentityTest {
|
||||
@Test
|
||||
public void testInsertGeneratedIdentityOnly(SessionFactoryScope scope) {
|
||||
final GeneratedValuesMutationDelegate delegate = getDelegate( scope, IdentityOnly.class, MutationType.INSERT );
|
||||
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
|
||||
inspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
final IdentityOnly entity = new IdentityOnly();
|
||||
session.persist( entity );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getId() ).isNotNull();
|
||||
assertThat( entity.getName() ).isNull();
|
||||
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "insert" );
|
||||
inspector.assertExecutedCount( delegate != null ? 1 : 2 );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertGeneratedValuesAndIdentity(SessionFactoryScope scope) {
|
||||
final GeneratedValuesMutationDelegate delegate = getDelegate(
|
||||
scope,
|
||||
IdentityAndValues.class,
|
||||
MutationType.INSERT
|
||||
);
|
||||
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
|
||||
inspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
final IdentityAndValues entity = new IdentityAndValues();
|
||||
session.persist( entity );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getId() ).isNotNull();
|
||||
assertThat( entity.getName() ).isEqualTo( "default_name" );
|
||||
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "insert" );
|
||||
inspector.assertExecutedCount(
|
||||
delegate != null && delegate.supportsArbitraryValues() ? 1 : 2
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateGeneratedValuesAndIdentity(SessionFactoryScope scope) {
|
||||
final GeneratedValuesMutationDelegate delegate = getDelegate(
|
||||
scope,
|
||||
IdentityAndValues.class,
|
||||
MutationType.UPDATE
|
||||
);
|
||||
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
|
||||
final Integer id = scope.fromTransaction( session -> {
|
||||
final IdentityAndValues entity = new IdentityAndValues();
|
||||
session.persist( entity );
|
||||
session.flush();
|
||||
return entity.getId();
|
||||
} );
|
||||
|
||||
inspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
final IdentityAndValues entity = session.find( IdentityAndValues.class, id );
|
||||
entity.setData( "changed" );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getUpdateDate() ).isNotNull();
|
||||
|
||||
inspector.assertIsSelect( 0 );
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "update" );
|
||||
inspector.assertExecutedCount(
|
||||
delegate != null && delegate.supportsArbitraryValues() ? 2 : 3
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertGeneratedValuesAndIdentityAndRowId(SessionFactoryScope scope) {
|
||||
final GeneratedValuesMutationDelegate delegate = getDelegate(
|
||||
scope,
|
||||
IdentityAndValuesAndRowId.class,
|
||||
MutationType.INSERT
|
||||
);
|
||||
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
|
||||
inspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
final IdentityAndValuesAndRowId entity = new IdentityAndValuesAndRowId();
|
||||
session.persist( entity );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getId() ).isNotNull();
|
||||
assertThat( entity.getName() ).isEqualTo( "default_name" );
|
||||
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "insert" );
|
||||
inspector.assertExecutedCount(
|
||||
delegate != null && delegate.supportsArbitraryValues() ? 1 : 2
|
||||
);
|
||||
|
||||
final boolean shouldHaveRowId = delegate != null && delegate.supportsRowId()
|
||||
&& scope.getSessionFactory().getJdbcServices().getDialect().rowId( "" ) != null;
|
||||
if ( shouldHaveRowId ) {
|
||||
// assert row-id was populated in entity entry
|
||||
final PersistenceContext pc = session.getPersistenceContextInternal();
|
||||
final EntityEntry entry = pc.getEntry( entity );
|
||||
assertThat( entry.getRowId() ).isNotNull();
|
||||
}
|
||||
|
||||
// test update in same transaction
|
||||
inspector.clear();
|
||||
|
||||
entity.setData( "changed" );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getUpdateDate() ).isNotNull();
|
||||
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "update" );
|
||||
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "id_column", shouldHaveRowId ? 0 : 1 );
|
||||
} );
|
||||
scope.inSession( session -> assertThat( session.find(
|
||||
IdentityAndValuesAndRowId.class,
|
||||
1
|
||||
).getUpdateDate() ).isNotNull() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertGeneratedValuesAndIdentityAndRowIdAndNaturalId(SessionFactoryScope scope) {
|
||||
final GeneratedValuesMutationDelegate delegate = getDelegate(
|
||||
scope,
|
||||
IdentityAndValuesAndRowIdAndNaturalId.class,
|
||||
MutationType.INSERT
|
||||
);
|
||||
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
|
||||
inspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
final IdentityAndValuesAndRowIdAndNaturalId entity = new IdentityAndValuesAndRowIdAndNaturalId(
|
||||
"naturalid_1"
|
||||
);
|
||||
session.persist( entity );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getId() ).isNotNull();
|
||||
assertThat( entity.getName() ).isEqualTo( "default_name" );
|
||||
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "insert" );
|
||||
final boolean isUniqueKeyDelegate = delegate instanceof UniqueKeySelectingDelegate;
|
||||
inspector.assertExecutedCount(
|
||||
delegate == null || !delegate.supportsArbitraryValues() || isUniqueKeyDelegate ? 2 : 1
|
||||
);
|
||||
if ( isUniqueKeyDelegate ) {
|
||||
inspector.assertNumberOfOccurrenceInQueryNoSpace( 1, "data", 1 );
|
||||
inspector.assertNumberOfOccurrenceInQueryNoSpace( 1, "id_column", 0 );
|
||||
}
|
||||
|
||||
final boolean shouldHaveRowId = delegate != null && delegate.supportsRowId()
|
||||
&& scope.getSessionFactory().getJdbcServices().getDialect().rowId( "" ) != null;
|
||||
if ( shouldHaveRowId ) {
|
||||
// assert row-id was populated in entity entry
|
||||
final PersistenceContext pc = session.getPersistenceContextInternal();
|
||||
final EntityEntry entry = pc.getEntry( entity );
|
||||
assertThat( entry.getRowId() ).isNotNull();
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
private static GeneratedValuesMutationDelegate getDelegate(
|
||||
SessionFactoryScope scope,
|
||||
Class<?> entityClass,
|
||||
MutationType mutationType) {
|
||||
final EntityPersister entityDescriptor = scope.getSessionFactory()
|
||||
.getMappingMetamodel()
|
||||
.findEntityDescriptor( entityClass );
|
||||
return entityDescriptor.getMutationDelegate( mutationType );
|
||||
}
|
||||
|
||||
@Entity( name = "IdentityOnly" )
|
||||
public static class IdentityOnly {
|
||||
@Id
|
||||
@GeneratedValue( strategy = GenerationType.IDENTITY )
|
||||
private Integer id;
|
||||
|
||||
private String name;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "IdentityAndValues" )
|
||||
@SuppressWarnings( "unused" )
|
||||
public static class IdentityAndValues {
|
||||
@Id
|
||||
@GeneratedValue( strategy = GenerationType.IDENTITY )
|
||||
private Integer id;
|
||||
|
||||
@Generated( event = EventType.INSERT )
|
||||
@ColumnDefault( "'default_name'" )
|
||||
private String name;
|
||||
|
||||
@UpdateTimestamp( source = SourceType.DB )
|
||||
private Date updateDate;
|
||||
|
||||
private String data;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Date getUpdateDate() {
|
||||
return updateDate;
|
||||
}
|
||||
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
@RowId
|
||||
@Entity( name = "IdentityAndValuesAndRowId" )
|
||||
@SuppressWarnings( "unused" )
|
||||
public static class IdentityAndValuesAndRowId {
|
||||
@Id
|
||||
@Column( name = "id_column" )
|
||||
@GeneratedValue( strategy = GenerationType.IDENTITY )
|
||||
private Integer id;
|
||||
|
||||
@Generated( event = EventType.INSERT )
|
||||
@ColumnDefault( "'default_name'" )
|
||||
private String name;
|
||||
|
||||
@UpdateTimestamp( source = SourceType.DB )
|
||||
private Date updateDate;
|
||||
|
||||
private String data;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Date getUpdateDate() {
|
||||
return updateDate;
|
||||
}
|
||||
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
@RowId
|
||||
@Entity( name = "IdentityAndValuesAndRowIdAndNaturalId" )
|
||||
@SuppressWarnings( "unused" )
|
||||
public static class IdentityAndValuesAndRowIdAndNaturalId {
|
||||
@Id
|
||||
@Column( name = "id_column" )
|
||||
@GeneratedValue( strategy = GenerationType.IDENTITY )
|
||||
private Integer id;
|
||||
|
||||
@Generated( event = EventType.INSERT )
|
||||
@ColumnDefault( "'default_name'" )
|
||||
private String name;
|
||||
|
||||
@NaturalId
|
||||
private String data;
|
||||
|
||||
public IdentityAndValuesAndRowIdAndNaturalId() {
|
||||
}
|
||||
|
||||
private IdentityAndValuesAndRowIdAndNaturalId(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.generated.delegate;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
import org.hibernate.annotations.Generated;
|
||||
import org.hibernate.annotations.SourceType;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.sql.model.MutationType;
|
||||
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Inheritance;
|
||||
import jakarta.persistence.InheritanceType;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests {@link GeneratedValuesMutationDelegate efficient generated values retrieval}
|
||||
* with {@link InheritanceType#JOINED} inheritance structures.
|
||||
*
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
@DomainModel( annotatedClasses = {
|
||||
MutationDelegateJoinedInheritanceTest.BaseEntity.class,
|
||||
MutationDelegateJoinedInheritanceTest.ChildEntity.class,
|
||||
} )
|
||||
@SessionFactory( useCollectingStatementInspector = true )
|
||||
public class MutationDelegateJoinedInheritanceTest {
|
||||
@AfterAll
|
||||
public void tearDown(SessionFactoryScope scope) {
|
||||
scope.inTransaction( session -> session.createMutationQuery( "delete from BaseEntity" ).executeUpdate() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertBaseEntity(SessionFactoryScope scope) {
|
||||
final GeneratedValuesMutationDelegate delegate = getDelegate(
|
||||
scope,
|
||||
BaseEntity.class,
|
||||
MutationType.INSERT
|
||||
);
|
||||
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
|
||||
inspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
final BaseEntity entity = new BaseEntity();
|
||||
session.persist( entity );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getId() ).isNotNull();
|
||||
assertThat( entity.getName() ).isEqualTo( "default_name" );
|
||||
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "insert" );
|
||||
inspector.assertExecutedCount(
|
||||
delegate != null && delegate.supportsArbitraryValues() ? 1 : 2
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertChildEntity(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
|
||||
inspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
final ChildEntity entity = new ChildEntity();
|
||||
session.persist( entity );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getId() ).isNotNull();
|
||||
assertThat( entity.getName() ).isEqualTo( "default_name" );
|
||||
assertThat( entity.getChildName() ).isEqualTo( "default_child_name" );
|
||||
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "insert" );
|
||||
assertThat( inspector.getSqlQueries().get( 1 ) ).contains( "insert" );
|
||||
// Note: this is a current restriction, mutation delegates only retrieve generated values
|
||||
// on the "root" table, and we expect other values to be read through a subsequent select
|
||||
inspector.assertIsSelect( 2 );
|
||||
inspector.assertExecutedCount( 3 );
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateBaseEntity(SessionFactoryScope scope) {
|
||||
final GeneratedValuesMutationDelegate delegate = getDelegate(
|
||||
scope,
|
||||
BaseEntity.class,
|
||||
MutationType.UPDATE
|
||||
);
|
||||
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
|
||||
final Integer id = scope.fromTransaction( session -> {
|
||||
final BaseEntity entity = new BaseEntity();
|
||||
session.persist( entity );
|
||||
session.flush();
|
||||
return entity.getId();
|
||||
} );
|
||||
|
||||
inspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
final BaseEntity entity = session.find( BaseEntity.class, id );
|
||||
entity.setData( "changed" );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getUpdateDate() ).isNotNull();
|
||||
|
||||
inspector.assertIsSelect( 0 );
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "update" );
|
||||
inspector.assertExecutedCount(
|
||||
delegate != null && delegate.supportsArbitraryValues() ? 2 : 3
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateChildEntity(SessionFactoryScope scope) {
|
||||
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
|
||||
final Integer id = scope.fromTransaction( session -> {
|
||||
final ChildEntity entity = new ChildEntity();
|
||||
session.persist( entity );
|
||||
session.flush();
|
||||
return entity.getId();
|
||||
} );
|
||||
|
||||
inspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
final ChildEntity entity = session.find( ChildEntity.class, id );
|
||||
entity.setData( "changed" );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getUpdateDate() ).isNotNull();
|
||||
assertThat( entity.getChildUpdateDate() ).isNotNull();
|
||||
|
||||
inspector.assertIsSelect( 0 );
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "update" );
|
||||
// Note: this is a current restriction, mutation delegates only retrieve generated values
|
||||
// on the "root" table, and we expect other values to be read through a subsequent select
|
||||
inspector.assertIsSelect( 2 );
|
||||
inspector.assertExecutedCount( 3 );
|
||||
} );
|
||||
}
|
||||
|
||||
private static GeneratedValuesMutationDelegate getDelegate(
|
||||
SessionFactoryScope scope,
|
||||
@SuppressWarnings( "SameParameterValue" ) Class<?> entityClass,
|
||||
MutationType mutationType) {
|
||||
final EntityPersister entityDescriptor = scope.getSessionFactory()
|
||||
.getMappingMetamodel()
|
||||
.findEntityDescriptor( entityClass );
|
||||
return entityDescriptor.getMutationDelegate( mutationType );
|
||||
}
|
||||
|
||||
@Entity( name = "BaseEntity" )
|
||||
@Inheritance( strategy = InheritanceType.JOINED )
|
||||
@SuppressWarnings( "unused" )
|
||||
public static class BaseEntity {
|
||||
@Id
|
||||
@GeneratedValue( strategy = GenerationType.IDENTITY )
|
||||
private Integer id;
|
||||
|
||||
@Generated( event = EventType.INSERT )
|
||||
@ColumnDefault( "'default_name'" )
|
||||
private String name;
|
||||
|
||||
@UpdateTimestamp( source = SourceType.DB )
|
||||
private Date updateDate;
|
||||
|
||||
@SuppressWarnings( "FieldCanBeLocal" )
|
||||
private String data;
|
||||
|
||||
public Integer getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Date getUpdateDate() {
|
||||
return updateDate;
|
||||
}
|
||||
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "ChildEntity" )
|
||||
@SuppressWarnings( "unused" )
|
||||
public static class ChildEntity extends BaseEntity {
|
||||
@Generated( event = EventType.INSERT )
|
||||
@ColumnDefault( "'default_child_name'" )
|
||||
private String childName;
|
||||
|
||||
@UpdateTimestamp( source = SourceType.DB )
|
||||
private Date childUpdateDate;
|
||||
|
||||
public String getChildName() {
|
||||
return childName;
|
||||
}
|
||||
|
||||
public Date getChildUpdateDate() {
|
||||
return childUpdateDate;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,283 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
|
||||
* See the lgpl.txt file in the root directory or http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
*/
|
||||
package org.hibernate.orm.test.mapping.generated.delegate;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.hibernate.annotations.ColumnDefault;
|
||||
import org.hibernate.annotations.Generated;
|
||||
import org.hibernate.annotations.NaturalId;
|
||||
import org.hibernate.annotations.RowId;
|
||||
import org.hibernate.annotations.SourceType;
|
||||
import org.hibernate.annotations.UpdateTimestamp;
|
||||
import org.hibernate.cfg.AvailableSettings;
|
||||
import org.hibernate.engine.spi.EntityEntry;
|
||||
import org.hibernate.engine.spi.PersistenceContext;
|
||||
import org.hibernate.generator.EventType;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.id.insert.UniqueKeySelectingDelegate;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.sql.model.MutationType;
|
||||
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
import org.hibernate.testing.orm.junit.ServiceRegistry;
|
||||
import org.hibernate.testing.orm.junit.SessionFactory;
|
||||
import org.hibernate.testing.orm.junit.SessionFactoryScope;
|
||||
import org.hibernate.testing.orm.junit.Setting;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Generic tests regarding {@link GeneratedValuesMutationDelegate efficient generated values retrieval}.
|
||||
*
|
||||
* @author Marco Belladelli
|
||||
*/
|
||||
@DomainModel( annotatedClasses = {
|
||||
MutationDelegateTest.ValuesOnly.class,
|
||||
MutationDelegateTest.ValuesAndRowId.class,
|
||||
MutationDelegateTest.ValuesAndNaturalId.class,
|
||||
} )
|
||||
@SessionFactory( useCollectingStatementInspector = true )
|
||||
// Batch size is only enabled to make sure it's ignored when using mutation delegates
|
||||
@ServiceRegistry( settings = @Setting( name = AvailableSettings.STATEMENT_BATCH_SIZE, value = "5" ) )
|
||||
public class MutationDelegateTest {
|
||||
@Test
|
||||
public void testInsertGeneratedValues(SessionFactoryScope scope) {
|
||||
final GeneratedValuesMutationDelegate delegate = getDelegate( scope, ValuesOnly.class, MutationType.INSERT );
|
||||
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
|
||||
inspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
final ValuesOnly entity = new ValuesOnly( 1 );
|
||||
session.persist( entity );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getName() ).isEqualTo( "default_name" );
|
||||
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "insert" );
|
||||
inspector.assertExecutedCount(
|
||||
delegate != null && delegate.supportsArbitraryValues() ? 1 : 2
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateGeneratedValues(SessionFactoryScope scope) {
|
||||
final GeneratedValuesMutationDelegate delegate = getDelegate( scope, ValuesOnly.class, MutationType.UPDATE );
|
||||
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
|
||||
scope.inTransaction( session -> {
|
||||
final ValuesOnly entity = new ValuesOnly( 2 );
|
||||
session.persist( entity );
|
||||
} );
|
||||
|
||||
inspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
final ValuesOnly entity = session.find( ValuesOnly.class, 2 );
|
||||
entity.setData( "changed" );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getUpdateDate() ).isNotNull();
|
||||
|
||||
inspector.assertIsSelect( 0 );
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "update" );
|
||||
inspector.assertExecutedCount(
|
||||
delegate != null && delegate.supportsArbitraryValues() ? 2 : 3
|
||||
);
|
||||
} );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGeneratedValuesAndRowId(SessionFactoryScope scope) {
|
||||
final GeneratedValuesMutationDelegate delegate = getDelegate(
|
||||
scope,
|
||||
ValuesAndRowId.class,
|
||||
MutationType.INSERT
|
||||
);
|
||||
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
|
||||
inspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
final ValuesAndRowId entity = new ValuesAndRowId( 1 );
|
||||
session.persist( entity );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getName() ).isEqualTo( "default_name" );
|
||||
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "insert" );
|
||||
inspector.assertExecutedCount(
|
||||
delegate != null && delegate.supportsArbitraryValues() ? 1 : 2
|
||||
);
|
||||
|
||||
final boolean shouldHaveRowId = delegate != null && delegate.supportsRowId()
|
||||
&& scope.getSessionFactory().getJdbcServices().getDialect().rowId( "" ) != null;
|
||||
if ( shouldHaveRowId ) {
|
||||
// assert row-id was populated in entity entry
|
||||
final PersistenceContext pc = session.getPersistenceContextInternal();
|
||||
final EntityEntry entry = pc.getEntry( entity );
|
||||
assertThat( entry.getRowId() ).isNotNull();
|
||||
}
|
||||
|
||||
// test update in same transaction
|
||||
inspector.clear();
|
||||
|
||||
entity.setData( "changed" );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getUpdateDate() ).isNotNull();
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "update" );
|
||||
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "id_column", shouldHaveRowId ? 0 : 1 );
|
||||
} );
|
||||
scope.inSession( session -> assertThat( session.find( ValuesAndRowId.class, 1 ).getUpdateDate() ).isNotNull() );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInsertGeneratedValuesAndNaturalId(SessionFactoryScope scope) {
|
||||
final GeneratedValuesMutationDelegate delegate = getDelegate(
|
||||
scope,
|
||||
ValuesAndNaturalId.class,
|
||||
MutationType.INSERT
|
||||
);
|
||||
final SQLStatementInspector inspector = scope.getCollectingStatementInspector();
|
||||
inspector.clear();
|
||||
|
||||
scope.inTransaction( session -> {
|
||||
final ValuesAndNaturalId entity = new ValuesAndNaturalId( 1, "natural_1" );
|
||||
session.persist( entity );
|
||||
session.flush();
|
||||
|
||||
assertThat( entity.getName() ).isEqualTo( "default_name" );
|
||||
|
||||
assertThat( inspector.getSqlQueries().get( 0 ) ).contains( "insert" );
|
||||
final boolean isUniqueKeyDelegate = delegate instanceof UniqueKeySelectingDelegate;
|
||||
inspector.assertExecutedCount(
|
||||
delegate == null || isUniqueKeyDelegate ? 2 : 1
|
||||
);
|
||||
if ( isUniqueKeyDelegate ) {
|
||||
inspector.assertNumberOfOccurrenceInQueryNoSpace( 1, "data", 1 );
|
||||
inspector.assertNumberOfOccurrenceInQueryNoSpace( 1, "id_column", 0 );
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
private static GeneratedValuesMutationDelegate getDelegate(
|
||||
SessionFactoryScope scope,
|
||||
Class<?> entityClass,
|
||||
MutationType mutationType) {
|
||||
final EntityPersister entityDescriptor = scope.getSessionFactory()
|
||||
.getMappingMetamodel()
|
||||
.findEntityDescriptor( entityClass );
|
||||
return entityDescriptor.getMutationDelegate( mutationType );
|
||||
}
|
||||
|
||||
@Entity( name = "ValuesOnly" )
|
||||
@SuppressWarnings( "unused" )
|
||||
public static class ValuesOnly {
|
||||
@Id
|
||||
private Integer id;
|
||||
|
||||
@Generated( event = EventType.INSERT )
|
||||
@ColumnDefault( "'default_name'" )
|
||||
private String name;
|
||||
|
||||
@UpdateTimestamp( source = SourceType.DB )
|
||||
private Date updateDate;
|
||||
|
||||
@SuppressWarnings( "FieldCanBeLocal" )
|
||||
private String data;
|
||||
|
||||
public ValuesOnly() {
|
||||
}
|
||||
|
||||
private ValuesOnly(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Date getUpdateDate() {
|
||||
return updateDate;
|
||||
}
|
||||
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
@RowId
|
||||
@Entity( name = "ValuesAndRowId" )
|
||||
@SuppressWarnings( "unused" )
|
||||
public static class ValuesAndRowId {
|
||||
@Id
|
||||
@Column( name = "id_column" )
|
||||
private Integer id;
|
||||
|
||||
@Generated( event = EventType.INSERT )
|
||||
@ColumnDefault( "'default_name'" )
|
||||
private String name;
|
||||
|
||||
@UpdateTimestamp( source = SourceType.DB )
|
||||
private Date updateDate;
|
||||
|
||||
@SuppressWarnings( "FieldCanBeLocal" )
|
||||
private String data;
|
||||
|
||||
public ValuesAndRowId() {
|
||||
}
|
||||
|
||||
private ValuesAndRowId(Integer id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Date getUpdateDate() {
|
||||
return updateDate;
|
||||
}
|
||||
|
||||
public void setData(String data) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
@Entity( name = "ValuesAndNaturalId" )
|
||||
@SuppressWarnings( "unused" )
|
||||
public static class ValuesAndNaturalId {
|
||||
@Id
|
||||
@Column( name = "id_column" )
|
||||
private Integer id;
|
||||
|
||||
@Generated( event = EventType.INSERT )
|
||||
@ColumnDefault( "'default_name'" )
|
||||
private String name;
|
||||
|
||||
@NaturalId
|
||||
private String data;
|
||||
|
||||
public ValuesAndNaturalId() {
|
||||
}
|
||||
|
||||
private ValuesAndNaturalId(Integer id, String data) {
|
||||
this.id = id;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,9 @@ package org.hibernate.orm.test.rowid;
|
|||
|
||||
import org.hibernate.annotations.RowId;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.generator.values.GeneratedValuesMutationDelegate;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.sql.model.MutationType;
|
||||
|
||||
import org.hibernate.testing.jdbc.SQLStatementInspector;
|
||||
import org.hibernate.testing.orm.junit.DomainModel;
|
||||
|
@ -67,8 +70,8 @@ public class RowIdUpdateAndDeleteTest {
|
|||
simpleEntity.setStatus( "new_status" );
|
||||
inspector.clear();
|
||||
} );
|
||||
// the update should have used the primary key, as the row-id value is not available
|
||||
checkUpdateQuery( inspector, true );
|
||||
// the update should have used the primary key when the row-id value is not available
|
||||
checkUpdateQuery( inspector, scope, true );
|
||||
scope.inTransaction( session -> assertThat(
|
||||
session.find( SimpleEntity.class, 3L ).getStatus()
|
||||
).isEqualTo( "new_status" ) );
|
||||
|
@ -85,8 +88,8 @@ public class RowIdUpdateAndDeleteTest {
|
|||
session.remove( simpleEntity );
|
||||
inspector.clear();
|
||||
} );
|
||||
// the update should have used the primary key, as the row-id value is not available
|
||||
checkUpdateQuery( inspector, true );
|
||||
// the update should have used the primary key when the row-id value is not available
|
||||
checkUpdateQuery( inspector, scope, true );
|
||||
scope.inTransaction( session -> assertThat( session.find( SimpleEntity.class, 13L ) ).isNull() );
|
||||
}
|
||||
|
||||
|
@ -107,8 +110,8 @@ public class RowIdUpdateAndDeleteTest {
|
|||
parent.getChild().setStatus( "new_status" );
|
||||
inspector.clear();
|
||||
} );
|
||||
// the update should have used the primary key, as the row-id value is not available
|
||||
checkUpdateQuery( inspector, true );
|
||||
// the update should have used the primary key when the row-id value is not available
|
||||
checkUpdateQuery( inspector, scope, true );
|
||||
scope.inTransaction( session -> assertThat(
|
||||
session.find( SimpleEntity.class, 4L ).getStatus()
|
||||
).isEqualTo( "new_status" ) );
|
||||
|
@ -122,8 +125,7 @@ public class RowIdUpdateAndDeleteTest {
|
|||
simpleEntity.setStatus( "new_status" );
|
||||
inspector.clear();
|
||||
} );
|
||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||
checkUpdateQuery( inspector, dialect.rowId( "" ) == null );
|
||||
checkUpdateQuery( inspector, scope, false );
|
||||
scope.inTransaction( session -> assertThat(
|
||||
session.find( SimpleEntity.class, 1L ).getStatus()
|
||||
).isEqualTo( "new_status" ) );
|
||||
|
@ -137,8 +139,7 @@ public class RowIdUpdateAndDeleteTest {
|
|||
session.remove( simpleEntity );
|
||||
inspector.clear();
|
||||
} );
|
||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||
checkUpdateQuery( inspector, dialect.rowId( "" ) == null );
|
||||
checkUpdateQuery( inspector, scope, false );
|
||||
scope.inTransaction( session -> assertThat( session.find( SimpleEntity.class, 11L ) ).isNull() );
|
||||
}
|
||||
|
||||
|
@ -150,14 +151,30 @@ public class RowIdUpdateAndDeleteTest {
|
|||
parent.getChild().setStatus( "new_status" );
|
||||
inspector.clear();
|
||||
} );
|
||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||
checkUpdateQuery( inspector, dialect.rowId( "" ) == null );
|
||||
checkUpdateQuery( inspector, scope, false );
|
||||
scope.inTransaction( session -> assertThat(
|
||||
session.find( SimpleEntity.class, 2L ).getStatus()
|
||||
).isEqualTo( "new_status" ) );
|
||||
}
|
||||
|
||||
private void checkUpdateQuery(SQLStatementInspector inspector, boolean shouldUsePrimaryKey) {
|
||||
private void checkUpdateQuery(SQLStatementInspector inspector, SessionFactoryScope scope, boolean sameTransaction) {
|
||||
final Dialect dialect = scope.getSessionFactory().getJdbcServices().getDialect();
|
||||
final boolean shouldUsePrimaryKey;
|
||||
if ( dialect.rowId( "" ) == null ) {
|
||||
shouldUsePrimaryKey = true;
|
||||
}
|
||||
else {
|
||||
if ( sameTransaction ) {
|
||||
final EntityPersister persister = scope.getSessionFactory()
|
||||
.getMappingMetamodel()
|
||||
.findEntityDescriptor( SimpleEntity.class );
|
||||
final GeneratedValuesMutationDelegate delegate = persister.getMutationDelegate( MutationType.INSERT );
|
||||
shouldUsePrimaryKey = delegate == null || !delegate.supportsRowId();
|
||||
}
|
||||
else {
|
||||
shouldUsePrimaryKey = false;
|
||||
}
|
||||
}
|
||||
inspector.assertNumberOfOccurrenceInQueryNoSpace( 0, "primary_key", shouldUsePrimaryKey ? 1 : 0 );
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue