JDBC

267 阅读8分钟

JDBC基础

1, 本文的内容

image.png jdbc使用mysql数据库大致步骤:
注册驱动-->获取连接-->使用(执行sql语句获取数据)-->关闭

根据这些过程分成了这些类:

  • Driver(用于获取连接,驱动类);
  • Connection(Driver获取到的连接对象类);
  • Statement(执行静态SQL语句并返回其生成的结果的对象,执行sql的类),还有一些子类;
  • ResultSet(结果集,用于存放查询到的多条数据);

2,获取连接(Connection)

  • 方法1:直接使用Driver获取连接
    Driver接口提供了获取连接的connect方法,只要传入数据库的url,账号密码即可,需要其他方法可以查看官方api文档
    Connection

    Driver driver = new Driver();
    String url="jdbc:mysql://localhost:3306/db123?characterEncoding=UTF-8&useSSL=false";
    Properties properties=new Properties();
    properties.setProperty("user","root");
    properties.setProperty("password","12345");
    Connection connection=driver.connect(url,properties)
    connection.close();
    
  • 方法2:通过DriverManger来管理驱动
    DriverManager提供了很多的方法,用来管理Driver相当方便,获取连接的方法进行了多次重载
    注册驱动获取连接即可
    示例

       String cl="com.mysql.jdbc.Driver";
       Class c=Class.forName(cl);
       DriverManager.registerDriver((com.mysql.jdbc.Driver)c.getConstructor().newInstance());
       String url="jdbc:mysql://localhost:3306/db123?characterEncoding=UTF-8&useSSL=false";
       String user="root";
       String password="asd456";
       Connection c=DriverManager.getConnction(url,user,password);
    
  • 可能会出现的问题,连接报错:

    • 1,Establishing SSL connection without server s identity verification is not recommended.According to MySQL 5.5.45+, 5.6.26+ and 5.7.6+ requirements SSL connection must be established by default if expl

      解决办法:在连接mysql的url中加上?useSSL=false或者?useUnicode=true&characterEncoding=UTF-8&useSSL=false,例如//jdbc:mysql://ip:端口/数据库?useUnicode=true&characterEncoding=UTF-8&useSSL=false

    • 2,user,password,拼写错误报错:Access denied for user 'root'@'localhost' (using password: NO)

    • 3,配置文件密码或者账号写错:Access denied for user 'root '@'localhost' (using password: YES)

    • 4,Access denied for user 'root '@'localhost' (using password: YES) 有可能是新版本mysql带来的url改变需要添加时区导致的错误

      解决办法:修改时区为serverTimezone=GMT==>serverTimezone=GMT%2B8,如果改为serverTimezone=UTC,插入时时间会变;UTC代表的是全球标准时间 ,但是我们使用的时间是北京时区也就是东八区,领先UTC八个小时。

3,执行语句的对象

封装了执行语句的方法,获取连接对象Connection后,通过其封装的方法获得

3.1,Statement

用于执行静态SQL语句并返回其生成的结果的对象,默认情况下,每个Statement对象只能同时打开一个ResultSet对象
statement中封装了很多方法,如果有需要可以查看官方api文档,如下是比较常用的方法

  • 1,boolean execute(String sql):执行sql语句,可以是update ,create,select,delete语句,任意类型

  • 2,ResultSet executeQuery(String sql):返回查找的ResultSet集合

使用流程:获取对象==>执行sql==>关闭
示例:

```
Statement statement=connection.createStatement();
statement.execute("drop table jdbctest01;");//sql语句可以是一个拼接的字符串,提高程序的灵活性
//关闭资源
statement.close();
connection.close;
```
3.2,PreparedStatement

表示预编译SQL语句的对象。SQL语句已预编译并存储在PreparedStatement对象中。 然后,可以使用此对象多次有效地执行此语句。
使用流程:传入包含占位符的sql并获取对象==>设置占位符的数据==>执行==>关闭

  • 1,优点:抛弃字符串拼接,使用?作为占位符。解决statament带来的sql注入问题。减少了编译次数,效率高

  • 2,获取连接后,通过连接对象获取preparestatement对象:prepareStatement(String sql):这里的sql是包含了占位符的sql语句

常用方法,其余有需要查看api文档

  • 1,setString(int parameterIndex, String x):给第parameterIndex个占位符附上String类型的x值,解决注入问题

  • 2,setInt(int parameterIndex, int x):给第parameterIndex个占位符附上Int类型的x值,解决注入问题*

  • 3,ResultSet executeQuery():返回查找的ResultSet集合,与statement的有区别,如果要填写sql语句需要完整的sql语句,具体看示例

  • 4,boolean execute():返回值提示操作成功与否,可以是update ,create,select,delete语句,任意类型,与statement的有区别,如果要填写sql语句需要完整的sql语句

  • 5,int executeUpdate():返回操作的行数,在该SQL语句PreparedStatement对象,它必须是一个SQL数据操纵语言(DML)语句,比如INSERT , UPDATE或DELETE ; 或者不返回任何内容的SQL语句,例如DDL语句。

示例:

```
//执行插入语句
String insertData="insert into adminTes02 values(?,?,?);";
PreparedStatement preparedStatement=connection.prepareStatement(insertData);
preparedStatement.setInt(1,1);
preparedStatement.setString(2,"jack");
preparedStatement.setString(3,"jack123");
preparedStatement.execute();//插入第一条
preparedStatement.close();
connection.close();
```
3.3,CallableStatement

用于执行SQL存储过程的接口。 JDBC API提供存储过程SQL转义语法,允许以标准方式为所有RDBMS调用存储过程
使用流程:获取对象==>设置输入参数,注册输出参数的类型(表明占位符位置,Types.包装类型)==>通过execute方法执行该过程==>处理数据(获得返回值,获取ResltSet集合)==>关闭资源连接
常用方法

  • 1,连接后获取对应存储过程对象:CallableStatement prepareCall(String sql)==说明:当存储过程有in 或 out 参数时,可以使用占位符号

  • 2,execute():执行对应对象的存储过程

  • 3,registerOutParameter(占位符位置,Types.数据类型):注册一个输出参数

  • 5,setString(int parameterIndex, String x):给第parameterIndex个占位符附上String类型的x值

  • 6,setInt(int parameterIndex, int x):给第parameterIndex个占位符附上Int类型的x值

  • 7,ResultSet getResultSet():获取输出结构的ResltSet

  • 8,int getInt(out占位位置):返回已经注册的返回参数值,int类型

  • 9,String getString(out占位符位置):返回已经注册的返回参数值,String类型

示例:

```
//调用该过程:s_ad(in Sid int,out Sname varchar(32) )String call="call s_ad(?,? )";
CallableStatement callableStatement=connection.prepareCall(call);//1,获取对象
callableStatement.setInt(1,2);//2,设置输入参数
callableStatement.registerOutParameter(2, Types.CHAR);//3,注册输出参数的类型
callableStatement.execute();//4,执行该过程
System.out.println(callableStatement.getString(2));//5,处理理数据//6,关闭资源
callableStatement.close();
connection.close();
```

4,结果集(ResultSet)

  • 1,resultSet接口接收查询结果

    • excuteQuery()方法返回一个ResultSet接口,运行类型为:com.mysql.jdbc.Jdbc42ResultSet
  • 2,用一个集合存放每一行的信息,需要时取出即可

  • 3,用一个集合存放每一行的信息的每一列的信息,按需取出即可

常用方法:get+数据类型(int culumnIndex):获取第几列的内容

  • boolean next():指针指向下一行,如果下一行为空既表示每一内容,则返回false * boolean previour():指针指向上一行,如果为首行,返回false *
  • getInt(int columnIndex [String columnString]):返回指针指向这行的特定列内容,以Int类型返回
  • getString(int culumnIndex [String columnString]):返回指针指向这行的特定内容,以String类型返回,会以机器默认的转码格式将byte转为String字符串,如果有需要,需要指定转码形式,用数据库中指定列的编码类型转出才不会乱码
  • getObject(int culumnIndex [String columnString]):以超类Object接收
  • close():关闭此Resultset对象,必须操作

使用流程:获取对象==>判断是否有下一行(必须操作)==>获取对象

示例

```
ResultSet resultSet=statement.executeQuery("select * from jdbctest01 ;");//取出对应行的内容:next():指向下一行,起始指向行名,如果不为空就返回true
while(resultSet.next()){    
     System.out.print(resultSet.getString(1)+" ");
     System.out.println(resultSet.getString(2));
}
resultSet.close();
statement.close();
connection.close();
```

5,批处理:addBatch()

当需要成批插入或者更新记录时,可以采用Java的批量更新机制, 这一机制允许多条语句一次性提交给数据库批量处理。通常情况下比单独提交处理更有效率
statement和preparedstatement都有这个批量处理的方法,执行时一次执行批量包中所有的语句
方法说明(具体看api文档):

  • addBatch():将当前语句添加到批量处理的包中
  • clearBatch():清空缓存包中的sql语句

示例

```
String sql = "insert into batch value(?,?)";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
for (int i = 0; i < 1000; i++) {
    preparedStatement.setInt(1, i);
    preparedStatement.setString(2, "batch" + i);
    //添加到批量处理的包中
    preparedStatement.addBatch(); 
    if (i % 1000 == 0) {
        preparedStatement.execute();
        //清空缓存包        
        preparedStatement.clearBatch();    
    }
}
```

6,制作工具包:jdbcUtils

  • 整个获取连接的流程基本一致,封装成工具,更加方便

  • url,驱动类型,user,password:加载jdbcUtiles时,加载到内存,而且这些数据可以被获取连接的所有类共享

    1,这些属性都应该是静态的

    2,这些属性,应该在第一次加载类时被初始化:有一个配置类,配置类载入,然后赋值

    3,应该把编译异常转换为运行异常,在运行时抛出

  • 提供静态获取连接的方法,提供静态的关闭资源的方法

  • 示例

        private static String user;
        private static String password;
        private static String Driver;
        private static String url;
        private jdbcUtil(){}
        static{//初始化配置
            try {
                Properties properties = new Properties();
                properties.load(new FileInputStream("src\Util\jdbcUtil.properties"));
                user=properties.getProperty("user");
                password=properties.getProperty("password");
                Driver=properties.getProperty("Driver");
                url=properties.getProperty("url");
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        public static Connection getConnection(){
            try {
                Class c=Class.forName(Driver);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            try{
            return DriverManager.getConnection(url,user,password);
            }catch(Exception e){
                throw new RuntimeException(e);
            }
        }
        public static void close(ResultSet resultSet, Statement statement,Connection connection) {
            try {
                if (resultSet != null) {
                    resultSet.close();
                }
                if (statement != null) {
                    statement.close();
                }
                connection.close();
            }catch (SQLException e){
                throw new RuntimeException(e);
            }
        }
    

7,事务

  • 1,事务仅支持innodb存储引擎,如果表的引擎不是innodb,回滚无效

  • 2,流程:

    • 关闭默认自动提交,既开启事务:使用连接类setAutoCommite方法设置为false:setAutoCommite(false),设置往后的语句需要手动提交,之前的语句不受影响
    • 设置回滚点:使用连接类的setSavePiont方法设置回滚点,回滚点要在事务内:SavePoint setSavePiont(String name)
    • 回滚:使用连接类的rollback方法回滚到特定点:rollback(SavePoint s)
    • 提交:commite();
  • Connection connection = jdbcUtil.getConnection();//只用工具获取连接
    connection.setAutoCommit(false);//设置为不自动提交事务
    String sql = "insert into acount values(?,?)";
    PreparedStatement preparedStatement = connection.prepareStatement(sql);
    Savepoint s;
    try {
        //执行两条语句
        preparedStatement.setString(1, "ekko");    
        preparedStatement.setInt(2, 1000);    
        preparedStatement.execute();
        s= connection.setSavepoint();// 设置回滚点:A
        preparedStatement.setString(1, "jack");    
        preparedStatement.setInt(2, 1000);
        preparedStatement.execute();
        //出现一个错误
        System.out.println(1/0);
    } catch (Exception e) {    
        try {
                connection.rollback(s);//回滚    
            }catch (SQLException e2){
                e2.printStackTrace();
            }
    } finally {    
        connection.commit();//提交
        jdbcUtil.close(null, preparedStatement,connection );}