mirror of
https://github.com/hibernate/hibernate-orm
synced 2025-02-16 16:15:06 +00:00
HHH-3309 : AbstractLazyInitializer + serialization
git-svn-id: https://svn.jboss.org/repos/hibernate/core/branches/Branch_3_2@14900 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
1295812ac5
commit
437b0cafea
@ -8,6 +8,7 @@
|
||||
import java.sql.SQLException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.ConcurrentModificationException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@ -298,28 +299,45 @@ public void closeStatements() {
|
||||
releasing = true;
|
||||
|
||||
try {
|
||||
if (batchUpdate!=null) batchUpdate.close();
|
||||
if ( batchUpdate != null ) {
|
||||
batchUpdate.close();
|
||||
}
|
||||
}
|
||||
catch (SQLException sqle) {
|
||||
catch ( SQLException sqle ) {
|
||||
//no big deal
|
||||
log.warn("Could not close a JDBC prepared statement", sqle);
|
||||
log.warn( "Could not close a JDBC prepared statement", sqle );
|
||||
}
|
||||
batchUpdate=null;
|
||||
batchUpdateSQL=null;
|
||||
batchUpdate = null;
|
||||
batchUpdateSQL = null;
|
||||
|
||||
Iterator iter = resultSetsToClose.iterator();
|
||||
while ( iter.hasNext() ) {
|
||||
try {
|
||||
logCloseResults();
|
||||
( (ResultSet) iter.next() ).close();
|
||||
( ( ResultSet ) iter.next() ).close();
|
||||
}
|
||||
catch (SQLException e) {
|
||||
catch ( SQLException e ) {
|
||||
// no big deal
|
||||
log.warn("Could not close a JDBC result set", e);
|
||||
log.warn( "Could not close a JDBC result set", e );
|
||||
}
|
||||
catch (Throwable e) {
|
||||
// sybase driver (jConnect) throwing NPE here in certain cases
|
||||
log.warn("Could not close a JDBC result set", e);
|
||||
catch ( ConcurrentModificationException e ) {
|
||||
// this has been shown to happen occasionally in rare cases
|
||||
// when using a transaction manager + transaction-timeout
|
||||
// where the timeout calls back through Hibernate's
|
||||
// registered transaction synchronization on a separate
|
||||
// "reaping" thread. In cases where that reaping thread
|
||||
// executes through this block at the same time the main
|
||||
// application thread does we can get into situations where
|
||||
// these CMEs occur. And though it is not "allowed" per-se,
|
||||
// the end result without handling it specifically is infinite
|
||||
// looping. So here, we simply break the loop
|
||||
log.info( "encountered CME attempting to release batcher; assuming cause is tx-timeout scenario and ignoring" );
|
||||
break;
|
||||
}
|
||||
catch ( Throwable e ) {
|
||||
// sybase driver (jConnect) throwing NPE here in certain
|
||||
// cases, but we'll just handle the general "unexpected" case
|
||||
log.warn( "Could not close a JDBC result set", e );
|
||||
}
|
||||
}
|
||||
resultSetsToClose.clear();
|
||||
@ -327,11 +345,16 @@ public void closeStatements() {
|
||||
iter = statementsToClose.iterator();
|
||||
while ( iter.hasNext() ) {
|
||||
try {
|
||||
closeQueryStatement( (PreparedStatement) iter.next() );
|
||||
closeQueryStatement( ( PreparedStatement ) iter.next() );
|
||||
}
|
||||
catch (SQLException e) {
|
||||
catch ( ConcurrentModificationException e ) {
|
||||
// see explanation above...
|
||||
log.info( "encountered CME attempting to release batcher; assuming cause is tx-timeout scenario and ignoring" );
|
||||
break;
|
||||
}
|
||||
catch ( SQLException e ) {
|
||||
// no big deal
|
||||
log.warn("Could not close a JDBC statement", e);
|
||||
log.warn( "Could not close a JDBC statement", e );
|
||||
}
|
||||
}
|
||||
statementsToClose.clear();
|
||||
|
@ -24,7 +24,13 @@ public abstract class AbstractLazyInitializer implements LazyInitializer {
|
||||
private Serializable id;
|
||||
private transient SessionImplementor session;
|
||||
private boolean unwrap;
|
||||
|
||||
|
||||
/**
|
||||
* For serialization from the non-pojo initializers (HHH-3309)
|
||||
*/
|
||||
protected AbstractLazyInitializer() {
|
||||
}
|
||||
|
||||
protected AbstractLazyInitializer(String entityName, Serializable id, SessionImplementor session) {
|
||||
this.id = id;
|
||||
this.session = session;
|
||||
|
@ -2,6 +2,8 @@
|
||||
package org.hibernate.tool.hbm2ddl;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileWriter;
|
||||
import java.io.Writer;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
@ -12,10 +14,12 @@
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.JDBCException;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.hibernate.cfg.NamingStrategy;
|
||||
import org.hibernate.cfg.Settings;
|
||||
import org.hibernate.dialect.Dialect;
|
||||
import org.hibernate.pretty.DDLFormatter;
|
||||
import org.hibernate.util.ReflectHelper;
|
||||
|
||||
/**
|
||||
@ -31,6 +35,10 @@ public class SchemaUpdate {
|
||||
private Configuration configuration;
|
||||
private Dialect dialect;
|
||||
private List exceptions;
|
||||
private boolean haltOnError = false;
|
||||
private boolean format = true;
|
||||
private String outputFile = null;
|
||||
private String delimiter;
|
||||
|
||||
public SchemaUpdate(Configuration cfg) throws HibernateException {
|
||||
this( cfg, cfg.getProperties() );
|
||||
@ -116,6 +124,7 @@ public void execute(boolean script, boolean doUpdate) {
|
||||
|
||||
Connection connection = null;
|
||||
Statement stmt = null;
|
||||
Writer outputFileWriter = null;
|
||||
|
||||
exceptions.clear();
|
||||
|
||||
@ -137,20 +146,36 @@ public void execute(boolean script, boolean doUpdate) {
|
||||
|
||||
log.info( "updating schema" );
|
||||
|
||||
|
||||
if ( outputFile != null ) {
|
||||
log.info( "writing generated schema to file: " + outputFile );
|
||||
outputFileWriter = new FileWriter( outputFile );
|
||||
}
|
||||
|
||||
String[] createSQL = configuration.generateSchemaUpdateScript( dialect, meta );
|
||||
for ( int j = 0; j < createSQL.length; j++ ) {
|
||||
|
||||
final String sql = createSQL[j];
|
||||
String formatted = format( sql );
|
||||
try {
|
||||
if ( delimiter != null ) {
|
||||
formatted += delimiter;
|
||||
}
|
||||
if ( script ) {
|
||||
System.out.println( sql );
|
||||
System.out.println( formatted );
|
||||
}
|
||||
if ( outputFile != null ) {
|
||||
outputFileWriter.write( formatted + "\n" );
|
||||
}
|
||||
if ( doUpdate ) {
|
||||
log.debug( sql );
|
||||
stmt.executeUpdate( sql );
|
||||
stmt.executeUpdate( formatted );
|
||||
}
|
||||
}
|
||||
catch ( SQLException e ) {
|
||||
if ( haltOnError ) {
|
||||
throw new JDBCException( "Error during DDL export", e );
|
||||
}
|
||||
exceptions.add( e );
|
||||
log.error( "Unsuccessful: " + sql );
|
||||
log.error( e.getMessage() );
|
||||
@ -176,7 +201,15 @@ public void execute(boolean script, boolean doUpdate) {
|
||||
exceptions.add( e );
|
||||
log.error( "Error closing connection", e );
|
||||
}
|
||||
|
||||
try {
|
||||
if( outputFileWriter != null ) {
|
||||
outputFileWriter.close();
|
||||
}
|
||||
}
|
||||
catch(Exception e) {
|
||||
exceptions.add(e);
|
||||
log.error( "Error closing connection", e );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -188,4 +221,26 @@ public void execute(boolean script, boolean doUpdate) {
|
||||
public List getExceptions() {
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
public void setHaltOnError(boolean haltOnError) {
|
||||
this.haltOnError = haltOnError;
|
||||
}
|
||||
|
||||
public void setFormat(boolean format) {
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
public void setOutputFile(String outputFile) {
|
||||
this.outputFile = outputFile;
|
||||
}
|
||||
|
||||
public void setDelimiter(String delimiter) {
|
||||
this.delimiter = delimiter;
|
||||
}
|
||||
|
||||
private String format(String sql) {
|
||||
return format ?
|
||||
new DDLFormatter( sql ).format() :
|
||||
sql;
|
||||
}
|
||||
}
|
||||
|
@ -47,9 +47,13 @@ public class SchemaUpdateTask extends MatchingTask {
|
||||
private List fileSets = new LinkedList();
|
||||
private File propertiesFile = null;
|
||||
private File configurationFile = null;
|
||||
private File outputFile = null;
|
||||
private boolean quiet = false;
|
||||
private boolean text = true;
|
||||
private boolean haltOnError = false;
|
||||
private String delimiter = null;
|
||||
private String namingStrategy = null;
|
||||
|
||||
|
||||
public void addFileset(FileSet set) {
|
||||
fileSets.add(set);
|
||||
@ -174,11 +178,39 @@ private SchemaUpdate getSchemaUpdate(Configuration cfg) throws HibernateExceptio
|
||||
properties.load( new FileInputStream(propertiesFile) );
|
||||
}
|
||||
cfg.setProperties(properties);
|
||||
return new SchemaUpdate(cfg);
|
||||
SchemaUpdate su = new SchemaUpdate(cfg);
|
||||
su.setOutputFile( outputFile.getPath() );
|
||||
su.setDelimiter(delimiter);
|
||||
su.setHaltOnError(haltOnError);
|
||||
return su;
|
||||
}
|
||||
|
||||
public void setNamingStrategy(String namingStrategy) {
|
||||
this.namingStrategy = namingStrategy;
|
||||
}
|
||||
|
||||
public File getOutputFile() {
|
||||
return outputFile;
|
||||
}
|
||||
|
||||
public void setOutputFile(File outputFile) {
|
||||
this.outputFile = outputFile;
|
||||
}
|
||||
|
||||
public boolean isHaltOnError() {
|
||||
return haltOnError;
|
||||
}
|
||||
|
||||
public void setHaltOnError(boolean haltOnError) {
|
||||
this.haltOnError = haltOnError;
|
||||
}
|
||||
|
||||
public String getDelimiter() {
|
||||
return delimiter;
|
||||
}
|
||||
|
||||
public void setDelimiter(String delimiter) {
|
||||
this.delimiter = delimiter;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,29 @@
|
||||
//$Id$
|
||||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Middleware LLC.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
package org.hibernate.transaction;
|
||||
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
import javax.transaction.Status;
|
||||
import javax.transaction.Synchronization;
|
||||
import javax.transaction.SystemException;
|
||||
@ -11,7 +32,7 @@
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hibernate.AssertionFailure;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.Transaction;
|
||||
import org.hibernate.TransactionException;
|
||||
@ -19,77 +40,58 @@
|
||||
import org.hibernate.util.JTAHelper;
|
||||
|
||||
/**
|
||||
* Implements a basic transaction strategy for JTA transactions. Instances check to
|
||||
* see if there is an existing JTA transaction. If none exists, a new transaction
|
||||
* is started. If one exists, all work is done in the existing context. The
|
||||
* following properties are used to locate the underlying <tt>UserTransaction</tt>:
|
||||
* <br><br>
|
||||
* <table>
|
||||
* <tr><td><tt>hibernate.jndi.url</tt></td><td>JNDI initial context URL</td></tr>
|
||||
* <tr><td><tt>hibernate.jndi.class</tt></td><td>JNDI provider class</td></tr>
|
||||
* <tr><td><tt>jta.UserTransaction</tt></td><td>JNDI name</td></tr>
|
||||
* </table>
|
||||
* {@link Transaction} implementation based on transaction management through
|
||||
* a JTA {@link UserTransaction}. Similar to {@link CMTTransaction}, except
|
||||
* here we are actually managing the transactions through the Hibernate
|
||||
* transaction mechanism.
|
||||
*
|
||||
* @author Gavin King
|
||||
* @author Steve Ebersole
|
||||
* @author Les Hazlewood
|
||||
*/
|
||||
public class JTATransaction implements Transaction {
|
||||
|
||||
private static final Log log = LogFactory.getLog(JTATransaction.class);
|
||||
private static final Log log = LogFactory.getLog( JTATransaction.class );
|
||||
|
||||
private final JDBCContext jdbcContext;
|
||||
private final TransactionFactory.Context transactionContext;
|
||||
|
||||
private UserTransaction ut;
|
||||
private UserTransaction userTransaction;
|
||||
private boolean newTransaction;
|
||||
private boolean begun;
|
||||
private boolean commitFailed;
|
||||
private boolean commitSucceeded;
|
||||
private boolean callback;
|
||||
|
||||
|
||||
public JTATransaction(
|
||||
InitialContext context,
|
||||
String utName,
|
||||
JDBCContext jdbcContext,
|
||||
TransactionFactory.Context transactionContext
|
||||
) {
|
||||
UserTransaction userTransaction,
|
||||
JDBCContext jdbcContext,
|
||||
TransactionFactory.Context transactionContext) {
|
||||
this.jdbcContext = jdbcContext;
|
||||
this.transactionContext = transactionContext;
|
||||
|
||||
log.debug("Looking for UserTransaction under: " + utName);
|
||||
|
||||
try {
|
||||
ut = (UserTransaction) context.lookup(utName);
|
||||
}
|
||||
catch (NamingException ne) {
|
||||
log.error("Could not find UserTransaction in JNDI", ne);
|
||||
throw new TransactionException("Could not find UserTransaction in JNDI: ", ne);
|
||||
}
|
||||
if (ut==null) {
|
||||
throw new AssertionFailure("A naming service lookup returned null");
|
||||
}
|
||||
|
||||
log.debug("Obtained UserTransaction");
|
||||
this.userTransaction = userTransaction;
|
||||
}
|
||||
|
||||
public void begin() throws HibernateException {
|
||||
if (begun) {
|
||||
if ( begun ) {
|
||||
return;
|
||||
}
|
||||
if (commitFailed) {
|
||||
throw new TransactionException("cannot re-start transaction after failed commit");
|
||||
if ( commitFailed ) {
|
||||
throw new TransactionException( "cannot re-start transaction after failed commit" );
|
||||
}
|
||||
|
||||
log.debug("begin");
|
||||
|
||||
log.debug( "begin" );
|
||||
|
||||
try {
|
||||
newTransaction = ut.getStatus() == Status.STATUS_NO_TRANSACTION;
|
||||
if (newTransaction) {
|
||||
ut.begin();
|
||||
log.debug("Began a new JTA transaction");
|
||||
newTransaction = userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION;
|
||||
if ( newTransaction ) {
|
||||
userTransaction.begin();
|
||||
log.debug( "Began a new JTA transaction" );
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("JTA transaction begin failed", e);
|
||||
throw new TransactionException("JTA transaction begin failed", e);
|
||||
catch ( Exception e ) {
|
||||
log.error( "JTA transaction begin failed", e );
|
||||
throw new TransactionException( "JTA transaction begin failed", e );
|
||||
}
|
||||
|
||||
/*if (newTransaction) {
|
||||
@ -102,10 +104,10 @@ public void begin() throws HibernateException {
|
||||
boolean synchronization = jdbcContext.registerSynchronizationIfPossible();
|
||||
|
||||
if ( !newTransaction && !synchronization ) {
|
||||
log.warn("You should set hibernate.transaction.manager_lookup_class if cache is enabled");
|
||||
log.warn( "You should set hibernate.transaction.manager_lookup_class if cache is enabled" );
|
||||
}
|
||||
|
||||
if (!synchronization) {
|
||||
if ( !synchronization ) {
|
||||
//if we could not register a synchronization,
|
||||
//do the before/after completion callbacks
|
||||
//ourself (but we need to let jdbcContext
|
||||
@ -117,40 +119,40 @@ public void begin() throws HibernateException {
|
||||
|
||||
begun = true;
|
||||
commitSucceeded = false;
|
||||
|
||||
jdbcContext.afterTransactionBegin(this);
|
||||
|
||||
jdbcContext.afterTransactionBegin( this );
|
||||
}
|
||||
|
||||
public void commit() throws HibernateException {
|
||||
if (!begun) {
|
||||
throw new TransactionException("Transaction not successfully started");
|
||||
if ( !begun ) {
|
||||
throw new TransactionException( "Transaction not successfully started" );
|
||||
}
|
||||
|
||||
log.debug("commit");
|
||||
log.debug( "commit" );
|
||||
|
||||
boolean flush = !transactionContext.isFlushModeNever()
|
||||
&& ( callback || !transactionContext.isFlushBeforeCompletionEnabled() );
|
||||
&& ( callback || !transactionContext.isFlushBeforeCompletionEnabled() );
|
||||
|
||||
if (flush) {
|
||||
if ( flush ) {
|
||||
transactionContext.managedFlush(); //if an exception occurs during flush, user must call rollback()
|
||||
}
|
||||
|
||||
if (callback && newTransaction) {
|
||||
jdbcContext.beforeTransactionCompletion(this);
|
||||
if ( callback && newTransaction ) {
|
||||
jdbcContext.beforeTransactionCompletion( this );
|
||||
}
|
||||
|
||||
closeIfRequired();
|
||||
|
||||
if (newTransaction) {
|
||||
if ( newTransaction ) {
|
||||
try {
|
||||
ut.commit();
|
||||
userTransaction.commit();
|
||||
commitSucceeded = true;
|
||||
log.debug("Committed JTA UserTransaction");
|
||||
log.debug( "Committed JTA UserTransaction" );
|
||||
}
|
||||
catch (Exception e) {
|
||||
catch ( Exception e ) {
|
||||
commitFailed = true; // so the transaction is already rolled back, by JTA spec
|
||||
log.error("JTA commit failed", e);
|
||||
throw new TransactionException("JTA commit failed: ", e);
|
||||
log.error( "JTA commit failed", e );
|
||||
throw new TransactionException( "JTA commit failed: ", e );
|
||||
}
|
||||
finally {
|
||||
afterCommitRollback();
|
||||
@ -167,11 +169,11 @@ public void commit() throws HibernateException {
|
||||
}
|
||||
|
||||
public void rollback() throws HibernateException {
|
||||
if (!begun && !commitFailed) {
|
||||
throw new TransactionException("Transaction not successfully started");
|
||||
if ( !begun && !commitFailed ) {
|
||||
throw new TransactionException( "Transaction not successfully started" );
|
||||
}
|
||||
|
||||
log.debug("rollback");
|
||||
log.debug( "rollback" );
|
||||
|
||||
/*if (!synchronization && newTransaction && !commitFailed) {
|
||||
jdbcContext.beforeTransactionCompletion(this);
|
||||
@ -180,26 +182,26 @@ public void rollback() throws HibernateException {
|
||||
try {
|
||||
closeIfRequired();
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("could not close session during rollback", e);
|
||||
catch ( Exception e ) {
|
||||
log.error( "could not close session during rollback", e );
|
||||
//swallow it, and continue to roll back JTA transaction
|
||||
}
|
||||
|
||||
try {
|
||||
if (newTransaction) {
|
||||
if (!commitFailed) {
|
||||
ut.rollback();
|
||||
log.debug("Rolled back JTA UserTransaction");
|
||||
if ( newTransaction ) {
|
||||
if ( !commitFailed ) {
|
||||
userTransaction.rollback();
|
||||
log.debug( "Rolled back JTA UserTransaction" );
|
||||
}
|
||||
}
|
||||
else {
|
||||
ut.setRollbackOnly();
|
||||
log.debug("set JTA UserTransaction to rollback only");
|
||||
userTransaction.setRollbackOnly();
|
||||
log.debug( "set JTA UserTransaction to rollback only" );
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("JTA rollback failed", e);
|
||||
throw new TransactionException("JTA rollback failed", e);
|
||||
catch ( Exception e ) {
|
||||
log.error( "JTA rollback failed", e );
|
||||
throw new TransactionException( "JTA rollback failed", e );
|
||||
}
|
||||
finally {
|
||||
afterCommitRollback();
|
||||
@ -209,28 +211,28 @@ public void rollback() throws HibernateException {
|
||||
private static final int NULL = Integer.MIN_VALUE;
|
||||
|
||||
private void afterCommitRollback() throws TransactionException {
|
||||
|
||||
|
||||
begun = false;
|
||||
|
||||
if (callback) { // this method is a noop if there is a Synchronization!
|
||||
if ( callback ) { // this method is a noop if there is a Synchronization!
|
||||
|
||||
if (!newTransaction) {
|
||||
log.warn("You should set hibernate.transaction.manager_lookup_class if cache is enabled");
|
||||
if ( !newTransaction ) {
|
||||
log.warn( "You should set hibernate.transaction.manager_lookup_class if cache is enabled" );
|
||||
}
|
||||
int status=NULL;
|
||||
int status = NULL;
|
||||
try {
|
||||
status = ut.getStatus();
|
||||
status = userTransaction.getStatus();
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error("Could not determine transaction status after commit", e);
|
||||
throw new TransactionException("Could not determine transaction status after commit", e);
|
||||
catch ( Exception e ) {
|
||||
log.error( "Could not determine transaction status after commit", e );
|
||||
throw new TransactionException( "Could not determine transaction status after commit", e );
|
||||
}
|
||||
finally {
|
||||
/*if (status!=Status.STATUS_COMMITTED && status!=Status.STATUS_ROLLEDBACK) {
|
||||
log.warn("Transaction not complete - you should set hibernate.transaction.manager_lookup_class if cache is enabled");
|
||||
//throw exception??
|
||||
}*/
|
||||
jdbcContext.afterTransactionCompletion(status==Status.STATUS_COMMITTED, this);
|
||||
jdbcContext.afterTransactionCompletion( status == Status.STATUS_COMMITTED, this );
|
||||
}
|
||||
|
||||
}
|
||||
@ -243,17 +245,17 @@ public boolean wasRolledBack() throws TransactionException {
|
||||
|
||||
final int status;
|
||||
try {
|
||||
status = ut.getStatus();
|
||||
status = userTransaction.getStatus();
|
||||
}
|
||||
catch (SystemException se) {
|
||||
log.error("Could not determine transaction status", se);
|
||||
throw new TransactionException("Could not determine transaction status", se);
|
||||
catch ( SystemException se ) {
|
||||
log.error( "Could not determine transaction status", se );
|
||||
throw new TransactionException( "Could not determine transaction status", se );
|
||||
}
|
||||
if (status==Status.STATUS_UNKNOWN) {
|
||||
throw new TransactionException("Could not determine transaction status");
|
||||
if ( status == Status.STATUS_UNKNOWN ) {
|
||||
throw new TransactionException( "Could not determine transaction status" );
|
||||
}
|
||||
else {
|
||||
return JTAHelper.isRollback(status);
|
||||
return JTAHelper.isRollback( status );
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,50 +265,52 @@ public boolean wasCommitted() throws TransactionException {
|
||||
|
||||
final int status;
|
||||
try {
|
||||
status = ut.getStatus();
|
||||
status = userTransaction.getStatus();
|
||||
}
|
||||
catch (SystemException se) {
|
||||
log.error("Could not determine transaction status", se);
|
||||
throw new TransactionException("Could not determine transaction status: ", se);
|
||||
catch ( SystemException se ) {
|
||||
log.error( "Could not determine transaction status", se );
|
||||
throw new TransactionException( "Could not determine transaction status: ", se );
|
||||
}
|
||||
if (status==Status.STATUS_UNKNOWN) {
|
||||
throw new TransactionException("Could not determine transaction status");
|
||||
if ( status == Status.STATUS_UNKNOWN ) {
|
||||
throw new TransactionException( "Could not determine transaction status" );
|
||||
}
|
||||
else {
|
||||
return status==Status.STATUS_COMMITTED;
|
||||
return status == Status.STATUS_COMMITTED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean isActive() throws TransactionException {
|
||||
|
||||
if (!begun || commitFailed || commitSucceeded) return false;
|
||||
if ( !begun || commitFailed || commitSucceeded ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
final int status;
|
||||
try {
|
||||
status = ut.getStatus();
|
||||
status = userTransaction.getStatus();
|
||||
}
|
||||
catch (SystemException se) {
|
||||
log.error("Could not determine transaction status", se);
|
||||
throw new TransactionException("Could not determine transaction status: ", se);
|
||||
catch ( SystemException se ) {
|
||||
log.error( "Could not determine transaction status", se );
|
||||
throw new TransactionException( "Could not determine transaction status: ", se );
|
||||
}
|
||||
if (status==Status.STATUS_UNKNOWN) {
|
||||
throw new TransactionException("Could not determine transaction status");
|
||||
if ( status == Status.STATUS_UNKNOWN ) {
|
||||
throw new TransactionException( "Could not determine transaction status" );
|
||||
}
|
||||
else {
|
||||
return status==Status.STATUS_ACTIVE;
|
||||
return status == Status.STATUS_ACTIVE;
|
||||
}
|
||||
}
|
||||
|
||||
public void registerSynchronization(Synchronization sync) throws HibernateException {
|
||||
if (getTransactionManager()==null) {
|
||||
throw new IllegalStateException("JTA TransactionManager not available");
|
||||
if ( getTransactionManager() == null ) {
|
||||
throw new IllegalStateException( "JTA TransactionManager not available" );
|
||||
}
|
||||
else {
|
||||
try {
|
||||
getTransactionManager().getTransaction().registerSynchronization(sync);
|
||||
getTransactionManager().getTransaction().registerSynchronization( sync );
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new TransactionException("could not register synchronization", e);
|
||||
catch ( Exception e ) {
|
||||
throw new TransactionException( "could not register synchronization", e );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -316,8 +320,8 @@ private TransactionManager getTransactionManager() {
|
||||
}
|
||||
|
||||
private void closeIfRequired() throws HibernateException {
|
||||
boolean close = callback &&
|
||||
transactionContext.shouldAutoClose() &&
|
||||
boolean close = callback &&
|
||||
transactionContext.shouldAutoClose() &&
|
||||
!transactionContext.isClosed();
|
||||
if ( close ) {
|
||||
transactionContext.managedClose();
|
||||
@ -326,14 +330,14 @@ private void closeIfRequired() throws HibernateException {
|
||||
|
||||
public void setTimeout(int seconds) {
|
||||
try {
|
||||
ut.setTransactionTimeout(seconds);
|
||||
userTransaction.setTransactionTimeout( seconds );
|
||||
}
|
||||
catch (SystemException se) {
|
||||
throw new TransactionException("could not set transaction timeout", se);
|
||||
catch ( SystemException se ) {
|
||||
throw new TransactionException( "could not set transaction timeout", se );
|
||||
}
|
||||
}
|
||||
|
||||
protected UserTransaction getUserTransaction() {
|
||||
return ut;
|
||||
return userTransaction;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,27 @@
|
||||
//$Id$
|
||||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* Copyright (c) 2008, Red Hat Middleware LLC or third-party contributors as
|
||||
* indicated by the @author tags or express copyright attribution
|
||||
* statements applied by the authors. All third-party contributions are
|
||||
* distributed under license by Red Hat Middleware LLC.
|
||||
*
|
||||
* This copyrighted material is made available to anyone wishing to use, modify,
|
||||
* copy, or redistribute it subject to the terms and conditions of the GNU
|
||||
* Lesser General Public License, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this distribution; if not, write to:
|
||||
* Free Software Foundation, Inc.
|
||||
* 51 Franklin Street, Fifth Floor
|
||||
* Boston, MA 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
package org.hibernate.transaction;
|
||||
|
||||
import java.util.Properties;
|
||||
@ -20,61 +43,184 @@
|
||||
import org.hibernate.util.JTAHelper;
|
||||
|
||||
/**
|
||||
* Factory for <tt>JTATransaction</tt>.
|
||||
* Factory for {@link JTATransaction} instances.
|
||||
* <p/>
|
||||
* To be completely accurate to the JTA spec, JTA implementations should
|
||||
* publish their contextual {@link UserTransaction} reference into JNDI.
|
||||
* However, in practice there are quite a few <tt>stand-alone</tt>
|
||||
* implementations intended for use outside of J2EE/JEE containers and
|
||||
* which therefore do not publish their {@link UserTransaction} references
|
||||
* into JNDI but which otherwise follow the aspects of the JTA specification.
|
||||
* This {@link TransactionFactory} implementation can support both models.
|
||||
* <p/>
|
||||
* For complete JTA implementations (including dependence on JNDI), the
|
||||
* {@link UserTransaction} reference is obtained by a call to
|
||||
* {@link #resolveInitialContext}. Hibernate will then attempt to locate the
|
||||
* {@link UserTransaction} within this resolved
|
||||
* {@link InitialContext} based on the namespace returned by
|
||||
* {@link #resolveUserTransactionName}.
|
||||
* <p/>
|
||||
* For the so-called <tt>stand-alone</tt> implementations, we do not care at
|
||||
* all about the JNDI aspects just described. Here, the implementation would
|
||||
* have a specific manner to obtain a reference to its contextual
|
||||
* {@link UserTransaction}; usually this would be a static code reference, but
|
||||
* again it varies. Anyway, for each implementation the integration would need
|
||||
* to override the {@link #getUserTransaction} method and return the appropriate
|
||||
* thing.
|
||||
*
|
||||
* @see JTATransaction
|
||||
* @author Gavin King
|
||||
* @author Steve Ebersole
|
||||
* @author Les Hazlewood
|
||||
*/
|
||||
public class JTATransactionFactory implements TransactionFactory {
|
||||
|
||||
public static final String DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction";
|
||||
private static final Log log = LogFactory.getLog(JTATransactionFactory.class);
|
||||
private static final String DEFAULT_USER_TRANSACTION_NAME = "java:comp/UserTransaction";
|
||||
|
||||
protected InitialContext context;
|
||||
protected String utName;
|
||||
protected InitialContext initialContext;
|
||||
protected String userTransactionName;
|
||||
|
||||
/**
|
||||
* Configure this transaction factory. Specifically here we are attempting to
|
||||
* resolve both an {@link #getInitialContext InitialContext} as well as the
|
||||
* {@link #getUserTransactionName() JNDI namespace} for the {@link UserTransaction}.
|
||||
*
|
||||
* @param props The configuration properties
|
||||
*
|
||||
* @exception HibernateException
|
||||
*/
|
||||
public void configure(Properties props) throws HibernateException {
|
||||
this.initialContext = resolveInitialContext( props );
|
||||
this.userTransactionName = resolveUserTransactionName( props );
|
||||
log.trace( "Configured JTATransactionFactory to use [" + userTransactionName + "] for UserTransaction JDNI namespace" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Given the lot of Hibernate configuration properties, resolve appropriate
|
||||
* reference to JNDI {@link InitialContext}.
|
||||
* <p/>
|
||||
* In general, the properties in which we are interested here all begin with
|
||||
* <tt>hibernate.jndi</tt>. Especially important depending on your
|
||||
* environment are {@link Environment#JNDI_URL hibernate.jndi.url} and
|
||||
* {@link Environment#JNDI_CLASS hibernate.jndi.class}
|
||||
*
|
||||
* @param properties The Hibernate config properties.
|
||||
* @return The resolved InitialContext.
|
||||
*/
|
||||
protected final InitialContext resolveInitialContext(Properties properties) {
|
||||
try {
|
||||
context = NamingHelper.getInitialContext(props);
|
||||
return NamingHelper.getInitialContext( properties );
|
||||
}
|
||||
catch (NamingException ne) {
|
||||
log.error("Could not obtain initial context", ne);
|
||||
catch ( NamingException ne ) {
|
||||
throw new HibernateException( "Could not obtain initial context", ne );
|
||||
}
|
||||
}
|
||||
|
||||
utName = props.getProperty(Environment.USER_TRANSACTION);
|
||||
|
||||
if (utName==null) {
|
||||
TransactionManagerLookup lookup = TransactionManagerLookupFactory.getTransactionManagerLookup(props);
|
||||
if (lookup!=null) utName = lookup.getUserTransactionName();
|
||||
/**
|
||||
* Given the lot of Hibernate configuration properties, resolve appropriate
|
||||
* JNDI namespace to use for {@link UserTransaction} resolution.
|
||||
* <p/>
|
||||
* We determine the namespace to use by<ol>
|
||||
* <li>Any specified {@link Environment#USER_TRANSACTION jta.UserTransaction} config property</li>
|
||||
* <li>If a {@link TransactionManagerLookup} was indicated, use its
|
||||
* {@link TransactionManagerLookup#getUserTransactionName}</li>
|
||||
* <li>finally, as a last resort, we use {@link #DEFAULT_USER_TRANSACTION_NAME}</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param properties The Hibernate config properties.
|
||||
* @return The resolved {@link UserTransaction} namespace
|
||||
*/
|
||||
protected final String resolveUserTransactionName(Properties properties) {
|
||||
String utName = properties.getProperty( Environment.USER_TRANSACTION );
|
||||
if ( utName == null ) {
|
||||
TransactionManagerLookup lookup = TransactionManagerLookupFactory.getTransactionManagerLookup( properties );
|
||||
if ( lookup != null ) {
|
||||
utName = lookup.getUserTransactionName();
|
||||
}
|
||||
}
|
||||
|
||||
if (utName==null) utName = DEFAULT_USER_TRANSACTION_NAME;
|
||||
return utName == null ? DEFAULT_USER_TRANSACTION_NAME : utName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public Transaction createTransaction(JDBCContext jdbcContext, Context transactionContext)
|
||||
throws HibernateException {
|
||||
return new JTATransaction(context, utName, jdbcContext, transactionContext);
|
||||
throws HibernateException {
|
||||
UserTransaction ut = getUserTransaction();
|
||||
return new JTATransaction( ut, jdbcContext, transactionContext );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link UserTransaction} reference.
|
||||
*
|
||||
* @return The appropriate {@link UserTransaction} reference.
|
||||
*/
|
||||
protected UserTransaction getUserTransaction() {
|
||||
log.trace( "Attempting to locate UserTransaction via JNDI [" + getUserTransactionName() + "]" );
|
||||
|
||||
try {
|
||||
UserTransaction ut = ( UserTransaction ) getInitialContext().lookup( getUserTransactionName() );
|
||||
if ( ut == null ) {
|
||||
throw new TransactionException( "Naming service lookup for UserTransaction returned null [" + getUserTransactionName() +"]" );
|
||||
}
|
||||
|
||||
log.trace( "Obtained UserTransaction" );
|
||||
|
||||
return ut;
|
||||
}
|
||||
catch ( NamingException ne ) {
|
||||
throw new TransactionException( "Could not find UserTransaction in JNDI [" + getUserTransaction() + "]", ne );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for property 'initialContext'.
|
||||
*
|
||||
* @return Value for property 'initialContext'.
|
||||
*/
|
||||
protected InitialContext getInitialContext() {
|
||||
return initialContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for property 'userTransactionName'.
|
||||
* The algorithm here is
|
||||
*
|
||||
* @return Value for property 'userTransactionName'.
|
||||
*/
|
||||
protected String getUserTransactionName() {
|
||||
return userTransactionName;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public ConnectionReleaseMode getDefaultReleaseMode() {
|
||||
return ConnectionReleaseMode.AFTER_STATEMENT;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isTransactionManagerRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean areCallbacksLocalToHibernateTransactions() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public boolean isTransactionInProgress(
|
||||
JDBCContext jdbcContext,
|
||||
Context transactionContext,
|
||||
Transaction transaction) {
|
||||
Context transactionContext,
|
||||
Transaction transaction) {
|
||||
try {
|
||||
// Essentially:
|
||||
// Essentially:
|
||||
// 1) If we have a local (Hibernate) transaction in progress
|
||||
// and it already has the UserTransaction cached, use that
|
||||
// UserTransaction to determine the status.
|
||||
@ -83,27 +229,22 @@ public boolean isTransactionInProgress(
|
||||
// 3) Finally, as the last resort, try to lookup the
|
||||
// UserTransaction via JNDI and use that to determine the
|
||||
// status.
|
||||
if ( transaction != null ) {
|
||||
if ( transaction != null ) {
|
||||
UserTransaction ut = ( ( JTATransaction ) transaction ).getUserTransaction();
|
||||
if ( ut != null ) {
|
||||
return JTAHelper.isInProgress( ut.getStatus() );
|
||||
}
|
||||
}
|
||||
if ( ut != null ) {
|
||||
return JTAHelper.isInProgress( ut.getStatus() );
|
||||
}
|
||||
}
|
||||
|
||||
if ( jdbcContext.getFactory().getTransactionManager() != null ) {
|
||||
return JTAHelper.isInProgress( jdbcContext.getFactory().getTransactionManager().getStatus() );
|
||||
}
|
||||
else {
|
||||
try {
|
||||
UserTransaction ut = ( UserTransaction ) context.lookup( utName );
|
||||
return ut != null && JTAHelper.isInProgress( ut.getStatus() );
|
||||
}
|
||||
catch ( NamingException ne ) {
|
||||
throw new TransactionException( "Unable to locate UserTransaction to check status", ne );
|
||||
}
|
||||
}
|
||||
if ( jdbcContext.getFactory().getTransactionManager() != null ) {
|
||||
return JTAHelper.isInProgress( jdbcContext.getFactory().getTransactionManager().getStatus() );
|
||||
}
|
||||
else {
|
||||
UserTransaction ut = getUserTransaction();
|
||||
return ut != null && JTAHelper.isInProgress( ut.getStatus() );
|
||||
}
|
||||
}
|
||||
catch( SystemException se ) {
|
||||
catch ( SystemException se ) {
|
||||
throw new TransactionException( "Unable to check transaction status", se );
|
||||
}
|
||||
}
|
||||
|
@ -550,12 +550,16 @@ public Object replace(
|
||||
//for arrays, replaceElements() may return a different reference, since
|
||||
//the array length might not match
|
||||
result = replaceElements( original, result, owner, copyCache, session );
|
||||
|
||||
if (original==target) {
|
||||
//get the elements back into the target
|
||||
|
||||
if ( original == target ) {
|
||||
// get the elements back into the target making sure to handle dirty flag
|
||||
boolean wasClean = PersistentCollection.class.isInstance( target ) && !( ( PersistentCollection ) target ).isDirty();
|
||||
//TODO: this is a little inefficient, don't need to do a whole
|
||||
// deep replaceElements() call
|
||||
replaceElements( result, target, owner, copyCache, session );
|
||||
if ( wasClean ) {
|
||||
( ( PersistentCollection ) target ).clearDirty();
|
||||
}
|
||||
result = target;
|
||||
}
|
||||
|
||||
|
@ -283,6 +283,55 @@ public void testNoExtraUpdatesOnMergeVersionedWithCollection() throws Exception
|
||||
// cleanup();
|
||||
}
|
||||
|
||||
public void testNoExtraUpdatesOnPersistentMergeVersionedWithCollection() throws Exception {
|
||||
Session s = openSession();
|
||||
s.beginTransaction();
|
||||
VersionedEntity parent = new VersionedEntity( "parent", "parent" );
|
||||
VersionedEntity child = new VersionedEntity( "child", "child" );
|
||||
parent.getChildren().add( child );
|
||||
child.setParent( parent );
|
||||
s.persist( parent );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
clearCounts();
|
||||
|
||||
// parent is now detached, but we have made no changes. so attempt to merge it
|
||||
// into this new session; this should cause no updates...
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
// load parent so that merge will follow entityIsPersistent path
|
||||
VersionedEntity persistentParent = ( VersionedEntity ) s.get( VersionedEntity.class, parent.getId() );
|
||||
// load children
|
||||
VersionedEntity persistentChild = ( VersionedEntity ) persistentParent.getChildren().iterator().next();
|
||||
VersionedEntity mergedParent = ( VersionedEntity ) s.merge( persistentParent ); // <-- This merge leads to failure
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
|
||||
assertUpdateCount( 0 );
|
||||
assertInsertCount( 0 );
|
||||
assertEquals( "unexpected parent version increment", parent.getVersion(), mergedParent.getVersion() );
|
||||
VersionedEntity mergedChild = ( VersionedEntity ) mergedParent.getChildren().iterator().next();
|
||||
assertEquals( "unexpected child version increment", child.getVersion(), mergedChild.getVersion() );
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// as a control measure, now update the node once it is loaded and
|
||||
// make sure we get an update as a result...
|
||||
s = openSession();
|
||||
s.beginTransaction();
|
||||
persistentParent = ( VersionedEntity ) s.get( VersionedEntity.class, parent.getId() );
|
||||
persistentParent.setName( "new name" );
|
||||
persistentParent.getChildren().add( new VersionedEntity( "child2", "new child" ) );
|
||||
persistentParent = ( VersionedEntity ) s.merge( persistentParent );
|
||||
s.getTransaction().commit();
|
||||
s.close();
|
||||
assertUpdateCount( 1 );
|
||||
assertInsertCount( 1 );
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
// cleanup();
|
||||
}
|
||||
|
||||
public void testPersistThenMergeInSameTxnWithVersion() {
|
||||
Session s = openSession();
|
||||
Transaction tx = s.beginTransaction();
|
||||
|
@ -191,7 +191,7 @@
|
||||
|
||||
<database-object>
|
||||
<create>
|
||||
CREATE PROCEDURE HIBDB2TST.selectAllEmployments ()
|
||||
CREATE PROCEDURE HIBDB2TST.selectAllEmployments () RESULT SETS 1
|
||||
P1: BEGIN
|
||||
DECLARE C1 CURSOR WITH RETURN FOR
|
||||
SELECT EMPLOYEE, EMPLOYER, STARTDATE, ENDDATE,
|
||||
@ -208,7 +208,7 @@
|
||||
|
||||
<database-object>
|
||||
<create>
|
||||
CREATE PROCEDURE HIBDB2TST.paramHandling (IN j SMALLINT, IN i SMALLINT)
|
||||
CREATE PROCEDURE HIBDB2TST.paramHandling (IN j SMALLINT, IN i SMALLINT) RESULT SETS 1
|
||||
P1: BEGIN
|
||||
DECLARE C1 CURSOR WITH RETURN FOR
|
||||
SELECT j as value, i as value2 from sysibm.sysdummy1;
|
||||
@ -222,7 +222,7 @@
|
||||
|
||||
<database-object>
|
||||
<create>
|
||||
CREATE PROCEDURE HIBDB2TST.simpleScalar (IN j SMALLINT)
|
||||
CREATE PROCEDURE HIBDB2TST.simpleScalar (IN j SMALLINT) RESULT SETS 1
|
||||
P1: BEGIN
|
||||
DECLARE C1 CURSOR WITH RETURN FOR
|
||||
SELECT j as value, 'getAll' as name from sysibm.sysdummy1;
|
||||
|
Loading…
x
Reference in New Issue
Block a user