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=

  • 相关阅读:
    JBOSS管理数据库连接
    PowerDesigner使用教程 —— 概念数据模型
    VC Delphi WM_COPYDATA 消息
    VC Delphi WM_COPYDATA
    DELPHI实现键盘勾子
    设置window任务管理器是否可用
    VS2005 MFC使用
    隐藏显示任务栏
    DELPHI实现键盘勾子
    MSN、腾讯QQ、SKYPE、阿里旺旺网页在线客服源代码
  • 原文地址:https://www.cnblogs.com/Mufasa/p/11163160.html
Copyright © 2011-2022 走看看