<commit_before>package org.voovan.db;

import org.voovan.tools.TObject;
import org.voovan.tools.TSQL;
import org.voovan.tools.log.Logger;
import org.voovan.tools.reflect.TReflect;

import javax.sql.DataSource;
import java.sql.*;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * jdbc 操作类
 * 		每个数据库操作函数 开启关闭一次连接. 使用:开始来标识参数,例如: Map 和对象形式用:":arg", List 和 Array 形式用":1"
 * 		所有行为均为非线程安全
 *
 * @author helyho
 *
 * Voovan Framework.
 * WebSite: https://github.com/helyho/Voovan
 * Licence: Apache v2 License
 */
public class JdbcOperate {
	private static Map<Long, JdbcOperate> JDBCOPERATE_THREAD_LIST = new ConcurrentHashMap<Long, JdbcOperate>();

	private DataSource	dataSource;
	private Connection	connection;
	private TranscationType transcationType;
	private Savepoint savepoint = null;

	private List<JdbcOperate> bindedJdbcOperate = new ArrayList<JdbcOperate>();
	private boolean isTransactionFinished = false;

	/**
	 * 构造函数
	 * @param dataSource	数据源
	 */
	public JdbcOperate(DataSource dataSource) {
		this.dataSource = dataSource;
		this.transcationType = TranscationType.NONE;
	}

	/**
	 * 构造函数
	 * @param dataSource	数据源
	 * @param isTrancation   是否启用事物支持, 默认使用的`NEST`(嵌套事务)的事物方式
	 */
	public JdbcOperate(DataSource dataSource,boolean isTrancation){
		this.dataSource = dataSource;
		this.transcationType = (isTrancation? TranscationType.NEST : TranscationType.NONE);
	}

	/**
	 * 构造函数
	 * @param dataSource	数据源
	 * @param transcationType    是否启用事务支持, 设置事务模式
	 */
	public JdbcOperate(DataSource dataSource,TranscationType transcationType){
		this.dataSource = dataSource;
		this.transcationType = transcationType;
	}

	/**
	 * 增加连接事务关联绑定
	 * 		只能绑定 TranscationType 为 NEST 的事务
	 * @param subJdbcOperate 连接操作对象
	 * @param bothway true: 双向绑定, false: 单向绑定,只建立当前连接和目标连接的绑定关系
	 *                双向绑定意味这任意一个节点的 commit 或者 rollback 操作都会直接联动所有的事物同时操作, 如果是单向绑定,只会联动下级事物同时操作
	 * @return true: 增加连接事务绑定成功, false: 增加连接事务绑定失败
	 */
	public synchronized boolean addBind(JdbcOperate subJdbcOperate, boolean bothway){
		if(subJdbcOperate.transcationType == TranscationType.NEST) {
			if(!bindedJdbcOperate.contains(subJdbcOperate)) {
				if(bothway) {
					subJdbcOperate.bindedJdbcOperate.add(this);
				}
				return bindedJdbcOperate.add(subJdbcOperate);
			}
		}

		return false;
	}

	/**
	 * 移除连接事务绑定
	 * @param subJdbcOperate 连接操作对象
	 * @param bothway true: 双向绑定, 解出当前连接和目标连接两个连接相互的绑定关系, : 单向绑定, 只接触当前连接和目标连接的绑定关系
	 * @return true: 移除连接事务绑定成功, false: 移除连接事务绑定失败
	 */
	public synchronized boolean removeBind(JdbcOperate subJdbcOperate, boolean bothway){
		if(bothway) {
			subJdbcOperate.bindedJdbcOperate.remove(this);
		}
		return bindedJdbcOperate.remove(subJdbcOperate);
	}

	/**
	 * 获取连接
	 *
	 * @return 获取数据库连接
	 * @throws SQLException SQL 异常
	 */
	public synchronized Connection getConnection() throws SQLException {
		long threadId = Thread.currentThread().getId();
		//如果连接不存在,或者连接已关闭则重取一个连接
		if (connection == null || connection.isClosed()) {
			//事务嵌套模式
			if (transcationType == TranscationType.NEST) {
				//判断是否有上层事务
				if (JDBCOPERATE_THREAD_LIST.containsKey(threadId)) {
					connection = JDBCOPERATE_THREAD_LIST.get(threadId).connection;
					savepoint = connection.setSavepoint();
				} else {
					connection = dataSource.getConnection();
					connection.setAutoCommit(false);
					JDBCOPERATE_THREAD_LIST.put(threadId, this);
				}
			}
			//孤立事务模式
			else if (transcationType == TranscationType.ALONE){
				connection = dataSource.getConnection();
				connection.setAutoCommit(false);
			}
			//非事务模式
			else if (transcationType == TranscationType.NONE){
				connection = dataSource.getConnection();
				connection.setAutoCommit(true);
			}
		}
		return connection;
	}

	/**
	 * 返回当前事务形式
	 * @return 事务类型
	 */
	public TranscationType getTranscationType() {
		return transcationType;
	}

	/**
	 * 提交事务
	 * @param isClose 是否关闭数据库连接
	 * @throws SQLException SQL 异常
	 */
	public synchronized void commit(boolean isClose) throws SQLException {
		if(connection==null){
			return ;
		}

		//关联事务提交
		for(JdbcOperate bindJdbcOperate : bindedJdbcOperate){
			if(this.equals(bindJdbcOperate)) {
				if(!bindJdbcOperate.isTransactionFinished) {
					bindJdbcOperate.commit(isClose);
				}
			}
		}
		connection.commit();
		if(isClose) {
			closeConnection(connection);
		}
		isTransactionFinished = true;
	}

	/**
	 * 回滚事务
	 * @param isClose 是否关闭数据库连接
	 * @throws SQLException SQL 异常
	 */
	public synchronized void rollback(boolean isClose) throws SQLException {
		if(connection==null){
			return ;
		}

		//有事务点则为: 子事务, 无事务点则为: 主事务
		//主事务回滚并可关闭连接
		//子事务回滚事务点, 并不可关闭连接
		if(savepoint!=null) {
			connection.rollback(savepoint);
		} else {
			//关联事务回滚
			for(JdbcOperate bindJdbcOperate : bindedJdbcOperate){
				if(this.equals(bindJdbcOperate)) {
					if(!bindJdbcOperate.isTransactionFinished) {
						bindJdbcOperate.rollback(isClose);
					}
				}
			}
			connection.rollback();
			if(isClose) {
				closeConnection(connection);
			}
			isTransactionFinished = true;
		}
	}

	/**<commit_msg>fix: 优化/jdbcOperate 关联操作的提交和回滚问题<commit_after>package org.voovan.db;

import org.voovan.tools.TObject;
import org.voovan.tools.TSQL;
import org.voovan.tools.log.Logger;
import org.voovan.tools.reflect.TReflect;

import javax.sql.DataSource;
import java.sql.*;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * jdbc 操作类
 * 		每个数据库操作函数 开启关闭一次连接. 使用:开始来标识参数,例如: Map 和对象形式用:":arg", List 和 Array 形式用":1"
 * 		所有行为均为非线程安全
 *
 * @author helyho
 *
 * Voovan Framework.
 * WebSite: https://github.com/helyho/Voovan
 * Licence: Apache v2 License
 */
public class JdbcOperate {
	private static Map<Long, JdbcOperate> JDBCOPERATE_THREAD_LIST = new ConcurrentHashMap<Long, JdbcOperate>();

	private DataSource	dataSource;
	private Connection	connection;
	private TranscationType transcationType;
	private Savepoint savepoint = null;

	private List<JdbcOperate> bindedJdbcOperate = new ArrayList<JdbcOperate>();
	private boolean isTransactionFinished = false;

	/**
	 * 构造函数
	 * @param dataSource	数据源
	 */
	public JdbcOperate(DataSource dataSource) {
		this.dataSource = dataSource;
		this.transcationType = TranscationType.NONE;
	}

	/**
	 * 构造函数
	 * @param dataSource	数据源
	 * @param isTrancation   是否启用事物支持, 默认使用的`NEST`(嵌套事务)的事物方式
	 */
	public JdbcOperate(DataSource dataSource,boolean isTrancation){
		this.dataSource = dataSource;
		this.transcationType = (isTrancation? TranscationType.NEST : TranscationType.NONE);
	}

	/**
	 * 构造函数
	 * @param dataSource	数据源
	 * @param transcationType    是否启用事务支持, 设置事务模式
	 */
	public JdbcOperate(DataSource dataSource,TranscationType transcationType){
		this.dataSource = dataSource;
		this.transcationType = transcationType;
	}

	/**
	 * 增加连接事务关联绑定
	 * 		只能绑定 TranscationType 为 NEST 的事务
	 * @param subJdbcOperate 连接操作对象
	 * @param bothway true: 双向绑定, false: 单向绑定,只建立当前连接和目标连接的绑定关系
	 *                双向绑定意味这任意一个节点的 commit 或者 rollback 操作都会直接联动所有的事物同时操作, 如果是单向绑定,只会联动下级事物同时操作
	 * @return true: 增加连接事务绑定成功, false: 增加连接事务绑定失败
	 */
	public synchronized boolean addBind(JdbcOperate subJdbcOperate, boolean bothway){
		if(subJdbcOperate.transcationType == TranscationType.NEST) {
			if(!bindedJdbcOperate.contains(subJdbcOperate)) {
				if(bothway) {
					subJdbcOperate.bindedJdbcOperate.add(this);
				}
				return bindedJdbcOperate.add(subJdbcOperate);
			}
		}

		return false;
	}

	/**
	 * 移除连接事务绑定
	 * @param subJdbcOperate 连接操作对象
	 * @param bothway true: 双向绑定, 解出当前连接和目标连接两个连接相互的绑定关系, : 单向绑定, 只接触当前连接和目标连接的绑定关系
	 * @return true: 移除连接事务绑定成功, false: 移除连接事务绑定失败
	 */
	public synchronized boolean removeBind(JdbcOperate subJdbcOperate, boolean bothway){
		if(bothway) {
			subJdbcOperate.bindedJdbcOperate.remove(this);
		}
		return bindedJdbcOperate.remove(subJdbcOperate);
	}

	/**
	 * 获取连接
	 *
	 * @return 获取数据库连接
	 * @throws SQLException SQL 异常
	 */
	public synchronized Connection getConnection() throws SQLException {
		long threadId = Thread.currentThread().getId();
		//如果连接不存在,或者连接已关闭则重取一个连接
		if (connection == null || connection.isClosed()) {
			//事务嵌套模式
			if (transcationType == TranscationType.NEST) {
				//判断是否有上层事务
				if (JDBCOPERATE_THREAD_LIST.containsKey(threadId)) {
					connection = JDBCOPERATE_THREAD_LIST.get(threadId).connection;
					savepoint = connection.setSavepoint();
				} else {
					connection = dataSource.getConnection();
					connection.setAutoCommit(false);
					JDBCOPERATE_THREAD_LIST.put(threadId, this);
				}
			}
			//孤立事务模式
			else if (transcationType == TranscationType.ALONE){
				connection = dataSource.getConnection();
				connection.setAutoCommit(false);
			}
			//非事务模式
			else if (transcationType == TranscationType.NONE){
				connection = dataSource.getConnection();
				connection.setAutoCommit(true);
			}
		}
		return connection;
	}

	/**
	 * 返回当前事务形式
	 * @return 事务类型
	 */
	public TranscationType getTranscationType() {
		return transcationType;
	}

	/**
	 * 提交事务
	 * @param isClose 是否关闭数据库连接
	 * @throws SQLException SQL 异常
	 */
	public synchronized void commit(boolean isClose) throws SQLException {
		if(connection==null){
			return ;
		}

		//关联事务提交
		for(JdbcOperate bindJdbcOperate : bindedJdbcOperate){
			if(this.equals(bindJdbcOperate)) {
				if(!bindJdbcOperate.isTransactionFinished) {
					bindJdbcOperate.commit(isClose);
				}
			}
		}
		connection.commit();

		if(isClose) {
			closeConnection(connection);
		}
		isTransactionFinished = true;
	}

	/**
	 * 回滚事务
	 * @param isClose 是否关闭数据库连接
	 * @throws SQLException SQL 异常
	 */
	public synchronized void rollback(boolean isClose) throws SQLException {
		if(connection==null){
			return ;
		}

		//有事务点则为: 子事务, 无事务点则为: 主事务
		//主事务回滚并可关闭连接
		//子事务回滚事务点, 并不可关闭连接
		if(savepoint!=null) {
			connection.rollback(savepoint);
		} else {
			//关联事务回滚
			for(JdbcOperate bindJdbcOperate : bindedJdbcOperate){
				if(this.equals(bindJdbcOperate)) {
					if(!bindJdbcOperate.isTransactionFinished) {
						bindJdbcOperate.rollback(isClose);
					}
				}
			}
			connection.rollback();

			if(isClose) {
				closeConnection(connection);
			}
			isTransactionFinished = true;
		}
	}

	/**
{'fraction_non_alphanumeric': 0.10773885039523763, 'fraction_numerical': 0.00039035815360593347, 'mean_word_length': 4.0186092066601375, 'pattern_counts': {'":': 4, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4887579', 'n_tokens_mistral': 4549, 'n_tokens_neox': 4323, 'n_words': 652}