001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.jdbc.datasource; 018 019import java.sql.SQLException; 020import java.sql.Savepoint; 021 022import org.apache.commons.logging.Log; 023import org.apache.commons.logging.LogFactory; 024 025import org.springframework.transaction.CannotCreateTransactionException; 026import org.springframework.transaction.NestedTransactionNotSupportedException; 027import org.springframework.transaction.SavepointManager; 028import org.springframework.transaction.TransactionException; 029import org.springframework.transaction.TransactionSystemException; 030import org.springframework.transaction.TransactionUsageException; 031import org.springframework.transaction.support.SmartTransactionObject; 032 033/** 034 * Convenient base class for JDBC-aware transaction objects. Can contain a 035 * {@link ConnectionHolder} with a JDBC {@code Connection}, and implements the 036 * {@link SavepointManager} interface based on that {@code ConnectionHolder}. 037 * 038 * <p>Allows for programmatic management of JDBC {@link java.sql.Savepoint Savepoints}. 039 * Spring's {@link org.springframework.transaction.support.DefaultTransactionStatus} 040 * automatically delegates to this, as it autodetects transaction objects which 041 * implement the {@link SavepointManager} interface. 042 * 043 * @author Juergen Hoeller 044 * @since 1.1 045 * @see DataSourceTransactionManager 046 */ 047public abstract class JdbcTransactionObjectSupport implements SavepointManager, SmartTransactionObject { 048 049 private static final Log logger = LogFactory.getLog(JdbcTransactionObjectSupport.class); 050 051 052 private ConnectionHolder connectionHolder; 053 054 private Integer previousIsolationLevel; 055 056 private boolean savepointAllowed = false; 057 058 059 public void setConnectionHolder(ConnectionHolder connectionHolder) { 060 this.connectionHolder = connectionHolder; 061 } 062 063 public ConnectionHolder getConnectionHolder() { 064 return this.connectionHolder; 065 } 066 067 public boolean hasConnectionHolder() { 068 return (this.connectionHolder != null); 069 } 070 071 public void setPreviousIsolationLevel(Integer previousIsolationLevel) { 072 this.previousIsolationLevel = previousIsolationLevel; 073 } 074 075 public Integer getPreviousIsolationLevel() { 076 return this.previousIsolationLevel; 077 } 078 079 public void setSavepointAllowed(boolean savepointAllowed) { 080 this.savepointAllowed = savepointAllowed; 081 } 082 083 public boolean isSavepointAllowed() { 084 return this.savepointAllowed; 085 } 086 087 @Override 088 public void flush() { 089 // no-op 090 } 091 092 093 //--------------------------------------------------------------------- 094 // Implementation of SavepointManager 095 //--------------------------------------------------------------------- 096 097 /** 098 * This implementation creates a JDBC 3.0 Savepoint and returns it. 099 * @see java.sql.Connection#setSavepoint 100 */ 101 @Override 102 public Object createSavepoint() throws TransactionException { 103 ConnectionHolder conHolder = getConnectionHolderForSavepoint(); 104 try { 105 if (!conHolder.supportsSavepoints()) { 106 throw new NestedTransactionNotSupportedException( 107 "Cannot create a nested transaction because savepoints are not supported by your JDBC driver"); 108 } 109 return conHolder.createSavepoint(); 110 } 111 catch (SQLException ex) { 112 throw new CannotCreateTransactionException("Could not create JDBC savepoint", ex); 113 } 114 } 115 116 /** 117 * This implementation rolls back to the given JDBC 3.0 Savepoint. 118 * @see java.sql.Connection#rollback(java.sql.Savepoint) 119 */ 120 @Override 121 public void rollbackToSavepoint(Object savepoint) throws TransactionException { 122 ConnectionHolder conHolder = getConnectionHolderForSavepoint(); 123 try { 124 conHolder.getConnection().rollback((Savepoint) savepoint); 125 } 126 catch (Throwable ex) { 127 throw new TransactionSystemException("Could not roll back to JDBC savepoint", ex); 128 } 129 } 130 131 /** 132 * This implementation releases the given JDBC 3.0 Savepoint. 133 * @see java.sql.Connection#releaseSavepoint 134 */ 135 @Override 136 public void releaseSavepoint(Object savepoint) throws TransactionException { 137 ConnectionHolder conHolder = getConnectionHolderForSavepoint(); 138 try { 139 conHolder.getConnection().releaseSavepoint((Savepoint) savepoint); 140 } 141 catch (Throwable ex) { 142 logger.debug("Could not explicitly release JDBC savepoint", ex); 143 } 144 } 145 146 protected ConnectionHolder getConnectionHolderForSavepoint() throws TransactionException { 147 if (!isSavepointAllowed()) { 148 throw new NestedTransactionNotSupportedException( 149 "Transaction manager does not allow nested transactions"); 150 } 151 if (!hasConnectionHolder()) { 152 throw new TransactionUsageException( 153 "Cannot create nested transaction when not exposing a JDBC transaction"); 154 } 155 return getConnectionHolder(); 156 } 157 158}