JDBC中的connection详解

1. connection实例是线程安全的吗?

创建connection实例的方法:

Class.forName("com.mysql.jdbc.Driver");
java.sql.Connection conn = DriverManager.getConnection(jdbcUrl);

我们在原生的JDBC中,每次与数据库操作,都是先获取Connection对象,然后在方法执行完调用close()方法,释放资源。

能不能只创建一次,共享connection对象?
答案是不能的!Connection不是线程安全的,他会在多线程环境下,导致数据库操作的混乱,特别是在事务存在的情况下:可能一个线程刚开启事务con.setAutoCommit(true);,而另一个线程直接提交事务con.commit();
对于单独查询的情况,似乎不会出现数据错乱的情况。是因为在JDBC中,使用了锁进行同步。

源码:com.mysql.cj.jdbc.CallableStatement#executeQuery

@Override
    public java.sql.ResultSet executeQuery() throws SQLException {
        synchronized (checkClosed().getConnectionMutex()) {
        ...
        }
    }

connection本身是线程不安全的,并且connection创建开销比较大,所以一般使用数据库连接池来统一的管理connection对象,例如druid连接池,c3p0连接池等等。

2. 在使用数据库连接池时,一个线程中所有DB操作都是使用同一个Connection实例吗?

在Spring环境中,获取connection源码如下图所示:

源码:org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection

public static Connection doGetConnection(DataSource dataSource) throws SQLException {
        Assert.notNull(dataSource, "No DataSource specified");
       //1. 若是事务情况下,在ConnectionHolder中获取Connection对象
        ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
        if (conHolder != null && (conHolder.hasConnection() || conHolder.isSynchronizedWithTransaction())) {
            conHolder.requested();
            if (!conHolder.hasConnection()) {
                logger.debug("Fetching resumed JDBC Connection from DataSource");
                conHolder.setConnection(fetchConnection(dataSource));
            }
            return conHolder.getConnection();
        }
         //Else we either got no holder or an empty thread-bound holder here.
        //2. 若未存在事务,直接在数据库连接池中获取Connection连接
        logger.debug("Fetching JDBC Connection from DataSource");
        Connection con = fetchConnection(dataSource);

        ....

        return con;
    }

2.1 非事务场景

在非事务场景中(同时没有使用Spring事务管理器),每一次访问数据库,都是在DataSource中取出一个connection实例,调用完毕之后归还资源,因此多次调用,应该是不同的connection实例。

2.2 事务场景

在使用事务的情况下 ,实际上是在ConnectionHolder中获取的Connection。而ConnectionHolder是在TransactionSynchronizationManager中获取的resources属性的值,即connection对象信息。

源码:org.springframework.transaction.support.TransactionSynchronizationManager#doGetResource

private static final ThreadLocal> resources =
            new NamedThreadLocal<>("Transactional resources");

ThreadLocal>线程上下文共享。即Connection对于与Thread 绑定。因此在事务中无论操作多少次DB,事实上都是操作的同一个Connection对象。

3. connection关闭自动提交

开启事务操作的关键是con.setAutoCommit(false);,JDBC默认是开启的。即sql执行完毕自动提交。

Connection conn = DriverManager.getConnection(...);
try{
  con.setAutoCommit(false);
  Statement stmt = con.createStatement();
  //1 or more queries or updates
  con.commit();
}catch(Exception e){
  con.rollback();
}finally{
con.close();
}

而关闭自动提交后,需要手动调用con.commit();方法提交事务;若出现异常,则调用con.rollback();方法回滚事务。

你可能感兴趣的:(JDBC中的connection详解)