ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

JDBC超详细基本使用总结,看这篇就够了!!!

2020-06-30 22:03:09  阅读:318  来源: 互联网

标签:总结 JDBC String 这篇 try connection sql null public


JDBC基础总结

如果需要总结中所有jar包,以及文章的doc格式,私信我即可

历时三天,万字以上的总结,分享给大家,若有不正确之处,欢迎大家指点~


文章中会用到的数据库的表
1.customers表Order表
2.order表
在这里插入图片描述
3.user表
在这里插入图片描述
4.user_table表
在这里插入图片描述
5.boys表
在这里插入图片描述
一、JDBC
概述

JDBC:Java DataBase Connectivity
JDBC本质:SUN公司定义的一套操作所有关系型数据库的规则(接口)。各个数据库厂商去实现这个接口,提供数据库驱动jar包。我们可以使用这套接口进行编程,真正执行的代码是驱动jar包中的实现类
在这里插入图片描述
二、快速入门
1.导入驱动jar包
2.注册驱动
3.获取数据库连接对象 Connection
4.定义sql语句
5.获取执行sql语句的对象 Statement
6.执行sql,接收返回结果
7.处理结果
8.释放资源,最后开启的资源要先释放
释放的资源有ResultSet(仅查询时使用)、Statement、Connection

三、导入驱动jar包
Eclipse中:
1.去官网下载jar包
在这里插入图片描述
2. 将上述jar包拷贝到java工程的一个目录中,习惯右键新建一个lib文件夹,将jar包导入
在这里插入图片描述
3. 点击
在这里插入图片描述
IDEA中:在当前moudle下右键new directory取名libs,将jar包复制导入,在libs文件夹右键Add as Library

四、数据库连接中使用到的类和接口
DriverManager类:驱动管理对象
Connection接口:数据库连接对象
Statement接口:用于执行静态 SQL 语句并返回它所生成结果的对象
ResultSet接口:接收Statement接口返回的结果集对象
PreparedStatement接口:是Statement的子接口,执行预编译SQL语句的对象(解决注入问题)

详解:
(1) DriverManager类:驱动管理对象
功能:1. 注册驱动:告诉程序该使用哪一个数据库驱动jar包;
static void registerDriver(Driver driver) 向 DriverManager 注册给定驱动程序;
Jar包中com.mysql.jdbc.Driver类中有静态代码块:
在这里插入图片描述
故加载运行时类Class.forName(“com.mysql.jdbc.Driver”);自动执行静态代码块完成注册驱动
注:mysql5之后的驱动jar包可省略注册驱动的步骤(不建议省略)

在这里插入图片描述
即此文件自动的注册驱动jar包
2. 获取数据库连接:建立到给定数据库 URL 的连接
static Connection getConnection(String url, String user, String password)
参数: url:指定连接的路径
语法:
在这里插入图片描述
其中协议与子协议是固定写法
子名称是 ip地址(域名):端口号/数据库名称
注意:如果连接的是本机的默认端口号是3306的mysql服务器,url可简写:
jdbc:mysql:///数据库名称,即省略ip地址(域名):端口号
user:数据库用户名
Password: 数据库登陆密码

(2) Connection接口:数据库连接对象
功能:1. 获取执行sql语句的对象
a. Statement createStatement( ) 创建一个 Statement 对象来将 SQL 语句发送到数据库
b. PreparedStatement prepareStatement(String sql)
创建一个 PreparedStatement 对象来将参数化的 SQL 语句发送到数据库
2. 管理事务:
开启事务:void setAutoCommit(boolean autoCommit) 参数设置为false开启事务
提交事务:void commit()
回滚事务:void rollback()

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

  1. int executeUpdate(String sql)
    执行除了select之外的其余sql语句,即不返回任何内容的sql语句,返回值是受影响的行数, 可以通过返回的int值判断sql语句是否执行成功(>0则成功,反之失败),不使用ResultSet
  2. ResultSet executeQuery(String sql)
    执行给定的select语句,该语句返回单个 ResultSet 对象
  3. boolean execute(String sql)
    执行给定的 SQL 语句,该语句可能返回多个结果 (了解,不常用)

代码演示1:JDBC的初步使用一

public static void main(String[] args) throws Exception {
    //所有的异常均抛出
    //导入jar包结束后,注册驱动
    Class.forName("com.mysql.jdbc.Driver");
    //获取连接
    Connection connection = 	DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","root");
    //创建执行sql语句的对象
    Statement statement = connection.createStatement();
    //定义sql语句
    String sql = "update customers set name = 'xxx' where id = 8 ";
    //执行sql语句
    int i = statement.executeUpdate(sql);
    System.out.println(i); //1行受影响,输出1
    //关闭资源
    connection.close();
    statement.close();
}
//运行成功后,mysql的customers表的id为10的行的name变成了xxx

代码演示2:JDBC的初步使用二

public static void main(String[] args) {
	//为了保证流一定被关闭,不再抛出异常
    Statement statement = null; //若在try中定义出了try无法使用,即无法关闭
    Connection connection = null; //若在try中定义出了try无法使用,即无法关闭
    try {
        Class.forName("com.mysql.jdbc.Driver");
        connection =
                DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
        statement = connection.createStatement();
        String sql = "insert into user_table values('高奇泽','123','6523')";
        int count = statement.executeUpdate(sql);
        if (count > 0) {
            System.out.println("执行成功");
        } else {
            System.out.println("执行失败");
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    } finally {
        if (statement != null) { //不判断可能会空指针异常
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (connection != null) { //不判断可能会空指针异常
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}
/*注:两个关闭流的操作不可写在一个try——catch中,否则第一个流关闭异常会导致第二个流关闭失败*/

(4) ResultSet接口:结果集对象(可认为是查询出的一张表)
有一个游标指向第一行的上一行,需要调用方法使游标下移,再调用方法获取指向行中某一列的值,注意:无法获取整行的值,只可以获取行中某一列的值
在这里插入图片描述

  1. boolean next() 游标向下移动一行,如果有下一行可以被指向,返回true,若无,返回false
  2. xxx getXxx(参数) 获取当前行中由参数所指定列的值;注:Xxx代表数据类型
    参数:1. int:代表列的编号,从1开始
    2. String:代表列的名称
    java和sql中对应类型的转换:
    在这里插入图片描述
    代码演示3:使用ResultSet获取并遍历结果集
public static void main(String[] args) {
    Connection connection = null;
    Statement statement = null;
    ResultSet resultSet = null;
    try {
        Class.forName("com.mysql.jdbc.Driver");
        connection = 
                DriverManager.getConnection("jdbc:mysql://localhost:3306/girls", "root", "root");
        statement = connection.createStatement();
        String sql = "select * from boys";
        resultSet = statement.executeQuery(sql);
        //循环的遍历结果集
        while (resultSet.next()) {
            int id = resultSet.getInt("id");
            String string = resultSet.getString(2);
            int userCP = resultSet.getInt("userCP");
            System.out.println(id + string + userCP);
        }
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    } finally {
        if (resultSet != null) {
            try {
                resultSet.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (statement != null) {
            try {
                statement.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        if (connection != null) {
            try {
                connection.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}

使用JDBC工具类:JDBCUtils
目的:简化书写,将每次使用JDBC都要重复写的代码抽取出来,放在静态方法中,方便调用
分析: 1.抽取注册驱动的代码
2.抽取一个方法获取连接对象
3.抽取一个方法释放资源

代码演示4:使用JDBCUtils对Order表进行查询,结果放入集合中,并返回集合
1.创建一个Order类

public class Order {
    //一个类代表一张表,类中的属性代表一列,一个对象代表一行(ORM编程思想)
    private int order_id;
    private String order_name;
    private Date order_date; //util下的Date
    //定义所有属性的getter/setter方法
    public int getOrder_id() {
        return order_id;
    }
    public void setOrder_id(int order_id) {
        this.order_id = order_id;
    }
    public String getOrder_name() {
        return order_name;
    }
    public void setOrder_name(String order_name) {
        this.order_name = order_name;
    }
    public Date getOrder_date() {
        return order_date;
    }
    public void setOrder_date(Date order_date) {
        this.order_date = order_date;
    }
    @Override
    public String toString() {
        return "Order{" +
                "order_id=" + order_id +
                ", order_name='" + order_name + '\'' +
                ", order_date=" + order_date +
                '}';
    }
}

2.在src下创建一个配置文件jdbc.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
user=root
password=root //更改数据库或驱动只需更改配置文件即可,其余无需更改,体现可扩展性

3.创建工具类JDBCUtils

public class JDBCUtils {
    //1.注册驱动
    private static String url;
    private static String user;
    private static String password;
    private static String password;
    private static String driver;
    //静态代码块
    static {
        try {
            //读取配置文件(都写在try中,一旦有一处出现异常其余没必要再执行)
            Properties properties = new Properties();
            ClassLoader classLoader = JDBCUtils.class.getClassLoader();
            InputStream resourceAsStream =
                    classLoader.getResourceAsStream("jdbc.properties");
            properties.load(resourceAsStream); //抛出异常
            url = properties.getProperty("url");
            user = properties.getProperty("user");
            password = properties.getProperty("password");
            driver = properties.getProperty("driver");
            //注册驱动
            Class.forName(driver);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    //2.获取连接(抛出异常)
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url,user,password);
    }
    /*可直接类名.getConnection注册驱动,静态方法调动类加载,从而调动静态代码块*/
    //3.关闭资源
    //可能需要关闭ResultSet,也可能不需要,故需重载
    public static void close(Statement stmt, Connection conn) {
        if (stmt!=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
    public static void close(ResultSet rs, Statement stmt, Connection conn) {
        if (rs!=null){
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stmt!=null){
            try {
                stmt.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (conn!=null){
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

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

4.编写测试类进行查询

public class MyTest {
    public List<Order> search() {
        List<Order> list = null;
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            connection = JDBCUtils.getConnection();
            statement = connection.createStatement();
            String sql = "select * from orders";
            resultSet = statement.executeQuery(sql);
            //创建Order类对象、集合
            Order order = null;
            list = new ArrayList<>();
            //循环
            while (resultSet.next()) {
                int order_id = resultSet.getInt("order_id");
                String order_name = resultSet.getString("order_name");
                Date order_date = resultSet.getDate("order_date");
	        //sql包的Date是Util包Date的子类,多态                
	        //每次遍历一行需创建一个对象,保存一行的所有数据
                order = new Order();
                order.setOrder_date(order_date);/*使用set赋值,而不使用构造器赋值,因为可
                order.setOrder_id(order_id);       能用户查询的列数并没有合适个数的构造器*/
                order.setOrder_name(order_name);
                //将order装入集合
                list.add(order);
                //继续下一次遍历
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JDBCUtils.close(resultSet,statement,connection);
        }
        return list;
    }
    //执行
    public static void main(String[] args) {
        List<Order> listResult = new MyTest().search();
        System.out.println(listResult);
        System.out.println(listResult.size());
    }
}
        return list;
    }
    //执行
    public static void main(String[] args) {
        List<Order> listResult = new MyTest().search();
        System.out.println(listResult);
        System.out.println(listResult.size());
    }
}

练习:
需求:通过键盘录入用户名和密码,判断用户是否可以登录成功(登陆信息在user表)
分析:此题是判断键盘输入的信息是否存在于数据库的user表中
select * from user where username = xxx and password = xxx;
若有查询结果则可以登录,否则无法登录
注意:如果使用Statement语句,则对应的sql语句是:(字符串中有字符串使用单引号)

String sql="select * from user where username= '"+username+"'and password ='"+password+"'";
//单引号中放两个双引号,两个双引号中再放两个加号,两个加号中间写变量,即为拼串
//拼串使得username不是等于username,而是等于username所代表的值

此种写法存在注入问题:使用sql的一些特殊关键字参与字符串的拼接,造成安全问题,假如用户 名随便输入,密码输入为:a’ or ‘a’ = ‘a,对应的sql语句是:
select * from user where username = ‘xxx’ and password = ‘a’ or ‘a’ = ‘a’;
其中,‘a’ = ‘a’是恒成立的,故user表中的所有行均会查询出,不存在的用户也可登录
解决:使用PreparedStatement接口

(5) PreparedStatement接口:执行预编译SQL语句:参数使用?作为占位符,则上述练习的sql语句为:
String sql="select * from user where username= ? and password = ?;
注意: 通过Connection获取此接口的方法为PreparedStatement prepareStatement(String sql);
需要把此预编译sql语句作为参数传递到构造器中,而获取Statement的对象时使用的 是空参构造器
使用步骤: 1. 获取PreparedStatement的对象之后,需要使用PreparedStatement接口的方法 给 ? 赋值:void setXxx(int p1,xxx p2);注:Xxx为类型
* p1:? 出现的位置,从1开始
* p2:给 ? 赋予的值
2. 使用PreparedStatement接口的空参方法:
(1) int executeUpdate(); (2) ResultSet executeQuery();

代码演示5:使用PreparedStatement完成上述练习

public class MyTest {
    //创建方法,根据是否可以登录成功返回boolean值
    public boolean logIn(String userName, String passWord) {
        if (userName == null || passWord == null) {
            return false;
        }
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        //获取连接,执行sql语句
        try {
            connection = JDBCUtils.getConnection();
            String sql = "select * from user where name = ? and password = ?";
            preparedStatement = connection.prepareStatement(sql);
            preparedStatement.setString(1,userName);
            preparedStatement.setString(2,passWord);
            resultSet = preparedStatement.executeQuery();
            return resultSet.next();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JDBCUtils.close(resultSet, preparedStatement,connection);
            //preparedStatement使用了多态,传递给了父类
        }
        return false; //出现异常,则返回false
    }
    //测试
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.println("输入姓名:");
        String name = scan.nextLine();
        System.out.println("输入密码:");
        String passWord = scan.nextLine();
        boolean b = new MyTest().logIn(name, passWord);
        if (b) {
            System.out.println("可以成功登录!");
        } else {
            System.out.println("无法成功登录!");
        }
    }
}

五、结果集的元数据
调用结果集ResultSet中的ResultSetMetaData getMetaData()方法,得到ResultSetMetaData的对象,
通过ResultSetMetaData接口中的方法可获得结果集的元数据:
(1) int getColumnCount();获取结果集中的列数
(2) String getColumnLabel(int i);获取表的列名,有别名,获取别名 ,无别名,获取列名
(3) String getColumnName(int i);仅可获取表的列名,无法获取别名(使用较少)
注:i从1开始

代码演示6:对Customers表创建通用的查询方法

  1. 创建一个类代表Customers表
public class Customers {
    //属性的名字与表中的列名不同
    private int ID;
    private String NAME;
    private String EMAIL;
    private Date BIRTH;
    public int getID() {
        return ID;
    }
    public void setID(int ID) {
        this.ID = ID;
    }
    public String getNAME() {
        return NAME;
    }
    public void setNAME(String NAME) {
        this.NAME = NAME;
    }
    public String getEMAIL() {
        return EMAIL;
    }
    public void setEMAIL(String EMAIL) {
        this.EMAIL = EMAIL;
    }
    public String getNAME() {
        return NAME;
    }
    public void setNAME(String NAME) {
        this.NAME = NAME;
    }
    public String getEMAIL() {
        return EMAIL;
    }
    public void setEMAIL(String EMAIL) {
        this.EMAIL = EMAIL;
    }
    public Date getBIRTH() {
        return BIRTH;
    }
    public void setBIRTH(Date BIRTH) {
        this.BIRTH = BIRTH;
    }
    @Override
    public String toString() {
        return "Customers{" +
                "ID=" + ID +
                ", NAME='" + NAME + '\'' +
                ", EMAIL='" + EMAIL + '\'' +
                ", BIRHT=" + BIRTH +
                '}';
    }
}

  1. 编写对customers表通用的查询操作
public class MyTest {
    /**
     * @param sql 要执行的sql语句
     * @param args sql语句中占位符?的值,有几个?写几个值,传递到可变个数形参中
     * @return 存放查询结果的List集合
     */
    public static List<Customers> Query(String sql, Object...args) {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            connection = JDBCUtils.getConnection();
            preparedStatement = connection.prepareStatement(sql);
            //通过循环给?赋值
            for (int i = 0; i < args.length; i++) {
                preparedStatement.setObject(i + 1, args[i]);
            }
            //执行sql语句
            resultSet = preparedStatement.executeQuery();
            /*不同的sql语句得到的结果集不同,需要获得结果集的列数和名字,
              通过结果集的列数和名字,可以得知Order类中的哪些属性要被赋值 */
            //获取结果集的元数据
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            //定义List集合
            List<Customers> list = new ArrayList<>();
            //开始遍历结果集并将结果放在List集合中
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            //定义List集合
            List<Customers> list = new ArrayList<>();
            //开始遍历结果集并将结果放在List集合中
            while (resultSet.next()) {
                //在while中判断为真才创建对象,若结果集无数据但创建了对象,造成空间浪费
                Customers customers = new Customers();
                for (int i = 0; i < columnCount; i++) {
                    //获取所在行的指定列的值,通过反射,将此值存入对应的属性
                    Object columnValue = resultSet.getObject(i + 1);
                    /*获取所在行的指定列的名称(属性名),由于属性名与列名不同,
                      故通过给列起别名使用getColumnLabel */
                    String columnLabel = metaData.getColumnLabel(i + 1);
                    //由于未知给哪个属性赋值,故通过反射将columnValue赋值给对应的属性
                    Field field = Customers.class.getDeclaredField(columnLabel);
                    field.setAccessible(true);
                    field.set(customers, columnValue);
                }
                list.add(customers);
            }
            return list;
        } catch (Exception throwables) {
            throwables.printStackTrace();
        } finally {
            JDBCUtils.close(resultSet, preparedStatement, connection);
        }
        return null;
    }
    //测试
    public static void main(String[] args) {
        String sql = "select email as EMAIL from customers where id = ? or id = ?";
        List<Customers> list = Query(sql,3,5);
        System.out.println(list);
    }
}

代码演示7:对不同表创建通用的查询方法

public class MyTest {
    public static <T> List<T> Query(Class<T> clazz, String sql, Object...args) {
        try {
            Connection connection = JDBCUtils.getConnection();
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                preparedStatement.setObject(i + 1, args[i]);
            }
            ResultSet resultSet = preparedStatement.executeQuery();
            ResultSetMetaData metaData = resultSet.getMetaData();
            int columnCount = metaData.getColumnCount();
            List<T> list = new ArrayList<>();
            while (resultSet.next()) {
                T t = clazz.newInstance();
                for (int i = 0; i < columnCount; i++) {
                    Object columnValue = resultSet.getObject(i + 1);
                    String columnName = metaData.getColumnLabel(i + 1);
                    Field field = clazz.getDeclaredField(columnName);
                    field.setAccessible(true);
                    field.set(t, columnValue);
                }
                list.add(t);
            }
            return list;
        } catch (Exception throwables) {
            throwables.printStackTrace();
        }
        return null;
    }   
    //测试
    public static void main(String[] args) {
        String sql =
                "select id as ID,name as NAME from customers where id = ? or id = ?";
        List<Customers> list = Query(Customers.class, sql, 3,4);
        System.out.println(list);

        String sql2 =
                "select order_name,order_date from order where order_id = ?";
        List<Order> list2 = Query(Order.class, sql2, 2);
        System.out.println(list2);
    }
}

PreparedStatement的好处:(以后均使用此接口,弃用Statement)

  1. 解决sql的注入问题
  2. 可以操作Blob的数据(二进制的大型数据)
    void setBlob(int parameterIndex, InputStream inputStream);
  3. 可以实现更高效的批量操作

代码演示8:在costumers表操作Blob类型的数据
1.向customers表插入Blob类型的数据

Connection connection = null;
PreparedStatement preparedStatement = null;
try {
    connection = JDBCUtils.getConnection();
    String sql = "insert into customers(id,photo) values(?,?)";
    preparedStatement = connection.prepareStatement(sql);
    preparedStatement.setInt(1,19);
    preparedStatement.setBlob(2,new FileInputStream("girl.jpg"));
    preparedStatement.executeUpdate();
} catch (SQLException | FileNotFoundException throwables) {
    throwables.printStackTrace();
} finally {
    JDBCUtils.close(preparedStatement,connection);
}

2.读取customers表中Blob类型的数据,并输出到本地

Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
FileOutputStream fileOutputStream = null;
InputStream inputStream = null;
try {
    connection = JDBCUtils.getConnection();
    String sql = "select photo from customers where id = ?";
    preparedStatement = connection.prepareStatement(sql);
    preparedStatement.setInt(1,19);
    resultSet = preparedStatement.executeQuery();
    while (resultSet.next()) {
        Blob blob = resultSet.getBlob("photo");
        //调用Blob中的getBinaryStream方法获取指定文件的输入流
        inputStream = blob.getBinaryStream();
        //指定输出流
        fileOutputStream = new FileOutputStream("result.jpg");
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inputStream.read(buffer)) != -1) {
            fileOutputStream.write(buffer, 0, len);
        }
    }
} catch (SQLException | FileNotFoundException throwables) {
    throwables.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    JDBCUtils.close(resultSet,preparedStatement, connection);
    if (inputStream != null) {
        try {
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    if (fileOutputStream != null) {
        try {
            fileOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
} /*执行完毕,当前moudle下会出现result.jpg,内容是id=19的photo列*/

注意:如果插入大小相匹配的Blob类型以后,还报错:xxx too large,那么在mysql的安装目录下,找my.ini文件在末尾加上如下的配置参数:max_allowed_packet=16M。同时注意:修改了my.ini文件之后,需要重新启动mysql服务。
Blob类型对应的大小如下:
在这里插入图片描述
代码演示9:向goods表插入20000条数据,体会不同方式的差别

  1. 使用Statement语句进行批量插入
public static void main(String[] args) {
    Connection connection = null;
    Statement statement = null;
    try {
        connection = JDBCUtils.getConnection();
        statement = connection.createStatement();
        //使用for循环插入20000条数据
        for (int i = 0; i < 20000; i++) {
            String sql = "insert into goods(name) values('name_"+i+"')";
            statement.executeUpdate(sql);
            System.out.println("正在插入第" + (i + 1) +"条数据");
        }
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    } finally {
        JDBCUtils.close(statement, connection);
    }
}
/*分析:每次插入都会创建一条sql语句,浪费空间,效率极低*/

  1. 使用PreparedStatement语句进行批量插入
public static void main(String[] args) {
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    try {
        connection = JDBCUtils.getConnection();
        String sql = "insert into goods set name = ?";
        preparedStatement = connection.prepareStatement(sql);
        //使用循环给占位符赋值
        for (int i = 0; i < 20000; i++) {
            preparedStatement.setString(1, "name_" + i);
            preparedStatement.executeUpdate();
            System.out.println("正在插入第" + (i + 1) + "条数据");
        }
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    } finally {
        JDBCUtils.close(preparedStatement, connection);
    }
} /*分析:需要多次与数据库交互,效率较低*/


注意:DBServer会对预编译语句提供性能优化。因为预编译语句有可能被重复调用,所以语句在被DBServer的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中就会得到执行。
而在statement语句中,即使是相同操作但因为数据内容不一样,所以整个语句本身不能匹配,没有缓存语句的意义。这样每执行一次都要对传入的语句编译一次。

  1. 使用Statement语句中专门用来处理批量数据的方法
    介绍Statement中的几个方法:
    addBatch(String sql);将给定的sql命令添加到此Statement对象的当前命令列表中,通过调用executeBatch 方法可以批量的执行此列表中的命令
    executeBatch();将一批命令提交给数据库执行
    clearBatch();清空此Statement对象的当前sql命令列表
    注意:1. MySQL服务器默认是关闭批处理服务的(不支持Batch方法),如果需要让MySQL开启批处理的支持,通过在url后面加 ?rewriteBatchedStatements=true
    2. 需要使用的mysql 驱动:mysql-connector-java-5.1.37-bin.jar,5.1.7不支持
public static void main(String[] args) {
    Connection connection = null;
    PreparedStatement preparedStatement = null;
    try {
        connection = JDBCUtils.getConnection();
        String sql = "insert into goods(name) values(?)";
        preparedStatement = connection.prepareStatement(sql);
        connection.setAutoCommit(false);//开启事务
        for (int i = 1; i <= 2000000; i++) {
            preparedStatement.setString(1, "name_" + i);
            //PreparedStatement中的addBatch方法是空参的(预编译)
            preparedStatement.addBatch();
            if (i % 500 == 0) {
                preparedStatement.executeBatch();
                preparedStatement.clearBatch();
            }
        }
        connection.commit();//提交事务
    } catch (SQLException throwables) {
        throwables.printStackTrace();
    } finally {
        try {
            if (connection != null) {
                connection.setAutoCommit(true);
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JDBCUtils.close(preparedStatement, connection);
        }
    }
} /*分析:目前进行批量操作效率最高的方法*/

六、JDBC管理事务
使用Connection接口的方法来管理事务:
* 开启事务:在执行sql语句之前
* 提交事务:当所有sql语句都可顺利执行
* 回滚事务:执行sql语句时出现异常,在catch中回滚事务
* 获取事务的隔离级别:int getTransactionIsolation();返回隔离级别对应的int值
* 设置事务的隔离级别:void setTransactionIsolation(int level);
注:参数一般使用全局常量:Connection.TRANSACTION_XXX

代码演示10:通过银行转账案例演示管理事务

public static void main(String[] args) {
    //在user_table表中,AA给BB转账500元
    Connection connection = null;
    PreparedStatement preparedStatement1 = null;
    PreparedStatement preparedStatement2 = null;
    try {
        //获取连接
        connection = JDBCUtils.getConnection();
        //开启事务
        connection.setAutoCommit(false);
        //定义sql语句
        String sql1 = "update user_table set balance = balance - ? where user = ?";
        String sql2 = "update user_table set balance = balance + ? where user = ?";
        //创建PreparedStatement的对象
        preparedStatement1 = connection.prepareStatement(sql1);
        preparedStatement2 = connection.prepareStatement(sql2);
        //给?赋值
        preparedStatement1.setDouble(1, 500);
        preparedStatement1.setString(2, "AA");
        preparedStatement2.setDouble(1, 500);
        preparedStatement2.setString(2, "BB");
        //执行sql语句
        preparedStatement1.executeUpdate();
        //手动的抛出异常,测试回滚事务,
        int i= 3 / 0; //产生异常ArithmeticException,跳转到catch结构中
        preparedStatement2.executeUpdate();
        //如果顺利执行完两条sql语句,则提交事务
        connection.commit();
        System.out.println("事务提交成功!");
    } catch (Exception e) { //保证出现任何异常都回滚,将异常类改为最大
        if (connection != null) {
            try {
                connection.rollback(); //抛出异常
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
        e.printStackTrace();
        System.out.println("事务回滚!");
    } finally {
        //未关闭连接时还可能执行其他的sql语句,故在执行close方法之前应当恢复自动提交功能
        connection.setAutoCommit(true);
        //两个PreparedStatement的对象均需关闭
        JDBCUtils.close(preparedStatement1, connection);
        if (preparedStatement2 != null) {
            try {
                preparedStatement2.close();
            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
        }
    }
}


七、数据库连接池

  1. 概念:是一个存放数据库连接的容器,容器被创建之后,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器
  2. 好处:节约资源、用户访问高效
  3. 实现:使用javax.sql包下的DataSource接口,DataSource 接口由驱动程序供应商实现
    DataSource中的方法:
    *获取连接:Connection getConnection();
    *归还连接:Connection中的close()方法:如果连接对象Connection是从连接池中获取 的,那么调用此方法则不会再关闭连接,而是将连接放回数据库连接池中
  4. C3P0数据库连接池技术:
    使用步骤:1. 创建libs文件夹导入三个jar包,文件夹右击add as library 在这里插入图片描述
    2. 定义配置文件c3p0-config.xml并放在src目录下,获取连接时会自动的被读取,
    在配置文件中更改url等参数
    3. 创建数据库连接池对象: new ComboPooledDataSource();
    4. 调用对象名.getConnection()获取连接(需要几个连接数就调用几次)
    注意:在配置文件中可以定义new ComboPooledDataSource();时指定不同的参数, 从而调用不同的连接池对象,也可定义最大连接数、初始个数等参数

代码演示11:演示C3P0数据库连接池的使用

public class C3P0Demo1 {
    public static void main(String[] args) throws SQLException {
        //创建数据库连接池对象
        ComboPooledDataSource dataSource = new ComboPooledDataSource();
        //获取连接(使用的是同一个池子)
        Connection connection = dataSource.getConnection();
        Connection connection1 = dataSource.getConnection();
        //之后的代码按照之前的步骤写
        String sql = "insert into goods values(?, ?)";
        String sql1 = "insert into goods values(?, ?)";
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        PreparedStatement preparedStatement1 = connection.prepareStatement(sql1);
        preparedStatement.setInt(1, 1);
        preparedStatement.setString(2,"高奇泽");
        preparedStatement1.setInt(1, 2);
        preparedStatement1.setString(2, "周杰伦");
        preparedStatement.executeUpdate();
        preparedStatement1.executeUpdate();
    }
}

  1. DBCP数据库连接池技术:
    使用步骤:1. 像之前一样的方式导入两个jar包
    在这里插入图片描述
    2. 创建配置文件dbcp.properties,其中定义:
    在这里插入图片描述
    3. 调用DataSource source = BasicDataSourceFactory.createDataSource(Properties pros);
    4. 调用Connection xxx = source.getConnection();获取连接(需要几个连接数就调用几次)

代码演示12:演示DBCP数据库连接池的使用

public static void main(String[] args) throws Exception {
    Properties pros = new Properties();
    FileInputStream is =
            new FileInputStream(new File("dbcp.properties"));
    pros.load(is);
    DataSource source = BasicDataSourceFactory.createDataSource(pros);
    Connection conn = source.getConnection();
}

  1. Druid数据库连接池技术(由阿里巴巴提供):
    1. 导入jar包
    2. 定义配置文件druid.properties,其中的内容是:
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=root
#初始化连接数量
initialSize=5
#最大连接数
maxActive=10
#最大等待超时时间
maxWait=3000

 3. 调用DataSource source = DruidDataSourceFactory.createDataSource(Properties pro);
 4. 调用Connection xxx = source.getConnection();获取连接(需要几个连接数就调用几次)

代码演示13:演示Druid数据库连接池的使用

Properties pro=new Properties();
InputStream is = 
        方法所在类.class.getClassLoader().getResourceAsStream("druid.properties");
pro.load(is);
DataSource source = DruidDataSourceFactory.createDataSource(pro);
Connection conn=source.getConnection();

代码演示14:使用Druid数据库连接池修改JDBCUtils工具类

public class JDBCUtils {
    //1.定义成员变量DataSource
    private static DataSource ds;
    static {
        try {
            //1.加载配置文件
            Properties pro=new Properties();
        pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
            //2.获取DataSource
            ds= DruidDataSourceFactory.createDataSource(pro);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    /**
     * 获取连接
     */
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }
/*
   获取连接池对象
     */
   public static DataSource getDataSource(){
      return ds;
  }
} ....其余关闭操作与之前一致

八、Apache - DBUtils的使用
需要导包
DBUtils是 Apache 组织提供的一个开源 JDBC工具类库,封装了针对于数据库的增删改查操作
在这里插入图片描述
代码演示15:DBUtils的使用

	//测试插入
	@Test
	public void testInsert() {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "insert into customers(name,email,birth)values(?,?,?)";
			int insertCount = 
					runner.update(conn, sql, "蔡徐坤","caixukun@126.com","1997-09-08");
			System.out.println("添加了" + insertCount + "条记录");
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);
		}
	}
				JDBCUtils.closeResource(conn, null);
		}
	}

	//测试查询
	/*
	 * BeanHander:是ResultSetHandler接口的实现类,用于封装表中的一条记录
	 */
	@Test
	public void testQuery1(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "select id,name,email,birth from customers where id = ?";
			BeanHandler<Customer> handler = new BeanHandler<>(Customer.class);
			Customer customer = runner.query(conn, sql, handler, 23);
			System.out.println(customer);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);
		}	
	}

	/*
	 * BeanListHandler:是ResultSetHandler接口的实现类,用于封装表中的多条记录构成的集合
	 */
	@Test
	public void testQuery2() {
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = 
					"select id,name,email,birth from customers where id < ?";	
			BeanListHandler<Customer>  handler =
					new BeanListHandler<>(Customer.class);
			List<Customer> list = runner.query(conn, sql, handler, 23);
			list.forEach(System.out::println);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{	
			JDBCUtils.closeResource(conn, null);
		}	
	}

	/*
	 * MapHander:是ResultSetHandler接口的实现类,对应表中的一条记录
	 * 将字段及相应字段的值作为map中的key和value
	 */
	@Test
	public void testQuery3(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "select id,name,email,birth from customers where id = ?";
			MapHandler handler = new MapHandler();
			Map<String, Object> map = runner.query(conn, sql, handler, 23);
			System.out.println(map);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);	
		}	
	}
	
	/*
	 * MapListHander:是ResultSetHandler接口的实现类,对应表中的多条记录。
	 * 将字段及相应字段的值作为map中的key和value。将这些map添加到List中
	 */
	@Test
	public void testQuery4(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "select id,name,email,birth from customers where id < ?";
			MapListHandler handler = new MapListHandler();
			List<Map<String, Object>> list = runner.query(conn, sql, handler, 23);
			list.forEach(System.out::println);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);	
		}
	}
	
	/*
	 * ScalarHandler:用于查询特殊值
	 */
	@Test
	public void testQuery5(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "select count(*) from customers";
			ScalarHandler handler = new ScalarHandler();
			Long count = (Long) runner.query(conn, sql, handler);
			System.out.println(count);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);	
		}	
	}
				e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);	
		}	
	}
	
	@Test
	public void testQuery6(){
		Connection conn = null;
		try {
			QueryRunner runner = new QueryRunner();
			conn = JDBCUtils.getConnection3();
			String sql = "select max(birth) from customers";
			ScalarHandler handler = new ScalarHandler();
			Date maxBirth = (Date) runner.query(conn, sql, handler);
			System.out.println(maxBirth);
		} catch (SQLException e) {
			e.printStackTrace();
		}finally{
			JDBCUtils.closeResource(conn, null);			
		}		
	}	
}

大家觉的不错的话,点个赞再走呗~~~

标签:总结,JDBC,String,这篇,try,connection,sql,null,public
来源: https://blog.csdn.net/qq_42880463/article/details/107019103

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有