JDBC

1、jdbc简介

JDBC(Java Database Connectivity)是一个独立于特定数据库管理系统的。通用的SQL数据库存取和操作的公共接口(一组API),定义了用来采访数据库的标准java类库,使用这个类库可以以一种标准的方法,方便的访问数据库资源(java.sql包中)

JDBC为访问不同的数据库提供了一种统一的途径,JDBC对开发组屏蔽了一些细节问题。

JDBC的目标是使应用程序开发人员使用JDBC可以连接任何提供了JDBC驱动程序的数据库系统,这样就使得程序员无需对特定的数据库系统的特点有过多的了解,从而大大简化和加快了开发过程。

JDBC包括两个层次

  1. 面向应用的API:Java API,抽象接口,供应用程序开发人员使用(连接数据库,执行SQL语句,获得结果集)

  2. 面向数据库API:Java Driver API,供开发数开发数据库驱动程序

主要概念

DriverManager(java.sql.DriverManager)

装载驱动程序,管理应用程序与驱动程序之间的连接

Driver(由驱动程序开放式提供)

​ 将应用程序的API请求转换为特定的数据库请求

​ Connection(java.sql.Connection)

​ 将应用程序连接到特定的数据库

​ Statement

在一个给定的连接中,用于执行一个数据库SQL语句u

​ ResultSet

​ SQL语句完成 后,返回的数据库结果集(包括行、列)

2、jdbc操作

​ 1>添加jar文件,测试jar文件是否添加成功

​ 2>加载驱动

1
Class.forName("com.mysql.cj.jdbc.Driver");//加载类

​ 3>获取与数据库的链接

1
2
3
4
String url="jdbc:mysql:///oa?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true";
String username = "root";
String pwd="123456";
Connection connection = DriverManager.getConnection(url,username,pwd);

​ 链接数据库需要提供什么? 链接的url 用户名 密码

​ 5.x 链接数据库的url jdbc:mysql://localhost:3306/test 如果是本机,端口号没改变 jdbc:mysql:///test

​ 8.x url

1
jdbc:mysql:///test?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true

​ 4>创建通道,发送sql指令

1
Statement stmt =  connection.createStatement();

​ 创建Statement 通过改对象发送sql指令

1
2
3
4
1、execute  该方法可以发送select或者 insert update delete语句,如果是增删改方法 返回false,如果是select语句返回true
该方法不常用
2、executeUpdate 返回sql语句影响的行数
3、exexuteQuery 返回结果集

​ 5>需要处理结果集

​ ResultSet

1
2
3
4
5
6
7
String sql="SELECT deptid did,deptname dname FROM dept";
rs = stmt.executeQuery(sql);
while(rs.next()){
int id = rs.getInt("did");//通过查询结果的列名获取数据
int id1 = rs.getInt(1);//通过列序号,从1开始
System.out.println(id+"---"+id1);
}

​ 6>关闭资源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void main(String[] args) {
Connection connection = null;
Statement stmt = null;
try {
Class.forName(JDBCUtils.DRIVER);
connection = DriverManager.getConnection(JDBCUtils.URL,JDBCUtils.USERNAME,JDBCUtils.PASSWORD);
stmt = connection.createStatement();
String sql="delete from dept where deptid=3";
int rows = stmt.executeUpdate(sql);
System.out.println(rows);
} catch (Exception e) {
e.printStackTrace();
} finally {

try {
if(stmt!=null){
stmt.close();
}
if(connection!=null){
connection.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public static void main(String[] args) {
Connection conn = null;
Statement stmt = null;
ResultSet rs = null;
try {
Class.forName(JDBCUtils.DRIVER);
conn = DriverManager.getConnection(JDBCUtils.URL,JDBCUtils.USERNAME,JDBCUtils.PASSWORD);
stmt = conn.createStatement();
// String sql="select * from dept";
String sql="SELECT deptid did,deptname dname FROM dept";
rs = stmt.executeQuery(sql);
while(rs.next()){
int id = rs.getInt("did");//通过查询结果的列名获取数据
int id1 = rs.getInt(1);//通过列序号,从1开始
System.out.println(id+"---"+id1);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(rs!=null){
rs.close();
}
if(stmt!=null){
stmt.close();
}
if(conn!=null){
conn.close();
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}

3、sql注入与PreparedStatement

​ 1、sql注入的含义

通过拼接特殊sql语句,获取到不该获取的数据

1
2
3
String sql="SELECT * FROM users WHERE uname='"+uname+"' AND upwd='"+upwd+"'";
//传入值的时候,密码传递的结果是 ' OR '1'='1 刚好拼接出一个 or '1'='1' 恒成立的条件 获取到了所有的数据
//SELECT * FROM users WHERE uname='aa' AND upwd='' OR '1'='1'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void main(String[] args) {

String inname ="aa";
String inpwd = "' OR '1'='1";
Connection connection = null;
Statement stmt = null;
ResultSet rs = null;
try {
Class.forName(JDBCUtils.DRIVER);
connection = DriverManager.getConnection(JDBCUtils.URL,JDBCUtils.USERNAME,JDBCUtils.PASSWORD);
stmt = connection.createStatement();
String sql="SELECT * FROM users WHERE uname='"+inname+"' AND upwd='"+inpwd+"'";
System.out.println(sql);
rs = stmt.executeQuery(sql);
while (rs.next()){
int id = rs.getInt("uid");
String name = rs.getString("uname");
String pwd = rs.getString("upwd");
System.out.println(id+"\t"+name+"\t"+pwd);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
....
//输入用户名和密码获取信息
}

如果避免,java提供了PreparedStatement 防止sql注入:在使用PreparedStatement的时候,sql语句中的参数,全部通过占位符的方式来使用,给占位符赋值

1
2
3
4
5
6
7
8
....   
String sql="SELECT * FROM users WHERE uname=? and upwd=? ";
.....
pstmt = connection.prepareStatement(sql);
pstmt.setString(1,inname);
pstmt.setString(2,inpwd);

rs = pstmt.executeQuery();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public static void main(String[] args) {

String inname ="zs";
// String inpwd = "' OR '1'='1";
String inpwd="147258";
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql="SELECT * FROM users WHERE uname=? and upwd=? ";
// String sql="SELECT * FROM users WHERE uname='"+inname+"' and upwd='"+inpwd+"' ";
try {
Class.forName(JDBCUtils.DRIVER);
connection = DriverManager.getConnection(JDBCUtils.URL,JDBCUtils.USERNAME,JDBCUtils.PASSWORD);

pstmt = connection.prepareStatement(sql);
pstmt.setString(1,inname);
pstmt.setString(2,inpwd);

rs = pstmt.executeQuery();
while (rs.next()){
int id = rs.getInt("uid");
String name = rs.getString("uname");
String pwd = rs.getString("upwd");
System.out.println(id+"\t"+name+"\t"+pwd);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
....
}

//输入用户名和密码获取信息
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Connection connection = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
Class.forName(JDBCUtils.DRIVER);
connection = DriverManager.getConnection(JDBCUtils.URL,JDBCUtils.USERNAME,JDBCUtils.PASSWORD);
String sql="INSERT INTO dept VALUES(NULL,?,?,?)";
pstmt = connection.prepareStatement(sql);
pstmt.setString(1,"采购部");
pstmt.setString(2,"pstmt实现");
pstmt.setInt(3,5);
int rows = pstmt.executeUpdate();
System.out.println(rows);
} catch (Exception e) {
e.printStackTrace();
} finally {
JDBCUtils.close(connection,pstmt,rs);
}

日期类型的处理

Java事务的类型有三种:JDBC事务、JTA(Java Transaction API)事务、容器事务。

事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public static void main(String[] args) {
Scanner input = new Scanner(System.in);
Connection conn = null;
Statement stmt = null;
System.out.println("请输入一个数");
int num = input.nextInt();//如果输入的是0 则代码执行不成功
try {
Class.forName(JDBCUtils.DRIVER);
conn = DriverManager.getConnection(JDBCUtils.URL,JDBCUtils.USERNAME,JDBCUtils.PASSWORD);
conn.setAutoCommit(false);//手动事务
stmt = conn.createStatement();
stmt.executeUpdate("INSERT INTO dept VALUES(NULL,'ceshinew2','xx',0)");
if(num==0){
throw new RuntimeException("数据添加失败");
}

stmt.executeUpdate("INSERT INTO dept VALUES(NULL,'ceshinew3','xx',0)");
conn.commit();
} catch (Exception e) {
e.printStackTrace();
try {
conn.rollback();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
} finally {
JDBCUtils.close(conn,stmt,null);
}
}

打点事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public  static  void test01(){
JDBCUtils jdbcUtils = new JDBCUtils();
Connection connection = null;
Statement stmt = null;
Savepoint sp = null;
Scanner input = new Scanner(System.in);
System.out.println("请输入一个数");
int num = input.nextInt();
try {
connection = jdbcUtils.getConnection();
connection.setAutoCommit(false);
stmt = connection.createStatement();
stmt.executeUpdate("update account set balance=balance-50 where accid=1");
sp = connection.setSavepoint();
stmt.executeUpdate("update account set balance=balance-5000 where accid=1");
if(num==2){
throw new RuntimeException("失败了");
}
stmt.executeUpdate("update account set balance=balance+5000 where accid=2");

} catch (SQLException throwables) {
System.err.println("发生了数据库异常");
try {
connection.rollback(sp);
} catch (SQLException e) {
e.printStackTrace();
}
}catch (RuntimeException e){
// e.printStackTrace();
System.err.println("发生未知的异常:"+e.getMessage());
try {
connection.rollback(sp);
} catch (SQLException e1) {
e1.printStackTrace();
}
}finally {
try {
connection.commit();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
jdbcUtils.close(connection,stmt,null);
}
}

批处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static  void test02(){
JDBCUtils jdbcUtils = new JDBCUtils();
//插入1000调记录
Connection connection = null;
PreparedStatement pstmt = null;
try {
connection = jdbcUtils.getConnection();
pstmt = connection.prepareStatement("INSERT INTO cities VALUES(NULL,?,NULL)");
long start = System.currentTimeMillis();
for(int i=1;i<=10000;i++){
pstmt.setString(1,"c"+i);
pstmt.addBatch();
if(i%1000==0){
pstmt.executeBatch();
pstmt.clearBatch();
}
}

long end = System.currentTimeMillis();
System.out.println("===="+(end-start));
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
jdbcUtils.close(connection,pstmt,null);
}
}

获取自增主键的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public static void main(String[] args) {
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
JDBCUtils jdbcUtils = new JDBCUtils();
try {
conn = jdbcUtils.getConnection();
pstmt = conn.prepareStatement("INSERT INTO cities VALUES(NULL,'c',NULL),(NULL,'c2',NULL);", Statement.RETURN_GENERATED_KEYS);

pstmt.executeUpdate();
rs = pstmt.getGeneratedKeys();//为什么自增主键放到结果集中,因为一条sql语句可能插入多条记录
while (rs.next()){
Object id = rs.getObject(1);
System.out.println(id);
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
jdbcUtils.close(conn,pstmt,rs);
}
}

callabledStatement的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public  static  void test01(){
Connection conn = null;
CallableStatement cstmt = null;
JDBCUtils jdbcUtils = new JDBCUtils();
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///mydb?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true","root","123456");
cstmt = conn.prepareCall("{call demo08(?,?,?,?)}");
cstmt.setInt(1,3);//输入类型的
cstmt.registerOutParameter(2, Types.VARCHAR);//输出的类型
cstmt.registerOutParameter(3,Types.VARCHAR);
cstmt.registerOutParameter(4,Types.INTEGER);
cstmt.execute();
String name = cstmt.getString(2);
String subname = cstmt.getString(3);
int score = cstmt.getInt(4);
System.out.println(name+"\t"+subname+"\t"+score);
} catch (Exception e) {
e.printStackTrace();
} finally {
jdbcUtils.close(conn,cstmt,null);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static  void test02(){
Connection conn = null;
CallableStatement cstmt = null;
JDBCUtils jdbcUtils = new JDBCUtils();
try {
Class.forName("com.mysql.cj.jdbc.Driver");
conn = DriverManager.getConnection("jdbc:mysql:///mydb?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true","root","123456");
cstmt = conn.prepareCall("{?=call fun_02(?)}");//函数有返回值,第一个?接收饭盒的结果
cstmt.registerOutParameter(1,Types.VARCHAR);
cstmt.setInt(2,2);
cstmt.execute();
String res = cstmt.getString(1);
System.out.println(res);
} catch (Exception e) {
e.printStackTrace();
} finally {
jdbcUtils.close(conn,cstmt,null);
}
}

源数据【】

1
2
3
4
5
输入用户名
xx
输入密码
xx
1、查看信息 2、添加 3、修改 4、删除 0、退出

面向对象的思想操作数据库【必须掌握】

4、内容回顾

​ 1>jdbc意义 接口 具体的实现是各个数据库厂商做的实现

​ 2>jdbc使用步骤

​ 添加Jar

​ 加载驱动

​ 获取链接

​ 创建通道 发送sql指令

​ 处理结果集

​ 关闭资源

​ 3>Statement sql注入

​ 4>RestultSet next() getXXX()

​ 5>PrepareStatement ? 占位符的方式

​ 6>事务与批处理 主键自增 自动事务,可以设置手动 打点事务 pstmt批处理

​ 7>CallabledStatement 调用存储过程和存储 函数的

5、面向对象的思想操作数据库

​ java 数据库【crud操作】

​ 类 表

​ 属性类型 列类型

​ 属性属性名 列名

​ 对象 记录

​ 业务中也是crud操作

分层:单人单职

​ bean:实体类,与数据库表对应的,属性和数据库的字段对应

​ dao:数据访问层,与底层数据交互

​ view:与使用软件的人交互