zoukankan      html  css  js  c++  java
  • 数据库及MYSQL基础(3)-JDBC

    教学视频链接:https://edu.aliyun.com/course/1694?spm=5176.11400004.0.0.29254768sg2H5P
    程序文件链接:https://pan.baidu.com/s/1PyJEgHl8y8BaP-VPcJpb8g 提取码:oe97

    JDBC入门

    1,什么是JDBC

    Java DataBase Connectivity就是Java数据库连接,使用Java语言来操作数据库。原来我们操作数据库是控制台使用SQL语句来操作数据库,JDBC是用Java语言向数据库发送SQL语句。

    ①导jar包:驱动;

    ②加载驱动类:class.forName("类名");

    ③给出url,username,password,其中url背下来;

    ④使用DriverManager类来得到Connection对象。

    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    public class Demo1 {
    /**
         * @throws ClassNotFoundException
         * ①没导入驱动包
         * ②名称、单词等关键字不正确
         * @throws SQLException
         * ①检测3个参数:url、username、password是否正确
         * ②是否开启
         */
    public static void fun1() throws ClassNotFoundException, SQLException {
    /**
             * jdbc四大配置参数:
             * >driverClassName:com.mysql.jdbc.Driver
             * >url:jdbc:mysql://localhost:3306/mydb1
             * >username:root
             * >password:无
             */
    Class.forName("com.mysql.jdbc.Driver");//驱动类加载注册驱动
    String url="jdbc:mysql://localhost:3306/mysql";
            String username="root";
            String password=null;
    Connection conn=DriverManager.getConnection(url,username,password);
    System.out.println(conn);
    }
    }

    2,JDBC原理

    早期SUN公司想编写一套可以连接所有数据库的API,但是当他们刚刚开始的时候就发现这是个不可能完成的任务,因为各个厂商的数据库服务器差异太大了。后来SUN开始与数据库厂商们讨论,最终得出的结论是,由SUN提供一套访问数据库的规范(就是一组接口),并且提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN的规范提供一套访问自己公司的数据库服务器的API出现。SUN提供的规范命名为JDBC,而各个厂商提供的-遵循JDBC规范的-可以访问自己数据库的API被称之为驱动。

    wps4

    Class.forName("com.mysql.jdbc.Driver");//驱动类加载注册驱动

    com.mysql.jdbc.Driver driver=new com.mysql.jdbc.Driver();
    DriverManager.registerDriver(driver);

    加载类的时候会执行静态代码块,所有的java.sql.Driver实现类,都提供了static块,块内的代码就是把自己注册到DriverManager中。

    在jdbc4.0之后,每个驱动jar包中,在META-INF/services目录下提供有名为java.sql.Driver的文件,文件内容就是该接口的实现类名称!

    static {
    try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
    throw new RuntimeException("Can't register driver!");
        }
    }

    3,JDBC完成增删改查

    ①数据库连接准备

    package cn.itcast.demo1;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.sql.Statement;
    public class Demo2 {
    public static void fun1() throws ClassNotFoundException, SQLException {
    //①准备四大参数;②加载驱动类;③对数据库进行增删改查;
    String driverClassName="com.mysql.jdbc.Driver";
            String url="jdbc:mysql://localhost:3306/mydb1";
    //jdbc协议的格式!jdbc:厂商名称:子协议(由厂商自己规定)
            //对于mysql而言,子协议结构://主机地址:端口号/数据库名称
    String username="root";
            String password="";
            Class.forName(driverClassName);
            Connection conn=DriverManager.getConnection(url,username,password);
    //1,通过Connection对象创建Statement>Statement语句发送器,他的功能是想数据库发送sql语句
    Statement stmt=conn.createStatement();
    //2,调用它的int executeUpdate(String sql),它可以发生DML、DDL
    String sql="INSERT INTO emp VALUES(NULL,'王武',23,'男')";
    int r=stmt.executeUpdate(sql);
            System.out.println(r);
        }
    }

    ②数据库DML操作

    String sql="INSERT INTO emp VALUES(NULL,'王武',23,'男')";
    int r=stmt.executeUpdate(sql);

    String sql="UPDATE emp";
    int r=stmt.executeUpdate(sql);

    String sql="UPDATE emp SET ename='红红的夏天',age=18,gender='女' WHERE eid=4";//修改
    int r=stmt.executeUpdate(sql);

    String sql="DELETE FROM emp";//删除

    int r=stmt.executeUpdate(sql);

    ③数据库DDL查询操作

    public ResultSet executeQuery(String sql) throws SQLException {
    /**
         * 解析ResultSet
         * ①把光标移动到第一行,可以调用next()完成
         */
    this.resultSet=this.stmt.executeQuery(sql);
    while(this.resultSet.next()){
    int eid=this.resultSet.getInt(1);
            String ename=this.resultSet.getString("ename");
    int age=this.resultSet.getInt("age");
            String gender=this.resultSet.getString("gender");
            System.out.println(eid+","+ename+","+age+","+gender);
        }
    return this.resultSet;

    wps5

    4,JDBC代码规范化

    package cn.itcast.demo1;
    import java.sql.*;
    public class Demo3 {
    public static void fun3() throws Exception {
            Connection conn=null;//定义引用对象
    Statement stmt=null;
            ResultSet rs=null;
    try{
                String driverClassName="com.mysql.jdbc.Driver";
    // String url="jdbc:mysql://localhost:3306/mydb1";
    String url="jdbc:mysql://localhost:3306/" +
    "mydb1?serverTimezone=GMT%2B8&useSSL=false";
    //这里需要修改一下
    String username="root";
                String password="";
                Class.forName(driverClassName);
                conn=DriverManager.getConnection(url,username,password);

    //实例化Connection
    stmt=conn.createStatement();//实例化Statement
    String sql="SELECT * FROM emp";
                rs=stmt.executeQuery(sql);
    while (rs.next()){
                    System.out.println(rs.getString(1)+","+
                            rs.getObject(2)+","+
                            rs.getString(3)+","+
                            rs.getObject(4));
                }
            }catch(Exception  e){//捕获异常打印
    e.printStackTrace();
            } finally {
    if(rs!=null)rs.close();//先判断在关闭
    if(stmt!=null)stmt.close();//由子到父关闭
    if(conn!=null)conn.close();
            }
        }
    }

    Connection->Statement->ResultSet->

    DriverManager

    其实今后我们只需要会用DriverManager的getConnection()方法即可:

    ①Class.forName(driverClassName);//注册驱动

    ②String url="jdbc:mysql://localhost:3306/mydb1?serverTimezone=GMT%2B8&useSSL=false";

    ③String username="root";

    ④String password="";

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

    上面的代码可能出现的两种异常:

    ①ClassNotFoundException——这个异常在第一句出现,可能为

    ·没有导入mysql的jar包;

    ·把类名称打错,查看的类名不是com.mysql.jdbc.Driver

    ②SQLException——异常出现在第五句,出现这个异常的就是三个参数的问题

    对于DriverManager.registerDriver()方法了解即可,因为今后注册驱动只会使用Class.forName()而不是用前一个方法。

    Connection

    Connection最为重要的方法就是获取Statement:

    ·Statement stmt=conn.createStatement();

    后面在学习ResultSet()方法时,还需要学习一下下面的方法【方法重载】:

    ·Statement stmt=conn.createStatement(int,int);//两个参数决定能够生成什么样的结果集

    Statement

    ·int executeUpdate(String sql)执行更新操作,即执行INSERT、UPDATE、DELETE语句,其实这个方法也可以执行CREATE TABLE、ALTER TABLE,以及DROP TABLE等语句,但我们很少会使用JDBC来执行这些语句。

    ·ResulSet executeQuery(String sql)执行查询操作,返回ResultSet结果集数据。

    ·boolean execute()执行增、删、改、查所有SQL语句,返回一个布尔类型,表示SQL语句是否有结果。如果execute()执行的是更新语句,那么还要强调用int getUpdateCount()来获取增删改影响的行数;如果执行的是查询语句,需要调用ResultSet getResultSet()来获取查询结果。

    5,结果集光标与元数据

    executeQuery()光标的位置:

    ①相对位移,下一行,不可滚动forward_only(只能rs.next());②绝对位移,第几行。

    void beforeFirst():把光标放在第一行的前面

    void afterLast():把光标放在最后一行的后面

    boolean first():把光标放在第一行,返回值表示光标调控是否成功

    boolean last()

    boolean isBeforeFirst():当前光标是否在第一行的前面

    boolean isAfterLast()

    boolean isFirst()

    boolean isLast()

    boolean previous():把光标往前挪动一行

    boolean next()

    boolean relative(int row):相对位移,当row为正数的时候,表示向下移动row行,为负数时表示向上移动row行

    boolean absolute(int row):绝对位移,把光标移动到指定的行上

    int getRow():返回当前光标所在行

    获取结果集元数

    rs.getMetaData()获取元数据,返回值为ResultSetMetaData;

    int getColumnCount()获取结果数据集

    String getColumnName(String colIndex)获取指定列的列名

    6,结果集的三大特性【滚动、敏感、可更新】

    当使用Connection的createStatement时,已经确定了Statement生成的结果集是什么类型。

    conn.createStatement();返回值->【不滚动-只有next()相对方法、不敏感、不可更新】

    conn.createStatement(int,int);//方法重载

    第一个参数:

    ResultSet.TYPE_FORWARD_ONLY:不滚动

    ResultSet.TYPE_SCROLL_INSENSITIVE:滚动结果集,但结果集数据不会跟随数据库变化而变化

    ResultSet.TYPE_SCROLL_SENSITIVE:滚动结果集,结果随着数据库变化而变化【实时性!理想!】

    第二个参数:

    CONCUR_READ_ONLY:结果集是只读的,不能通过修改结果集而反向影响数据库

    CONCUR_UPDATE:结果集是读写的,可以通过修改结果集而反向影响数据库

    注意:mysql数据库默认全是可滚动的,没有FORWARD_ONLY的

    ResultSet方法:

    getInt()、getString()、getDouble()、getBoolean()、getObject()

    7,PreparedStatement的用法

    PreparedStatement是Statement的子接口,优点:①防SQL攻击;②提高代码的可读性与可维护性;③提高效率。

    ·范例:一个简单的SQL攻击程序

    package cn.itcast.demo4;
    import javax.swing.plaf.nimbus.State;
    import java.sql.*;
    /**
    * 演示:SQL攻击
    * @author Mufasa
    */
    public class Demo4 {
    /**
         * 使用username和password查询数据,

         * 查询出结果返回true否则返回false
         * @param username
    * @param age
    * @return boolean
    */
    public static boolean login(String username,String age) throws ClassNotFoundException, SQLException {
            String driverClassName="com.mysql.jdbc.Driver";
            String url="jdbc:mysql://localhost:3306/mydb1";
            String mysqlusername="root";
            String mysqlpassword="";
            Class.forName(driverClassName);
            Connection conn= DriverManager.getConnection(url,mysqlusername,mysqlpassword);
            Statement stmt=conn.createStatement();
            String sql="SELECT * FROM emp WHERE ename='"+username+"'and age="+age;
            System.out.println(sql);
            ResultSet rs=stmt.executeQuery(sql);
    return rs.next();
        }
    }

    import cn.itcast.demo4.Demo4;
    public class Main001 {
    public static void main(String[] args) throws Exception {
    //        System.out.println(Demo4.login("红红的夏天",18));
    System.out.println(Demo4.login("a' or 'a'='a","18 or 'a'='a'"));//SQL攻击
    }
    }

    SELECT * FROM emp WHERE ename='红红的夏天'and age=18//→正常SQL

    SELECT * FROM emp WHERE ename='a' or 'a'='a'and age=18 or 'a'='a'

    学习使用PreparedStatement的用法:

    ①给出SQL模板;

    ②调用Connection中的preparedStatement(String sql模板);

    ③调用pstmt的setXxx()系列方法为sql模板中的?赋值;

    ④调用pstmt的executeUpdate()或executeQury()但是它的方法都没有参数。

    public static boolean login2(String username,String age) throws ClassNotFoundException, SQLException {
        String driverClassName="com.mysql.jdbc.Driver";
        String url="jdbc:mysql://localhost:3306/mydb1";
        String mysqlusername="root";
        String mysqlpassword="";
        Class.forName(driverClassName);
        Connection conn= DriverManager.getConnection(url,mysqlusername,mysqlpassword);
        String sql="SELECT * FROM emp WHERE ename=? and age=?";

    //①给出SQL模板
    PreparedStatement pstmt=conn.prepareStatement(sql);

    //②调用prepareStatement
    pstmt.setString(1,username);//给第一个问号赋值
    pstmt.setString(2,age);//给第一个问号赋值
    ResultSet rs=pstmt.executeQuery();

    //调用查询方法向数据库发送SQL语句,返回ResultSet数据
    return rs.next();
    }

    8,预处理的原理

    服务器的工作:

    ①校验sql语句的语法【耗时】;

    ②编译,与函数类似的东西;

    ③执行,调用函数;

    PreparedStatement:

    ①前提,数据库必须支持预处理【现在几乎没有不支持的】;

    ②每个pstmt都与一个sql语句绑定在一起,先把sql模板给数据库,数据库先进行校验、再进行编译、执行时只是把参数传递过去;

    ③二次执行时,无需再次校验语法,直接执行。【类似懒汉-单例-模式】

    9,mysql的预编译功能

    MySQL执行预编译分为三步【预处理MySQL4.0之后默认关闭】:

    ①执行预编译语句,例如:prepare myfun from'SELECT * FROM emp WHERE eid=?'

    ②设置变量,例如:set @str='b1'

    ③执行语句,例如:execute myfun using @str

    如果二次执行myfun,那么就不再需要第一步【预编译语句】

    使用Statement执行预编译就是把上面的SQL语句执行一次。

    Connection conn=JdbcUtils.getConnection();

    Statement stmt=conn.createStatement();

    stmt.executeUpdate("prepare myfun from'SELECT * FROM emp WHERE eid=?'");

    stmt.executeUpdate("set @str='b1'");

    ResulSet rs=stmt.executeQuery("execute myfun using @str");

    while(rs.next()){

        System.out.println(rs.getString(1));

    }

    stmt.executeUpdate("set @str='b2'");//二次运行

    ResulSet rs=stmt.executeQuery("execute myfun using @str");

    while(rs.next()){

        System.out.println(rs.getString(1));

    }

    rs.close();

    stmt.close(0;

    conn.close();

    MySQL默认使用PreparedStatement是不能执行预处理编译的,这需要在url中给出useServerPrepStmts=true参数;当使用不同的PreparedStatement对象来执行相同的SQL语句时,还是会出现编译两次的现象,这是因为驱动没有缓存编译后的函数key,导致二次编译,如果希望缓存编译后函数的key,那么就要设置cachePrepStmts参数为true;打开批处理功能,rewriteBatchedStatements=true

    例如:

    ①jdbc:mysql://localhost:3306/mydb1?useServerPrepStmts=true

    ②jdbc:mysql://localhost:3306/mydb1?useServerPrepStmts=true&cachePrepStmts=true

    ③jdbc:mysql://localhost:3306/mydb1?useServerPrepStmts=true&cachePrepStmts=true&rewriteBatchedStatements=true

    url中的关键字及其值的作用

    10,JdbcUtils1.0小工具

    JdbcUtils-V1.0【单次初始化配置文件、数据库】

    package cn.itcast.demo1;
    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.util.Properties;
    public class JdbcUtils {
    private static Properties props=null;
    static {//静态代码段,对props进行初始化
    try {
                InputStream in=JdbcUtils.class.getClassLoader().getResourceAsStream("configure//dbconfig.properties");
    props=new Properties();
    props.load(in);
                Class.forName(props.getProperty("driverClassName"));
            } catch (IOException e) {
    throw new RuntimeException();
            } catch (ClassNotFoundException e) {
    throw new RuntimeException();
            }finally {
                System.out.println("配置文件加载成功");
            }
        }
    public static Connection getConnection() throws SQLException {
    return DriverManager.getConnection(props.getProperty("url"),
    props.getProperty("mysqlusername"),
    props.getProperty("mysqlpassword"));
        }
    }

    11,面向接口编程

    修改项目:

    ①把UserDao修改为接口,然后把原来的UserDao修改类名为UserDaoImpl

    ②修改UserService中对UserDao的实例化:private UserDao userDao=DaoFactory.getUserDao()

    ③创建DaoFactory,提供getUserDao()方法

    ④使用配置文件来获取实体类的名称

    DAO【Data Access Object】模式

    DAO模式就是写一个类,把访问数据库的代码封装起来。DAO在数据库与业务【Service】逻辑之间。

    ·实体域,即操作的对象,例如我们操作的表示user表,那么就需要先写一个User类;

    ·DAO模式需要先提供一个DAO接口;

    ·然后提供一个DAO接口的实现类;

    ·再编写一个DAO工厂,Service通过工厂来获取DAO实现。

    12,修改day14的登录案例

    开闭原则:允许加,不允许改->需要重新测试

    package cn.itcast.demo1;
    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    public class JdbcUserDaoImpl implements UserDao {
        Connection conn=null;
        PreparedStatement pstmt=null;
    @Override
    public void addUser() throws SQLException {
    try{
    conn=JdbcUtils.getConnection();
                String sql="INSERT INTO emp VALUES(?,?,?,?)";
    pstmt=conn.prepareStatement(sql);
    pstmt.setString(1,from.getEid());
    pstmt.setString(2,from.getEname());
    pstmt.setString(3,from.getEage());
    pstmt.setString(4,from.getEgender());
    pstmt.executeUpdate();
            }catch (Exception e){
    throw new RuntimeException(e);
            }finally {
    if(pstmt!=null) pstmt.close();
    if(conn!=null) conn.close();
            }
        }
    }

    13,Util包下的Date与sql包下的时间类型转换

    java.sql的东西不能出现在Dao以外的地方,否则就干扰了Service或者Server类

    数据库类型与java中类型的对应关系:

    DATE->java.sql.Date

    TIME->java.sql.Time

    TIMESTAMP->java.sql.Timestamp

    领域对象【domain】中的所有属性不能出现java.sql包下的东西!即不能使用java.sql.Date

    ·ResultSet#getDate()返回的是java.sql.Date()

    ·PreparedStatement#setDate(int,Date),其中第二个参数也是java.sql.Date类型

    时间类似的转换:【sql是util的子类】

    ·java.util.Date->java.sql.Date、Time、Timestamp

    ①先把util的Date转换成毫秒值;

    ②使用毫秒值创建sql的Date、Time、Timestamp

    java.util.Date date=new java.util.Date();

    long l=date.getTime();

    java.sql.Date sqlDate=new java.sql.Date(l);

    ·java.sql.Date、Time、Timestamp->java.util.Date

    java.util.Date date=java.sql.Date()

    14,大数据

    目标:把MP3保存到数据库之中!

    USE mydb1;

    CREATE TABLE tab_bin(

    id INT PRIMARY KEY AUTO_INCREMENT,

    filename VARCHAR(100),

    DATA MEDIUMBLOB

    );

    import java.io.IOException;
    import java.io.InputStream;
    import java.sql.Connection;
    import java.sql.DriverManager;
    import java.sql.SQLException;
    import java.util.Properties;
    public class JdbcUtils {
    private static Properties props=null;
    static {//静态代码段,对props进行初始化
    try {
                InputStream in=JdbcUtils.class.getClassLoader().getResourceAsStream("configure/dbconfig.properties");
    props=new Properties();
    props.load(in);
                System.out.println(props.getProperty("driverClassName"));
                Class.forName(props.getProperty("driverClassName"));
            } catch (IOException e) {
    throw new RuntimeException();
            } catch (ClassNotFoundException e) {
    throw new RuntimeException();
            }finally {
                System.out.println("配置文件加载成功");
            }
        }
    public static Connection getConnection() throws SQLException {
    return DriverManager.getConnection(props.getProperty("url"),
    props.getProperty("mysqlusername"),
    props.getProperty("mysqlpassword"));
        }
    }

    import org.apache.commons.io.IOUtils;
    import javax.sql.rowset.serial.SerialBlob;
    import java.io.*;
    import java.sql.*;
    //import static com.sun.tools.doclint.Entity.copy;

    public class Wrmp3 {
    public static void write_mp3() throws SQLException, IOException {
            Connection conn=JdbcUtils.getConnection();//①得到Connection
    String sql="INSERT INTO tab_bin VALUES(?,?,?)";//②给出sql模板
    PreparedStatement pstmt=conn.prepareStatement(sql);//创建pstmt
    pstmt.setInt(1,1);
            pstmt.setString(2,"RockDrums.mp3");
    /**
             * 有文件需要得到Blob
             * ①先把文件编程byte[]类型
             * ②再使用byte[]创建Blob
             */
    String mp3Path="E:\RockDrums.mp3";
    byte[] bytes=getMp3Content(mp3Path);
            Blob blob=new SerialBlob(bytes);
            pstmt.setBlob(3,blob);
            pstmt.executeUpdate();
        }
    public static void read_mp3() throws SQLException, IOException {
            Connection conn=JdbcUtils.getConnection();
            String sql="SELECT * FROM tab_bin";
            PreparedStatement pstmt=conn.prepareStatement(sql);
            ResultSet rs=pstmt.executeQuery();
    if(rs.next()){
                Blob blob=rs.getBlob("data");
                InputStream in = blob.getBinaryStream();
                OutputStream out=new FileOutputStream("E:\RockDrums_复写版.mp3");
                IOUtils.copy(in,out);
            }
        }
    public static byte[] getMp3Content(String filePath) throws IOException {
            File file = new File(filePath);
    long fileSize = file.length();
    if (fileSize > Integer.MAX_VALUE) {
                System.out.println("file too big...");
    return null;
            }else {
                System.out.println(fileSize);
            }
            FileInputStream fi = new FileInputStream(file);
    byte[] buffer = new byte[(int) fileSize];
    int offset = 0;
    int numRead = 0;
    while (offset < buffer.length
    && (numRead = fi.read(buffer, offset, buffer.length - offset)) >= 0) {
                offset += numRead;
            }
    // 确保所有数据均被读取
    if (offset != buffer.length) {
    throw new IOException("Could not completely read file " + file.getName());
            }
            fi.close();
    return buffer;
        }
    }

    import java.io.IOException;
    import java.sql.Connection;
    import java.sql.SQLException;
    public class Main {
    public static void main(String[] args) throws SQLException, IOException {
    //        Wrmp3.write_mp3();
    Wrmp3.read_mp3();
        }
    }

    wps6

    15,批处理

    批处理就是一批一批的处理,而不是一个一个的处理。

    import java.sql.Connection;
    import java.sql.PreparedStatement;
    import java.sql.SQLException;
    public class Demo5 {
    public static void fun5() throws SQLException {
            Connection conn=JdbcUtils.getConnection();
            String sql="INSERT INTO batch_emp VALUES(?,?,?,?)";
            PreparedStatement pstmt=conn.prepareStatement(sql);
    /* 疯狂添加数据*/
    for(int i=0;i<10000;i++){
                pstmt.setInt(1,i+1);
                pstmt.setString(2,"emp_"+i);
                pstmt.setInt(3,18);
                pstmt.setString(4,i%2==0?"男":"女");
                pstmt.addBatch();//添加批
    }
    long start = System.currentTimeMillis();
            pstmt.executeBatch();//执行批
    long end = System.currentTimeMillis();
            System.out.println(end-start);
        }
    }

    driverClassName=com.mysql.jdbc.Driver
    url=jdbc:mysql://localhost:3306/mydb1?useServerPrepStmts=true&cachePrepStmts=true&rewriteBatchedStatements=true
    mysqlusername=root
    mysqlpassword=

  • 相关阅读:
    bzoj 4012: [HNOI2015]开店
    POJ 1054 The Troublesome Frog
    POJ 3171 Cleaning Shifts
    POJ 3411 Paid Roads
    POJ 3045 Cow Acrobats
    POJ 1742 Coins
    POJ 3181 Dollar Dayz
    POJ 3040 Allowance
    POJ 3666 Making the Grade
    洛谷 P3657 [USACO17FEB]Why Did the Cow Cross the Road II P
  • 原文地址:https://www.cnblogs.com/Mufasa/p/11163160.html
Copyright © 2011-2022 走看看