View Javadoc
1   /*
2    * Copyright 2004-2026 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *    https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package com.ibatis.sqlmap.engine.transaction.jta;
17  
18  import com.ibatis.common.jdbc.logging.ConnectionLogProxy;
19  import com.ibatis.common.logging.Log;
20  import com.ibatis.common.logging.LogFactory;
21  import com.ibatis.sqlmap.engine.transaction.IsolationLevel;
22  import com.ibatis.sqlmap.engine.transaction.Transaction;
23  import com.ibatis.sqlmap.engine.transaction.TransactionException;
24  
25  import jakarta.transaction.Status;
26  import jakarta.transaction.UserTransaction;
27  
28  import java.sql.Connection;
29  import java.sql.SQLException;
30  
31  import javax.sql.DataSource;
32  
33  /**
34   * The Class JtaTransaction.
35   */
36  public class JakartaTransaction implements Transaction {
37  
38    /** The Constant connectionLog. */
39    private static final Log connectionLog = LogFactory.getLog(Connection.class);
40  
41    /** The user transaction. */
42    private UserTransaction userTransaction;
43  
44    /** The data source. */
45    private DataSource dataSource;
46  
47    /** The connection. */
48    private Connection connection;
49  
50    /** The isolation level. */
51    private IsolationLevel isolationLevel = new IsolationLevel();
52  
53    /** The commmitted. */
54    private boolean commmitted = false;
55  
56    /** The new transaction. */
57    private boolean newTransaction = false;
58  
59    /**
60     * Instantiates a new jta transaction.
61     *
62     * @param utx
63     *          the utx
64     * @param ds
65     *          the ds
66     * @param isolationLevel
67     *          the isolation level
68     *
69     * @throws TransactionException
70     *           the transaction exception
71     */
72    public JakartaTransaction(UserTransaction utx, DataSource ds, int isolationLevel) throws TransactionException {
73      // Check parameters
74      userTransaction = utx;
75      dataSource = ds;
76      if (userTransaction == null) {
77        throw new TransactionException("JtaTransaction initialization failed.  UserTransaction was null.");
78      }
79      if (dataSource == null) {
80        throw new TransactionException("JtaTransaction initialization failed.  DataSource was null.");
81      }
82      this.isolationLevel.setIsolationLevel(isolationLevel);
83    }
84  
85    /**
86     * Inits the.
87     *
88     * @throws TransactionException
89     *           the transaction exception
90     * @throws SQLException
91     *           the SQL exception
92     */
93    private void init() throws TransactionException, SQLException {
94      // Start JTA Transaction
95      try {
96        newTransaction = userTransaction.getStatus() == Status.STATUS_NO_TRANSACTION;
97        if (newTransaction) {
98          userTransaction.begin();
99        }
100     } catch (Exception e) {
101       throw new TransactionException("JtaTransaction could not start transaction.  Cause: ", e);
102     }
103 
104     // Open JDBC Connection
105     connection = dataSource.getConnection();
106     if (connection == null) {
107       throw new TransactionException(
108           "JtaTransaction could not start transaction.  Cause: The DataSource returned a null connection.");
109     }
110     // Isolation Level
111     isolationLevel.applyIsolationLevel(connection);
112     // AutoCommit
113     if (connection.getAutoCommit()) {
114       connection.setAutoCommit(false);
115     }
116     // Debug
117     if (connectionLog.isDebugEnabled()) {
118       connection = ConnectionLogProxy.newInstance(connection);
119     }
120   }
121 
122   @Override
123   public void commit() throws SQLException, TransactionException {
124     if (connection != null) {
125       if (commmitted) {
126         throw new TransactionException(
127             "JtaTransaction could not commit because this transaction has already been committed.");
128       }
129       try {
130         if (newTransaction) {
131           userTransaction.commit();
132         }
133       } catch (Exception e) {
134         throw new TransactionException("JtaTransaction could not commit.  Cause: ", e);
135       }
136       commmitted = true;
137     }
138   }
139 
140   @Override
141   public void rollback() throws SQLException, TransactionException {
142     if (connection != null && !commmitted) {
143       try {
144         if (userTransaction != null) {
145           if (newTransaction) {
146             userTransaction.rollback();
147           } else {
148             userTransaction.setRollbackOnly();
149           }
150         }
151       } catch (Exception e) {
152         throw new TransactionException("JtaTransaction could not rollback.  Cause: ", e);
153       }
154     }
155   }
156 
157   @Override
158   public void close() throws SQLException, TransactionException {
159     if (connection != null) {
160       try {
161         isolationLevel.restoreIsolationLevel(connection);
162       } finally {
163         connection.close();
164         connection = null;
165       }
166     }
167   }
168 
169   @Override
170   public Connection getConnection() throws SQLException, TransactionException {
171     if (connection == null) {
172       init();
173     }
174     return connection;
175   }
176 
177 }