JDBC
什么是JDBC
Java Data Base Connectivity:Java数据库连接
JDBC是Java访问数据库的标准规范
JDBC的作用
通过JDBC可以让Java程序操作数据库
JDBC四个核心对象
这几个类都是在java.sql包中
DriverManager: 用于注册驱动
Connection: 表示数据库的连接
Statement: 执行SQL语句的对象
ResultSet: 结果集或一张虚拟表
JDBC的使用步骤
1、注册驱动
2、获取数据库连接
3、获取执SQL语句对象
4、执行SQL语句并返回结果
5、(处理结果 )
6、释放资源
1、注册驱动
public class Demo02 {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
// 向JDBC注册MySQL驱动.表示我们将来通过JDBC操作MySQL数据库
// 我们自己注册一次驱动, new Driver()触发Driver类中的静态代码块执行,也会注册一次(驱动重复注册)
//DriverManager.registerDriver(new Driver());
// 我们只要触发com.mysql.jdbc包中的Driver类的静态代码块执行即可注册驱动
// 标准方式
Class.forName("com.mysql.jdbc.Driver");
}
}
注意:
1、DriverManager.registerDriver(new Driver())
会执行2次驱动注册,如果只是走静态代码块,可以使用 Class.forName(“com.mysql.jdbc.Driver”);
2、获取class类的三种方式的区别可以参考反射获取三种class类的区别
2、获取Connection连接
3、获取Statement对象
参数说明
String url:连接数据库的URL,用于说明连接数据库的位置
String user:数据库的账号
String password:数据库的密码
连接数据库的URL地址格式:协议名:子协议://服务器名或IP地址:端口号/数据库名MySQL写法:jdbc:mysql://localhost:3306/day20
如果是本地服务器,端口号是默认的3306,则可以简写:jdbc:mysql:///day20
增删改等语句使用executeUpdate()
public class Demo03 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql:///day20", "root", "root");
System.out.println("conn = " + conn); // com.mysql.jdbc.JDBC4Connection@1e7c7811
// 3.获取Statement执行对象
Statement stmt = conn.createStatement();
System.out.println("stmt = " + stmt); // com.mysql.jdbc.StatementImpl@77ec78b9
// 4.执行增删改的SQL语句
String sql = "DELETE FROM category WHERE cid=1;";
int row = stmt.executeUpdate(sql);
System.out.println("影响的行数: " + row);
// 5.释放资源
stmt.close();
conn.close();
}
}
查询语句使用executeQuery()
ResultSet的原理
ResultSet用于保存执行查询SQL语句的结果。我们不能一次性取出所有的数据,需要一行一行的取出。
ResultSet内部有一个指针,记录获取到哪行数据 next()方法
1.会将游标向下移动一行
2.如果有数据返回true,没有数据返回false
public class Demo04 {
public static void main(String[] args) throws Exception {
// 1.注册驱动
Class.forName("com.mysql.jdbc.Driver");
// 2.获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql:///day20", "root", "root");
// 3.获取Statement
Statement stmt = conn.createStatement();
// 4.执行查询的SQL语句
String sql = "SELECT * FROM category;";
ResultSet rs = stmt.executeQuery(sql);
// 5.处理结果
while (rs.next()) {
// 有数据,获取数据
int cid = rs.getInt("cid");
String cname = rs.getString("cname");
System.out.println(cid + " == " + cname);
}
// 6.释放资源
rs.close();
stmt.close();
conn.close();
}
}
JDBC事务
使用步骤
1、注册驱动
2、获取连接
3、开启事务
4、获取到Statement
5、Statement执行SQL
6、提交或回滚事务
7、关闭资源
public class Demo05 {
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
// 1.注册驱动
try {
Class.forName("com.mysql.jdbc.Driver");
// 2.获取连接
conn = DriverManager.getConnection("jdbc:mysql:///day20", "root", "root");
// 3.开启事务
conn.setAutoCommit(false);
// 4.获取到Statement
stmt = conn.createStatement();
// 5.Statement执行SQL
// 张三-500
stmt.executeUpdate("UPDATE account SET balance=balance-500 WHERE id=1;");
int a = 10 / 0;
// 李四+500
stmt.executeUpdate("UPDATE account SET balance=balance+500 WHERE id=2;");
// 6.提交事务
System.out.println("成功, 提交事务!");
conn.commit();
} catch (Exception e) {
if (conn != null) {
try {
// 6.回滚事务
System.out.println("失败, 回滚事务!");
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
e.printStackTrace();
} finally {
// 7.关闭资源
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
JDBC 工具类
编写JDBC工具类步骤
将固定字符串定义为常量
在静态代码块中注册驱动
提供一个获取连接的方法static Connection getConneciton();
定义关闭资源的方法close(Connection conn, Statement stmt, ResultSet rs)
重载关闭方法close(Connection conn, Statement stmt)
public class JDBCUtils {
// 将固定字符串定义为常量
private static final String DRIVER_CLASS_NAME = "com.mysql.jdbc.Driver";
private static final String URL = "jdbc:mysql:///day20";
private static final String USER = "root";
private static final String PASSWORD = "root";
// 在静态代码块中注册驱动
static {
try {
Class.forName(DRIVER_CLASS_NAME);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
// 提供一个获取连接的方法`static Connection getConneciton();`
// getConnection返回连接给别人用,出现问题.告诉别人
public static Connection getConnection() throws SQLException {
Connection conn = DriverManager.getConnection(URL, USER, PASSWORD);
return conn;
}
// 定义关闭资源的方法`close(Connection conn, Statement stmt, ResultSet rs)`
// 将来别人调用close方法把对象给我们使用,有问题就自己处理
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
// 重载关闭方法`close(Connection conn, Statement stmt)`
public static void close(Connection conn, Statement stmt) {
close(conn, stmt, null);
}
}
SQL注入攻击(用户登录)
先看代码(使用上面的JDBC工具类)
public class Demo07 {
public static void main(String[] args) throws SQLException {
// 1.使用数据库保存用户的账号和密码
// 2.让用户输入账号和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入账号:");
String user = sc.next();
System.out.println("请输入密码:");
String password = sc.next();
// 3.使用SQL根据用户的账号和密码去数据库查询数据
Connection conn = JDBCUtils.getConnection();
Statement stmt = conn.createStatement();
String sql = "SELECT * FROM user WHERE name='" + user + "' AND password='" + password + "';";
System.out.println("sql = " + sql);
ResultSet rs = stmt.executeQuery(sql);
if (rs.next()) {
// 4.如果查询到数据,说明登录成功
System.out.println("欢迎您, " + user);
} else {
// 5.如果查询不到数据,说明登录失败
System.out.println("登录失败!");
}
JDBCUtils.close(conn, stmt, rs);
}
}
运行结果
SQL注入攻击的原理:
1、 Statement对象在执行sql语句时,将密码的一部分内容当做查询条件来执行了。
sql = SELECT * FROM user WHERE name=‘asdfdfg’ AND password=‘a’or’1’=‘1’;
1=1 的结果是true,原句就变成了sql = SELECT * FROM user WHERE true;
2、数据库是不安全的
PreparedStatement(改进用户登录)
使用上面的JDBC工具类
public class Demo09 {
public static void main(String[] args) throws SQLException {
// 1.使用数据库保存用户的账号和密码
// 2.让用户输入账号和密码
Scanner sc = new Scanner(System.in);
System.out.println("请输入账号:");
String user = sc.next();
System.out.println("请输入密码:");
String password = sc.next();
// 3.使用SQL根据用户的账号和密码去数据库查询数据
Connection conn = JDBCUtils.getConnection();
String sql = "SELECT * FROM user WHERE name=? AND password=?;";
System.out.println("sql = " + sql);
PreparedStatement pstmt = conn.prepareStatement(sql);
// 给?赋值, parameterIndex: 指定第几个?, String x: ?的具体值
pstmt.setString(1, user);
pstmt.setString(2, password);
ResultSet rs = pstmt.executeQuery();
if (rs.next()) {
// 4.如果查询到数据,说明登录成功
System.out.println("欢迎您, " + user);
} else {
// 5.如果查询不到数据,说明登录失败
System.out.println("登录失败!");
}
JDBCUtils.close(conn, pstmt, rs);
}
}
运行结果
PreparedStatement预编译执行者对象
在执行sql语句之前,将sql语句进行提前编译。明确sql语句的格式后就不会改变了。剩余的内容都会认为是参数!
SQL语句中的参数使用?作为占位符
PreparedStatement实现查询(保存到集合中)
使用上面的JDBC工具类
public class Employee {
private int id;
private String NAME;
private int age;
private String address;
//默认无参,满参,set,get
}
public class Demo10 {
public static void main(String[] args) throws SQLException {
// 定义Employee类
// 执行SQL语句,得到ResultSet
Connection conn = JDBCUtils.getConnection();
String sql = "SELECT * FROM employee WHERE id<=?;";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, 4);
ResultSet rs = pstmt.executeQuery();
// 创建一个集合用于保存所有的员工对象
ArrayList<Employee> list = new ArrayList<>();
// 每次循环将一条记录存的数据放到一个员工对象中
while (rs.next()) {
int id = rs.getInt("id");
String name = rs.getString("name");
int age = rs.getInt("age");
String address = rs.getString("address");
Employee emp = new Employee(id, name, age, address);
// 把员工对象放到集合中
list.add(emp);
}
// 关闭资源
JDBCUtils.close(conn, pstmt, rs);
// 遍历集合操作数据
for (Employee emp : list) {
System.out.println(emp);
}
}
}
连接池
连接池的概念:
连接池就是一个容器,连接池中保存了一些数据库连接,这些连接是可以重复使用的。
连接池的原理:
启动连接池,连接池就会初始化一些连接
当用户需要使用数据库连接,直接从连接池中取出
当用户使用完连接,会将连接重新放回连接池中
CP30连接池
地址、账户、密码都在xml文件都进行修改
CP30配置文件
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 数据库连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day20</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">3000</property>
</default-config>
<named-config name="nba">
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day19</property>
<property name="user">root</property>
<property name="password">root</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">6666</property>
<property name="checkoutTimeout">1000</property>
</named-config>
</c3p0-config>
public class Demo11 {
public static void main(String[] args) throws SQLException {
// 导入jar包c3p0-0.9.5.2.jar和mchange-commons-java-0.2.12.jar
// 复制配置文件c3p0-config.xml放在src目录下,配置对应参数
// 创建连接池对象ComboPooledDataSource
ComboPooledDataSource ds = new ComboPooledDataSource();
// 从连接池中获取连接对象
Connection conn = ds.getConnection();
// 使用连接对象操作数据库
String sql = "DELETE FROM employee WHERE id=4;";
PreparedStatement pstmt = conn.prepareStatement(sql);
int row = pstmt.executeUpdate();
System.out.println("row = " + row);
// 关闭资源,将连接还回连接池中
JDBCUtils.close(conn, pstmt);
}
}
DRUID连接池
地址、账户、密码都在.properties文件都进行修改
public class Demo12 {
public static void main(String[] args) throws Exception {
// 导入druid-1.0.0.jar的jar包
// 复制druid.properties文件到src下,并设置对应参数
// 加载properties文件的内容到Properties对象中
Properties pp = new Properties();
pp.load(new FileReader("study_day20\\src\\druid.properties"));
// 创建Druid连接池,使用配置文件中的参数
DataSource ds = DruidDataSourceFactory.createDataSource(pp);
//从连接池中获取connection
Connection conn = ds.getConnection();
// 使用连接对象操作数据库
String sql = "DELETE FROM employee WHERE id=4;";
PreparedStatement pstmt = conn.prepareStatement(sql);
int row = pstmt.executeUpdate();
System.out.println("row = " + row);
// 关闭资源,将连接还回连接池中
JDBCUtils.close(conn, pstmt);
}
}
Druid properties文件
url=jdbc:mysql://localhost:3306/day20
username=root
password=root
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
maxWait=2000
连接池工具类
连接池工具类步骤
声明静态连接池成员变量
创建连接池对象
定义得到连接对象的方法
定义关闭资源的方法
public class DataSourceUtils {
// 声明静态连接池成员变量
private static DataSource ds;
// 创建连接池对象
static {
try {
Properties pp = new Properties();
pp.load(new FileReader("study_day20\\src\\druid.properties"));
ds = DruidDataSourceFactory.createDataSource(pp);
} catch (Exception e) {
e.printStackTrace();
}
}
// 定义得到连接对象的方法
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
// 定义关闭资源的方法
public static void close(Connection conn, Statement stmt, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt != null) {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void close(Connection conn, Statement stmt) {
close(conn, stmt, null);
}
}