文章目录
JDBC入门
一、什么是JDBC
Java数据库连接-Java DataBase Connectivity
JDBC可让Java通过程序操作关系型数据库
JDBC基于驱动程序实现与数据库的连接与操作
1、JDBC的优点
统一的API,提供一致的开发过程
易于学习,容易上手,代码结构稳定
功能强大,执行效率高,可处理海量数据
二、JDBC驱动的秘密
1、创建数据库连接代码
Class.forName的作用:
用于加载指定的JDBC驱动类。
本质是通过JDBC注册这个驱动类。
驱动由数据库厂商自行开发,连接字符串也不同。
DriverManager
用于注册/管理JDBC驱动程序。
DriverManager.getConnection(连接字符串,用户名,密码)
返回值Connection对象,对应数据库的物理网络连接。
Connection对象
用于JDBC与数据库的网络通信对象。
java.sql.Connection是一个接口,具体由驱动厂商实现。
所有数据库的操作都建立在Connection基础上。
MySQL连接字符串
格式:jdbc:mysql://[主机ip][:端口]/数据库名?参数列表
主机ip与端口是可选设置,默认值为127.0.0.1与3306
参数列表采用url编码,格式:参数1=值1&参数2=值2&…
三、JDBC开发流程
public class QueryCommand implements Command{
public void execute(){
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try{
//1.加载并注册JDBC驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.创建数据库连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/databaseName?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrievel=true","root","root");
//3.创建Statement对象
stmt = conn.createStatement();
//结果集
rs = stmt.executeQuery("select * from employee where dname='研发部'");
//4.遍历查询结果
//rs.next()返回布尔值,代表是否存在下一条记录
//如果有,返回true,同时结果集提取下一条记录
//如果没有,返回false,循环会停止
while(rs.next()){
Integer eno = rs.getInt(1);//JDBC中字段索引从1开始,而非0
String ename = rs.getString("ename");
sout(eno+":"+ename);
}
}catch(ClassNotFoundException e){
e.printStackTrace();
}catch(SQLException e){
e.printStackTrace();
}finally{
//5.关闭连接,释放资源
try{
if(rs!=null){
rs.close();
}
}catch(SQLException e){
e.printStackTrace();
}
try{
if(stmt!=null){
stmt.close();
}
}catch(SQLException e){
e.printStackTrace();
}
try{
if(conn!=null){
conn.close();
}
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
四、PreparedStatement
1、SQL注入攻击
SQL注入攻击是指利用SQL漏铜越权获取数据的黑客行为。
SQL注入攻击根源是未对原始SQL中的敏感字符做特殊处理。
解决方法:放弃Statement改用PreparedStatement处理SQL。
2、PreparedStatement
PreparedStatement 预编译Statement是Statement的子接口。
PreparedStatement对SQL进行参数化,预防SQL注入攻击。
原始代码:
用PreparedStatement改造之后的代码:
3、PreparedStatement开发流程
public class PstmtQueryCommand implements Command{
public void execute(){
System.out.print("请输入部门名称:");
Scanner in = new Scanner(System.in);
String pdname = in.nextLine();
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try{
//1.加载并注册JDBC驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.创建数据库连接
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/databaseName?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrievel=true","root","root");
//3.创建Statement对象
String sql = "select * from employee where dname = ? ";
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, pdname);//注意:参数索引从1开始
//结果集
rs = pstmt.executeQuery();
//4.遍历查询结果
//rs.next()返回布尔值,代表是否存在下一条记录
//如果有,返回true,同时结果集提取下一条记录
//如果没有,返回false,循环会停止
while(rs.next()){
Integer eno = rs.getInt(1);//JDBC中字段索引从1开始,而非0
String ename = rs.getString("ename");
sout(eno+":"+ename);
}
}catch(ClassNotFoundException e){
e.printStackTrace();
}catch(SQLException e){
e.printStackTrace();
}finally{
//5.关闭连接,释放资源
try{
if(rs!=null){
rs.close();
}
}catch(SQLException e){
e.printStackTrace();
}
try{
if(stmt!=null){
stmt.close();
}
}catch(SQLException e){
e.printStackTrace();
}
try{
if(conn!=null){
conn.close();
}
}catch(SQLException e){
e.printStackTrace();
}
}
}
}
错误的使用方式
select * from employee where ?=‘abc’
select * from employee where salary = ?+100
select ? from employee where ename = ?
五、封装DbUtils工具类
public class DbUtils{
//创建新的数据库连接
public static Connection getConnection() throws SQLException, ClassNotFoundException{
//1.加载并注册JDBC驱动
Class.forName("com.mysql.cj.jdbc.Driver");
//2.创建数据库连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/databaseName?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrievel=true","root","root");
return conn;
}
//关闭连接,释放资源
public static void closeConnection(ResultSet rs, Statement stmt, Connection conn){
try{
if(rs!=null){
rs.close();
}
}catch(SQLException e){
e.printStackTrace();
}
try{
if(stmt!=null){
stmt.close();
}
}catch(SQLException e){
e.printStackTrace();
}
try{
if(conn!=null){
conn.close();
}
}catch(SQLException e){
e.printStackTrace();
}
}
}
六、JDBC实现写数据
1、JDBC执行INSERT语句
2、JDBC执行UPDATE语句
3、JDBC执行DELETE语句
七、JDBC事务管理
1、什么是事务
事务是以一种可靠的、一致的方式,访问和操作数据库的程序单元。
说人话:要么把事情做完,要么什么都不做,不做一半。
事务依赖于数据库实现,MySQL通过事务区作为数据缓冲地带。
事务的提交操作
事务的回滚操作
2、JDBC两种事务模式
自动提交事务模式
指每执行一次写操作SQL,自动提交事务。
自动提交开启方法:conn.setAutoCommit(true)
自动事务是JDBC默认行为,此模式无法保证多数据一致性
手动提交事务模式
指显式调用commit()与rollback()方法管理事务
手动提交开启方法:conn.setAutoCommit(false)
手动提交事务可以保证数据一致性,但必须手动调用提交/回滚方法
3、JDBC事务案例
一次性新增1000名员工:
public class TransactionSample{
public static void main(String[] args){
Connection conn = null;
PreparedStatement pstmt = null;
try{
conn = DbUtils.getConnection();
conn.setAutoCommit(false);//关闭自动提交
String sql = "insert into employee(eno,ename,salary,dname) values (?,?,?,?)";
for(int i=1000;i<2000;i++){
if(i==1005){
throw new RuntimeException("插入失败");
}
pstmt= conn.prepareStatement(sql);
pstmt.setInt(1,i);
pstmt.setString(2,"员工"+i);
pstmt.setFloat(3, 4000f);
pstmt.setString(4,"市场部");
pstmt.executeUpdate();
}
conn.commit();
}catch(Exception e){
e.printStackTrace();
try{
if(conn!=null && !conn.isClosed()){
conn.rollback();//回滚数据
}
}catch(SQLException ex){
ex.printStackTrace();
}
}finally{
DbUtils.closeConnection(null,pstmt,conn);
}
}
}
八、JDBC常用技巧
1、基于实体类实现分页数据封装
....
conn = DbUtils.getConnection();
String sql = "select * from employee limit ?,10";
pstmt = conn.prepareStatement(sql);
pstmt.setInt(1, (page-1)*10);
rs = pstmt.executeQuery();
while(rs.next()){
Integer eno = rs.getInt("eno");
String ename = rs.getString("ename");
Employee emp = new Employee();
emp.setEno(eno);
emp.setEname(ename);
list.add(emp);
}
...
2、JDBC中Date日期对象处理
JDBC对MySQL数据库中的date和datetime类型的处理是一样的。
在封装实体类时常用java.util包中的Date类。
...
//JDBC查询时间返回的是java.sql.Date类型
java.sql.Date createTime = rs.getDate("create_time");
//java.sql.Date类型是继承自java.util.Date类型,所以把返回结果可以直接set到实体对象的java.util.Date类型的属性
emp.setCreateTime(createTime);
...
在新增操作时需要把字符串转换成java.sql.Date类型
...
String createTime = "2022-03-10 22:04:00";
//1.String转为java.util.Date
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
java.util.Date udCreateTime = sdf.parse(createTime);
//2.java.util.Date转为java.sql.Date
long time = udCreateTime.getTime();//获取自1970年到现在的毫秒数
java.sql.Date sdCreateTime = new java.sql.Date(time);
pstmt.setDate(1, sdCreateTime);
...
3、JDBC数据批处理
...
String sql = "insert into employee(eno,ename,salary,dname) values (?,?,?,?)";
pstmt= conn.prepareStatement(sql);
for(int i=1000;i<2000;i++){
//pstmt= conn.prepareStatement(sql);
pstmt.setInt(1,i);
pstmt.setString(2,"员工"+i);
pstmt.setFloat(3, 4000f);
pstmt.setString(4,"市场部");
pstmt.addBatch();//将参数加入批处理任务
//pstmt.executeUpdate();
}
pstmt.executeBatch();//执行批处理任务
...