JDBC

Posted 奔跑的路奇

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDBC相关的知识,希望对你有一定的参考价值。

1 JDBC的介绍

1.1 简介

JDBC(Java DataBase Connectivity)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。各种不同类型的数据库都有相应的实现,(本文中的代码都是针对mysql数据库实现的)。

简单的描述就是可以通过JDBC直接对数据库进行操作。

1.2 JDBC核心组件


 - DriverManager: 此类管理数据库驱动程序列表。使用通信子协议将来自java应用程序的连接请求与适当的数据库驱动程序匹配。                   
 - Driver:此接口处理与数据库服务器的通信,我们很少会直接与Driver对象进行交互。而是使用DriverManager对象来管理这种类型的对象。
 - Connection:该界面具有用于联系数据库的所有方法。连接对象表示通信上下文,即,与数据库的所有通信仅通过连接对象。
 - Statement:使用从此接口创建的对象将SQL语句提交到数据库。除了执行存储过程之外,一些派生接口还接受参数。
 - ResultSet:在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据。它作为一个迭代器,允许我们移动其数据。
 - SQLException:此类处理数据库应用程序中发生的任何错误

2 JDBC连接步骤

2.1 注册驱动

注意要先导入对应的mysql连接数据库的jar包

mysql6以下驱动:com.mysql.jdbc.Driver
mysql6和6以上驱动:com.mysql.cj.jdbc.Driver
初始化驱动


try {
       Class.forName("com.mysql.cj.jdbc.Driver");		
    } catch (ClassNotFoundException e) { 				
        e.printStackTrace();
}

Class.forName是把这个类加载到JVM中,加载的时候,就会执行其中的静态初始化块,完成驱动的初始化的相关工作。

2.2 建立JDBC和数据库之间的Connection连接

我这里是写成了方法,后面会有完整的代码演示


	private String userName = "root";//用户名
    private String passWord = "123456";//密码
    //数据库服务端的IP地址:127.0.0.1 (这是本机,如果连接其他电脑上的数据库,需填写相应的IP地址) ;
    // demo:是自己的建的数据库名称; serverTimezone=UTC:是时区,如果不写对应版本高的数据库来说会报错
    private String url = "jdbc:mysql://localhost:3306/demo?serverTimezone=UTC";
    
	protected Connection getConnection(){
	        try {
	            connection = DriverManager.getConnection(url, userName, passWord);
	        } catch (SQLException throwables) {
	            throwables.printStackTrace();
	        }
	        return connection;
	 }

2.3 执行查询

2.3.1 第一种方式:使用Statement接口

执行查询是使用executeQuery方法


            statement = connection.createStatement();
            resultSet = statement.executeQuery("select * from employee;"); //executeQuery:执行查询的sql        
            //4 取出查询到的结果
            while (resultSet.next()){
                System.out.println("姓名:"+resultSet.getString("name") );
            }
            

执行删除,添加时使用的是executeUpdate,返回的结果是受影响的行数


            statement = connection.createStatement();
            int i = statement.executeUpdate("update employee set sex='女' where empid=1;");//executeUpdate:执行修改,删除,添加 时使用
            //4 判断执行是否成功  i:代表执行sql语句影响的数    
            if (i>0){      
                System.out.println("操作成功:"+i);
            }else {
                System.out.println("操作失败");
            }

但注意::使用第一种方式可能会导入有SQL注入的问题
SQL注入:将(恶意的)SQL命令注入到后台数据库引擎执行的能力

例如:

	String username = "admin"; 
	String password =" 'abc' or 1=1 "; 
	String sql ="select * from users where username= '"+username+"' and password= "+password; //不论密码是否输入正确,都会执行成功               

2.3.2 第二种方式:使用PreparedStatement接口

该PreparedStatement的接口扩展了Statement接口,它为您提供了一个通用的Statement对象有两个优点附加功能。


            String sql = "update employee set sex=? where empid=?";              
            //使用预编译的语句,防止sql注入
            preparedStatement = connection.prepareStatement(sql);
            String sex = "女";//要插入的sex
            int empid = 1; //插入的id
            //给占位符(?)赋值
            psm.setString(1,sex); //是从1开始,不是从0   
            psm.setInt(2,empid);
            //执行sql
            int i = psm.executeUpdate();

            //判断执行是否成功  i:代表执行sql语句影响的数
            if (i>0){
                System.out.println("操作成功:"+i);
            }else {
                System.out.println("操作失败");
            }
  1. 使用PreparedStatement时,他的SQL语句不再采用字符串拼接的方式,而是采用占位符的方式。
  2. 后面需要用到PreparedStatement接口创建的pstmt的set方法给占位符进行赋值。注意一点,这里的参数索引是从1开始的。
  3. 增删改都使用psm.executeUpdate();语句进行SQL语句的提交
  4. 查询使用psm.executeQuery();得到的结果是返回查询到的结果集

2.3.3 对比statement和PreparedStatement


	(1)statement属于状态通道,PreparedStatement属于预状态通道
	(2)预状态通道会先编译sql语句,再去执行,比statement执行效率高
	(3)预状态通道支持占位符?,给占位符赋值的时候,位置从1开始
	(4)预状态通道可以防止sql注入,原因:预状态通道在处理值的时候以字符串的方式处理
	

2.4 使用ResultSet处理和显示结果


       statement = connection.createStatement();
       resultSet = statement.executeQuery("select * from employee;"); //executeQuery:执行查询的sql        
       //4 取出查询到的结果
       while (resultSet.next()){
            System.out.println("姓名:"+resultSet.getString("name") );
       }
            

2.5 释放资源

在JDBC编码的过程中我们创建了Connection、ResultSet等资源,这些资源在使用完毕之后是一定要进行关闭的。
需要明确地关闭所有数据库资源,而不依赖于JVM的垃圾收集。

写成了一个方法,可以更好对进行代码复用


	protected void closeAll(){
        try {
            if (connection != null){
                connection.close();
            }
            if (preparedStatement !=null){
                preparedStatement.close();
            }
            if (resultSet != null){
                resultSet.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
	}

3 将上述写成完整的工具类

使用的是PreparedStatement,还编写了一个为占位符赋值的setParams方法,在调用增删改方法和查询方法的时候,其内部调用了setParams方法,不用自己赋值


public class JDBCUtil {

    Connection connection = null;
    PreparedStatement preparedStatement = null;
    protected ResultSet resultSet = null;

    private int count = 0;

    private String userName = "root";
    private String passWord = "123456";
    private String url = "jdbc:mysql://localhost:3306/kkb1?serverTimezone=UTC";


    //1 加载驱动
    static {
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }


    //2 获取连接
    protected Connection getConnection(){
        try {
            connection = DriverManager.getConnection(url, userName, passWord);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return connection;
    }


    //3 定义sql , 创建连接通道
    protected PreparedStatement getPps(String sql){
        try {
            getConnection();
            preparedStatement = connection.prepareStatement(sql);
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return preparedStatement;
    }

    //4 给占位符赋值,因为参数可以有多个,所有是一个集合形式
    protected void setParams(List list){
        try {
            if (list!=null && list.size()>0){
                for (int i = 0; i < list.size(); i++) {
                    preparedStatement.setObject(i+1 , list.get(i));
                }
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }



    //5 增删改调取的方法; 传入的是sql语句 和 参数
    protected int update(String sql , List params){
        try {
            getPps(sql);
            //在执行方法的时候,这里调用了方法为占位符赋值的方法
            setParams(params);
            count = preparedStatement.executeUpdate();
            return count;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
        return count;
    }




    //6 执行查询的方法 ,传入的是sql语句 和 参数,没有参数的话,就传入null即可
    protected ResultSet query(String sql , List params){
        try {
            getPps(sql);
            //给占位符赋值
            setParams(params);
            resultSet = preparedStatement.executeQuery();
            return resultSet;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }


    //7 关闭资源
    protected void closeAll(){
        try {
            if (connection != null){
                connection.close();
            }
            if (preparedStatement !=null){
                preparedStatement.close();
            }
            if (resultSet != null){
                resultSet.close();
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

测试上述方法:


    /**
     * 根据地址查询对应信息
     */
	@Override
    public List<AirInfo> getByAddress(String address) {
    	//一个地址对应可以有多个信息,所以创建集合接收数据
        List<AirInfo> lists = new ArrayList<AirInfo>();
        
        try {
            String sql = "select * from airinfo where address=?";
            //创建的集合是为了存放参数
            List list = new ArrayList();
            list.add(address);
             //执行工具类的查询 , 在query查询方法中,调用了为占位符赋值的方法
            ResultSet query = query(sql, list);
            //从数据库中查询到的值,赋值给自己创建的bean类
            //然后就可以通过getId , getAddress...,得到数据
            while (query.next()){
                AirInfo airInfo = new AirInfo();
                airInfo.setId(query.getInt("id"));
                airInfo.setAriId(query.getString("airId"));
                airInfo.setAddress(query.getString("address"));
                airInfo.setFilDate(query.getString("filDate"));

                lists.add(airInfo);
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        //返回查询到的结果集合
        return lists;
    }


    /**
     * 根据航id更新对应的信息
     */
	@Override
    public int updateById(String airId, String address, String filDate, int id ) {
    
        int i = 0;
        try {
            String sql = "update airinfo set airId=?, address=?, filDate=? where id=?";
            List list = new ArrayList();
            list.add(airId);
            list.add(address);
            list.add(filDate);
            list.add(id);

             i = update(sql, list);

        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            closeAll();
        }
        //返回受影响的数
        return i;
 }

最后,如果有问题,希望指正,一起进步。

以上是关于JDBC的主要内容,如果未能解决你的问题,请参考以下文章

面试常用的代码片段

mysql jdbc源码分析片段 和 Tomcat's JDBC Pool

JDBC操作数据库之查询数据

如何在片段中填充列表视图?

在 myeclipse中进行连接sql server的测试

MYBATIS05_ifwherechoosewhentrimsetforEach标签sql片段