文章目录
1、JDBC概述
- 全称:Java Database Connectivity
- 一组独立于任何 DBMS 的 API
JDBC是一组规范接口,其实现类由各数据库厂商提供
2、JDBC使用步骤(以MySQL为例)
- 注册驱动
- 使用驱动来创建数据库连接
- 执行SQL
- 释放资源
2.1、注册驱动相关源码
com.mysql.jdbc.Driver
(其实现了java.sql.Driver
接口)源码如下
可以看到:类加载时,有个静态代码块,自动注册驱动
package com.mysql.jdbc;
import java.sql.DriverManager;
import java.sql.SQLException;
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
DriverManager.registerDriver(new Driver());
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
2.2、把驱动jar添加到项目中
2.3、代码示例
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
public class TestJDBC {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
// 1、注册驱动(下面两行任选一行都可)
Class.forName("com.mysql.jdbc.Driver");
//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
// 2、连接数据库(TCP/IP协议)
// jdbc:mysql是应用层协议;localhost是地址;3306是端口号;b1是数据库名
String url = "jdbc:mysql://localhost:3306/b1";
String user = "root";
String password = "密码";
Connection conn = DriverManager.getConnection(url, user, password);
// 3、执行SQL
/*
* 回忆TCP/IP
* Socket代表连接
* socket.getOutputStream()来发送数据,
* socket.getInputStream()来接收数据
*
* 可以把Connection比喻成Socket,把Statement比喻成OutputStream
*/
// 3.1、获取Statement对象
Statement st = conn.createStatement();
// 3.2、执行SQL,返回成功操作的记录数
String sql = "insert into tb1 values(null,'value1','value2')";
int len = st.executeUpdate(sql);
System.out.println(len > 0 ? "成功" : "失败");
// 4、释放资源
st.close();
conn.close();
}
}
3、SQL注入 和 blob类数据 的解决
sql注入
String sql = "SELECT * FROM t_employee where ename='" + ename + "'";
// 如果我此时从键盘输入ename值的时候,输入:【张三' OR '1'= '1】
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
// 结果会把所有数据都查询出来
处理blob等类型的数据
String sql = "insert into user(username,photo) values('chailinyan', 图片字节流)";
// 此时photo是blob类型的数据时,无法在sql中直接拼接
解决方法:使用PreparedStatement
String sql = "insert into t_employee(ename,tel,gender,salary) values(?,?,?,?)";
PreparedStatement pst = conn.prepareStatement(sql);
// 这里要传带?的sql,然后mysql端就会对这个sql进行预编译
//设置?的具体值
pst.setObject(1, ename);
pst.setObject(2, tel);
pst.setObject(3, gender);
pst.setObject(4, salary);
int len = pst.executeUpdate(); //此处不用传sql
System.out.println(len);
String sql = "insert into user(username,photo) values(?,?)";
PreparedStatement pst = conn.prepareStatement(sql);
//设置?的值
pst.setObject(1, "chailinyan");
FileInputStream fis = new FileInputStream("D:/QMDownload/img/美女/15.jpg");
pst.setBlob(2, fis);
int len = pst.executeUpdate();
System.out.println(len > 0 ? "成功" : "失败");
4、批处理
在url中要加一个参数rewriteBatchedStatements=true
方法:addBatch()
和executeBatch()
注意:如果批量添加时,insert
要用values
而不要用value
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
public class TestBatch {
public static void main(String[] args) throws Exception {
// 注册驱动
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true";
// 获取连接
Connection conn = DriverManager.getConnection(url, "root", "123456");
// SQL
String sql = "insert into t_department values(null,?,?)";
PreparedStatement pst = conn.prepareStatement(sql);
// 批量插入
for (int i = 1; i <= 90000; i++) {
pst.setObject(1, "模拟部门" + i);
pst.setObject(2, "模拟部门的简介" + i);
// 添加到批处理一组操作中,攒一块处理
pst.addBatch();
// 分批后可以再分批(可选)
if (i % 1000 == 0) {
// 执行
pst.executeBatch();
// 清空
pst.clearBatch();
}
}
pst.executeBatch();
// 关闭
pst.close();
conn.close();
}
}
5、事务
- 在执行之前,设置手动提交事务:
setAutoCommit(false)
- 成功:
commit()
;失败:rollback()
- 还原为自动提交:
setAutoCommit(true)
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
public class TestTransaction {
public static void main(String[] args) throws Exception {
/*
* update t_department set description = 'xx' where did = 2;
* update t_department set description = 'yy' where did = 3;
*
* 我希望上面两个语句要么一起成功,要么一起回滚
* 为了制造失败,我故意把第二条语句写错
* update t_department set description = 'yy' (少了where) did = 3;
*/
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
Connection c = DriverManager.getConnection("jdbc:mysql://localhost:3306/b1", "root", "密码");
//设置手动提交事务
c.setAutoCommit(false);
//3、执行sql
String sql1 = "update t_department set description = 'xx' where did = 2";
String sql2 = "update t_department set description = 'yy' did = 3"; // 这句错的,造成回滚
//使用prepareStatement的sql也可以不带?
PreparedStatement pst = null;
try {
pst = c.prepareStatement(sql1);
int len = pst.executeUpdate();
System.out.println("第一条:" + (len > 0 ? "成功" : "失败"));
pst = c.prepareStatement(sql2);
len = pst.executeUpdate();
System.out.println("第二条:" + (len > 0 ? "成功" : "失败"));
System.out.println("提交");
c.commit();
} catch (Exception e) {
System.out.println("回滚");
c.rollback();
}
//4、关闭
if (pst != null) {
pst.close();
}
c.setAutoCommit(true); // 还原为自动提交
c.close();
}
}
6、数据库连接池
-
数据库连接池
负责分配、管理和释放数据库连接
允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个 -
最小连接数是连接池一直保持的数据库连接
-
若数据库连接请求超过最大连接数,后面的数据库连接请求将被加入到等待队列中
-
大于最小连接数的数据库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或是空闲超时后被释放
import com.alibaba.druid.pool.DruidDataSource; // 数据库连接池 类
import java.sql.Connection;
import java.sql.SQLException;
public class Hello {
public static void main(String[] args) throws SQLException {
// 1、创建数据源(数据库连接池)对象
DruidDataSource ds = new DruidDataSource();
// 2、设置参数
// 2.1、连接参数
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/b0");
ds.setUsername("root");
ds.setPassword("");
// 2.2、连接数
ds.setInitialSize(5);//一开始提前申请好5个连接,不够了,重写申请
ds.setMaxActive(10);//最多不超过10个,如果10都用完了,还没还回来,就会出现等待
ds.setMaxWait(1000);//用户最多等1000毫秒,如果1000毫秒还没有人还回来,就异常了
// 3、获取连接
for (int i = 1; i <= 15; i++) {
Connection conn = ds.getConnection();
System.out.println("第" + i + "个:" + conn);
// 如果这里没有关闭,就相当于没有还(超过最大连接数后会报错)
conn.close(); // 这里关闭,是还回池中
}
}
}
7、MyBatis
- MyBatis是一款优秀的 持久层 框架,用于简化JDBC开发
- 支持自定义 SQL、存储过程以及高级映射
- 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作
- 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO 为数据库中的记录
JDBC缺点
MyBatis简化JDBC开发
7.1、使用 Maven 来构建项目
pom.xml
添加依赖
<!-- MyBatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.7</version>
</dependency>
<!-- MySQL的JDBC -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
7.2、创建表和类
创建teacher表
CREATE TABLE teacher (
id INT PRIMARY KEY AUTO_INCREMENT,
gender char(1),
pwd char(20));
INSERT INTO teacher VALUES
(1,'男','abc'),
(2,'女','abc'),
(3,'女','bcd');
创建Teacher类
package a.b.pojo;
public class Teacher {
private Integer id;
private String gender;
private String pwd;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "Teacher{" +
"id=" + id +
", gender='" + gender + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
7.3、从 XML 中构建 SqlSessionFactory
- 在
resources
目录下创建mybatis-config.xml
- 修改数据库连接参数【
driver
、url
、username
、password
】 - 修改SQL映射文件路径
mapper
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 数据库环境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/b0"/>
<property name="username" value="root"/>
<property name="password" value="密码"/>
</dataSource>
</environment>
</environments>
<!-- SQL映射文件 -->
<mappers>
<mapper resource="a/b/mapper/TeacherMapper.xml"/>
</mappers>
</configuration>
7.4、SQL映射
- SQL映射文件要与Mapper接口同名(都是
TeacherMapper
) - Mapper接口和SQL映射文件 的目录要相同(都是
a/b/mapper/
) - SQL映射文件的
namespace
属性为Mapper接口全限定名 - 在Mapper接口中定义方法,方法名是SQL映射文件中sql语句的
id
,并保持参数类型和返回值类型一致
映射接口a/b/mapper/TeacherMapper.java
(在java
目录下)
package a.b.mapper;
import a.b.pojo.Teacher;
import java.util.List;
public interface TeacherMapper {
List<Teacher> selectAll();
}
SQL映射文件a/b/mapper/TeacherMapper.xml
(在resources
目录下)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="a.b.mapper.TeacherMapper">
<select id="selectAll" resultType="a.b.pojo.Teacher">
select * from teacher
</select>
</mapper>
7.5、主类
- 加载MyBatis核心配置文件(路径要跟上面
mybatis-config.xml
一致),获取SqlSessionFactory
对象 - 使用 SqlSession工厂对象 创建 SqlSession对象
- 使用 SqlSession对象 获取Mapper对象
- 使用Mapper对象执行SQL
- 释放资源
package a.b;
import a.b.mapper.TeacherMapper;
import a.b.pojo.Teacher;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class Demo {
public static void main(String[] args) throws IOException {
// 1、加载MyBatis核心配置文件,获取SqlSessionFactory对象
InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 2、使用 SqlSession工厂对象 创建 SqlSession对象
SqlSession session = sqlSessionFactory.openSession();
// 3、使用 SqlSession对象 获取Mapper对象
TeacherMapper mapper = session.getMapper(TeacherMapper.class);
// 4、使用Mapper对象执行SQL
List<Teacher> teachers = mapper.selectAll();
System.out.println(teachers);
// 5、释放资源
session.close();
}
}