前言

1、JDBC是什么?

Java DataBase Connectivity(Java语言连接数据库)

2、JDBC的本质

是sun公司制定的一个接口。在java.sql.*包下。

为什么SUN需要制定一套JDBC接口?

因为每一个数据库的底层实现原理都不一样。Oracle数据库有自己的原理,MySQL数据库也有自己的原理,MS SqlServer数据库也有自己的原理…

每一个数据库产品都有自己独特的实现原理,如果没有这套接口就需要写多套java程序。

pgsql numeric java 用什么类型接收 javasql包接口_jdbc


驱动:就是一个jar包,里面包含了JDBC的实现类。

3、在idea工具中连接Mysql数据库

(1)先去官网下载jar包
下载jar包,具体可看此文章 jar包尽量下载最新的,因为下载旧版本可能会出现各种编译错误。

(2)导入驱动 Jar 包

使用IDEA工具JDBC代码配置驱动,导入jar包(两种方法)

pgsql numeric java 用什么类型接收 javasql包接口_数据库_02

4、JDBC编程六步

-

说明

第一步:注册驱动

作用:告诉Java程序,即将要连接的是哪个品牌的数据库

第二步:获取连接

表示JVM的进程和数据库进程之间的通道打开了,这属于进程之间的通信使用完之后一定要关闭通道

第三步:获取数据库操作对象

专门执行sql语句的对象

第四步:执行SQL语句

DQL DML…

第五步:处理查询结果集

只有当第四步执行的是select语句的时候,才有这第五步处理查询结果集

第六步:释放资源

使用完资源之后一定要关闭资源。Java和数据库属于进程间的通信,开启之后一定要关闭

一、JDBC编程

1、创建一个mysql数据库作为测试

create database test;
use test;
DROP TABLE IF EXISTS EMP;
DROP TABLE IF EXISTS DEPT;
DROP TABLE IF EXISTS SALGRADE;

CREATE TABLE DEPT
       (DEPTNO int(2) not null ,
	DNAME VARCHAR(14) ,
	LOC VARCHAR(13),
	primary key (DEPTNO)
	);
CREATE TABLE EMP
       (EMPNO int(4)  not null ,
	ENAME VARCHAR(10),
	JOB VARCHAR(9),
	MGR INT(4),
	HIREDATE DATE  DEFAULT NULL,
	SAL DOUBLE(7,2),
	COMM DOUBLE(7,2),
	primary key (EMPNO),
	DEPTNO INT(2) 
	)
	;

CREATE TABLE SALGRADE
      ( GRADE INT,
	LOSAL INT,
	HISAL INT );

INSERT INTO DEPT ( DEPTNO, DNAME, LOC ) VALUES ( 
10, 'ACCOUNTING', 'NEW YORK'); 
INSERT INTO DEPT ( DEPTNO, DNAME, LOC ) VALUES ( 
20, 'RESEARCH', 'DALLAS'); 
INSERT INTO DEPT ( DEPTNO, DNAME, LOC ) VALUES ( 
30, 'SALES', 'CHICAGO'); 
INSERT INTO DEPT ( DEPTNO, DNAME, LOC ) VALUES ( 
40, 'OPERATIONS', 'BOSTON'); 
commit;
 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,
DEPTNO ) VALUES ( 
7369, 'SMITH', 'CLERK', 7902,  '1980-12-17'
, 800, NULL, 20); 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,
DEPTNO ) VALUES ( 
7499, 'ALLEN', 'SALESMAN', 7698,  '1981-02-20'
, 1600, 300, 30); 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,
DEPTNO ) VALUES ( 
7521, 'WARD', 'SALESMAN', 7698,  '1981-02-22'
, 1250, 500, 30); 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,
DEPTNO ) VALUES ( 
7566, 'JONES', 'MANAGER', 7839,  '1981-04-02'
, 2975, NULL, 20); 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,
DEPTNO ) VALUES ( 
7654, 'MARTIN', 'SALESMAN', 7698,  '1981-09-28'
, 1250, 1400, 30); 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,
DEPTNO ) VALUES ( 
7698, 'BLAKE', 'MANAGER', 7839,  '1981-05-01'
, 2850, NULL, 30); 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,
DEPTNO ) VALUES ( 
7782, 'CLARK', 'MANAGER', 7839,  '1981-06-09'
, 2450, NULL, 10); 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,
DEPTNO ) VALUES ( 
7788, 'SCOTT', 'ANALYST', 7566,  '1987-04-19'
, 3000, NULL, 20); 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,
DEPTNO ) VALUES ( 
7839, 'KING', 'PRESIDENT', NULL,  '1981-11-17'
, 5000, NULL, 10); 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,
DEPTNO ) VALUES ( 
7844, 'TURNER', 'SALESMAN', 7698,  '1981-09-08'
, 1500, 0, 30); 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,
DEPTNO ) VALUES ( 
7876, 'ADAMS', 'CLERK', 7788,  '1987-05-23'
, 1100, NULL, 20); 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,
DEPTNO ) VALUES ( 
7900, 'JAMES', 'CLERK', 7698,  '1981-12-03'
, 950, NULL, 30); 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,
DEPTNO ) VALUES ( 
7902, 'FORD', 'ANALYST', 7566,  '1981-12-03'
, 3000, NULL, 20); 
INSERT INTO EMP ( EMPNO, ENAME, JOB, MGR, HIREDATE, SAL, COMM,
DEPTNO ) VALUES ( 
7934, 'MILLER', 'CLERK', 7782,  '1982-01-23'
, 1300, NULL, 10); 
commit;
 
INSERT INTO SALGRADE ( GRADE, LOSAL, HISAL ) VALUES ( 
1, 700, 1200); 
INSERT INTO SALGRADE ( GRADE, LOSAL, HISAL ) VALUES ( 
2, 1201, 1400); 
INSERT INTO SALGRADE ( GRADE, LOSAL, HISAL ) VALUES ( 
3, 1401, 2000); 
INSERT INTO SALGRADE ( GRADE, LOSAL, HISAL ) VALUES ( 
4, 2001, 3000); 
INSERT INTO SALGRADE ( GRADE, LOSAL, HISAL ) VALUES ( 
5, 3001, 9999); 
commit;

pgsql numeric java 用什么类型接收 javasql包接口_sql_03


包含3表:部门表、员工表、薪资等级表。

2、JDBC中重要的接口和类

DriverManager类(驱动管理类)里面全是静态方法,我们用它来注册驱动:

DriverManager 类中的静态方法

说明

static void registerDriver(Driver driver)

向 DriverManager 注册驱动程序

static Connection getConnection(String url, String user, String password)

建立到给定数据库 URL 的连接

Statement接口(执行sql)用于执行静态 SQL 语句并返回它所生成结果的对象:

statement中的方法

说明

int executeUpdate(String sql)

执行更新语句,该语句可能为 INSERT、UPDATE 或 DELETE 语句,返回值是“影响数据库中的记录条数”

ResultSet executeQuery(String sql)

执行 SQL查询语句,该语句返回单个 ResultSet 对象

ResultSet接口(查询结果集),sql查询语句后可以将结果封装到ResultSet中:

ResultSet中的方法

说明

boolean next()

将光标从当前位置向前移一行。ResultSet 光标最初位于第一行之前;第一次调用 next 方法使第一行成为当前行;第二次调用使第二行成为当前行,依此类推(用来查询结果)

String getString(int columnIndex)

不管数据库中的数据类型是什么,都以String的形式取出(columnIndex是指取列数,第一列,第二列…)

String getString(String columnLabel)

不管数据库中的数据类型是什么,都以String的形式取出( columnLabel指查询语句中的列名)

3、反射机制注册驱动(最常用的注册驱动方法)

加载和注册数据库驱动,数据库驱动由 mysql 厂商 "com.mysql.jdbc.Driver"提供

public static void main(String[] args) {
        try {
            //1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2、获取连接
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false","root","root");
            System.out.println(conn);
            
        } catch (SQLException e) {
           e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

4、示例1:插入操作

import java.sql.*;

public class JDBCTest01 {
    public static void main(String[] args) {

        Connection conn = null;
        Statement stmt = null;
        try {
            //1、注册驱动的第二种方法,告诉java我要连接mysql
            Driver driver = new com.mysql.jdbc.Driver();
            DriverManager.registerDriver(driver);
            //2、获取连接,告诉java我要连接哪台机器的mysql,并写入用户和密码
            //127.0.0.1和localhost都是本地ip地址
            String url = "jdbc:mysql://127.0.0.1:3306/test?useSSL=false";
            String user = "root";
            String password = "root";
            conn = DriverManager.getConnection(url,user,password);
            System.out.println(conn);

            //3、获取数据库操作对象(statement专门执行sql语句)
            stmt = conn.createStatement();

            //4、执行sql
            String sql = "insert into dept(deptno,dname,loc) values(50,'军部','长安');";
            //专门执行DML语句(insert、delete、update)
            //返回值是“影响数据库中的记录条数”
            int a = stmt.executeUpdate(sql);
            System.out.println(a);
            System.out.println(a == 1 ? "保存成功":"保存失败");

            //5、处理查询结果集
			//插入语句,暂时不需要查询

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //6、释放资源
            //为了保证资源一定释放,在finally语句块中关闭资源
            //分别要遵循从小到大依次关闭
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}

5、示例2:删除操作

import java.sql.*;

public class JDBCTest02 {
    public static void main(String[] args) {

        Connection conn = null;
        Statement stmt = null;
        try {
            //1、注册驱动
            DriverManager.registerDriver(new com.mysql.jdbc.Driver());
            //2、获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false","root","root");
            //3、获取数据库操作对象
            stmt = conn.createStatement();
            //4、执行sql语句
            String sql = "delete from dept where deptno = 50;";
            int count = stmt.executeUpdate(sql);
            System.out.println(count == 1 ? "删除成功":"删除失败");

        } catch(SQLException e) {
            e.printStackTrace();
        } finally {
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

6、示例3:从资源文件中读取连接数据库信息

将连接数据库的所有信息配置到配置文件jdbc.properties中:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test?useSSL=false
user=root
password=root

实际开发中不建议把来连接数据库的信息写死到java程序中,因为用户可能不会把数据库的密码给你。

import java.sql.*;
import java.util.*;

public class JDBCTest04 {
	public static void main(String[] args) {

		ResourceBundle bundle = ResourceBundle.getBundle("jdbc");
		String driver = bundle.getString("driver");
		String url = bundle.getString("url");
		String user = bundle.getString("user");
		String password = bundle.getString("password");

		Connection conn = null;
		Statement stmt = null;
		try {
			Class.forName(driver);

			conn = DriverManager.getConnection(url,user,password);

			stmt = conn.createStatement();

			int count = stmt.executeUpdate("insert into dept(deptno,dname,loc) values(50,'人事部','北京');");

			System.out.println(count == 1? "保存成功":"保存失败");

		} catch(SQLException e){
			e.printStackTrace();
		} catch(ClassNotFoundException e) {
			e.printStackTrace();	
		} finally {
			if(conn != null) {
				try {
					conn.close();
				} catch(SQLException e){
					e.printStackTrace();
				}
			}
			if(stmt != null) {
				try {
					stmt.close();
				} catch(SQLException e){
					e.printStackTrace();
				}
			}
		}
	}
}

7、示例4:处理查询结果集(遍历查询结果)

ResultSet中的next( )方法:

pgsql numeric java 用什么类型接收 javasql包接口_jdbc_04


next()中的光标:

pgsql numeric java 用什么类型接收 javasql包接口_数据库_05

import java.sql.*;

public class JDBCTest05 {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            //1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2、获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false","root","root");
            //3、获取数据库连接对象
            stmt = conn.createStatement();
            //执行sql
            String sql = "select empno,ename,sal from emp;";
            rs = stmt.executeQuery(sql);
            //5、处理查询结构集
            while (rs.next()) {
                //1、第一种方法
                //String empno = rs.getString(1); //第一列
                //String ename = rs.getString(2); //第二列
                //String sal = rs.getString(3);   //第三列
                //System.out.println(empno + "\t" + ename + "\t" + sal + "\t"); //jdbc中所有下标从1开始

                //2、第二种方法
                String empno = rs.getString("empno"); //第一列
                String ename = rs.getString("ename"); //第二列
                String sal = rs.getString("sal");   //第三列
                System.out.println(empno + "\t" + ename + "\t" + sal + "\t"); //jdbc中所有下标从1开始
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //6、释放资源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}

释放资源时先释放查询结果集,再释放数据库操作对象,最后释放数据库连接对象。

二、sql注入现象

1、用户登录程序

在数据库中先建一个用户表:

create table t_user
(
   id                   bigint auto_increment,
   loginName            varchar(255),
   loginPwd             varchar(255),
   realName             varchar(255),
   primary key (id)
);

insert into t_user(loginName,loginPwd,realName) values('zhangsan','123','张三');
insert into t_user(loginName,loginPwd,realName) values('jack','123','杰克');
commit;
select * from t_user;

pgsql numeric java 用什么类型接收 javasql包接口_数据库_06


编写一个java程序,当输入的账户和密码与表中的数据一致时,显示“登录成功”,否则“登录失败”。

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.sql.*;

//模拟用户登录成功功能的实现
public class JDBCTest06 {
    public static void main(String[] args) {

        Map<String,String> userLoginInfo = initUI();
        System.out.println(login(userLoginInfo) ? "登录成功" : "登录失败");
    }

	//传入一个map集合,并拿它与数据进行对比,如果账户密码合法就返回“true”,否则返回“false”
    private static boolean login(Map<String, String> userLoginInfo) {
        boolean flag = false;
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try {
            //1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            //2、获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false","root","root");
            //3、获取数据库操作对象
            stmt = conn.createStatement();
            //4、执行sql语句
            //String sql = "select .* from t_user where userName = '" + userLoginInfo.get("userName") + "' and userPassword = '" + userLoginInfo.get("' userPassword;");
            String sql = "select * from t_user where loginName = '"+ userLoginInfo.get("userName")+ "' and loginPwd = '" + userLoginInfo.get("userPassword")+ "'";
            rs = stmt.executeQuery(sql);
            flag = rs.next();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        return flag;
    }
	
	//输入函数,用户在此函数中输入自己的账户和密码,并存入map集合中去与数据库中的数据进行比较
    private static Map<String,String> initUI() {
        Scanner s = new Scanner(System.in);

        System.out.printf("请输入账号: ");
        String userName = s.nextLine();
        System.out.printf("请输入密码: ");
        String userPassword = s.nextLine();

        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("userName",userName);
        userLoginInfo.put("userPassword",userPassword);

        return userLoginInfo;
    }
}

执行结果:

pgsql numeric java 用什么类型接收 javasql包接口_mysql_07

2、演示sql注入现象

当我输入以下账户和密码时,它也显示登录成功!!

pgsql numeric java 用什么类型接收 javasql包接口_jdbc_08


这种现象称为sql注入(黑客经常使用),这是一个安全隐患。

为什么会出现sql注入现象?
用户输入的信息中含有sql语句的关键字,并且这些关键字参与sql语句的编译过程。导致sql语句的原意被扭曲,进而达到sql注入。
因为我们上面的sql语句被写成了 select * from t_user where loginName = ‘lll’ and loginPwd = ‘lll’ or ‘1’ = ‘1’; 这个1=1是恒成立的。

3、解决sql注入

思想:不让用户的输入信息参加编译。 要解决 SQL 注入就不能让用户输入的密码和我们的 SQL 语句进行简单的字符串拼接。

方法:使用PreparedStatement接口
(1)prepareStatement()会先将 SQL 语句发送给数据库预编译。PreparedStatement 会引用着预编译后的结果。可以多次传入不同的参数给 PreparedStatement 对象并执行。减少 SQL 编译次数,提高效率。
(2)安全性更高,没有 SQL 注入的隐患。

操作请看注释,解释代码:

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class JDBCTest07 {
    public static void main(String[] args) {
        // 初始化界面
        Map<String,String> userLoginInfo = initUI();
        // 验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        // 输出最后结果
        System.out.println(loginSuccess ? "登录成功" : "登录失败");
    }

    /**
     * 用户登录
     * @param userLoginInfo 用户登录信息
     * @return true表示登录成功,false表示登录失败
     */
    private static boolean login(Map<String, String> userLoginInfo) {
        boolean loginSuccess = false;
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            // 1、注册驱动
            Class.forName("com.mysql.jdbc.Driver");
            // 2、获取连接
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test?useSSL=false","root","root");
            // 3、获取预编译的数据库操作对象
            // sql语句的框架中,一个?,表示一个占位符,一个?将来接收一个值。注意:?不要用单引号括起来
            String sql = "select * from t_user where loginName = ? and loginPwd = ?";
            // 程序执行到此处,会发送sql语句框架给DBMS,DBMS对sql语句框架进行预编译。
            ps = conn.prepareStatement(sql);
            // 给占位符?传值,第一个?的下标是1,第二个?的下标是2(JDBC中下标都从1开始)
            ps.setString(1,userLoginInfo.get("userName"));
            ps.setString(2,userLoginInfo.get("userPassword"));
            // 4、执行sql语句
            rs = ps.executeQuery();
            // 5、处理结果集
            if(rs.next()) {
                loginSuccess = true;
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 6、释放资源
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        return loginSuccess;
    }


    /**
     * 初试化界面
     * @return 用户输入的用户名和密码等登录信息
     */
    private static Map<String, String> initUI() {
        Scanner s = new Scanner(System.in);

        System.out.print("请输入用户:");
        String userName = s.nextLine();
        System.out.print("请输入密码:");
        String userPassword = s.nextLine();

        Map<String,String> userLoginInfo = new HashMap<>();
        userLoginInfo.put("userName",userName);
        userLoginInfo.put("userPassword",userPassword);

        return userLoginInfo;
    }
}

三、JDBC事务

把注册驱动和获取连接封装到一个工具类中,以后就不用次次写代码都写那么多了:

import java.sql.*;

/*
    JDBC工具类,简化JDBC编程
*/
public class DBUtil {

    /**
     * 工具类中的构造方法是私有的
     * 因为工具类中的方法都是静态的,直接通过类名去调即可。
     */
    private DBUtil(){}

    /**
     * 静态代码块,类加载的时候执行
     * 把注册驱动程序的代码放在静态代码块中,避免多次获取连接对象时重复调用
     */
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase?useSSL=false","root","root");
    }

    public static void close(Connection conn, Statement ps, ResultSet rs){
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

在java中执行sql事务:

package ustc.java.jdbc;
import ustc.java.jdbc.DBUtil.DBUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/*
    行级锁/悲观锁
 */
public class JDBCTest14 {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            conn = DBUtil.getConnection();
            // 开启事务
            conn.setAutoCommit(false);

            ps = conn.prepareStatement("select ename,job,sal from emp where job = ? for update ");
            ps.setString(1,"MANAGER");

            rs = ps.executeQuery();

            while(rs.next()) {
                System.out.println(rs.getString("ename") + "," + rs.getString("job") + "," + rs.getString("sal"));
            }
            // 提交事务(事务结束)
            conn.commit();
        } catch (SQLException throwables) {
            // 回滚事务(事务结束)
            try {
                conn.rollback();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            throwables.printStackTrace();
        } finally {
            DBUtil.close(conn,ps,rs);
        }
    }
}