JDBC(Java Data Base Connectivity),是 Java连接数据库的技术。
是一种执行SQL的API, 可以为多种关系型数据库提供统一的访问。
它是由一组用java语言编写的类和接口组成, 是Java访问数据库的标准规范。
数据库驱动就是直接操作数据库的一个程序
在程序中需要依赖数据库驱动来完成对数据库的操作
总结:JDBC就是由sun公司定义的一套操作所有关系型数据库的规则(接口), 而数据库厂商需要实现这套接口, 厂商的实现类在引入的数据库驱动jar包中。
将MySQL的驱动jar包添加到项目中的lib文件夹, 并将jar添加到libraries。
MySQL的Driver类是MySQL提供的数据库驱动类,实现了JDBC的Driver接口
//注册驱动(使用反射)
Class.forName("com.mysql.cj.jdbc.Driver");
JDBC提供了Connection接口, 代表一个数据库连接接口
MySQL通过DriverManager类中的静态方法getConnection可以获取连接
获取连接的静态方法 | 说明 |
---|---|
Connection getConnection(String url, String user, String password) | 通过数据库url、用户名和密码来获取数据库连接 |
url:连接数据库的地址,MySQL的格式为:jdbc:mysql://ip地址:端口号/数据库名
user:数据库用户名
password:数据库密码
String url="jdbc:mysql://localhost:3306/day04?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai";
String username = "root";
String userpwd="080900";
Connection connection = DriverManager.getConnection(url, username, userpwd);
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true" +
"&characterEncoding=utf8&serverTimezone=Asia/Shanghai";第一部分是协议 jdbc,这是固定的
第二部分是子协议,就是数据库名称
第三部分是由数据库厂商规定的
通过Connection 的 createStatement方法获取sql语句执行对象。
Connection接口中的方法 | 说明 |
---|---|
Statement createStatement() | 创建SQL语句执行对象 |
//3. 获取SQL语句执行对象
Statement statement = connection.createStatement();
Statement对象用于发送SQL语句给MySQL服务器,执行SQL 语句并返回它所生成结果。
Statement类常用方法 | 说明 |
---|---|
int executeUpdate(String sql) | 执行insert、delete、update语句,返回int类型,代表受影响的函数 |
ResultSet executeQuery(String sql) | 执行select语句,返回ResultSet结果集对象 |
//4. 执行SQL语句得到结果
String sql = "select * from jdbc_user";
ResultSet resultSet = statement.executeQuery(sql);
//5. 遍历ResultSet结果集对象
while(resultSet.next()){
int id = resultSet.getInt("id");
String username = resultSet.getString("username");
String password1 = resultSet.getString("password");
int age = resultSet.getInt("age");
System.out.println("id:" + id + ", username:" + username + ", password:" + password1 + ", age:" + age);
}
//6. 释放资源
statement.close();
connection.close();
JDBC操作步骤的总结:
加载驱动(可以省略)
获取连接
创建SQL执行器
发送SQL并执行, 拿到返回结果集
处理结果集(查询需要)
资源的释放
1.插入记录
public class Test2 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1. 注册数据库驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2. 获取数据库连接
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true" +
"&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
Connection connection = DriverManager.getConnection(url, "root", "root3306");
//3. 创建执行语句对象
Statement statement = connection.createStatement();
String sql = "insert into jdbc_user values(default,'tom','123',20)";
//4. 执行SQL并得到结果
int i = statement.executeUpdate(sql);
System.out.println("受影响的行数是:" + i);
//5. 释放资源
statement.close();
connection.close();
}
}
2.修改记录
public class Test2 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1. 注册数据库驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2. 获取数据库连接
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true" +
"&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
Connection connection = DriverManager.getConnection(url, "root", "root3306");
//3. 创建执行语句对象
Statement statement = connection.createStatement();
String sql = "update jdbc_user set username='jack' where id=4";
//4. 执行SQL并得到结果
int i = statement.executeUpdate(sql);
System.out.println("受影响的行数是:" + i);
//5. 释放资源
statement.close();
connection.close();
}
}
3.删除记录
public class Test2 {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
//1. 注册数据库驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2. 获取数据库连接
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true" +
"&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
Connection connection = DriverManager.getConnection(url, "root", "root3306");
//3. 创建执行语句对象
Statement statement = connection.createStatement();
String sql = "delete from jdbc_user where id=4";
//4. 执行SQL并得到结果
int i = statement.executeUpdate(sql);
System.out.println("受影响的行数是:" + i);
//5. 释放资源
statement.close();
connection.close();
}
}
对象关系映射(英语:Object Relational Mapping,简称ORM,或O/R mapping)是一种为了解决面向对象语言与关系数据库存在的互不匹配的现象。
需求 用户在控制台上输入用户名和密码, 显示 欢迎xxx登陆成功 或 登录失败
实现思路
获取用户从控制台上输入的用户名和密码来查询数据库
JDBC查询操作的6步
根据用户名和密码查询
查询到记录(该用户存在), 登陆成功
查询不到记录(该用户不存在), 登陆失败
public class Test3 {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
try {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.next();
System.out.println("请输入密码:");
String password = scanner.next();
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true" +
"&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
connection = DriverManager.getConnection(url, "root", "root3306");
//3.创建执行语句对象
statement = connection.createStatement();
String sql = "select * from jdbc_user where username='"+username + "' and password='"+password+"'";
//4.执行SQL得到结果集
ResultSet resultSet = statement.executeQuery(sql);
if(resultSet.next()){
System.out.println("登录成功!");
}else{
System.out.println("登录失败!");
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
if(statement != null){
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
我们让用户输入的密码和 SQL 语句进行字符串拼接。用户输入的内容作为了 SQL 语句语法的一部分,但是改变了原有 SQL真正的意义,以上问题称为SQL注入。
SQL注入: 输入的内容作为sql执行一部分, 改变了原有sql的真正含义。
/*
* ?:为占位符
* */
PreparedStatement ps = connection.prepareStatement("select * from user where username=? and password=?");
//SQL中存在占位符,为占位符赋值
//为第一个占位符赋值
ps.setString(1,"zs");
//为第二个占位符赋值
ps.setString(2,"000000");
//执行SQL(数据库执行sql)
ResultSet rs = ps.executeQuery();
ArrayList list = new ArrayList<>();
PreparedStatement 是Statement 接口的子接口,继承于父接口中所有的方法。它是一个预编译的 SQL 语句对象。
预编译:是指SQL 语句被预编译, 并存储在PreparedStatement 对象中。然后可以使用此对象多次高效地执行该语句。
因为有预先编译的功能,提高SQL的执行效率
可以有效的防止SQL 注入的问题,安全性更高
通过Connection创建PreparedStatement对象。
Connection接口中的方法 | 说明 |
---|---|
PreparedStatement prepareStatement(String sql) | 指定预编译的SQL语句,SQL语句中使用占位符?创建一个语句对象 |
1.编写SQL语句, 未知内容使用 ? 占位
String sql = "select * from jdbc_user where username=? and password=?";
2.获取PreparedStatement对象
3.设置实际参数: setXxx(占位符的位置, 真实的值)
4.执行SQL
批处理是指将关联的SQL语句组合成一个批处理,并将他们当成一次调用提交给数据库, 一次发送多个SQL语句到数据库,可以减少通信的资源消耗,从而提高了性能
executeBatch() 方法用于启动执行所有组合在一起的语句。
executeBatch() 方法返回一个整数数组,数组中的每个元素代表了各自的影响行数
public class Test6 {
public static void main(String[] args) throws Exception {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//获取数据库连接
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true" + "&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
Connection connection = DriverManager.getConnection(url, "root", "root3306");
String sql = "insert into jdbc_user values(null,?,?,?)";
//创建预编译语句对象
PreparedStatement ps = connection.prepareStatement(sql);
//设置参数
ps.setString(1, "张无忌");
ps.setString(2, "111");
ps.setInt(3, 20);
ps.addBatch(); //添加到批量操作
ps.setString(1, "赵敏");
ps.setString(2, "222");
ps.setInt(3, 25);
ps.addBatch(); //添加到批量操作
//执行批量操作
int[] ints = ps.executeBatch();
System.out.println(Arrays.toString(ints));
ps.close();
connection.close();
}
}
//6.获取自增主键
ResultSet resultSet = ps.getGeneratedKeys();
if(resultSet.next()){
System.out.println("主键id是:" + resultSet.getInt(1));
}
事务(ACID): 原子性、一致性、隔离性、持久性。 事务的隔离级别:
1. 读未提交: 脏读 不可重复读 幻读
2. 读已提交: 不可重复读 幻读
3. 可重复读: 幻读
4. 串行化: 完全解决,整张表加锁, 同时只能有一个客户端操作。
加载驱动(可以省略)
获取连接
开启事务控制
获取SQL执行器(PreparedStatement对象)
执行SQL
没出现异常, 事务提交
出现异常, 事务回滚
释放资源
public class Test8 {
public static void main(String[] args) {
Connection connection = null;
PreparedStatement ps = null;
try {
//1.注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.获取数据库连接
String url = "jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true" + "&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
connection = DriverManager.getConnection(url, "root", "root3306");
//3.设置手动提交事务
connection.setAutoCommit(false);
String sql = "update account set money=money+? where name=?";
//4.创建预编译语句对象
ps = connection.prepareStatement(sql);
//5.设置占位符参数
ps.setInt(1, -500);
ps.setString(2, "张三");
//6.执行sql
ps.executeUpdate();
int i = 1 / 0;
//7.设置占位符参数
ps.setInt(1, 500);
ps.setString(2, "李四");
//8.执行sql
ps.executeUpdate();
//提交事务
connection.commit();
} catch (Exception e) {
e.printStackTrace();
try {
//事务回滚
connection.rollback();
} catch (SQLException ex) {
ex.printStackTrace();
}
} finally {
if(ps != null){
try {
ps.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
数据库连接池的基本原理是连接池对象中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法
外部使用者可通过getConnection 方法获取连接,使用完毕后再通closeConnection方法将连接返回,注意此时连接并没有关闭,而是放回连接池,并为下一次使用做好准备。
public class MyConnectionPool {
private static LinkedList list = new LinkedList<>();
//在类加载的时候会执行,只会执行一次
static {
try {
//注册驱动
Class.forName("com.mysql.cj.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/zqwl?useSSL=false&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
for (int i = 0; i < 50; i++) {
//获取连接
Connection connection = DriverManager.getConnection(url, "root", "root");
list.add(connection);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
}
//从连接池中获取连接
public synchronized static Connection getConnection() {
if (list.size() == 0) {
try {
MyConnectionPool.class.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//移除链表中头部的元素,返回值就是这个元素
Connection connection = list.remove(0);
return connection;
}
//将连接放回连接池
public synchronized static void closeConnection(Connection connection) {
list.add(connection);
//唤醒等待的线程
MyConnectionPool.class.notifyAll();
}
}
C3P0数据库连接池,速度相对较慢,稳定性不错。(hibernate,spring)
DBCP数据库连接池,速度相比C3P0快,但不稳定。
Druid(德鲁伊)数据库连接池,是由阿里提供的连接池,及DBCP,C3P0,...优点于一身的数据库连接池。
//创建一个数据源对象
ComboPool edDataSource cpds = new ComboPooledDataSource();
String url = "jdbc:mysql://localhost:3306/zqwl?useSSL=false&useUnicode=true" +
"&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
String driver = "com.mysql.cj.jdbc.Driver";
String username = "root";
String password = "root";
cpds.setDriverClass(driver);
cpds.setJdbcUrl(url);
cpds.setUser(username);
cpds.setPassword(password);
//设置初始化连接数
cpds.setInitialPoolSize(10);
//最大连接数
cpds.setMaxPoolSize(50);
//测试连接池的效率, 测试对 mysql 5000 次操作
long start = System.currentTimeMillis();
for (int i = 0; i < 5000; i++) {
Connection connection = cpds.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("c3p0 5000 连接 mysql 耗时=" + (end - start));
DruidDataSource dbs = new DruidDataSource();
String url = "jdbc:mysql://localhost:3306/zqwl?useSSL=false&useUnicode=true" +
"&characterEncoding=utf8&serverTimezone=Asia/Shanghai";
String driver = "com.mysql.cj.jdbc.Driver";
String username = "root";
String password = "root";
dbs.setDriverClassName(driver);
dbs.setUrl(url);
dbs.setUsername(username);
dbs.setPassword(password);
//测试连接池的效率, 测试对 mysql 5000 次操作
long start = System.currentTimeMillis();
for (int i = 0; i < 500000; i++) {
Connection connection = dbs.getConnection();
connection.close();
}
long end = System.currentTimeMillis();
System.out.println("druid 5000 连接 mysql 耗时=" + (end - start));