数据库及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=

原文地址:https://www.cnblogs.com/Mufasa/p/11163160.html