JDBC的使用
一、JDBC初始
构建JDBC应用程序涉及以下六个步骤:
导入JDBC驱动包:需要下载包含数据库编程所需的JDBC的jar包。
注册JDBC驱动程序:要求你初始化驱动程序,以便您可以打开与数据库的通信通道。
创建连接:需要使用DriverManager.getConnection()方法创建一个Connection对象,该对象表示与数据库的物理连接。
执行查询:需要使用类型为Statement 或 PreparedStatement的对象来构建和提交SQL语句到数据库。
从结果集中提取数据:需要使用相应的ResultSet.getXXX()方法从结果集中检索数据。
释放资源:需要明确地关闭所有数据库资源,而不依赖于JVM的垃圾收集。
1、导入jar包,在项目下创建lib目录,把mysql的jdbc包放入此目录总,并添加到build path中。
2、注册驱动
第一种方式(推荐写法):Class.forName()
注册驱动程序最常见的方法是使用Java的Class.forName()方法,将驱动程序的类文件动态加载到内存中,并将其自动注册
try { Class.forName("com.mysql.jdbc.Driver"); } catch(ClassNotFoundException ex) { System.out.println("Error: unable to load driver class!"); System.exit(1); }
3、获取连接
3.1数据库URL配置
加载驱动程序后,可以使用DriverManager.getConnection()方法建立连接。为了方便参考,我列出三个重载的DriverManager.getConnection()方法
getConnection(String url)
getConnection(String url,Properties prop)
getConnection(String url,String user,String password)
RDBMS JDBC驱动程序名称 连接字符串格式 MySQL的 com.mysql.jdbc.Driver jdbc:mysql://hostname:3306 / databaseName ORACLE oracle.jdbc.driver.OracleDriver jdbc:oracle:thin:@ hostname:port Number:databaseName DB2 COM.ibm.db2.jdbc.net.DB2Driver jdbc:db2:hostname:port Number / databaseName SYBASE com.sybase.jdbc.SybDriver jdbc:sybase:Tds: hostname:port Number / databaseName
3.2 创建数据库连接对象
String URL = "jdbc:mysql://localhost:3306/emp"; String USER = "root"; String PASS = "root" Connection conn = DriverManager.getConnection(URL, USER, PASS);
二、JDBC执行SQL语句
一旦获得了连接,我们可以与数据库进行交互。JDBC Statement和PreparedStatement接口定义了使您能够发送SQL命令并从数据库接收数据的方法和属性。
三、执行查询+处理结果
Statement
使用Connection对象的createStatement()方法创建Statement对象,如下例所示:
Connection conn = DriverManager.getConnection(URL, USER, PASS); Statement stmt = null; try { stmt = conn.createStatement( ); . . . } catch (SQLException e) { . . . } finally { . . . }
创建Statement对象后,可以使用它来执行一个SQL语句,有三个执行方法:
boolean execute(String SQL):如果可以检索到ResultSet对象,则返回一个布尔值true; 否则返回false。此方法用于执行SQL DDL语句或需要使用真正的动态SQL时。
int executeUpdate(String SQL):返回受SQL语句执行影响的行数。一般用于INSERT,UPDATE或DELETE语句。
ResultSet executeQuery(String SQL):返回一个ResultSet对象。即一张数据表,一般用于返回 SELECT 语句所查询的结果集
演示案例1:添加数据
public class JdbcDemo3 { public static void main(String[] args) { Connection connection=null; Statement stat=null; //数据的增、删、改(创建sql命令) String sqlIns = "insert into dept(deptno,dname,loc) values(80,'市场部','天津')" String sqlDel = "delete from dept where deptno=80"; String sqlUpd = "update dept set loc='内蒙' where deptno=80"; //2 注册驱动 try { Class.forName("com.mysql.jdbc.Driver"); //3打开连接 connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/school", "root", "root"); //4执行sql命令 stat=connection.createStatement(); int result1=stat.executeUpdate(sqlIns); //插入指令 int result2=stat.executeUpdate(sqlDel); //删除指令 int result3=stat.executeUpdate(sqlUpd); //更新指令 if(result>0){ System.out.println("成功"); }else{ System.out.println("失败"); } } catch (Exception e) { e.printStackTrace(); }finally { //关闭连接 if(stat!=null){ try { stat.close(); } catch (SQLException e) { e.printStackTrace(); } } if(connection!=null){ try { connection.close(); } catch (SQLException e) { e.printStackTrace(); } } } } }
ResultSet
用于接收SELECT从数据库中获取的到的查询结果,可以接收多行多列数据。
获取ResultSet对象结果集中数据原理类似于使用 Iterator 遍历集合的原理类似。
如果没有指定任何 ResultSet 获取数据方法,您将自动获得一个TYPE_FORWARD_ONLY(只能向前遍历)
演示案例2:查询数据
//1导入jar包 //... public class JdbcDemo2 { public static void main(String[] args) { Connection connection=null; Statement stat=null; ResultSet rs=null; try { //2注册驱动 Class.forName("com.mysql.jdbc.Driver"); //3获取连接 connection=DriverManager.getConnection("jdbc:mysql://localhost:3306/school", "root", "root"); //4创建命令(语句)对象 stat=connection.createStatement(); //执行命令查询 rs=stat.executeQuery("select ename,empno from emp"); //5处理结果 while(rs.next()){ int empno=rs.getInt("empno"); String empname=rs.getString("ename"); //可以使用数字代表取出数据的位置,从1开始。与上面的等价 //int empno=rs.getInt(1); //String empname=rs.getString(2); System.out.println(empno+"...."+empname); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); }finally { //关闭资源 if(rs!=null){ try { rs.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(stat!=null){ try { stat.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(connection!=null){ try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } }
SQL注入
以上方式进行数据的查询是很不安全的,比如现在我们有一个用户登录系统,我们通过SELECT 查询用户输入的用户名和密码:
Scanner input=new Scanner(System.in); //用户输入用户名 System.out.println("请输入用户名"); String username=input.nextLine(); //用户输入密码 System.out.println("请输入密码"); String pass=input.nextLine(); ... //将用户输入的数据插入到将要执行的SQL语句中 resultSet=stat.executeQuery("select * from user where username='"+username+"' and password='"+pass+"'"); //查看是否有效,有效则登陆成功 if(resultSet.next()){ //有数据 System.out.println("登录成功"); }else{ System.out.println("账号或密码错误"); }
如果此时用户进行非正常操作,即:
输入用户名:fgghd' or 1=1 -- //这里是两个减号和一个空格 输入密码: gfjlg //用户名单引号前面的和密码随便输
这样拼成的SQL语句为:
select * from user where username='fgghd' or 1=1 -- 'and password='gfjlg' 由于 “-- ”在SQL语句中代表注释的意思 所以 1=1后面的内容全部被注释掉了 所以数据库真正执行的语句为: select * from user where username='fgghd' or 1=1
此时where后面的语句一定为true,以至于所有用户的信息都会被泄露出去。
PreparedStatement
为了解决SQL注入问题,于是就有了PreparedStatement接口。
该PreparedStatement的接口扩展了Statement接口,它为您提供了一个通用的Statement对象有两个优点附加功能。
作用:
预编译,效率高
安全,避免SQL注入
此语句使您可以动态地提供参数。
PreparedStatement pstmt = null; try { String SQL = "Update Employees SET age = ? WHERE id = ?"; pstmt = conn.prepareStatement(SQL); . . . } catch (SQLException e) { . . . } finally { . . . }
JDBC中的所有参数都由?符号,这被称为参数标记。在执行SQL语句之前,必须为每个参数提供值。
所述的setXXX()方法将值绑定到所述参数,其中XXX代表要绑定到输入参数的值的Java数据类型。如果忘记提供值,将收到一个SQLException。
每个参数标记由其顺序位置引用。第一个标记表示位置1,下一个位置2等等。
练习:使用PreparedStatement实现emp表的数据添加
public class Demo { public static void main(String[] args) throws Exception{ Scanner input=new Scanner(System.in); System.out.println("请输入员工编号"); int empno=input.nextInt(); System.out.println("请输入员工姓名"); String ename=input.next(); System.out.println("请输入工作"); String job=input.next(); System.out.println("请输入经理的编号"); int mgr=input.nextInt(); System.out.println("请输入入职日期"); String date=input.next(); SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd"); Date hiredate=sdf.parse(date); System.out.println("请输入工资"); double salary=input.nextDouble(); System.out.println("请输入奖金"); double comm=input.nextDouble(); System.out.println("请输入部门"); int deptno=input.nextInt(); //1注册驱动 Class.forName("com.mysql.jdbc.Driver"); //2获取连接 String url="jdbc:mysql:///school"; Connection conn=DriverManager.getConnection(url, "root", "root"); //3创建命令 PreparedStatement pstat=conn.prepareStatement("insert into emp(empno,ename,job,mgr,hiredate,sal,comm,deptno) values(?,?,?,?,?,?,?,?);"); //4参数赋值 pstat.setObject(1, empno); pstat.setObject(2, ename); pstat.setObject(3, job); pstat.setObject(4, mgr); pstat.setObject(5, hiredate); pstat.setObject(6, salary); pstat.setObject(7, comm); pstat.setObject(8, deptno); //5执行 int count=pstat.executeUpdate(); if(count>0) { System.out.println("添加成功"); }else { System.out.println("添加失败"); } //6释放资源 pstat.close(); conn.close(); } }
四、关闭数据库连接
数据库的资源是宝贵的,为了保证一个用户进行完数据库的操作后能够及时的释放资源,我们就需要将其占用的资源进行关闭:
if(resultSet!=null){ try { resultSet.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(statement!=null){ try { statement.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(connection!=null){ try { connection.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } }
为了确保一定能够关闭,我们一般将其放在finally内执行。