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

时间:2020-6-30 作者:admin


JDBC基础总结

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

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


文章中会用到的数据库的表
1.customers表JDBC超详细基本使用总结,看这篇就够了!!!
2.order表
JDBC超详细基本使用总结,看这篇就够了!!!
3.user表
JDBC超详细基本使用总结,看这篇就够了!!!
4.user_table表
JDBC超详细基本使用总结,看这篇就够了!!!
5.boys表
JDBC超详细基本使用总结,看这篇就够了!!!
一、JDBC
概述

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

三、导入驱动jar包
Eclipse中:
1.去官网下载jar包
JDBC超详细基本使用总结,看这篇就够了!!!
2. 将上述jar包拷贝到java工程的一个目录中,习惯右键新建一个lib文件夹,将jar包导入
JDBC超详细基本使用总结,看这篇就够了!!!
3. 点击
JDBC超详细基本使用总结,看这篇就够了!!!
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类中有静态代码块:
JDBC超详细基本使用总结,看这篇就够了!!!
故加载运行时类Class.forName(“com.mysql.jdbc.Driver”);自动执行静态代码块完成注册驱动
注:mysql5之后的驱动jar包可省略注册驱动的步骤(不建议省略)

JDBC超详细基本使用总结,看这篇就够了!!!
即此文件自动的注册驱动jar包
2. 获取数据库连接:建立到给定数据库 URL 的连接
static Connection getConnection(String url, String user, String password)
参数: url:指定连接的路径
语法:
JDBC超详细基本使用总结,看这篇就够了!!!
其中协议与子协议是固定写法
子名称是 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接口:结果集对象(可认为是查询出的一张表)
有一个游标指向第一行的上一行,需要调用方法使游标下移,再调用方法获取指向行中某一列的值,注意:无法获取整行的值,只可以获取行中某一列的值
JDBC超详细基本使用总结,看这篇就够了!!!

  1. boolean next() 游标向下移动一行,如果有下一行可以被指向,返回true,若无,返回false
  2. xxx getXxx(参数) 获取当前行中由参数所指定列的值;注:Xxx代表数据类型
    参数:1. int:代表列的编号,从1开始
    2. String:代表列的名称
    java和sql中对应类型的转换:
    JDBC超详细基本使用总结,看这篇就够了!!!
    代码演示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类型对应的大小如下:
JDBC超详细基本使用总结,看这篇就够了!!!
代码演示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 JDBC超详细基本使用总结,看这篇就够了!!!
    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包
    JDBC超详细基本使用总结,看这篇就够了!!!
    2. 创建配置文件dbcp.properties,其中定义:
    JDBC超详细基本使用总结,看这篇就够了!!!
    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工具类库,封装了针对于数据库的增删改查操作
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);			
		}		
	}	
}

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

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:87074139@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。