|
From: <hib...@li...> - 2006-03-24 18:10:12
|
Author: ste...@jb...
Date: 2006-03-24 13:10:04 -0500 (Fri, 24 Mar 2006)
New Revision: 9681
Added:
trunk/Hibernate3/src/org/hibernate/id/insert/
trunk/Hibernate3/src/org/hibernate/id/insert/AbstractReturningDelegate.java
trunk/Hibernate3/src/org/hibernate/id/insert/AbstractSelectingDelegate.java
trunk/Hibernate3/src/org/hibernate/id/insert/Binder.java
trunk/Hibernate3/src/org/hibernate/id/insert/IdentifierGeneratingInsert.java
trunk/Hibernate3/src/org/hibernate/id/insert/InsertGeneratedIdentifierDelegate.java
trunk/Hibernate3/src/org/hibernate/id/insert/InsertSelectIdentityInsert.java
Modified:
trunk/Hibernate3/src/org/hibernate/id/AbstractPostInsertGenerator.java
trunk/Hibernate3/src/org/hibernate/id/IdentityGenerator.java
trunk/Hibernate3/src/org/hibernate/id/PostInsertIdentifierGenerator.java
trunk/Hibernate3/src/org/hibernate/id/PostInsertIdentityPersister.java
trunk/Hibernate3/src/org/hibernate/id/SelectGenerator.java
trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java
trunk/Hibernate3/src/org/hibernate/sql/Insert.java
Log:
HHH-1590 : changed how PostInsertIdentifierGenerators are handled
Modified: trunk/Hibernate3/src/org/hibernate/id/AbstractPostInsertGenerator.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/AbstractPostInsertGenerator.java 2006-03-22 23:47:31 UTC (rev 9680)
+++ trunk/Hibernate3/src/org/hibernate/id/AbstractPostInsertGenerator.java 2006-03-24 18:10:04 UTC (rev 9681)
@@ -2,65 +2,14 @@
package org.hibernate.id;
import java.io.Serializable;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
-import org.hibernate.exception.JDBCExceptionHelper;
-import org.hibernate.pretty.MessageHelper;
/**
* @author Gavin King
*/
public abstract class AbstractPostInsertGenerator implements PostInsertIdentifierGenerator{
-
public Serializable generate(SessionImplementor s, Object obj) {
return IdentifierGeneratorFactory.POST_INSERT_INDICATOR;
}
-
- protected abstract String getSQL(PostInsertIdentityPersister persister);
-
- protected void bindParameters(SessionImplementor session, PreparedStatement ps, Object object, PostInsertIdentityPersister persister)
- throws SQLException {}
-
- protected abstract Serializable getResult(SessionImplementor session, ResultSet rs, Object object, PostInsertIdentityPersister persister)
- throws SQLException;
-
- public Serializable getGenerated(SessionImplementor session, Object object, PostInsertIdentityPersister persister)
- throws HibernateException {
-
- final String sql = getSQL(persister);
-
- try {
-
- //fetch the generated id in a separate query
- PreparedStatement idSelect = session.getBatcher().prepareStatement(sql);
- try {
- bindParameters(session, idSelect, object, persister);
- ResultSet rs = idSelect.executeQuery();
- try {
- return getResult(session, rs, object, persister);
- }
- finally {
- rs.close();
- }
- }
- finally {
- session.getBatcher().closeStatement(idSelect);
- }
-
- }
- catch ( SQLException sqle ) {
- throw JDBCExceptionHelper.convert(
- session.getFactory().getSQLExceptionConverter(),
- sqle,
- "could not insert: " + MessageHelper.infoString( persister ),
- sql
- );
- }
-
- }
-
}
Modified: trunk/Hibernate3/src/org/hibernate/id/IdentityGenerator.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/IdentityGenerator.java 2006-03-22 23:47:31 UTC (rev 9680)
+++ trunk/Hibernate3/src/org/hibernate/id/IdentityGenerator.java 2006-03-24 18:10:04 UTC (rev 9681)
@@ -4,11 +4,22 @@
import java.io.Serializable;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.sql.PreparedStatement;
import org.hibernate.engine.SessionImplementor;
+import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
+import org.hibernate.id.insert.IdentifierGeneratingInsert;
+import org.hibernate.id.insert.AbstractSelectingDelegate;
+import org.hibernate.id.insert.AbstractReturningDelegate;
+import org.hibernate.id.insert.InsertSelectIdentityInsert;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.HibernateException;
+import org.hibernate.AssertionFailure;
+import org.hibernate.util.GetGeneratedKeysHelper;
/**
+ * A generator for use with ANSI-SQL IDENTITY columns used as the primary key.
* The IdentityGenerator for autoincrement/identity key generation.
* <br><br>
* Indicates to the <tt>Session</tt> that identity (ie. identity/autoincrement
@@ -18,18 +29,133 @@
*/
public class IdentityGenerator extends AbstractPostInsertGenerator {
- protected String getSQL(PostInsertIdentityPersister persister) {
- return persister.getIdentitySelectString();
+ public InsertGeneratedIdentifierDelegate getInsertGeneratedIdentifierDelegate(
+ PostInsertIdentityPersister persister,
+ Dialect dialect,
+ boolean isGetGeneratedKeysEnabled) throws HibernateException {
+ if ( isGetGeneratedKeysEnabled ) {
+ return new GetGeneratedKeysDelegate( persister, dialect );
+ }
+ else if ( dialect.supportsInsertSelectIdentity() ) {
+ return new InsertSelectDelegate( persister, dialect );
+ }
+ else {
+ return new BasicDelegate( persister, dialect );
+ }
}
-
- protected Serializable getResult(SessionImplementor session, ResultSet rs, Object object, PostInsertIdentityPersister persister)
- throws SQLException {
- return IdentifierGeneratorFactory.getGeneratedIdentity( rs, persister.getIdentifierType() );
+
+ /**
+ * Delegate for dealing with IDENTITY columns using JDBC3 getGeneratedKeys
+ */
+ public static class GetGeneratedKeysDelegate
+ extends AbstractReturningDelegate
+ implements InsertGeneratedIdentifierDelegate {
+ private final PostInsertIdentityPersister persister;
+ private final Dialect dialect;
+
+ public GetGeneratedKeysDelegate(PostInsertIdentityPersister persister, Dialect dialect) {
+ super( persister );
+ this.persister = persister;
+ this.dialect = dialect;
+ }
+
+ public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() {
+ IdentifierGeneratingInsert insert = new IdentifierGeneratingInsert( dialect );
+ insert.addIdentityColumn( persister.getRootTableKeyColumnNames()[0] );
+ return insert;
+ }
+
+ protected PreparedStatement prepare(String insertSQL, SessionImplementor session) throws SQLException {
+ return session.getBatcher().prepareStatement( insertSQL, true );
+ }
+
+ public Serializable executeAndExtract(PreparedStatement insert) throws SQLException {
+ insert.executeUpdate();
+ return IdentifierGeneratorFactory.getGeneratedIdentity(
+ GetGeneratedKeysHelper.getGeneratedKey( insert ),
+ persister.getIdentifierType()
+ );
+ }
}
-}
+ /**
+ * Delegate for dealing with IDENTITY columns where the dialect supports returning
+ * the generated IDENTITY value directly from the insert statement.
+ */
+ public static class InsertSelectDelegate
+ extends AbstractReturningDelegate
+ implements InsertGeneratedIdentifierDelegate {
+ private final PostInsertIdentityPersister persister;
+ private final Dialect dialect;
+ public InsertSelectDelegate(PostInsertIdentityPersister persister, Dialect dialect) {
+ super( persister );
+ this.persister = persister;
+ this.dialect = dialect;
+ }
+ public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() {
+ InsertSelectIdentityInsert insert = new InsertSelectIdentityInsert( dialect );
+ insert.addIdentityColumn( persister.getRootTableKeyColumnNames()[0] );
+ return insert;
+ }
+ protected PreparedStatement prepare(String insertSQL, SessionImplementor session) throws SQLException {
+ return session.getBatcher().prepareStatement( insertSQL, false );
+ }
+ public Serializable executeAndExtract(PreparedStatement insert) throws SQLException {
+ if ( !insert.execute() ) {
+ while ( !insert.getMoreResults() && insert.getUpdateCount() != -1 ) {
+ // do nothing until we hit the rsult set containing the generated id
+ }
+ }
+ ResultSet rs = insert.getResultSet();
+ try {
+ return IdentifierGeneratorFactory.getGeneratedIdentity( rs, persister.getIdentifierType() );
+ }
+ finally {
+ rs.close();
+ }
+ }
+ public Serializable determineGeneratedIdentifier(SessionImplementor session, Object entity) {
+ throw new AssertionFailure( "insert statement returns generated value" );
+ }
+ }
+
+ /**
+ * Delegate for dealing with IDENTITY columns where the dialect requires an
+ * additional command execution to retrieve the generated IDENTITY value
+ */
+ public static class BasicDelegate
+ extends AbstractSelectingDelegate
+ implements InsertGeneratedIdentifierDelegate {
+ private final PostInsertIdentityPersister persister;
+ private final Dialect dialect;
+
+ public BasicDelegate(PostInsertIdentityPersister persister, Dialect dialect) {
+ super( persister );
+ this.persister = persister;
+ this.dialect = dialect;
+ }
+
+ public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() {
+ IdentifierGeneratingInsert insert = new IdentifierGeneratingInsert( dialect );
+ insert.addIdentityColumn( persister.getRootTableKeyColumnNames()[0] );
+ return insert;
+ }
+
+ protected String getSelectSQL() {
+ return persister.getIdentitySelectString();
+ }
+
+ protected Serializable getResult(
+ SessionImplementor session,
+ ResultSet rs,
+ Object object) throws SQLException {
+ return IdentifierGeneratorFactory.getGeneratedIdentity( rs, persister.getIdentifierType() );
+ }
+ }
+
+}
Modified: trunk/Hibernate3/src/org/hibernate/id/PostInsertIdentifierGenerator.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/PostInsertIdentifierGenerator.java 2006-03-22 23:47:31 UTC (rev 9680)
+++ trunk/Hibernate3/src/org/hibernate/id/PostInsertIdentifierGenerator.java 2006-03-24 18:10:04 UTC (rev 9681)
@@ -1,15 +1,16 @@
//$Id$
package org.hibernate.id;
-import java.io.Serializable;
-
import org.hibernate.HibernateException;
-import org.hibernate.engine.SessionImplementor;
+import org.hibernate.dialect.Dialect;
+import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
/**
* @author Gavin King
*/
public interface PostInsertIdentifierGenerator extends IdentifierGenerator {
- public Serializable getGenerated(SessionImplementor session, Object object, PostInsertIdentityPersister persister)
- throws HibernateException;
+ public InsertGeneratedIdentifierDelegate getInsertGeneratedIdentifierDelegate(
+ PostInsertIdentityPersister persister,
+ Dialect dialect,
+ boolean isGetGeneratedKeysEnabled) throws HibernateException;
}
Modified: trunk/Hibernate3/src/org/hibernate/id/PostInsertIdentityPersister.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/PostInsertIdentityPersister.java 2006-03-22 23:47:31 UTC (rev 9680)
+++ trunk/Hibernate3/src/org/hibernate/id/PostInsertIdentityPersister.java 2006-03-24 18:10:04 UTC (rev 9681)
@@ -6,12 +6,32 @@
/**
* A persister that may have an identity assigned by execution of
* a SQL <tt>INSERT</tt>.
+ *
* @author Gavin King
*/
public interface PostInsertIdentityPersister extends EntityPersister {
-
+ /**
+ * 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
+ */
public String getSelectByUniqueKeyString(String propertyName);
+
+ /**
+ * Get the database-specific SQL command to retrieve the last
+ * generated IDENTITY value.
+ *
+ * @return The SQL command string
+ */
public String getIdentitySelectString();
+
+ /**
+ * The names of the primary key columns in the root table.
+ *
+ * @return The primary key column names.
+ */
public String[] getRootTableKeyColumnNames();
-
}
Modified: trunk/Hibernate3/src/org/hibernate/id/SelectGenerator.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/SelectGenerator.java 2006-03-22 23:47:31 UTC (rev 9680)
+++ trunk/Hibernate3/src/org/hibernate/id/SelectGenerator.java 2006-03-24 18:10:04 UTC (rev 9681)
@@ -8,92 +8,129 @@
import java.util.Properties;
import org.hibernate.MappingException;
+import org.hibernate.HibernateException;
+import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
+import org.hibernate.id.insert.IdentifierGeneratingInsert;
+import org.hibernate.id.insert.AbstractSelectingDelegate;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.type.Type;
/**
- * An IdentityGenerator that selects the inserted row, to determine
- * an identifier value assigned by the database. The correct row is
- * located using a unique key.
- * <br><br>
- * One mapping parameter is required: key.
+ * A generator that selects the just inserted row to determine the identifier
+ * value assigned by the database. The correct row is located using a unique
+ * key.
+ * <p/>
+ * One mapping parameter is required: key (unless a natural-id is defined in the mapping).
*
* @author Gavin King
*/
public class SelectGenerator extends AbstractPostInsertGenerator implements Configurable {
private String uniqueKeyPropertyName;
- private Type idType;
- private String entityName;
public void configure(Type type, Properties params, Dialect d) throws MappingException {
- uniqueKeyPropertyName = params.getProperty("key");
- entityName = params.getProperty(ENTITY_NAME);
- this.idType = type;
+ uniqueKeyPropertyName = params.getProperty( "key" );
}
-
- protected String getSQL(PostInsertIdentityPersister persister) {
- if ( entityName == null ) {
- entityName = persister.getEntityName();
+
+ public InsertGeneratedIdentifierDelegate getInsertGeneratedIdentifierDelegate(
+ PostInsertIdentityPersister persister,
+ Dialect dialect,
+ boolean isGetGeneratedKeysEnabled) throws HibernateException {
+ return new SelectGeneratorDelegate( persister, dialect, uniqueKeyPropertyName );
+ }
+
+ private static String determineNameOfPropertyToUse(PostInsertIdentityPersister persister, String supplied) {
+ if ( supplied != null ) {
+ return supplied;
}
- if ( uniqueKeyPropertyName == null ) {
- int[] naturalIdPropertyIndices = persister.getNaturalIdentifierProperties();
- if ( naturalIdPropertyIndices == null ){
- throw new IdentifierGenerationException(
- "no natural-id property defined; need to specify [key] in " +
- "generator parameters"
- );
- }
- if ( naturalIdPropertyIndices.length > 1 ) {
- throw new IdentifierGenerationException(
- "select generator does not currently support composite " +
- "natural-id properties; need to specify [key] in generator parameters"
- );
- }
- if ( persister.getPropertyInsertGeneration() [ naturalIdPropertyIndices[0] ] ) {
- throw new IdentifierGenerationException(
- "natural-id also defined as insert-generated; need to specify [key] " +
- "in generator parameters"
- );
- }
- uniqueKeyPropertyName = persister.getPropertyNames() [ naturalIdPropertyIndices[0] ];
+ int[] naturalIdPropertyIndices = persister.getNaturalIdentifierProperties();
+ if ( naturalIdPropertyIndices == null ){
+ throw new IdentifierGenerationException(
+ "no natural-id property defined; need to specify [key] in " +
+ "generator parameters"
+ );
}
- return persister.getSelectByUniqueKeyString( uniqueKeyPropertyName );
+ if ( naturalIdPropertyIndices.length > 1 ) {
+ throw new IdentifierGenerationException(
+ "select generator does not currently support composite " +
+ "natural-id properties; need to specify [key] in generator parameters"
+ );
+ }
+ if ( persister.getPropertyInsertGeneration() [ naturalIdPropertyIndices[0] ] ) {
+ throw new IdentifierGenerationException(
+ "natural-id also defined as insert-generated; need to specify [key] " +
+ "in generator parameters"
+ );
+ }
+ return persister.getPropertyNames() [ naturalIdPropertyIndices[0] ];
}
- protected void bindParameters(
- SessionImplementor session,
- PreparedStatement ps,
- Object object,
- PostInsertIdentityPersister persister) throws SQLException {
- Type uniqueKeyPropertyType = session.getFactory()
- .getClassMetadata( entityName )
- .getPropertyType( uniqueKeyPropertyName );
- Object uniqueKeyValue = persister.getPropertyValue(
- object,
- uniqueKeyPropertyName,
- session.getEntityMode()
- );
- uniqueKeyPropertyType.nullSafeSet( ps, uniqueKeyValue, 1, session );
- }
- protected Serializable getResult(
- SessionImplementor session,
- ResultSet rs,
- Object object,
- PostInsertIdentityPersister persister) throws SQLException {
- if ( !rs.next() ) {
- throw new IdentifierGenerationException(
- "the inserted row could not be located by the unique key: " +
- uniqueKeyPropertyName
+ /**
+ * The delegate for the select generation strategy.
+ */
+ public static class SelectGeneratorDelegate
+ extends AbstractSelectingDelegate
+ implements InsertGeneratedIdentifierDelegate {
+ private final PostInsertIdentityPersister persister;
+ private final Dialect dialect;
+
+ private final String uniqueKeyPropertyName;
+ private final Type uniqueKeyType;
+ private final Type idType;
+
+ private final String idSelectString;
+
+ private SelectGeneratorDelegate(
+ PostInsertIdentityPersister persister,
+ Dialect dialect,
+ String suppliedUniqueKeyPropertyName) {
+ super( persister );
+ this.persister = persister;
+ this.dialect = dialect;
+ this.uniqueKeyPropertyName = determineNameOfPropertyToUse( persister, suppliedUniqueKeyPropertyName );
+
+ idSelectString = persister.getSelectByUniqueKeyString( uniqueKeyPropertyName );
+ uniqueKeyType = persister.getPropertyType( uniqueKeyPropertyName );
+ idType = persister.getIdentifierType();
+ }
+
+ public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert() {
+ return new IdentifierGeneratingInsert( dialect );
+ }
+
+
+ // AbstractSelectingDelegate impl ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ protected String getSelectSQL() {
+ return idSelectString;
+ }
+
+ protected void bindParameters(
+ SessionImplementor session,
+ PreparedStatement ps,
+ Object entity) throws SQLException {
+ Object uniqueKeyValue = persister.getPropertyValue( entity, uniqueKeyPropertyName, session.getEntityMode() );
+ uniqueKeyType.nullSafeSet( ps, uniqueKeyValue, 1, session );
+ }
+
+ protected Serializable getResult(
+ SessionImplementor session,
+ ResultSet rs,
+ Object entity) throws SQLException {
+ if ( !rs.next() ) {
+ throw new IdentifierGenerationException(
+ "the inserted row could not be located by the unique key: " +
+ uniqueKeyPropertyName
+ );
+ }
+ return ( Serializable ) idType.nullSafeGet(
+ rs,
+ persister.getRootTableKeyColumnNames(),
+ session,
+ entity
);
}
- return ( Serializable ) idType.nullSafeGet(
- rs,
- persister.getRootTableKeyColumnNames(),
- session,
- object
- );
}
}
Added: trunk/Hibernate3/src/org/hibernate/id/insert/AbstractReturningDelegate.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/insert/AbstractReturningDelegate.java 2006-03-22 23:47:31 UTC (rev 9680)
+++ trunk/Hibernate3/src/org/hibernate/id/insert/AbstractReturningDelegate.java 2006-03-24 18:10:04 UTC (rev 9681)
@@ -0,0 +1,52 @@
+package org.hibernate.id.insert;
+
+import org.hibernate.id.PostInsertIdentityPersister;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.pretty.MessageHelper;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * Abstract InsertGeneratedIdentifierDelegate implementation where the
+ * underlying strategy causes the enerated identitifer 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 identitifer.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractReturningDelegate implements InsertGeneratedIdentifierDelegate {
+ private final PostInsertIdentityPersister persister;
+
+ public AbstractReturningDelegate(PostInsertIdentityPersister persister) {
+ this.persister = persister;
+ }
+
+ public final Serializable performInsert(String insertSQL, SessionImplementor session, Binder binder) {
+ try {
+ // prepare and execute the insert
+ PreparedStatement insert = prepare( insertSQL, session );
+ try {
+ binder.bindValues( insert );
+ return executeAndExtract( insert );
+ }
+ finally {
+ session.getBatcher().closeStatement( insert );
+ }
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not insert: " + MessageHelper.infoString( persister ),
+ insertSQL
+ );
+ }
+ }
+
+ protected abstract PreparedStatement prepare(String insertSQL, SessionImplementor session) throws SQLException;
+
+ protected abstract Serializable executeAndExtract(PreparedStatement insert) throws SQLException;
+}
Added: trunk/Hibernate3/src/org/hibernate/id/insert/AbstractSelectingDelegate.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/insert/AbstractSelectingDelegate.java 2006-03-22 23:47:31 UTC (rev 9680)
+++ trunk/Hibernate3/src/org/hibernate/id/insert/AbstractSelectingDelegate.java 2006-03-24 18:10:04 UTC (rev 9681)
@@ -0,0 +1,113 @@
+package org.hibernate.id.insert;
+
+import org.hibernate.exception.JDBCExceptionHelper;
+import org.hibernate.pretty.MessageHelper;
+import org.hibernate.engine.SessionImplementor;
+import org.hibernate.id.PostInsertIdentityPersister;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.ResultSet;
+import java.io.Serializable;
+
+/**
+ * Abstract InsertGeneratedIdentifierDelegate implementation where the
+ * underlying strategy requires an subsequent select after the insert
+ * to determine the generated identifier.
+ *
+ * @author Steve Ebersole
+ */
+public abstract class AbstractSelectingDelegate implements InsertGeneratedIdentifierDelegate {
+ private final PostInsertIdentityPersister persister;
+
+ protected AbstractSelectingDelegate(PostInsertIdentityPersister persister) {
+ this.persister = persister;
+ }
+
+ public final Serializable performInsert(String insertSQL, SessionImplementor session, Binder binder) {
+ try {
+ // prepare and execute the insert
+ PreparedStatement insert = session.getBatcher().prepareStatement( insertSQL, false );
+ try {
+ binder.bindValues( insert );
+ insert.executeUpdate();
+ }
+ finally {
+ session.getBatcher().closeStatement( insert );
+ }
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not insert: " + MessageHelper.infoString( persister ),
+ insertSQL
+ );
+ }
+
+ final String selectSQL = getSelectSQL();
+
+ try {
+ //fetch the generated id in a separate query
+ PreparedStatement idSelect = session.getBatcher().prepareStatement( selectSQL );
+ try {
+ bindParameters( session, idSelect, binder.getEntity() );
+ ResultSet rs = idSelect.executeQuery();
+ try {
+ return getResult( session, rs, binder.getEntity() );
+ }
+ finally {
+ rs.close();
+ }
+ }
+ finally {
+ session.getBatcher().closeStatement( idSelect );
+ }
+
+ }
+ catch ( SQLException sqle ) {
+ throw JDBCExceptionHelper.convert(
+ session.getFactory().getSQLExceptionConverter(),
+ sqle,
+ "could not retrieve generated id after insert: " + MessageHelper.infoString( persister ),
+ insertSQL
+ );
+ }
+ }
+
+ /**
+ * Get the SQL statement to be used to retrieve generated key values.
+ *
+ * @return The SQL command string
+ */
+ protected abstract String getSelectSQL();
+
+ /**
+ * Bind any required parameter values into the SQL command {@link #getSelectSQL}.
+ *
+ * @param session The session
+ * @param ps The prepared {@link #getSelectSQL SQL} command
+ * @param entity The entity being saved.
+ * @throws SQLException
+ */
+ protected void bindParameters(
+ SessionImplementor session,
+ PreparedStatement ps,
+ Object entity) throws SQLException {
+ }
+
+ /**
+ * Extract the generated key value from the given result set.
+ *
+ * @param session The session
+ * @param rs The result set containing the generated primay key values.
+ * @param entity The entity being saved.
+ * @return The generated identifier
+ * @throws SQLException
+ */
+ protected abstract Serializable getResult(
+ SessionImplementor session,
+ ResultSet rs,
+ Object entity) throws SQLException;
+
+}
Added: trunk/Hibernate3/src/org/hibernate/id/insert/Binder.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/insert/Binder.java 2006-03-22 23:47:31 UTC (rev 9680)
+++ trunk/Hibernate3/src/org/hibernate/id/insert/Binder.java 2006-03-24 18:10:04 UTC (rev 9681)
@@ -0,0 +1,12 @@
+package org.hibernate.id.insert;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * @author Steve Ebersole
+ */
+public interface Binder {
+ public void bindValues(PreparedStatement ps) throws SQLException;
+ public Object getEntity();
+}
Added: trunk/Hibernate3/src/org/hibernate/id/insert/IdentifierGeneratingInsert.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/insert/IdentifierGeneratingInsert.java 2006-03-22 23:47:31 UTC (rev 9680)
+++ trunk/Hibernate3/src/org/hibernate/id/insert/IdentifierGeneratingInsert.java 2006-03-24 18:10:04 UTC (rev 9681)
@@ -0,0 +1,17 @@
+package org.hibernate.id.insert;
+
+import org.hibernate.sql.Insert;
+import org.hibernate.dialect.Dialect;
+
+/**
+ * Nothing more than a distinguishing subclass of Insert used to indicate
+ * intent. Some subclasses of this also provided some additional
+ * functionality or semantic to the genernated SQL statement string.
+ *
+ * @author Steve Ebersole
+ */
+public class IdentifierGeneratingInsert extends Insert {
+ public IdentifierGeneratingInsert(Dialect dialect) {
+ super( dialect );
+ }
+}
Added: trunk/Hibernate3/src/org/hibernate/id/insert/InsertGeneratedIdentifierDelegate.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/insert/InsertGeneratedIdentifierDelegate.java 2006-03-22 23:47:31 UTC (rev 9680)
+++ trunk/Hibernate3/src/org/hibernate/id/insert/InsertGeneratedIdentifierDelegate.java 2006-03-24 18:10:04 UTC (rev 9681)
@@ -0,0 +1,39 @@
+package org.hibernate.id.insert;
+
+import org.hibernate.engine.SessionImplementor;
+
+import java.io.Serializable;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+/**
+ * Responsible for handling delegation relating to variants in how
+ * insert-generated-identifier generator strategies dictate processing:<ul>
+ * <li>building the sql insert statement
+ * <li>determination of the generated identifier value
+ * </ul>
+ *
+ * @author Steve Ebersole
+ */
+public interface InsertGeneratedIdentifierDelegate {
+
+ /**
+ * Build a {@link org.hibernate.sql.Insert} specific to the delegate's mode
+ * of handling generated key values.
+ *
+ * @return The insert object.
+ */
+ public IdentifierGeneratingInsert prepareIdentifierGeneratingInsert();
+
+ /**
+ * Perform the indicated insert SQL statement and determine the identifier value
+ * generated.
+ *
+ * @param insertSQL
+ * @param session
+ * @param binder
+ * @return The generated identifier value.
+ */
+ public Serializable performInsert(String insertSQL, SessionImplementor session, Binder binder);
+
+}
Added: trunk/Hibernate3/src/org/hibernate/id/insert/InsertSelectIdentityInsert.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/id/insert/InsertSelectIdentityInsert.java 2006-03-22 23:47:31 UTC (rev 9680)
+++ trunk/Hibernate3/src/org/hibernate/id/insert/InsertSelectIdentityInsert.java 2006-03-24 18:10:04 UTC (rev 9681)
@@ -0,0 +1,20 @@
+package org.hibernate.id.insert;
+
+import org.hibernate.dialect.Dialect;
+
+/**
+ * Specialized IdentifierGeneratingInsert which appends the database
+ * specific clause which signifies to return generated IDENTITY values
+ * to the end of the insert statement.
+ *
+ * @author Steve Ebersole
+ */
+public class InsertSelectIdentityInsert extends IdentifierGeneratingInsert {
+ public InsertSelectIdentityInsert(Dialect dialect) {
+ super( dialect );
+ }
+
+ public String toStatementString() {
+ return getDialect().appendIdentitySelectToInsert( super.toStatementString() );
+ }
+}
Modified: trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java 2006-03-22 23:47:31 UTC (rev 9680)
+++ trunk/Hibernate3/src/org/hibernate/persister/entity/AbstractEntityPersister.java 2006-03-24 18:10:04 UTC (rev 9681)
@@ -27,7 +27,6 @@
import org.hibernate.QueryException;
import org.hibernate.StaleObjectStateException;
import org.hibernate.dialect.lock.LockingStrategy;
-import org.hibernate.dialect.lock.SelectLockingStrategy;
import org.hibernate.cache.CacheConcurrencyStrategy;
import org.hibernate.cache.CacheKey;
import org.hibernate.cache.entry.CacheEntry;
@@ -43,9 +42,10 @@
import org.hibernate.engine.Versioning;
import org.hibernate.exception.JDBCExceptionHelper;
import org.hibernate.id.IdentifierGenerator;
-import org.hibernate.id.IdentifierGeneratorFactory;
import org.hibernate.id.PostInsertIdentifierGenerator;
import org.hibernate.id.PostInsertIdentityPersister;
+import org.hibernate.id.insert.InsertGeneratedIdentifierDelegate;
+import org.hibernate.id.insert.Binder;
import org.hibernate.intercept.LazyPropertyInitializer;
import org.hibernate.intercept.FieldInterceptionHelper;
import org.hibernate.intercept.FieldInterceptor;
@@ -82,7 +82,6 @@
import org.hibernate.util.ArrayHelper;
import org.hibernate.util.CollectionHelper;
import org.hibernate.util.FilterHelper;
-import org.hibernate.util.GetGeneratedKeysHelper;
import org.hibernate.util.StringHelper;
/**
@@ -197,6 +196,8 @@
protected String[] customSQLUpdate;
protected String[] customSQLDelete;
+ private InsertGeneratedIdentifierDelegate identityDelegate;
+
private boolean[] tableHasColumns;
private final String loaderName;
@@ -1289,93 +1290,6 @@
return getRootTableKeyColumnNames();
}
-// /**
-// * Generate the SQL that pessimistic locks a row by id (and version)
-// */
-// protected String generateLockString(LockMode lockMode) {
-// SimpleSelect select = new SimpleSelect( getFactory().getDialect() )
-// .setLockMode( lockMode )
-// .setTableName( getVersionedTableName() )
-// .addColumn( rootTableKeyColumnNames[0] )
-// .addCondition( rootTableKeyColumnNames, "=?" );
-// if ( isVersioned() ) {
-// select.addCondition( getVersionColumnName(), "=?" );
-// }
-// if ( getFactory().getSettings().isCommentsEnabled() ) {
-// select.setComment( "lock " + getEntityName() );
-// }
-// return select.toStatementString();
-// }
-//
-// protected void initLockers() {
-// lockers.put( LockMode.READ, generateLockString( LockMode.READ ) );
-// lockers.put( LockMode.UPGRADE, generateLockString( LockMode.UPGRADE ) );
-// lockers.put( LockMode.UPGRADE_NOWAIT, generateLockString( LockMode.UPGRADE_NOWAIT ) );
-// }
-//
-// private String getLockString(LockMode lockMode) {
-// return ( String ) lockers.get( lockMode );
-// }
-//
-// /**
-// * Do a version check
-// */
-// public void lock(Serializable id, Object version, Object object, LockMode lockMode, SessionImplementor session)
-// throws HibernateException {
-//
-// if ( lockMode != LockMode.NONE ) {
-//
-// if ( log.isTraceEnabled() ) {
-// log.trace( "Locking entity: " + MessageHelper.infoString( this, id, getFactory() ) );
-// if ( isVersioned() ) {
-// log.trace( "Version: " + version );
-// }
-// }
-//
-// final String sql = getLockString( lockMode );
-//
-// try {
-//
-// PreparedStatement st = session.getBatcher().prepareSelectStatement( sql );
-// try {
-// getIdentifierType().nullSafeSet( st, id, 1, session );
-// if ( isVersioned() ) {
-// getVersionType().nullSafeSet( st, version, getIdentifierColumnSpan() + 1, session );
-// }
-//
-// ResultSet rs = st.executeQuery();
-// try {
-// if ( !rs.next() ) {
-// if ( getFactory().getStatistics().isStatisticsEnabled() ) {
-// getFactory().getStatisticsImplementor()
-// .optimisticFailure( getEntityName() );
-// }
-// throw new StaleObjectStateException( getEntityName(), id );
-// }
-// }
-// finally {
-// rs.close();
-// }
-// }
-// finally {
-// session.getBatcher().closeStatement( st );
-// }
-//
-// }
-// catch ( SQLException sqle ) {
-// throw JDBCExceptionHelper.convert(
-// getFactory().getSQLExceptionConverter(),
-// sqle,
-// "could not lock: " +
-// MessageHelper.infoString( this, id, getFactory() ),
-// sql
-// );
-// }
-//
-// }
-//
-// }
-
public String[] toColumns(String alias, String propertyName) throws QueryException {
return propertyMapping.toColumns( alias, propertyName );
}
@@ -1872,6 +1786,9 @@
*/
protected String generateInsertString(boolean identityInsert, boolean[] includeProperty, int j) {
+ // todo : remove the identityInsert param and variations;
+ // identity-insert strings are now generated from generateIdentityInsertString()
+
Insert insert = new Insert( getFactory().getDialect() )
.setTableName( getTableName( j ) );
@@ -1911,6 +1828,39 @@
}
/**
+ * Used to generate an insery statement against the root table in the
+ * case of identifier generation strategies where the insert statement
+ * executions actually generates the identifier value.
+ *
+ * @param includeProperty indices of the properties to include in the
+ * insert statement.
+ * @return The insert SQL statement string
+ */
+ protected String generateIdentityInsertString(boolean[] includeProperty) {
+ Insert insert = identityDelegate.prepareIdentifierGeneratingInsert();
+ insert.setTableName( getTableName( 0 ) );
+
+ // add normal properties
+ for ( int i = 0; i < entityMetamodel.getPropertySpan(); i++ ) {
+ if ( includeProperty[i] && isPropertyOfTable( i, 0 ) ) {
+ // this property belongs on the table and is to be inserted
+ insert.addColumns( getPropertyColumnNames(i), propertyColumnInsertable[i] );
+ }
+ }
+
+ // add the discriminator
+ addDiscriminatorToInsert( insert );
+
+ // delegate already handles PK columns
+
+ if ( getFactory().getSettings().isCommentsEnabled() ) {
+ insert.setComment( "insert " + getEntityName() );
+ }
+
+ return insert.toStatementString();
+ }
+
+ /**
* Generate the SQL that deletes a row by id (and version)
*/
protected String generateDeleteString(int j) {
@@ -1933,24 +1883,23 @@
boolean[][] includeColumns,
int j,
PreparedStatement st,
- SessionImplementor session)
- throws HibernateException, SQLException {
+ SessionImplementor session) throws HibernateException, SQLException {
return dehydrate( id, fields, null, includeProperty, includeColumns, j, st, session, 1 );
}
/**
* Marshall the fields of a persistent instance to a prepared statement
*/
- protected int dehydrate(final Serializable id,
- final Object[] fields,
- final Object rowId,
- final boolean[] includeProperty,
- final boolean[][] includeColumns,
- final int j,
- final PreparedStatement ps,
- final SessionImplementor session,
- int index)
- throws SQLException, HibernateException {
+ protected int dehydrate(
+ final Serializable id,
+ final Object[] fields,
+ final Object rowId,
+ final boolean[] includeProperty,
+ final boolean[][] includeColumns,
+ final int j,
+ final PreparedStatement ps,
+ final SessionImplementor session,
+ int index) throws SQLException, HibernateException {
if ( log.isTraceEnabled() ) {
log.trace( "Dehydrating entity: " + MessageHelper.infoString( this, id, getFactory() ) );
@@ -1982,14 +1931,14 @@
* without resolving associations or collections. Question: should
* this really be here, or should it be sent back to Loader?
*/
- public Object[] hydrate(final ResultSet rs,
- final Serializable id,
- final Object object,
- final Loadable rootLoadable,
- final String[][] suffixedPropertyColumns,
- final boolean allProperties,
- final SessionImplementor session)
- throws SQLException, HibernateException {
+ public Object[] hydrate(
+ final ResultSet rs,
+ final Serializable id,
+ final Object object,
+ final Loadable rootLoadable,
+ final String[][] suffixedPropertyColumns,
+ final boolean allProperties,
+ final SessionImplementor session) throws SQLException, HibernateException {
if ( log.isTraceEnabled() ) {
log.trace( "Hydrating entity: " + MessageHelper.infoString( this, id, getFactory() ) );
@@ -2065,14 +2014,17 @@
}
/**
- * Perform an SQL INSERT, and then retrieve a generated identifier
+ * Perform an SQL INSERT, and then retrieve a generated identifier.
+ * <p/>
+ * This form is used for PostInsertIdentifierGenerator-style ids (IDENTITY,
+ * select, etc).
*/
- protected Serializable insert(final Object[] fields,
- final boolean[] notNull,
- String sql,
- final Object object,
- final SessionImplementor session)
- throws HibernateException {
+ protected Serializable insert(
+ final Object[] fields,
+ final boolean[] notNull,
+ String sql,
+ final Object object,
+ final SessionImplementor session) throws HibernateException {
if ( log.isTraceEnabled() ) {
log.trace( "Inserting entity: " + getEntityName() + " (native id)" );
@@ -2081,57 +2033,15 @@
}
}
- try {
-
- //do the insert
- PreparedStatement insert = session.getBatcher().prepareStatement( sql, useGetGeneratedKeys() );
- try {
- dehydrate( null, fields, notNull, propertyColumnInsertable, 0, insert, session );
-
- if ( useInsertSelectIdentity() ) {
- if ( !insert.execute() ) {
- while ( !insert.getMoreResults() && insert.getUpdateCount() != -1 ) {
- continue; // Do nothing (but stop checkstyle from complaining).
- }
- }
- //note early exit!
- ResultSet rs = insert.getResultSet();
- try {
- return IdentifierGeneratorFactory.getGeneratedIdentity( rs, getIdentifierType() );
- }
- finally {
- rs.close();
- }
- }
- else if ( useGetGeneratedKeys() ) {
- insert.executeUpdate();
- //note early exit!
- return IdentifierGeneratorFactory.getGeneratedIdentity(
- GetGeneratedKeysHelper.getGeneratedKey(insert),
- getIdentifierType()
- );
- }
- else {
- insert.executeUpdate();
- }
-
+ Binder binder = new Binder() {
+ public void bindValues(PreparedStatement ps) throws SQLException {
+ dehydrate( null, fields, notNull, propertyColumnInsertable, 0, ps, session );
}
- finally {
- session.getBatcher().closeStatement( insert );
+ public Object getEntity() {
+ return object;
}
-
- }
- catch ( SQLException sqle ) {
- throw JDBCExceptionHelper.convert(
- getFactory().getSQLExceptionConverter(),
- sqle,
- "could not insert: " + MessageHelper.infoString(this),
- sql
- );
- }
-
- return ( (PostInsertIdentifierGenerator) getIdentifierGenerator() ).getGenerated(session, object, this);
-
+ };
+ return identityDelegate.performInsert( sql, session, binder );
}
public String getIdentitySelectString() {
@@ -2152,16 +2062,19 @@
}
/**
- * Perform an SQL INSERT
+ * Perform an SQL INSERT.
+ * <p/>
+ * This for is used for all non-root tables as well as the root table
+ * in cases where the identifier value is known before the insert occurs.
*/
- protected void insert(final Serializable id,
- final Object[] fields,
- final boolean[] notNull,
- final int j,
- final String sql,
- final Object object,
- final SessionImplementor session)
- throws HibernateException {
+ protected void insert(
+ final Serializable id,
+ final Object[] fields,
+ final boolean[] notNull,
+ final int j,
+ final String sql,
+ final Object object,
+ final SessionImplementor session) throws HibernateException {
if ( isInverseTable( j ) ) {
return;
@@ -2237,17 +2150,17 @@
/**
* Perform an SQL UPDATE or SQL INSERT
*/
- protected void updateOrInsert(final Serializable id,
- final Object[] fields,
- final Object[] oldFields,
- final Object rowId,
- final boolean[] includeProperty,
- final int j,
- final Object oldVersion,
- final Object object,
- final String sql,
- final SessionImplementor session)
- throws HibernateException {
+ protected void updateOrInsert(
+ final Serializable id,
+ final Object[] fields,
+ final Object[] oldFields,
+ final Object rowId,
+ final boolean[] includeProperty,
+ final int j,
+ final Object oldVersion,
+ final Object object,
+ final String sql,
+ final SessionImplementor session) throws HibernateException {
if ( !isInverseTable( j ) ) {
@@ -2278,17 +2191,17 @@
}
- protected boolean update(final Serializable id,
- final Object[] fields,
- final Object[] oldFields,
- final Object rowId,
- final boolean[] includeProperty,
- final int j,
- final Object oldVersion,
- final Object object,
- final String sql,
- final SessionImplementor session)
- throws HibernateException {
+ protected boolean update(
+ final Serializable id,
+ final Object[] fields,
+ final Object[] oldFields,
+ final Object rowId,
+ final boolean[] includeProperty,
+ final int j,
+ final Object oldVersion,
+ final Object object,
+ final String sql,
+ final SessionImplementor session) throws HibernateException {
final boolean useVersion = j == 0 && isVersioned();
final boolean callable = isUpdateCallable( j );
@@ -2386,13 +2299,13 @@
/**
* Perform an SQL DELETE
*/
- protected void delete(final Serializable id,
- final Object version,
- final int j,
- final Object object,
- final String sql,
- final SessionImplementor session)
- throws HibernateException {
+ protected void delete(
+ final Serializable id,
+ final Object version,
+ final int j,
+ final Object object,
+ final String sql,
+ final SessionImplementor session) throws HibernateException {
if ( isInverseTable( j ) ) {
return;
@@ -2492,16 +2405,16 @@
/**
* Update an object
*/
- public void update(final Serializable id,
- final Object[] fields,
- final int[] dirtyFields,
- final boolean hasDirtyCollection,
- final Object[] oldFields,
- final Object oldVersion,
- final Object object,
- final Object rowId,
- final SessionImplementor session)
- throws HibernateException {
+ public void update(
+ final Serializable id,
+ final Object[] fields,
+ final int[] dirtyFields,
+ final boolean hasDirtyCollection,
+ final Object[] oldFields,
+ final Object oldVersion,
+ final Object object,
+ final Object rowId,
+ final SessionImplementor session) throws HibernateException {
//note: dirtyFields==null means we had no snapshot, and we couldn't get one using select-before-update
// oldFields==null just means we had no snapshot to begin with (we might have used select-before-update to get the dirtyFields)
@@ -2747,9 +2660,10 @@
StringHelper.qualify( alias, getSubclassTableKeyColumns( tableNumber ) ) ) + "=?";
}
- protected String renderSelect(final int[] tableNumbers,
- final int[] columnNumbers,
- final int[] formulaNumbers) {
+ protected String renderSelect(
+ final int[] tableNumbers,
+ final int[] columnNumbers,
+ final int[] formulaNumbers) {
Arrays.sort( tableNumbers ); //get 'em in the right order (not that it really matters)
@@ -2829,9 +2743,9 @@
sqlUpdateGeneratedValuesSelectString = generateUpdateGeneratedValuesSelectString();
}
if ( isIdentifierAssignedByInsert() ) {
- sqlIdentityInsertString = customSQLInsert[0] == null ?
- generateInsertString( true, getPropertyInsertability() ) :
- customSQLInsert[0];
+ identityDelegate = ( ( PostInsertIdentifierGenerator ) getIdentifierGenerator() )
+ .getInsertGeneratedIdentifierDelegate( this, getFactory().getDialect(), useGetGeneratedKeys() );
+ sqlIdentityInsertString = generateIdentityInsertString( getPropertyInsertability() );
}
else {
sqlIdentityInsertString = null;
Modified: trunk/Hibernate3/src/org/hibernate/sql/Insert.java
===================================================================
--- trunk/Hibernate3/src/org/hibernate/sql/Insert.java 2006-03-22 23:47:31 UTC (rev 9680)
+++ trunk/Hibernate3/src/org/hibernate/sql/Insert.java 2006-03-24 18:10:04 UTC (rev 9681)
@@ -14,22 +14,24 @@
* @author Gavin King
*/
public class Insert {
+ private Dialect dialect;
+ private String tableName;
+ private String comment;
+ private Map columns = new SequencedHashMap();
public Insert(Dialect dialect) {
this.dialect = dialect;
}
- private String comment;
+ protected Dialect getDialect() {
+ return dialect;
+ }
+
public Insert setComment(String comment) {
this.comment = comment;
return this;
}
- private Dialect dialect;
- private String tableName;
-
- private Map columns = new SequencedHashMap();
-
public Insert addColumn(String columnName) {
return addColumn(columnName, "?");
}
@@ -43,7 +45,9 @@
public Insert addColumns(String[] columnNames, boolean[] insertable) {
for ( int i=0; i<columnNames.length; i++ ) {
- if ( insertable[i] ) addColumn( columnNames[i] );
+ if ( insertable[i] ) {
+ addColumn( columnNames[i] );
+ }
}
return this;
}
@@ -59,7 +63,9 @@
public Insert addIdentityColumn(String columnName) {
String value = dialect.getIdentityInsertString();
- if (value!=null) addColumn(columnName, value);
+ if ( value != null ) {
+ addColumn( columnName, value );
+ }
return this;
}
@@ -70,7 +76,9 @@
public String toStatementString() {
StringBuffer buf = new StringBuffer( columns.size()*15 + tableName.length() + 10 );
- if (comment!=null) buf.append("/* ").append(comment).append(" */ ");
+ if ( comment != null ) {
+ buf.append( "/* " ).append( comment ).append( " */ " );
+ }
buf.append("insert into ")
.append(tableName);
if ( columns.size()==0 ) {
@@ -81,13 +89,17 @@
Iterator iter = columns.keySet().iterator();
while ( iter.hasNext() ) {
buf.append( iter.next() );
- if ( iter.hasNext() ) buf.append(", ");
+ if ( iter.hasNext() ) {
+ buf.append( ", " );
+ }
}
buf.append(") values (");
iter = columns.values().iterator();
while ( iter.hasNext() ) {
buf.append( iter.next() );
- if ( iter.hasNext() ) buf.append(", ");
+ if ( iter.hasNext() ) {
+ buf.append( ", " );
+ }
}
buf.append(')');
}
|