Hey, Nice to meet You. 

必有过人之节.人情有所不能忍者,匹夫见辱,拔剑而起,挺身而斗,此不足为勇也,天下有大勇者,猝然临之而不惊,无故加之而不怒.此其所挟持者甚大,而其志甚远也.          ☆☆☆所谓豪杰之士,

JDBC的使用详解

1、JDBC的介绍

JDBC的全称是Java Data Base Connectivity(Java数据库连接)。是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问(例如MySQL,Oracle等),它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序,JDBC实现了所有这些面向标准的目标并且具有简单、严格类型定义且高性能实现的接口。JDBC给数据库的连接搭建了桥梁,然后再根据不同的数据库厂商实现JDBC接口的驱动,就可以轻松的连接各种关系型数据库了。

注:JDBC是Java操作数据库的唯一方式,所以说JDBC非常的重要,尽管我们后面会学习框架,但是这是框架的底层必定都封装了JDBC的代码。

image

上面的图片是各种不同类型的数据库都有相应的实现,本文中的代码都是使用MySQL数据库实现的。所以我们需要MySQL的驱动,驱动下载网址:https://repo1.maven.org/maven2/mysql/mysql-connector-java/ (我选的是mysql-connector-java-5.1.48.jar)

2、JDBC常用接口

①、Driver接口

Driver接口由数据库厂家提供,作为java开发人员,只需要使用Driver接口就可以了。在编程中要连接数据库,必须先装载特定厂商的数据库驱动程序,不同的数据库有不同的装载方法。如:

  • 装载MySql驱动:Class.forName("com.mysql.jdbc.Driver");
  • 装载Oracle驱动:Class.forName("oracle.jdbc.driver.OracleDriver");

②、Connection接口

Connection与特定数据库的连接(会话),在连接上下文中执行sql语句并返回结果。DriverManager.getConnection(url, user, password)方法建立在JDBC URL中定义的数据库Connection连接上。

  • 连接MySql数据库:Connection conn = DriverManager.getConnection("jdbc:mysql://host:port/database", "user", "password");
  • 连接Oracle数据库:Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@host:port:database", "user", "password");
  • 连接SqlServer数据库:Connection conn = DriverManager.getConnection("jdbc:microsoft:sqlserver://host:port; DatabaseName=database", "user", "password");

Connection中常用的方法:

  • createStatement():创建向数据库发送sql的statement对象。
  • prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
  • prepareCall(sql):创建执行存储过程的callableStatement对象。
  • setAutoCommit(boolean autoCommit):设置事务是否自动提交。
  • commit() :在链接上提交事务。
  • rollback() :在此链接上回滚事务。

③、Statement接口

Statement用于执行静态SQL语句并返回它所生成结果的对象。Statement类有三种:

  • Statement:由createStatement创建,用于发送简单的SQL语句(不带参数)。
  • PreparedStatement :继承自Statement接口,由preparedStatement创建,用于发送含有一个或多个参数的SQL语句。PreparedStatement对象比Statement对象的效率更高,并且可以防止SQL注入,所以我们一般都使用PreparedStatement。
  • CallableStatement:继承自PreparedStatement接口,由方法prepareCall创建,用于调用存储过程。

Statement中常用的方法:

  • execute(String sql):运行语句,返回是否有结果集
  • executeQuery(String sql):运行select语句,返回ResultSet结果集。
  • executeUpdate(String sql):运行insert/update/delete操作,返回更新的行数。
  • addBatch(String sql) :把多条sql语句放到一个批处理中。
  • executeBatch():向数据库发送一批sql语句执行。

④、ResultSet接口

esultSet提供检索不同类型字段的方法,常用的有:

  • getString(int index)、getString(String columnName):获得在数据库里是varchar、char等类型的数据对象。
  • getFloat(int index)、getFloat(String columnName):获得在数据库里是Float类型的数据对象。
  • getDate(int index)、getDate(String columnName):获得在数据库里是Date类型的数据。
  • getBoolean(int index)、getBoolean(String columnName):获得在数据库里是Boolean类型的数据。
  • getObject(int index)、getObject(String columnName):获取在数据库里任意类型的数据。

ResultSet还提供了对结果集进行滚动的方法:

  • next():移动到下一行
  • Previous():移动到前一行
  • absolute(int row):移动到指定行
  • beforeFirst():移动resultSet的最前面。
  • afterLast() :移动到resultSet的最后面。

3、JDBC创建步骤

JDBC创建步骤可以分为如下:导包—>加载驱动—>创建连接—>执行sql—>返回结果—>关闭连接


①、首先需要导入对应数据库的驱动包。

要访问MySQL时间就必须要用到MySQL驱动包(前面已经给出了链接),下载然后导入即可。本例用的是IDEA导入这个jar包。

导包步骤: 点击当前项目左上角File—>Project Structur—>Modules—>点击右边+号—>Jars Or directories

image

②、加载驱动

驱动的加载我们一般使用反射Class.forName(“com.mysql.jdbc.Driver”)来加载Driver这个类(因为它只会创建一次)。也可以使用 DriverManager.registerDriver(new Driver()) 来加载,但这种方式会new两个Driver,从而造成资源浪费,所以不推荐使用这种方式。

try {
    Class.forName("com.mysql.jdbc.Driver");

} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

注意:Class.forName是需要捕获ClassNotFoundException的。

③、建立连接

建立连接需要我们提供三个参数,分别是URLUSERPASSWORD

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

Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?characterEncoding=UTF-8","root","root");

它们各自的含义依次如下:

  • 协议:jdbc
  • 子协议:mysql
  • 数据库服务端的IP地址:localhost或127.0.0.1
  • 数据库端口:3306
  • 连接的数据库名称:mydb
  • 编码格式:?characterEncoding=UTF-8
  • 数据库用户名:root
  • 数据库密码:root (数据库密码是你安装时设置的密码)
try {
    Class.forName("com.mysql.jdbc.Driver");
    Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?characterEncoding=UTF-8", "root", "root");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (SQLException e) {
    e.printStackTrace();
}

Connection的getConnection方法也需要捕获SQLException异常。

④、执行SQL语句(Statement和PreparedStatement)

  • 使用Statement执行语句

Statement对象的获取可以使用createStatement()方法。当Statement对象创建之后,就可以执行SQL语句,完成对数据库的增删改查操作。

  • 对于查询类的SQL语句使用:executeQuery(sql),sql是一个字符串sql语句,返回结果是一个结果集
  • 对于更新类(插入、删除、更新)的语句使用:executeUpdate(sql),sql是一个字符串sql语句,返回结果是一个整数(受影响的行数)
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?characterEncoding=UTF-8", "root", "root");
Statement statement = con.createStatement();
//添加数据
String insert_sql="insert into t_user(username,password) values ('张三',123456)";
int count = statement.executeUpdate(insert_sql);
System.out.println("受影响行数:"+count);

在Statement中是可以使用字符串拼接的方式,该方式存在句法复杂,容易犯错等缺点,字符串拼接方式的SQL语句是非常繁琐的,中间有很多的单引号和双引号的混用,极易出错。

而且使用Statement有一个极大的缺点,会导致SQL注入。当SQL语句的 where 条件后面带有 or 1=1 的条件时,数据库会认为是true可以执行的,所以外界可以对数据库进行删、改操作,这样的数据库如同虚设。

所以一般不推荐使用Statement(如果是处理批量数据则推荐使用它),并且它在实际过程中使用的非常的少。而是使用后面的PreparedStatement。

Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?characterEncoding=UTF-8", "root", "root");
Statement statement = con.createStatement();
//删除数据
String id="1 or 1=1";
String delete_sql="delete from t_user where id="+id;
int count = statement.executeUpdate(delete_sql);
System.out.println("受影响行数:"+count);

上面的例子典型的SQL注入,这样会将数据库中的所有数据全部删除。

  • 使用PreparedStatement执行语句

PreparedStatement继承自Statement接口,它的实例对象可以通过调用preparedStatement(sql)方法来获取,注意它是直接传入了一条SQL语句。与Statement不同的是PrepareStatement中的SQL使用了占位符(也可以执行没有占位符的SQL语句)。“?”在SQL中就起到占位符的作用。这种方式除了避免了Statement拼接字符串的繁琐之外,还能够提高性能。每次SQL语句都是一样的,Java类就不会再次编译,这样能够显著提高性能。

然后再逐一给占位符填入数据,填入数据使用PreparedStatement实例的setXXX(int parameterIndex, String x)方法,其中第一个参数时索引位置,第二个是传入数据。

Connection con=null;
PreparedStatement ps=null;
try {
    Class.forName("com.mysql.jdbc.Driver");
    con = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?characterEncoding=UTF-8", "root", "root");
    String sql="insert into t_user(username,password) values (?,?)";
    ps=con.prepareStatement(sql);
    ps.setString(1,"李四");
    ps.setString(2,"123456");
    int count = ps.executeUpdate();
    System.out.println("受影响行数:"+count);
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (SQLException e) {
    e.printStackTrace();
}

注意PreparedStatement中的索引是从 1开始的,而不是从0。

⑤、返回结果

当执行了查询操作后,会返回一个结果集,返回结果是一个结果集,它有一个光标指向结果的每一行,最开始它不指向结果,第一次执行next()后,它指向第一行结果,继续执行next(),他会继续指向下一行。next的返回结果是布尔值,它可以用来判断是否有下一行。ResultSet中常用的方法在前面第二点已经给出来了,可自行划上去瞄一眼。

  • 而对于每一行结果,可以使用getXXX方法(XXX代表某一基本数据类型)来获取某一列的结果,getXXX方法的参数可以为字段名,也可以为索引号(从1开始)
Connection con=null;
PreparedStatement ps=null;
ResultSet rs=null;
try {
    Class.forName("com.mysql.jdbc.Driver");
    con = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb?characterEncoding=UTF-8", "root", "root");
    String sql="select * from t_user where username like ?";//查询SQL
    ps= con.prepareStatement(sql);
    ps.setString(1,"张"+"%");//查询条件
    rs = ps.executeQuery();//执行查询操作,并返回数据
    while (rs.next()){
        int id=rs.getInt(1);//用索引
        String username=rs.getString("username");//用字段名
        String password=rs.getString("password");
        System.out.print("编号:"+id+";");
        System.out.print("姓名:"+username+";");
        System.out.print("密码:"+password+";");
        System.out.println();
    }
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (SQLException e) {
    e.printStackTrace();
}

⑥、关闭连接

当程序对数据库的操作完成后,切记一定要关闭连接,因为数据库连接非常的耗资源。顺序是后创建的先关闭,这些对象通常是ResultSet, Statement和Connection。

而且还要在关闭语句中加try catch已经以防止前面关闭出错,导致后面的关闭不了。

}finally {
    if (rs!=null){
        try {
            rs.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (ps != null) {
        try {
            ps.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    if (con != null) {
        try {
            con.close();
        } catch (SQLException e) {
 
 
        }
    }
}

注意:为确保资源释放代码能运行,资源释放代码最好放在finally语句中。

⑦、JDBC操作数据库完整代码

public class JDBCTest {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            //1、加载驱动
            Class.forName("com.mysql.jdbc.Driver");
            String url = "jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8";
            String username = "root";
            String password = "root";
            //2、创建链接
            con = DriverManager.getConnection(url, username, password);
            String sql = "select * from t_user where username like ?";//查询SQL
            //3、预编译处理,用来执行SQL
            ps = con.prepareStatement(sql);
            ps.setString(1, "张" + "%");//查询条件
            //4、执行SQL,并且获取结果集
            rs = ps.executeQuery();
            // 5、遍历 数据
            while (rs.next()) {
                int id = rs.getInt(1);//用索引
                String name = rs.getString("username");//用字段名
                String pwd = rs.getString("password");
                System.out.print("编号:" + id + ";");
                System.out.print("姓名:" + name + ";");
                System.out.print("密码:" + pwd + ";");
                System.out.println();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
            if (con != null) {
                try {
                    con.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

4、自定义JDBCUtil工具

前面Jdbc的基本使用使用已经讲得差不多了,但如果我们经常要创建连接,关闭连接的话,那么每次都要写重复的代码,显然这样非常的麻烦,为了避免这种情况,编写了一个JdbcUtil工具类,如下。

db.properties文件,用于放置驱动名称,连接数据库地址,数据库账号和密码。(db.properties要放在src或者默认会加载的目录下,不要放在包下面了)

#MySQL连接配置
mysqlDriver=com.mysql.jdbc.Driver
mysqlURL=jdbc:mysql://localhost:3306/mydb?characterEncoding=utf8&useSSL=false
mysqlUser=root
mysqlPwd=root

创建JdbcUtil工具类,代码如下:

public class JdbcUtil {

    static Properties pros = null;  //读取和处理资源文件中的信息

    static { //加载JdbcUtil类的时候调用db.properties文件
        pros = new Properties();
        try {
            pros.load(JdbcUtil.class.getClassLoader().getResourceAsStream("db.properties"));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //创建连接
    public static Connection getConnection() {
        try {
            Class.forName(pros.getProperty("mysqlDriver"));
            return DriverManager.getConnection(
                    //获取properties文件中对应key的value
                    pros.getProperty("mysqlURL"),
                    pros.getProperty("mysqlUser"),
                    pros.getProperty("mysqlPwd"));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    //关闭资源(如果没有传入null即可)
    public static void close(Connection con, PreparedStatement ps, ResultSet rs) {
        //ResultSet关闭
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //Statement关闭
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //Connection关闭
        if (con != null) {
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

JdbcUtil的简单示例:

public static void main(String[] args) {
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        //调用JdbcUtil来创建连接
        con = JdbcUtil.getConnection();
        String sql = "select * from t_user";
        try {
            ps = con.prepareStatement(sql);
            rs = ps.executeQuery();
            while (rs.next()) {
                System.out.print("编号:" + rs.getInt(1) + ";");
                System.out.print("姓名:" + rs.getString(2) + ";");
                System.out.print("密码:" + rs.getString(3) + ";");
                System.out.println();
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(con, ps, rs);
        }
    }

5、JDBC的事物(重要)

5.1、事物的基本概念

事务是若干个SQL语句构成的一个操作序列,这些操作表示一个完整的功能,并且需要保证功能的完整性,因此要求在该事务中要求所有的sql要么都执行,要么都不执行,是一个不可分割的整体单位。

5.2、事务的四个基本要素(ACID)

  • 原子性(Atomicity):事务是一个不可分割的整体,所有操作要么全做,要么全不做;只要事务中有一个操作出错,回滚到事务开始前的状态的话,那么之前已经执行的所有操作都是无效的,都应该回滚到开始前的状态。
  • 一致性(Consistency):事务开始前和结束后,数据库的完整性约束没有被破坏 。比如A向B转账,不可能A扣了钱,B却没收到。
  • 隔离性(Isolation):同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱结束前,B不能向这张卡转账。
  • 持久性(Durability):事务一旦被提交后,事务对数据库的所有更新将被永远保存到数据库,不能回滚。

5.3、事务并发产生的问题

  • 脏读:事务A读取了事务B更新并且未提交的数据,然后B回滚操作,那么A读取到的数据是脏数据
  • 不可重复读:事务 A 多次读取同一数据,事务 B 在事务A多次读取的过程中,对数据作了更新并提交,导致事务A多次读取同一数据时,结果不一致。
  • 幻读:事务A从一个表中读取了一个字段,然后B在该表中插入/删除了一些新的行。 之后, 如果 A 再次读取同一个表, 就会多/少几行,就好像发生了幻觉一样,这就叫幻读。

5.4、事务的隔离级别

由于数据库事物可以并发执行,而并发操作可能会带来数据的不一致性,包括脏读、不可重复读、幻读等。数据库为了避免数据不一致问题提供了隔离级别来让我们有针对性地选择事务的隔离级别。

SQL标准定义了4种隔离级别(从低到高),分别对应可能出现的数据不一致的情况:

事务隔离级别 脏读 不可重复读 幻读
读未提交(read-uncommitted)
不可重复读(read-committed)
可重复读(repeatable-read)
串行化(serializable)

4种隔离级别的描述:

  • 读未提交(read-uncommitted):允许A事务读取其他事务未提交和已提交的数据
  • 不可重复读(read-committed):只允许A事务读取其他事务已提交的数据
  • 可重复读(repeatable-read):确保事务可以多次从一个字段中读取相同的值。在这个事务持续期间,禁止其他事务对这个字段进行更新;注意:mysql中使用了MVCC多版本控制技术,在这个级别也可以避免幻读。
  • 串行化(serializable):确保事务可以从一个表中读取相同的行,相同的记录。在这个事务持续期间,禁止其他事务对该表执行插入、更新、删除操作(效率非常低,基本不用)

  • 查看当前mysql连接的隔离级别:
select @@tx_isolation;
  • 查看全局的隔离级别:
select @@global.tx_isolation;
  • 设置当前 mysql连接的隔离级别:
set tx_isolation ='read-uncommitted';
set tx_isolation ='read-committed';
set tx_isolation ='repeatable-read';

更详细的事物隔离级别:理解事务的4种隔离级别

5.5、JDBC事务原理

JDBC中的事物分为两种: 自动事务和手动事务。注意这两者有一定的区别需要记住。

①、自动事务(默认)con.setAutoCommit(true)

在自动事务的情况下,只要SQL语句是正确的,执行的过程中没有发生异常、错误,就会提交到是数据库中,一般情况下没有指定的话就是默认为自动事务,实际上没有设定过的数据库就是自动事务,也就是说我们平时的数据库操作都是自动事务,所以自动事务会出现数据残留现象。

②、手动事务con.setAutoCommit(false)

在手动事务的情况下,需要自己调用提交或回滚来接结束事务,不然事务处理不会结束,手动事务有自定义的好处,而且能够自己判断语句的操作结果是否是自己想要的,如果不是自己想要的就可以进行回滚,是自己想要的操作结果才提交,自动事务则只要语句没出错都会进行提交。在大部分情况下,使用手动事务要多一些,因为使用自动事务的话语句没出错就自动把操作结果提交了,当SQL语句里的值写错了,或者操作结果不是正确的,就没办法进行回滚了,这些情况下SQL语句不会报错。


首先我们来看一下自动提交事物的例子,我们把这两次添加数据看成是一次事务:

public class Demo1 {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement ps = null;
        try {
            con = JdbcUtil.getConnection();
            //默认自动提交事务,这段可以省略
            con.setAutoCommit(true);

            ps = con.prepareStatement("insert into t_user(username,password) values(?,?)");
            ps.setString(1, "张三");
            ps.setString(2, "123456");
            ps.executeUpdate();
            System.out.println("添加第一条...");

            //这里多添加一个占位符用来报错
            ps = con.prepareStatement("insert into t_user(username,password) values(?,?,?)");
            ps.setString(1, "李四");
            ps.setString(2, "654321");
            ps.executeUpdate();
            System.out.println("添加第二条...");
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(con, ps, null);
        }
    }
}

自动提交事物的代码运行完成之后的数据库如下:

image

发现第一条添加成功了,而第二因为出错没有添加成功,所以自动提交事物会导致正确的SQL语句依然会执行,从而造成数据的残留。(不推荐使用)

注意:mysql中默认情况下,一个sql独占一个事务,且自动提交,从上面就可以看得出来


手动事物代码示例如下,注意它们的步骤:

  1. 获取Connection
  2. 设置autocommit(false)
  3. 需要执行的sql语句
  4. 提交事物,commit(提交事务的代码一定要在执行完指定的若干条SQL语句的后面)
  5. 出错,rollback
  6. 关闭连接

开启事务,提交事务和回滚事务都是使用的Connection对象。

public class Demo2 {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement ps = null;
        try {
            con = JdbcUtil.getConnection();
            //关闭自动事物,改为手动提交事务
            con.setAutoCommit(false);

            ps = con.prepareStatement("insert into t_user(username,password) values(?,?)");
            ps.setString(1, "张三");
            ps.setString(2, "123456");
            ps.executeUpdate();
            System.out.println("添加第一条...");

            //这里多添加一个占位符用来报错
            ps = con.prepareStatement("insert into t_user(username,password) values(?,?,?)");
            ps.setString(1, "李四");
            ps.setString(2, "654321");
            ps.executeUpdate();
            System.out.println("添加第二条...");

            con.commit();//提交事物
        } catch (SQLException e) {
            e.printStackTrace();
            try {
                con.rollback();//回滚
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        } finally {
            JdbcUtil.close(con, ps, null);
        }
    }
}

运行之后的数据库:

image

可以发现后面的SQL出现了异常,而前面的数据并没有加入到数据库中,说明整个事物都回滚了。

注意:如果开启了手动提交事物而不调用commit、rollback方法默认是会回滚的。

6、JDBC其他操作(了解)

1、批量处理(建议使用Statement)

/**
 * @author Administrator
 * @date 2020-02-15
 * @desc 批处理
 */
public class Demo3 {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement ps = null;
        //开始时间
        long start = System.currentTimeMillis();
        try {
            //创建连接
            con = JdbcUtil.getConnection();
            con.setAutoCommit(false);//关闭自动事物
            //预编译SQL
            ps = con.prepareStatement("insert into t_user(username,password) values (?,?)");
            for (int i = 1; i <= 10000; i++) { //添加一万条数据
                ps.setObject(1, "张三" + i);
                ps.setObject(2, i);
                ps.addBatch();
            }
            ps.executeBatch();
            con.commit();//提交事物
            long end = System.currentTimeMillis();
            System.out.println("耗时:" + (end - start) + " ms");

        } catch (SQLException e) {
            e.printStackTrace();
            try {
                con.rollback();//回滚
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        } finally {
            try {
                con.setAutoCommit(true);
            } catch (SQLException e) {
                e.printStackTrace();
            }
            JdbcUtil.close(con, ps, null);
        }
    }
}

2、获取自增主键

/**
 * @author Administrator
 * @date 2020-02-15
 * @desc 获取自动生成主键
 */
public class Demo4 {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            con = JdbcUtil.getConnection();
            String sql = "insert into t_user(username) values(?)";
            ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            ps.setString(1, "张三");//在插入username时数据库会自动生成id
            ps.executeUpdate();
            //获取自动生成的主键
            rs = ps.getGeneratedKeys();
            if (rs.next()) {
                System.out.println(rs.getInt(1));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(con, ps, rs);
        }
    }
}

为了测试接下来的数据,给数据库表添加了一些字段,如下:

CREATE TABLE `t_user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `regDate` date NULL DEFAULT NULL,
  `regDateTime` timestamp(0) NULL DEFAULT NULL,
  `info` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
  `image` longblob NULL,
  PRIMARY KEY (`id`) USING BTREE
)

3、处理日期时间(Date和TimeStamp)

/**
 * @author Administrator
 * @date 2020-02-15
 * @desc 处理日期时间
 */
public class Demo5 {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        try {
            con = JdbcUtil.getConnection();

            //插入
            ps = con.prepareStatement("insert into t_user(username,password,regDate,regDateTime)values(?,?,?,?)");
            ps.setString(1, "张三");
            ps.setString(2, "123456");
            ps.setDate(3, new Date(System.currentTimeMillis()));//日期
            ps.setTimestamp(4, new Timestamp(System.currentTimeMillis()));//日期和时间
            ps.executeUpdate();

            //查询
            ps = con.prepareStatement("select * from t_user");
            rs = ps.executeQuery();
            while (rs.next()) {
                System.out.println(rs.getString("username") + "--" +
                        rs.getString("password") + "--" +
                        rs.getDate("regDate") + "--" +
                        rs.getTimestamp("regDateTime"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(con, ps, rs);
        }
    }
}

4、存取文本文件(.txt文件)

CLOB(Character Large Object)用于存储大文本(mysql中无clob,存储大文本采用的是Text)

在MySQL中相关的类型:

  • TINYTEXT 最大长度为255(2^[8]-1)字符的TEXT列。
  • TEXT 最大长度为65535(2^[16]-1)字符的TEXT列。
  • MEDIUMTEXT 最大长度为16777215(2^[24]-1)字符的TEXT列。
  • LONGTEXT 最大长度为4GB(2^[32]-1)字符的TEXT列。
/**
 * @author Administrator
 * @date 2020-02-15
 * @desc CLOB处理文本
 */
public class Demo6 {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        Reader r = null;

        try {
            con = JdbcUtil.getConnection();

            //插入
            ps = con.prepareStatement("insert into t_user(username,info)values(?,?)");
            ps.setString(1, "张三");

            //将文本文件内容直接输入到数据库中
            ps.setClob(2, new FileReader(new File("D:/1.txt")));

            //将程序中的字符串输入到数据库中的CLOB字段中
            //ps.setClob(2, new BufferedReader(new InputStreamReader(new ByteArrayInputStream("我是张三".getBytes()))));

            ps.executeUpdate();
            System.out.println("插入成功...");

            //查询
            ps = con.prepareStatement("select * from t_user");
            rs = ps.executeQuery();
            System.out.println("查询成功...");
            while (rs.next()) {
                Clob c = rs.getClob("info");
                r = c.getCharacterStream();
                int temp = 0;
                while ((temp = r.read()) != -1) {
                    System.out.print((char) temp);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(con, ps, rs);
        }
    }
}

5、存取二进制文件(图片,视频),注:实际开发中永远不可能将图片或视频存储在数据库中。

BLOB(Binary large Object)用于大量存储二进制文件,在MySQL中相关的类型:

  • TINYBLOB 最大长度为255(2^[8]-1)字符的BLOB列。
  • BLOB 最大长度为65535(2^[16]-1)字符的BLOB列。
  • MEDIUMBLOB 最大长度为16777215(2^[24]-1)字符的BLOB列。
  • LONGBLOB 最大长度为4GB(2^[32]-1)字符的BLOB列。
/**
 * @author Administrator
 * @date 2020-02-15
 * @desc BLOB 处理二进制文件
 */
public class Demo7 {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        InputStream is = null;
        OutputStream os = null;

        try {
            con = JdbcUtil.getConnection();

            //插入
            ps = con.prepareStatement("insert into t_user(username,image)values(?,?)");
            ps.setString(1, "张三");
            ps.setBlob(2, new FileInputStream("C:/1.jpg"));
            ps.executeUpdate();
            System.out.println("插入成功...");

            //查询
            ps = con.prepareStatement("select * from t_user where id=?");
            rs = ps.executeQuery();
            System.out.println("查询成功...");
            while (rs.next()) {
                Blob b = rs.getBlob("image");
                is = b.getBinaryStream();
                os = new FileOutputStream("D:/2.jpg");
                int temp = 0;
                while ((temp = is.read()) != -1) {
                    os.write(temp);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(con, ps, rs);
        }
    }
}
posted @ 2020-04-30 18:12  唐浩荣  阅读(1655)  评论(0编辑  收藏  举报