public class JdbcUtil {
private static String url = "jdbc:mysql://localhost:3306/db_data_show_5?useSSL=false&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF8&autoReconnect=true&failOverReadOnly=false";
private static String password = "root";
private static String user = "root";
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 释放资源
*
* @param rs
* @param stmt
* @param conn
*/
public static void release(ResultSet rs, Statement stmt, Connection conn) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (stmt != null) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
/**
* 获取连接
*
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
final Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
}
版本二:抽取连接信息后的工具类
- 第一步:在resources目录下创建mysql.properties文件
url=jdbc:mysql://localhost:3306/db_data_show_5?useSSL=false&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF8&autoReconnect=true&failOverReadOnly=false
password=root
user=root
- 第二步:使用配置文件的工具类
public class JdbcUtil {
private static String url ;
private static String password;
private static String user;
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
final Properties properties = new Properties();
//getResourceAsStream:读取文件,在JavaSE和javaWeb中都可以用,注意路径问题
final InputStream is = JdbcUtil.class.getResourceAsStream("/mysql.properties");
properties.load(is);
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
is.close();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 获取连接
*
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
final Connection conn= DriverManager.getConnection(url, user, password);
return conn;
}
/**
* 释放资源
*
* @param rs
* @param stmt
* @param conn
*/
public static void release(ResultSet rs, Statement stmt, Connection conn) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (stmt != null) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws SQLException {
final Connection conn = getConnection();
System.out.println(conn);
}
}
版本三:添加Druid数据库连接支持
- 添加Druid依赖
com.alibaba
druid
1.1.17
- 修改mysql.properties
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/db_data_show_5?useSSL=false&serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF8&autoReconnect=true&failOverReadOnly=false
username=root
password=root
# 初始化时创建的连接的个数
initialSize=10
# 处于活跃状态的连接的个数
maxActive=20
# 连接最多等待时长
maxWait=6000
- 修改JdbcUtil:
public class JdbcUtil {
private static DataSource dataSource;
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
final Properties properties = new Properties();
//getResourceAsStream:读取文件,在JavaSE和javaWeb中都可以用,注意路径问题
final InputStream is = JdbcUtil.class.getResourceAsStream("/mysql.properties");
properties.load(is);
dataSource = DruidDataSourceFactory.createDataSource(properties);
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接
*
* @return
* @throws SQLException
*/
public static Connection getConnection() throws SQLException {
final Connection conn = DriverManager.getConnection(url, user, password);
return conn;
}
/**
* 释放资源
*
* @param rs
* @param stmt
* @param conn
*/
public static void release(ResultSet rs, Statement stmt, Connection conn) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (stmt != null) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws SQLException {
final Connection conn = getConnection();
System.out.println(conn);
}
}
上面代码在单线程中使用是没有任何问题的,但是如果在多线程中使用呢?很显然,在多线程中使用会存在线程安全问题:第一,上面方法有进行同步,很可能在getConnection方法中会多次创建connection;第二,由于connection是共享变量,那么必然在调用connection的地方需要使用到同步来保障线程安全,因为很可能一个线程在使用connection进行数据库操作,而另外一个线程调用release关闭链接。
所以出于线程安全的考虑,必须将这段代码的两个方法进行同步处理,并且在调用connection的地方需要进行同步处理。这样将会大大影响程序执行效率,因为一个线程在使用connection进行数据库操作的时候,其他线程只有等待。
仔细分析一下这个问题,这地方到底需不需要将connection变量进行共享?事实上,是不需要的。假如每个线程中都有一个connection变量,各个线程之间对connection变量的访问实际上是没有依赖关系的,即一个线程不需要关心其他线程是否对这个connection进行了修改的。到这里,可能会有朋友想到,既然不需要在线程之间共享这个变量,可以直接这样处理,在每个需要使用数据库连接的方法中具体使用时才创建数据库链接,然后在方法调用完毕再释放这个连接。比如下面这样:
public class BookDaoImpl implements BookDao {
@Override
public int insert(Book book) throws SQLException {
Connection conn = JdbcUtil.getConnection();
......
}
@Override
public int update(Book book) throws SQLException {
Connection conn = JdbcUtil.getConnection();
......
}
@Override
public int deleteById(Integer id) throws SQLException {
Connection conn = JdbcUtil.getConnection();
......
}
@Override
public List selectAll() throws SQLException {
Connection conn = JdbcUtil.getConnection();
......
}
@Override
public List selectWithPage(Integer pageNum, Integer pageSize) throws SQLException {
Connection conn = JdbcUtil.getConnection();
......
}
@Override
public List selectByName(String name) throws SQLException {
Connection conn = JdbcUtil.getConnection();
......
}
@Override
public Book selectById(Integer id) throws SQLException {
Connection conn = JdbcUtil.getConnection();
......
}
}
由于每次都是在方法内部创建的连接,那么线程之间自然不存在线程安全问题。但是这样会有一个致命的影响:导致服务器压力非常大,并且严重影响程序执行性能。由于在方法中需要频繁地开启和关闭数据库连接,这样不尽严重影响程序执行效率,还可能导致服务器压力巨大。
这种情况下使用ThreadLocal是再适合不过的了,因为ThreadLocal在每个线程中对该变量会创建一个副本,即每个线程内部都会有一个该变量,且在线程内部任何地方都可以使用,线程之间互不影响,这样一来就不存在线程安全问题,也不会严重影响程序执行性能。
但是要注意,虽然ThreadLocal能够解决上面说的问题,但是由于在每个线程中都创建了副本,所以要考虑它对资源的消耗,比如内存的占用会比不使用ThreadLocal要大。
版本四:添加LocalThread以支持并发public class JdbcUtil {
private static DataSource dataSource;
private static ThreadLocal threadLocal = new ThreadLocal();
static {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
final Properties properties = new Properties();
//getResourceAsStream:读取文件,在JavaSE和javaWeb中都可以用,注意路径问题
final InputStream is = JdbcUtil.class.getResourceAsStream("/mysql.properties");
properties.load(is);
dataSource = DruidDataSourceFactory.createDataSource(properties);
is.close();
} catch (Exception e) {
throw new RuntimeException("读取配置文件错误");
}
}
/**
* 获取连接
*
* @return
* @throws SQLException
*/
public static Connection getConnection() {
Connection conn = threadLocal.get();
try {
if (conn == null || conn.isClosed()) {
conn = dataSource.getConnection();
threadLocal.set(conn);
}
} catch (SQLException e) {
throw new RuntimeException("获取连接失败!");
}
return conn;
}
/**
* 释放资源
*
* @param rs
* @param stmt
* @param conn
*/
public static void release(ResultSet rs, Statement stmt, Connection conn) {
try {
if (rs != null) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (stmt != null) {
stmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
try {
if (conn != null) {
conn.close();
threadLocal.remove();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws SQLException {
final Connection conn = getConnection();
System.out.println(conn);
}
}