1.Statement的操作数据库

1 加载驱动,	通过驱动连接数据库
	static{
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }


    }
2 创建连接 选择数据库的类型mysql 
  输入连接名,ip port 3306 选择数据库  用户名 root  密码 自己设置的密码
  //        创建连接
        try {
           Connection conn =  DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/s_t", "root", "1234");
        } catch (SQLException e) {
            e.printStackTrace();
        }
3 在当前连接下创建查询窗口
//        基于连接conn对象,创建查询窗口对象
        Statement state = conn.createStatement();
4 写sql语句 
String sql="insert into student(sno,sname,sage) values('0899903','abc',4)";
5 执行sql语句
//          执行语句的功能,在查询窗口上, 运行的方法在state中
            int i = state.executeUpdate(sql);
6 查看运行结果
//            查看结果
		System.out.println(i);
7 关闭查询窗口,关闭连接
finally {
//            防止关闭时出现空指针的报错,判断
                try {
                    if (state!=null){
                        state.close();
                    }
                    if(conn!=null){
                        conn.close();
                    }
                } catch (SQLException e) {
                    e.printStackTrace();
                }
}

优化上面封装的方法,已添加为例:
    添加方法缺点:
    1 我们每次添加的数据,每次调用方法前都要进行修改
    ----------》数据灵活,调用方法传入什么值,变量就是什么值---》数据方法的参数

    public static void addTest(String sno,String name,Integer sage){
    1 sql语句的书写:不可行
    向表中插入空数据,values(表中的列名),不会插入真正的数据
    String sql="insert into student(sno,sname,sage) values(sno,sname,sage)";
    
    2 sql语句的书写方式,sno认为是需要插入的字符串
      String sql="insert into student(sno,sname,sage) values('sno','sname',sage)"
   
   	3 sql语句: 字符串的拼接 把变量直接放到sql语句,但是没有sql语句对于字符串进行标识的单引号
    String sql="insert into student(sno,sname,sage) values("+sno+","+sname+","+sage+")";
    
    4 成功的语句
    String sql="insert into student(sno,sname,sage) values('"+sno+"','"+sname+"',"+sage+")";

 2.PreparedStatement

 public static void addEnd(String sql,Object... objs){
        Connection conn=null;
        PreparedStatement pstmt=null;
//        创建连接
        try {
            conn =  DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/s_t", "root", "1234");
//        基于连接conn对象,创建查询窗口对象
//            语句:sql语句中固定的部分进行了书写
//            例如 向student表中数据的插入,但是语句中不固定是要插入的数据值
//            不固定的数据值,用英文状态下的问号占位,要插入几个值,就写介个问号=====》问号,占位

            pstmt = conn.prepareStatement(sql);
//            运行语句前,要把sql中?占位的位置,进行赋值,sql语句完整
//            pstmt.set+数据类型  例如 sno赋值,sno字符串类型,setString
//            给问号进行赋值,?位置从1开始
          for(int i=0;i<objs.length;i++){
              pstmt.setObject(i+1,objs[i] );
          }
            int i = pstmt.executeUpdate();


//            查看结果
            System.out.println(i);


        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
//            防止关闭时出现空指针的报错,判断
            try {
                if (pstmt!=null){
                    pstmt.close();
                }
                if(conn!=null){
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }

    }

3.优化(增删改方法)

 conn =  DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/s_t", "root", "1234");
连接,修改不方便,===============》定义一次,多次使用========》变量
private static final String URL="jdbc:mysql://127.0.0.1:3306/s_t";
private static final String USER="root";
private static final String PWD="root";

public static Connection getConn(){
        Connection conn=null;
        try {
            conn =  DriverManager.getConnection(URL, USER, PWD);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
 public static int executeUpdate(String sql,Object... objs){
        Connection conn=getConn();
        PreparedStatement pstmt=null;
        int result=0;
        try {
            pstmt = conn.prepareStatement(sql);
            for(int i=0;i<objs.length;i++){
                pstmt.setObject(i+1,objs[i] );
            }
            result = pstmt.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            try {
                if (pstmt!=null){
                    pstmt.close();
                }
                if(conn!=null){
                    conn.close();
                }
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

3.1 java向数据库写数据,出现?

java向数据库写数据,出现?,不是编码集,从java向数据库传入时,
没有指定编码集,数据没有被识别

解决中文问题:
    private static final String URL="jdbc:mysql://127.0.0.1:3306/s_t?useUnicode=true&characterEncoding=utf8";

3.2 sql注入

使用Statement容易出现sql注入,如下Statement需要自己拼接sql语句,如果后面拼接or 1=1 查询条件恒成立,会查出所有数据,会造成数据安全问题

 public static void get1(String sno){
        List<Student> lists = new ArrayList<>();
        Connection conn = JdbcUtil.getConn();
        Statement state=null;
        ResultSet rs=null;
        try {
            state = conn.createStatement();
            String sql = "select * from student where sno = '"+sno+"' or 1=1";
            *************************
            语句问题:
            	数据不安全,======》sql注入
            ***************************
            rs = state.executeQuery(sql);
//            结果集的数据如何查看
            while (rs.next()){
//                每次拿到的数据都是一条数据 一条数据由多个列组成
//                一条数据如果想要进行获取,需要获取到当前这一条数据每个列上的值
//                rs.get+数据类型  例如:获取sno列,数据类型字符串,getString
                String sno1 = rs.getString("sno");
                String sname = rs.getString("sname");
                int sage = rs.getInt("sage");
                String ssex = rs.getString("ssex");
                String dept = rs.getString("dept");
                Student s = new Student();
                s.setDept(dept);
                s.setSage(sage);
                s.setSname(sname);
                s.setSno(sno1);
                s.setSsex(ssex);
                System.out.println(s);

                lists.add(s);


            }


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

        }


    }

 prepareStatement不需要手动进行拼接,不需要关注参数注入,使用站位即可,可以减少注入的风险,降低数据安全风险

使用preparedStatement
 public static void getPre(String sno){
        List<Student> lists = new ArrayList<>();
        Connection conn = JdbcUtil.getConn();
        PreparedStatement pstmt=null;
        ResultSet rs=null;
        try {
            String sql = "select * from student where sno =?";
            pstmt = conn.prepareStatement(sql);
            pstmt.setString(1,sno);
            *************************
            自己不用关注参数注入,使用方法进行的占位
            =============》如果非要加后面的条件,出现注入
            比较:
            	相比较而言:PreparedStatement 安全,在网络上,没有绝对的安全
			*********************
            rs = pstmt.executeQuery();
//            结果集的数据如何查看
            while (rs.next()){
//                每次拿到的数据都是一条数据 一条数据由多个列组成
//                一条数据如果想要进行获取,需要获取到当前这一条数据每个列上的值
//                rs.get+数据类型  例如:获取sno列,数据类型字符串,getString
                String sno1 = rs.getString("sno");
                String sname = rs.getString("sname");
                int sage = rs.getInt("sage");
                String ssex = rs.getString("ssex");
                String dept = rs.getString("dept");
                Student s = new Student();
                s.setDept(dept);
                s.setSage(sage);
                s.setSname(sname);
                s.setSno(sno1);
                s.setSsex(ssex);
                System.out.println(s);

                lists.add(s);
            }


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

4. prepareStatement操作数据库最终封装

1.需要定义一个接口,接口的目的是,我传给你一个ResultSet对象,你讲ResultSet对象的值,赋值给,一个实体对象,并且将该实体对象返回

import java.sql.ResultSet;

public interface RowMap<T> {
    /**
     * 传入ResultSet对象,返回一个对象
     * @param res
     * @return
     */
    T rowMapping(ResultSet res);
}

2.封装的增删改查的方法类 

import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class JdbcUtil {
    static {
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    static String URL = "jdbc:mysql://127.0.0.1:3306/s_t?useUnicode=true&characterEncoding=UTF-8";
    static String NAME = "root";
    static String PASSWORD = "123456";
    private static Connection conn = null;

    /**
     * 获取Connection连接
     * @return
     */
    public static Connection getConnection() {
        try {
            conn = DriverManager.getConnection(URL, NAME, PASSWORD);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    /**
     * 将sql的参数注入到sql中,返回PreparedStatement对象
     * @param sql
     * @param param
     * @return
     */
    public static PreparedStatement setJdbcData(String sql, Object[] param){
        Connection conn = getConnection();
        PreparedStatement pst = null;

        try {
            pst = conn.prepareStatement(sql);
            for (int i = 0; i < param.length; i++) {
                pst.setObject(i + 1, param[i]);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return pst;
    }

    /**
     * 增删改
     * @param sql sql
     * @param param 参数
     * @return
     */
    public static int update(String sql, Object... param) {
        int result = 0;
        PreparedStatement pst = setJdbcData(sql, param);
        try {
            result = pst.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            close(conn, pst);
        }
        return result;
    }

    /**
     * 查询sql信息,并且返回一个List对象集合
     * @param sql
     * @param rowMap 接口,目的:将result的信息赋值给实体对象,并且返回该实体对象
     * @param param sql中填写站位的参数
     * @param <T> 实体对象
     * @return
     */
    public  static <T> List<T> query(String sql, RowMap<T> rowMap, Object... param){

        PreparedStatement pst = setJdbcData(sql, param);
        ArrayList<T> list = new ArrayList<>();
        ResultSet res = null;
        try {
            res = pst.executeQuery();
            while (res.next()){
                /**
                 * 我们查询的数据,应该用对象存储,我们数据库一张表就是对应java一个实体类,因此我new一个实体类对象,将一条数据存在一个实体类对象中。
                 * 我们现在有个问题,每张表对应不同的实体类,我们传进来的sql不同,就是对应不同的表,因此对应不同的对象;
                 * rowMap.rowMapping的方法就是传入你一个result,将result的信息赋值给对象,并且返回该对象
                 */
                T t = rowMap.rowMapping(res);
                list.add(t);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            close(conn, pst, res);
        }
        return list;
    }

    /**
     * 关闭方法
     * @param conn
     * @param pst
     */
    public static void close(Connection conn, PreparedStatement pst) {
        try {
            if (pst != null) {
                pst.close();
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭方法
     * @param conn
     * @param pst
     * @param res
     */
    public static void close(Connection conn, PreparedStatement pst, ResultSet res) {
        try {
            if (res != null) {
                res.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        close(conn, pst);
    }
}

3.测试封装的方法

3.1 创建实体类(我们一张表就是一个实体类)目的是将查询的数据放到实体类中

public class Student {
    private String Sno;
    private String Sname;
    private String Ssex;
    private int Sage;
    private String Dept;

    public Student(String sno, String sname, String ssex, int sage, String dept) {
        Sno = sno;
        Sname = sname;
        Ssex = ssex;
        Sage = sage;
        Dept = dept;
    }

    public Student() {
    }

    public String getSno() {
        return Sno;
    }

    public void setSno(String sno) {
        Sno = sno;
    }

    public String getSname() {
        return Sname;
    }

    public void setSname(String sname) {
        Sname = sname;
    }

    public String getSsex() {
        return Ssex;
    }

    public void setSsex(String ssex) {
        Ssex = ssex;
    }

    public int getSage() {
        return Sage;
    }

    public void setSage(int sage) {
        Sage = sage;
    }

    public String getDept() {
        return Dept;
    }

    public void setDept(String dept) {
        Dept = dept;
    }

    @Override
    public String toString() {
        return "Student{" +
                "Sno='" + Sno + '\'' +
                ", Sname='" + Sname + '\'' +
                ", Ssex='" + Ssex + '\'' +
                ", Sage=" + Sage +
                ", Dept='" + Dept + '\'' +
                '}';
    }
}

 3.2 测试方法

import com.zxb.test16.mapper.Student;

import java.sql.SQLException;
import java.util.List;

public class test {
    public static void main(String[] args) {
        /**
         * 查询数据
         */
        String sql1 = "SELECT * FROM student WHERE dept in (?,?) GROUP BY dept";


        List<Student> list = JdbcUtil.query(sql1,(res)->{
            Student stu = new Student();
            try {
                stu.setSno(res.getString("Sno"));
                stu.setSname(res.getString("Sname"));
                stu.setSsex(res.getString("Ssex"));
                stu.setSage(res.getInt("Sage"));
            } catch (SQLException e) {
                e.printStackTrace();
            }
            return stu;

        }, "计算机系", "信息管理系");

        System.out.println(list);
        /**
         * 测试修改,返回影响行数
         */
        String sql2 = "update student set Sage=108  WHERE Sno=?";
        int num = JdbcUtil.update(sql2, "0831104");
        System.out.println(num);


    }
}

5. 比较查询statement、prepareStatement对象,查询操作数据库效率 

从下面实验中可以看出statement插入2000条数据,需要1729ms,而preparedStatement插入2000条数据,需要1580ms.可以看的出,preparedStatement效率较高与statement

import com.zxb.util.JdbcUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;

public class demo01 {
    public static void main(String[] args) {
        /**
         * 测试statement
         */
//        long start = System.currentTimeMillis();
//        statementTest();
//        System.out.println("statement总共耗费时间是:" +(System.currentTimeMillis() - start)); //statement总共耗费时间是:1729

        /**
         * 测试
         */

        long start = System.currentTimeMillis();
        preparedStatementTest();
        System.out.println("preparedStatement总共耗费时间是:" +(System.currentTimeMillis() - start)); //preparedStatement总共耗费时间是:1580
    }

    public static void statementTest() {
        /**
         * 使用statement,插入2000条数据
         */
        Statement statement = null;
        Connection conn = JdbcUtil.getConnection();
        try {
            statement = conn.createStatement();
            for (int i = 0; i < 2000; i++) {
                String sql = "insert into test(name,age) values('" + "张三" + i + "'," + i + ")";
                //批量创建,把所有创建的sql语句存到statement对象
                statement.addBatch(sql);
            }
            //批量执行有存在statement对象中的sql语句
            statement.executeBatch();
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {

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

    }

    public static void preparedStatementTest(){
        /**
         * 使用prepareStatement,插入2000条数据
         */
        Connection conn = JdbcUtil.getConnection();
        String sql = "insert into test(name,age) values(?,?)";
        PreparedStatement pst = null;
        try {
            pst = conn.prepareStatement(sql);
            for (int i = 0; i < 2000; i++) {
                pst.setString(1, "张三"+i);
                pst.setInt(2, i);
                //批量存储执行语言
                pst.addBatch();
            }
            //批量执行
            pst.executeBatch();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtil.close(conn, pst);
        }

    }
}

Statement 和 PreparedStatement比较:
两者关系:
    PreparedStatement 是Statement的子接口
    子接口拥有父接口的方法,可以进行方法的扩展
区别:
    1 数据的增删改,进行参数注入,Statement需要拼接,语句麻烦
      PreparedStatement 通过方法进行的参数注入,注入方式简单
    2 数据查询:
        Statement sql 注入情况
        PreparedStatement 预防sql注入
    3 效率问题:
        Statement 普通的查询窗口对象
        PreparedStatement 预处理的查询查询窗口
        Statement 普通的查询窗口对象        
            state = conn.createStatement();
            String sql = "select * from student where sno = '"+sno+"' or 1=1";
            rs = state.executeQuery(sql);
        运行sql语句是,进行sql语句的传入==========》编译
        
            PreparedStatement 预处理的查询查询窗口
            String sql = "select * from student where sno =?";
            pstmt = conn.prepareStatement(sql);========》提前编译(提前准备)
            pstmt.setString(1,sno);
            rs = pstmt.executeQuery();======》运行时不用在进行语句的编译,因此PreparedStatement运行效率快!

 PreparedStatement 与 statement

1. PreparedStatement 可以减少sql注入的安全问题;

2. PreparedStatement 执行效率也高于statement,由于PreparedStatement先会将sql传到conn中,进行预处理提前进行编译,而statement会在sql语句传入时候进行编译;

6. 事务操作 

jdbc中,自动提交默认开启
查看事务:
    自动提交关闭: 默认是true 默认开启
    在jdbc中设置了自动提交关闭,就会默认开启了一个事务
    conn.setAutoCommit(false);
    conn.commit();  提交事务

1.读未提交 

1.1 我们打开一个mysql客户端,设置事务的隔离级别

set tx_isolation="read-uncommitted"

1.2 查询数据,现在目前最新的数据 

SELECT * from student WHERE Sno= '0831104';

2. java代码在String a = null处,打断点,debug代码

import com.zxb.util.JdbcUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class demo02 {
    /**
     * JDBC操作事务
     */
    public static void main(String[] args) {
        Connection conn = JdbcUtil.getConnection();
        String sql = "update student set Sage=?  where Sno = ?";

        try {
            /**设置事务隔离级别
             * 读未提交 int TRANSACTION_READ_UNCOMMITTED = 1;
             * 读已提交 int TRANSACTION_READ_COMMITTED   = 2;
             * 不可重复读 int TRANSACTION_REPEATABLE_READ  = 4;
             * 串行化 int TRANSACTION_SERIALIZABLE     = 8;
             */
            conn.setTransactionIsolation(1);
            //关闭自动提交,自动开启事务
            conn.setAutoCommit(false);
            PreparedStatement pst = conn.prepareStatement(sql);
            pst.setInt(1, 20);
            pst.setString(2, "0831104");
            pst.executeUpdate();

            /**
             * 构造代码出现空指针异常
             */
            String a = null;
            String s = a.toUpperCase();
            conn.commit(); // 只要事务执行过程中不发生异常,执行手动提交

        } catch (Exception e) {
            e.printStackTrace();
            try {
                //代码发生异常 事务回滚
                conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }


    }
}

 3.此时mysql客户继续执行查询语句,查询结果显示脏读

SELECT * from student WHERE Sno= '0831104';

 

 2.读已提交

 2.1 我们打开一个mysql客户端,设置事务的隔离级别 

set tx_isolation="read-committed";

2.2. java代码在conn.commit()处,打断点,debug代码

import com.zxb.util.JdbcUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class demo02 {
    /**
     * JDBC操作事务
     */
    public static void main(String[] args) {
        Connection conn = JdbcUtil.getConnection();
        String sql = "update student set Sage=?  where Sno = ?";

        try {
            /**设置事务隔离级别
             * 读未提交 int TRANSACTION_READ_UNCOMMITTED = 1;
             * 读已提交 int TRANSACTION_READ_COMMITTED   = 2;
             * 不可重复读 int TRANSACTION_REPEATABLE_READ  = 4;
             * 串行化 int TRANSACTION_SERIALIZABLE     = 8;
             */
            conn.setTransactionIsolation(2);
            //关闭自动提交,自动开启事务
            conn.setAutoCommit(false);
            PreparedStatement pst = conn.prepareStatement(sql);
            pst.setInt(1, 20);
            pst.setString(2, "0831104");
            pst.executeUpdate();
            conn.commit(); // 只要事务执行过程中不发生异常,执行手动提交

        } catch (Exception e) {
            e.printStackTrace();
            try {
                //代码发生异常 事务回滚
                conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
        }

2.3 此时我们查询该数据,显示还是未修改前的数据 

2.4 java代码继续debug,执行commit代码后,再次查询该数据,显示已更新的数据 

 3.不可重复读、串行化也是如此操作,只需要修改conn.setTransactionIsolation()

//不可重复读
conn.setTransactionIsolation(4)
//串行化
conn.setTransactionIsolation(8)

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐