JDBC 就是使用Java语言操作关系型数据库的一套API,是一套标准的接口。
全称:( Java DataBase Connectivity ) Java 数据库连接
可以用同一套Java代码操作不同的关系型数据库。SUN公司制定了一套标准接口JDBC,接口是无法直接使用的,我们需要使用接口的实现类(驱动),不同的数据库厂商分别实现了对应的JDBC接口,即驱动。
-
创建工程,导入MySQL的驱动jar包
mysql-connector-java-5.1.48.jar
-
注册驱动
Class.forName("com.mysql.jdbc.Driver");
-
获取连接
Connection conn = DriverManager.getConnection(url, username, password);
Java代码需要发送SQL给MySQL服务端,就需要先建立连接
-
定义SQL语句
String sql = “update…” ;
-
获取执行SQL对象
执行SQL语句需要SQL执行对象,而这个执行对象就是Statement对象
Statement stmt = conn.createStatement();
-
执行SQL
stmt.executeUpdate(sql);
-
处理返回结果
-
释放资源
JDBC使用示例
准备工作:创建一个空的项目,然后项目下新建一个文件夹lib(名称可以随意),然后将驱动jar包放到该目录下进行管理,最后右键-Add as a Library,有三个选项:
Global Library 全局有效 Project Library 项目有效 Module Library 模块有效。 选择全局有效即可。
然后在src下创建类如下
public class JDBCDemo {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 1. 注册驱动类
Class.forName("com.mysql.jdbc.Driver");
// 2.数据库连接所需要的信息
String url = "jdbc:mysql://127.0.0.1:3306/db_test";
String username = "root";
String password = "xxxxxx";
// 3.获取连接
final Connection connection = DriverManager.getConnection(url, username, password);
// 4.编写sql语句
String sql = "update tb_user set password=456 where username=\"zhangsan\"";
// 5.获取执行sql的对象Statement
final Statement statement = connection.createStatement();
// 6.执行sql语句并得到返回结果
final int count = statement.executeUpdate(sql);
System.out.println(count);
// 7.释放资源
statement.close();
connection.close();
}
}
语法:jdbc:mysql://ip地址(域名):端口号/数据库名称?参数键值对1&参数键值对2…
示例:jdbc:mysql://127.0.0.1:3306/db1
- 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称?参数键值对,即省略了IP地址和端口号
- 配置
useSSL=false
参数,禁用安全连接方式SSL,解决警告提示 - MySQL5之后的驱动包,可以省略注册驱动的步骤。
Connection
的作用: 获取执行Sql的对象,管理事务。
JDBC手动提交事务
JDBC的事务由Connection
来进行管理:
- JDBC是自动提交事务的,需要设置
Connection.setAutoCommit(false)
取消自动提交事务 - 提交事务:
Connection.commit()
- 事务回滚:
Connection.rollback()
public class JDBCDemo3_Connection {
public static void main(String[] args) throws Exception {
//1. 注册驱动
//Class.forName("com.mysql.jdbc.Driver");
//2. 获取连接:如果连接的是本机mysql并且端口是默认的 3306 可以简化书写
String url = "jdbc:mysql:///db1?useSSL=false";
String username = "root";
String password = "1234";
Connection conn = DriverManager.getConnection(url, username, password);
//3. 定义sql
String sql1 = "update account set money = 3000 where id = 1";
String sql2 = "update account set money = 3000 where id = 2";
//4. 获取执行sql的对象 Statement
Statement stmt = conn.createStatement();
try {
//按住ctrl + alt + t 可以选择自动生成try catch包裹
// ============开启事务==========
conn.setAutoCommit(false);
//5. 执行sql
int count1 = stmt.executeUpdate(sql1);//受影响的行数
//6. 处理结果
System.out.println(count1);
int i = 3/0;
//5. 执行sql
int count2 = stmt.executeUpdate(sql2);//受影响的行数
//6. 处理结果
System.out.println(count2);
// ============提交事务==========
//程序运行到此处,说明没有出现任何问题,则需求提交事务
conn.commit();
} catch (Exception e) {
// ============回滚事务==========
//程序在出现异常时会执行到这个地方,此时就需要回滚事务
conn.rollback();
e.printStackTrace();
}
//7. 释放资源
stmt.close();
conn.close();
}
}
PreparedStatement预编译
预编译SQL语句并执行:预防SQL注入问题
下面演示一种SQL注入问题:它会导致用户输入的密码检查通过。
String pwd = "' or '1' = '1";
String sql = "select * from tb_user where username = '" + name + "' and password = '" +pwd + "'";
select * from tb_user where username = 'sjdljfld' and password = '' or '1' = '1'; //永远都是true
// SQL语句中的参数值,使用?占位符替代
String sql = "select * from user where username = ? and password = ?";
// 通过Connection对象获取,并传入对应的sql语句
PreparedStatement pstmt = conn.prepareStatement(sql);
//传入值,1和2表示第几个?
pstmt.setString(1,name);
pstmt.setString(2,pwd);
PreparedStatement
的原理是将特殊字符进行了转义,转义的SQL如下
select * from tb_user where username = 'sjdljfld' and password = '\'or \'1\' = \'1'
进行转义之后就不会出现这种情况
'1' = '1' ; //因为'不代表原来的含义了,而是被转义成为了普通的字符。
在连接数据库的时候使用useServerPrepStmts=true
开启预编译功能:
String url = "jdbc:mysql:///db_test?useSSL=false&useServerPrepStmts=true";
预编译案例代码:
public class PrepareSQL {
public static void main(String[] args) throws SQLException {
// 数据库连接信息,开启预编译功能
String url = "jdbc:mysql:///db_test?useSSL=false&useServerPrepStmts=true";
String username = "root";
String password = "root";
// 获取Connection
Connection connection = DriverManager.getConnection(url, username, password);
// 编写sql语句,用?占位符表示参数
String sql = "select * from tb_user where username=? and password=?";
// 获取预编译执行
PreparedStatement preparedStatement = connection.prepareStatement(sql);
// 设置sql参数,用的是index,从1开始。表示第一个?
preparedStatement.setString(1,"zhangsan");
preparedStatement.setString(2,"456");
// 执行sql,得到结果集合
ResultSet resultSet = preparedStatement.executeQuery();
System.out.println(resultSet);
}
}
预编译执行和一般执行的区别在于:sql语句使用了占位符,相当于一个模板,Mysql对其进行编译之后,下次执行相同的语句只需要替换参数即可,而不用重新编译,一方面提高了性能,另一方面还能防止SQL注入问题。
sql语句执行流程:
- 将sql语句发送到MYSQL服务端
- MYSQL服务端:检查sql语句的语法是否正确; 编译sql语句称为可执行的函数;
- 检查和编译花费的时间比执行sql的时间还要长,如果只是重新设置参数,那么检查和编译sql语句将不需要重复执行,提高了性能。
- 执行sql语句
ResultSet
当查询之后,返回结果为ResultSet
boolean next()
- 将光标从当前位置向前移动一行
- 判断当前行是否为有效行
方法返回值说明:
- true : 有效行,当前行有数据
- false : 无效行,当前行没有数据
xxx getXxx(参数):获取数据
- xxx : 数据类型;如: int getInt(参数) ;String getString(参数)
- 参数
- int类型的参数:列的编号,从1开始
- String类型的参数: 列的名称
get方法参数有两种形式:一种是列的编号,从1开始,另一种是列的名称为字符串
String sql = "select * from tb_user;";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
while(resultSet.next()){
int id = resultSet.getInt(1);//or getInt("id");
String name = resultSet.getString("username");
String pword = resultSet.getString("password");//or getString(3)
}
JDBC案例
需求:查询account账户表数据,封装为Account对象中,并且存储到ArrayList集合中
首先在数据库中创建表
create table account(
id int primary key auto_increment,
name varchar(50) not null unique,
money double(7,2) default 0);
然后插入一些合法的数据
insert into account(name,money) values("zhangsan",100),("lisi",200);
封装一个Account对象
public class Account {
public int id;
public String name;
public double money;
public Account(int id, String name, double money) {
this.id = id;
this.name = name;
this.money = money;
}
... 省略了getter和setter方法
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
alt+inseret:可以自动插入getter setter toString 以及构造函数等。
String sql = "select * from account";
Statement statement = connection.createStatement();
ResultSet res = statement.executeQuery(sql);
//准备一个集合
ArrayList<Account> accounts = new ArrayList<>();
while(res.next()){
int id = res.getInt("id");
String name = res.getString("name");
double money = res.getDouble("money");
//构造一个Account对象
Account account = new Account(id, name, money);
//加入到集合中
accounts.add(account);
}
//遍历集合访问
for(Account a:accounts){
System.out.println(a);
}
输出结果(Account重写了toString方法):
Account{id=1, name='jack', money=1000.0}
Account{id=2, name='marry', money=99999.99}
Account{id=3, name='mike', money=0.0}
Account{id=4, name='zhangsan', money=100.0}
Account{id=5, name='lisi', money=200.0}
数据库连接池Druid
- 数据库连接池是一个容器,负责分配,管理数据库连接(Connection)
- 它允许应用程序重复使用一个现有的数据库连接,而不是需要的时候再重新建立一个
- 好处:资源重用,提升响应速度,避免数据库连接遗漏(连接了但是一直没用,可以设置一个最大空闲时间释放该连接)
之前我们代码中使用连接是没有使用都创建一个Connection对象,使用完毕就会将其销毁。这样重复创建销毁的过程是特别耗费计算机的性能的及消耗时间的。
连接池是在一开始就创建好了一些连接(Connection)对象存储起来。用户需要连接数据库时,不需要自己创建连接,而只需要从连接池中获取一个连接进行使用,使用完毕后再将连接对象归还给连接池;这样就可以起到资源重用,也节省了频繁创建连接销毁连接所花费的时间,从而提升了系统响应的速度。
官方(SUN公司)提供了数据库连接池的标准接口DataSource
,由第三方组织实现该接口,该接口提供获取连接的功能:Connection getConnection()
这样就不需要通过
DriverManager
对象获取Connection
了,而是可以通过连接池DataSource
获取Connection
对象。
常见的数据库连接池有:DPCP,C3P0,Druid.
- Druid(德鲁伊)
- Druid连接池是阿里巴巴开源的数据库连接池项目
- 功能强大,性能优秀,是Java语言最好的数据库连接池之一
使用方法:
导入jar包: druid-1.1.12.jar 并且Add as Library,这样就可以省略注册的过程: Class.forName(...)
首先写一个配置文件:druid.properties
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql:///db_test?useSSL=false&useServerPrepStmts=true
username=root
password=root
# 初始化连接数量
initialSize=5
# 最大连接数
maxActive=10
# 最大等待时间
maxWait=3000
使用过程
public class DruidDemo {
public static void main(String[] args) throws Exception {
// 1.加载配置文件
Properties prop = new Properties();
prop.load(new FileInputStream("Jdbc-demo/druid.properties"));
// 通过工厂静态方法获取DataSource
DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);
// 获取Connection
Connection connection = dataSource.getConnection();
// 配置文件中已经开启了预编译功能
PreparedStatement preparedStatement = connection.prepareStatement("select * from tb_user where username=? and password=?");
// 设置sql语句的参数
preparedStatement.setString(1,"jack");
preparedStatement.setString(2,"1234");
ResultSet resultSet = preparedStatement.executeQuery();
System.out.println(resultSet);
}
}
总结
- JDBC和DataSource都是官方定义的接口,我们使用的时候需要第三方实现的接口类(即驱动)
- JDBC的执行流程是:获取Connection,获取Statement,执行SQL语句
- 如果开启了预编译,需要在数据库连接的url中添加参数
useServerPrepStmts=true
,并且使用预编译对象来执行sql语句 - 预编译相当于sql语句是模板,执行之前给定参数,能够提高性能+防止SQL注入问题
- 后面可以用maven来管理项目,就不用手动添加这些第三方包了。
JDBC练习
完成商品品牌数据的增删改查操作
- 查询:查询所有数据
- 添加:添加品牌
- 修改:根据id修改
- 删除:根据id删除
数据准备
-- 删除tb_brand表
drop table if exists tb_brand;
-- 创建tb_brand表
create table tb_brand (
-- id 主键
id int primary key auto_increment,
-- 品牌名称
brand_name varchar(20),
-- 企业名称
company_name varchar(20),
-- 排序字段
ordered int,
-- 描述信息
description varchar(100),
-- 状态:0:禁用 1:启用
status int
);
-- 添加数据
insert into tb_brand (brand_name, company_name, ordered, description, status)
values ('三只松鼠', '三只松鼠股份有限公司', 5, '好吃不上火', 0),
('华为', '华为技术有限公司', 100, '华为致力于把数字世界带入每个人、每个家庭、每个组织,构建万物互联的智能世界', 1),
('小米', '小米科技有限公司', 50, 'are you ok', 1);
Brand类
public class Brand {
private Integer id;
private String brandName;
private String companyName;
private Integer ordered;//排序字段
private Integer status;//状态 1表示开启 0表示禁用
//alt+insert 自动生成
public Brand(Integer id, String brandName, String companyName, Integer ordered, Integer status) {
this.id = id;
this.brandName = brandName;
this.companyName = companyName;
this.ordered = ordered;
this.status = status;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getBrandName() {
return brandName;
}
public void setBrandName(String brandName) {
this.brandName = brandName;
}
public String getCompanyName() {
return companyName;
}
public void setCompanyName(String companyName) {
this.companyName = companyName;
}
public Integer getOrdered() {
return ordered;
}
public void setOrdered(Integer ordered) {
this.ordered = ordered;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
@Override
public String toString() {
return "Brand{" +
"id=" + id +
", brandName='" + brandName + '\'' +
", companyName='" + companyName + '\'' +
", ordered=" + ordered +
", status=" + status +
'}';
}
}
查询所有
public class DruidDemo {
public static void main(String[] args) throws Exception {
final Properties properties = new Properties();//配置文件
properties.load(new FileInputStream("jdbc/src/druid.properties"));//加载配置文件
final DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);//获取连接池
final Connection connection = dataSource.getConnection();//获取连接
String sql = "select * from tb_brand";
final PreparedStatement statement = connection.prepareStatement(sql);//获取statement用来执行sql
final ResultSet res = statement.executeQuery();//执行sql,返回结果集
final ArrayList<Brand> brands = new ArrayList<>();
while(res.next()){
final int id = res.getInt("id");
final String brandName = res.getString("brand_name");
final String companyName = res.getString("company_name");
final int status = res.getInt("status");
final int ordered = res.getInt("ordered");
final Brand brand = new Brand(id, brandName, companyName, ordered, status);
brands.add(brand);
}
System.out.println(brands);
//释放资源
res.close();
statement.close();
connection.close();
}
}
插入数据
public static void addBrand(Brand brand) throws Exception {
String sql = "insert into tb_brand(id,brand_name,company_name,ordered,status) values(?,?,?,?,?)";
final Properties properties = new Properties();
properties.load(new FileInputStream("jdbc/src/druid.properties"));
final DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);
final Connection connection = dataSource.getConnection();
final PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,brand.getId());
preparedStatement.setString(2,brand.getBrandName());
preparedStatement.setString(3,brand.getCompanyName());
preparedStatement.setInt(4,brand.getOrdered());
preparedStatement.setInt(5,brand.getStatus());
final int count = preparedStatement.executeUpdate();
System.out.println(count);
if(count>0){
System.out.println("sucessfully insert.");
}
preparedStatement.close();
connection.close();
}
reparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1,brand.getId());
preparedStatement.setString(2,brand.getBrandName());
preparedStatement.setString(3,brand.getCompanyName());
preparedStatement.setInt(4,brand.getOrdered());
preparedStatement.setInt(5,brand.getStatus());
final int count = preparedStatement.executeUpdate();
System.out.println(count);
if(count>0){
System.out.println("sucessfully insert.");
}
preparedStatement.close();
connection.close();
}