JDBC(Java DataBase Connectivity),简单来讲JDBC是利用Java语言或程序连接并且访问数据库的一门技术,是Java语言中用来规范客户端程序如何访问数据库的应用程序接口,提供了查询和更新数据库操作方法,通常是面向关系型数据库的。
步骤
数据准备
create database testjdbc_db charset utf8;
use testjdbc_db;
create table account(
id int primary key auto_increment,
name varchar(50),
money double
);
insert into account values(null, 'tom', 1000);
insert into account values(null, 'andy', 1000);
insert into account values(null, 'tony', 1000);
mysql
驱动包下载
创建 lib
目录,将 jar
包拷贝到里面,然后将整个 lib
目录 Add as Library
示例:
package com.hubery.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.SQLException;
public class ConnectionBasic {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/testjdbc_db?characterEncoding=UTF-8&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true",
"root",
"root"
);
//3.获取传输器
Statement st = conn.createStatement();
//4.通过传输器发送SQL到服务器执行并且返回执行结果
String sql = "select * from account";
ResultSet rs = st.executeQuery(sql);
//5.数据处理
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
double money = rs.getDouble("money");
System.out.println(id + ":" + name + ":" + money);
}
//6.释放资源
if (rs != null) {
try {
rs.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
rs = null;
}
}
if (st != null) {
try {
st.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
st = null;
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
conn = null;
}
}
}
}
1、注册驱动
com.mysql.jdbc.Driver
com.mydql.cj.jdbc.Driver
2、 连接
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/testjdbc_db?characterEncoding=UTF-8&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true&useSSL=false",
"root",
"root"
)
// 协议名称
jdbc:mysql
// mysql 主机和端口
localhost:3306
// 数据库名称
testjdbc_db
// 设置编码和时区(mysql8 后需要自己设置时区)
characterEncoding=UTF-8&serverTimezone=GMT%2B
// 保证 mysql 驱动把连接器 PreparedStaement 类型传输来的 SQL 语句发送给数据库进行预编译为函数交给服务器存储 key
useServerPrepStms=true
// 保证在执行相同 SQL 语句时数据库不会二次编译
cachePrepStms=true
// mysql 用户名及密码
root
root
3、发送 SQL
语句即处理结果集
st.executeQuery(sql)
:执行查询类型的 sql
语句,返回 ResultSet
结果集st.executeUpdate(sql)
:执行更新(添加、删除、修改)类型的 sql
语句,返回 int
值,表示影响的记录数ResultSet
结果集使用 next()
方法可以判断是否还有下一个,获取值可用以下方法:
// 不同类型用不同方法获取值
while (rs.next()) {
getInt(int columnIndex)
getInt(String columnLable)
getString(int columnIndex)
getString(String columnLable)
getDouble(int columnIndex)
getDouble(String columnLable)
getObject(int columnIndex)
getObject(String columnLable)
}
4、释放资源
遵循的规则:越晚获取的资源越先关闭
可以将上述连接数据库、关闭资源这一系列步骤封装成工具类,直接调用即可,简化操作
1、JDBCUtils.java
package com.hubery.jdbc.utils;
import java.sql.*;
/**
* 注册驱动 + 获取连接
*/
public class JDBCUtils {
public static Connection getConnection() {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/testjdbc_db?characterEncoding=UTF-8&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true",
"root",
"root"
);
return conn;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static Connection getConnection(String dbName, String username, String password) {
try {
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取连接
Connection conn = DriverManager.getConnection(
"jdbc:mysql://localhost:3306/" + dbName + "?characterEncoding=UTF-8&useSSL=false&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true",
username,
password
);
return conn;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 关闭连接
*
* @param conn
* @param statement
* @param resultSet
*/
public static void close(Connection conn, Statement statement, ResultSet resultSet) {
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
conn = null;
}
}
if (statement != null) {
try {
statement.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
statement = null;
}
}
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
resultSet = null;
}
}
}
}
2、测试类 testJdbcUtil.java
import com.hubery.jdbc.utils.JDBCUtils;
import org.junit.Test;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
public class testJdbcUtil {
/**
* 查询所有数据
*/
@Test
public void testSelect() {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = JDBCUtils.getConnection(); // 注册驱动、获取连接
statement = connection.createStatement();
// 通过传输器将 SQL 语句发送过去
String sql = "select * from account";
resultSet = statement.executeQuery(sql);
// 处理结果
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
double money = resultSet.getDouble("money");
System.out.println("id: " + id + ", name: " + name + ", money: " + money);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭连接,释放资源
JDBCUtils.close(connection, statement, resultSet);
}
}
/**
* 修改、删除、插入数据
*/
@Test
public void testUpdate() {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
connection = JDBCUtils.getConnection(); // 注册驱动、获取连接
statement = connection.createStatement();
// 通过传输器将 SQL 语句发送过去
String sql = "update account set money = 4800 where name = 'tony'";
// 删除数据
// String sql = "delete from account where name = 'tom'";
// 插入数据
// String sql = "insert into account values(null, 'Anny', 40000) ";
int rows = statement.executeUpdate(sql);
// 处理结果
System.out.println("更新完毕,影响行数:" + rows);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭连接,释放资源
JDBCUtils.close(connection, statement, resultSet);
}
}
}
PreparedStatement
为 Statement
子接口,比之更安全,效率更高
Statement
:每次都需要将语句发送给数据库编译,正确则执行,否则抛出异常,每次都是一次新的操作,参数以拼接形式线程不安全PreparedStatement
:线程安全,先将语句发送给数据库预编译,错误则抛出异常,正确则生成骨架函数,用户只需传递参数即可通过拼接的方式来传递一些 SQL
语句需要的参数时,有时候可能会因为一些特殊的符号导致 SQL
语句语义发生变化,导致一些有问题的语句也能成功执行(比如下面几个示例中,不需要用户名或密码也能登录成功):
stat = conn.createStatement();
String sql = "select * from user where username='"+ user + "'and password = '"+ password +"'";
resultSet = stat.executeQuery(sql);
// 正常 SQL 语句
select * from user where username = 'rose' and password = '123';
// 用户名输入 rose '#',密码为空
select * from user where username = 'rose '#'' and password = '';
// 用户名输入 rose 'or' 1=1,密码为空
select * from user where username = 'rose 'or' 1=1' and password = '';
// 用户名为空,密码为 or' 1=1
select * from user where username = '' and password = ''or' 1=1';
解决办法
使用 PreparedStatement
对象替换 Statement
,对传递的参数进行校验:
String user = "rose";
String password = "123456";
//1.注册驱动,2.获取连接
conn = JdbcUtil.getConnection();
//3.获取传输器,通过传输器发送SQL语句到服务器执行并返回结果
String sql = "select * from user where username = ? and password = ?";
ps = conn.prepareStatement(sql);
//4.设置SQL语句中的参数,其中 1/2 表示 sql 语句中 ? 的位置
ps.setString(1, user);
ps.setString(2, password);
//5.执行SQL语句
rs = ps.executeQuery();
在上面我们每次操作数据库都需要创建连接、初始化连接、关闭连接,这些操作都比较耗时,效率低下,要解决这种问题可以使用连接池;连接池可以一次性创建多个连接,当 close
后,就把连接放回池子中,而不是直接关闭,这样就避免了频繁地创建、初始化、关闭连接。
获取连接方式:
c3p0.properties
)c3p0-config.xml
)1、src/c3p0-config.xml
<c3p0-config>
<default-config>
<property name="driverClass">
com.mysql.cj.jdbc.Driver
property>
<property name="jdbcUrl">
jdbc:mysql:///testjdbc_db?characterEncoding=UTF-8&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true
property>
<property name="user">
root
property>
<property name="password">
root
property>
default-config>
c3p0-config>
2、src/c3p0.properties
#key=value
c3p0.driverClass=com.mysql.cj.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql:///testjdbc_db?characterEncoding=UTF-8&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true
c3p0.user=root
c3p0.password=root
3、工具类:JdbcPoolUtil.java
:
package com.hubery.jdbc.utils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.sql.*;
public class JdbcPoolUtil {
// 创建连接池对象
static ComboPooledDataSource pool = new ComboPooledDataSource();
// // 设置连接池信息(方式一)
// static {
// try {
// pool.setDriverClass("com.mysql.cj.jdbc.Driver");
// pool.setJdbcUrl("jdbc:mysql://localhost:3306/testjdbc_db?characterEncoding=UTF-8&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true");
// pool.setUser("root");
// pool.setPassword("root");
// } catch (Exception e) {
// e.printStackTrace();
// }
// }
// 获取连接(配置文件方式)
public static Connection getConnection() throws SQLException {
return pool.getConnection();
}
public static void close(Connection conn, Statement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
resultSet = null;
}
}
if (statement != null) {
try {
statement.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
statement = null;
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
conn = null;
}
}
}
}
4、使用 TestJdbcPoolDemo.java
import com.hubery.jdbc.utils.JdbcPoolUtil;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.junit.Test;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestJdbcPoolDemo {
// 查询所有数据
@Test
public void testFindAll() throws SQLException {
// 初始化连接对象
Connection connection = JdbcPoolUtil.getConnection();
// 编写 SQL,获取连接器
String sql = "select * from account";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 查询
ResultSet resultSet = preparedStatement.executeQuery();
// 处理结果
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
double money = resultSet.getDouble("money");
System.out.println(id + ":" + name + ":" + money);
}
// 关闭连接
JdbcPoolUtil.close(connection, preparedStatement, resultSet);
}
}
以上使用的是 C3P0
连接池,速度较慢,但稳定性不错,更多选择可参考:JDBC连接池
静态代码封装读取 properties 配置文件
1、JdbcPoolUtil.java
工具类:
package com.hubery.jdbc.utils;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import java.beans.PropertyVetoException;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.sql.*;
import java.util.Properties;
public class JdbcPoolUtil {
// 创建连接池对象
static ComboPooledDataSource pool = new ComboPooledDataSource();
// 文件读取采用静态代码读取,只需要读取一次
static {
try {
// 创建 Properties 集合
Properties properties = new Properties();
// 获取 src 下文件的文件方式:ClassLoader 类加载器
ClassLoader classLoader = JdbcPoolUtil.class.getClassLoader();
URL res = classLoader.getResource("jdbc.properties");
String path = res.getPath();
// 加载文件
properties.load(new FileReader(path));
// 获取值
String url = properties.getProperty("url");
String user = properties.getProperty("user");
String password = properties.getProperty("password");
String driver = properties.getProperty("driver");
System.out.println("url " + url + ", user" + user + ", password: " + password + ", driver: " + driver);
// 给数据源设置相关参数
pool.setDriverClass(driver);
pool.setJdbcUrl(url);
pool.setUser(user);
pool.setPassword(password);
// 初始化连接数
pool.setInitialPoolSize(10);
pool.setMaxPoolSize(50); // 最大连接数
System.out.println("连接成功!");
} catch (IOException | PropertyVetoException e) {
e.printStackTrace();
}
}
public static Connection getConnection() throws SQLException {
return pool.getConnection();
}
public static void close(Connection conn, Statement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
resultSet = null;
}
}
if (statement != null) {
try {
statement.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
statement = null;
}
}
if (conn != null) {
try {
conn.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
conn = null;
}
}
}
}
2、jdbc.properties
:
driver=com.mysql.cj.jdbc.Driver
user=root
password=root
url=jdbc:mysql:///testjdbc_db?characterEncoding=UTF-8&serverTimezone=GMT%2B8&useServerPrepStms=true&cachePrepStms=true
参考地址:JDBC连接数据库详讲