Java全栈JavaSE:3.数据库之JDBC上

Posted new nm个对象

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java全栈JavaSE:3.数据库之JDBC上相关的知识,希望对你有一定的参考价值。

第一章 JDBC概述

之前我们学习了JavaSE,编写了Java程序,数据保存在变量、数组、集合等中,无法持久化,后来学习了IO流可以将数据写入文件,但不方便管理数据以及维护数据的关系;

后来我们学习了数据库管理软件mysql,可以方便的管理数据。

那么如何将它俩结合起来呢?即Java程序<==>MySQL,实现数据的存储和处理。

那么就可以使用JDBC技术。

1.1 JDBC概述

JDBC:Java Database Connectivity,它是代表一组独立于任何数据库管理系统(DBMS)的API,声明在java.sql与javax.sql包中,是SUN(现在Oracle)提供的一组接口规范。由各个数据库厂商来提供实现类,这些实现类的集合构成了数据库驱动jar。

即JDBC技术包含两个部分:

(1)java.sql包和javax.sql包中的API

因为为了项目代码的可移植性,可维护性,SUN公司从最初就制定了Java程序连接各种数据库的统一接口规范。这样的话,不管是连接哪一种DBMS软件,Java代码可以保持一致性。

(2)各个数据库厂商提供的jar

因为各个数据库厂商的DBMS软件各有不同,那么内部如何通过sql实现增、删、改、查等管理数据,只有这个数据库厂商自己更清楚,因此把接口规范的实现交给各个数据库厂商自己实现。

1.2 JDBC使用步骤

代码编写步骤:

JDBC访问数据库步骤

1:注册一个Driver驱动
```
三部曲:

​    (1)将DBMS数据库管理软件的驱动jar拷贝到项目的libs目录中

​              例如:mysql-connector-java-5.1.36-bin.jar

​     (2)把驱动jar添加到项目的build path中。(IDEA中右键点击驱动jar包》点击add as library)

​     (3)将驱动类(DriverManager)加载到内存中,DriverManager可以用于创建命令发送器。来执行sql语句
               DriverManager.registerDriver(new Driver());  # 方式1
               Class.forName("com.mysql.cj.jdbc.Driver"); # 方式2。不同版本的mysql驱动包Driver类路径可能不同
```

两种加载驱动类DriverManager的方式:

  • DriverManager.registerDriver(new Driver());:注册一个Driver实例
  • Class.forName("com.mysql.cj.jdbc.Driver"):初始化Driver类。
    package com.mysql.cj.jdbc;
    
    import java.sql.DriverManager;
    import java.sql.SQLException;
    
    public class Driver extends NonRegisteringDriver implements java.sql.Driver 
        public Driver() throws SQLException 
        
    	// 本质上是初始化Driver类时,会执行该静态代码块,完成Driver实例对象的创建。跟方式1一样
        static 
            try 
                DriverManager.registerDriver(new Driver());
             catch (SQLException var1) 
                throw new RuntimeException("Can't register driver!");
            
        
    
    

2:创建数据库连接(Connection)

Connection conn = DriverManager.getConnection(url,username,password);// mysql的url:jdbc:mysql://ip:端口/数据库名?参数名=参数值// 例如:jdbc:mysql://localhost:3306/testdb?useUnicode=true&characterEncoding=utf8

​ (如果JDBC程序与服务器端的字符集不一致,会导致乱码,那么可以通过参数指定服务器端的字符集)

3:创建SQL命令发送器Statement

// 创建Statement或PreparedStatement对象
// 创建statement对象
Connection connection = DriverManager.getConnection(url, "root", "xxx");
Statement statement = connection.createStatement();

4:通过Statement发送SQL命令并得到结果

// 注意:sql语句中的字符串只能用单引号括起来,因为外层是java中的字符串必须使用双引号,不能冲突
// executeUpdate用于提交增删改操作的sql,返回int数据。大于0表示执行成功
int res = statement.executeUpdate("insert into employ3 values(41,'西门庆','男',14000.22,1)");
// executeQuery用于执行查询操作的sql,返回ResultSet 对象。其中储存了sql查询的结果
ResultSet resultSet = statement.executeQuery("select * from employ3");

5:处理结果(select语句需要)
6:关闭数据库资源ResultSet Statement Connection

statement.close(); // 关闭命令发射器
connection.close(); // 关闭连接

相关的API:

1、DriverManager:驱动管理类

2、Connection:代表数据库连接

3、Statement和PreparedStatement:用来执行sql

​	执行增、删、改:int executeUpate()

​	执行查询:ResultSet executeQuery()

4、如何遍历ResultSet ?
	(0)ResultSet可以使用迭代来查询
​	(1)boolean next():判断是否还有下一行
​	(2)getString(字段名或序号):获取字符串类型字段的数据内容。注意如果使用序号,mysql中是以1开始的
	(3)getInt(字段名或序号):获取整数类型字段的数据内容。注意如果使用序号,mysql中是以1开始的
	(4)getObject(字段名或序号):获取任意类型字段的数据内容。注意如果使用序号,mysql中是以1开始的

示例代码1:增、删、改

public class TestJDBC 
	public static void main(String[] args) throws ClassNotFoundException, SQLException 
		//1、注册驱动
	//	(1)方式一:Class.forName("驱动类的全名称")
		Class.forName("com.mysql.jdbc.Driver");
 //		(2)创建驱动类的对象
//		new com.mysql.jdbc.Driver();//硬编码
		//(3)通过DriverManager注册驱动
//		DriverManager.registerDriver(new com.mysql.jdbc.Driver());//硬编码
		
		//2、获取连接,连接数据库
        //TCP/IP协议编程,需要服务器的IP地址和端口号
		//mysql的url格式:jdbc协议:子协议://主机名:端口号/要连接的数据库名
		String url = "jdbc:mysql://localhost:3306/test";//其中test是数据库名
		String user = "root";
		String password = "123456";
		Connection conn = DriverManager.getConnection(url, user, password);
	
		//3、执行sql
		//添加一个部门到数据库的t_department表中
		//(1)编写sql
		String sql = "insert into t_department values(null,'计算部2','计算钞票2')";
		/*
		 * 回忆:	TCP/IP程序时
		 * Socket代表连接
		 * socket.getOutputStream()来发送数据,
		 * socket.getInputStream()来接收数据
		 * 
		 * 可以把Connection比喻成Socket
		 *    把Statement比喻成OutputStream
		 */
		//(2)获取Statement对象
		Statement st = conn.createStatement();
		//(3)执行sql
		int len = st.executeUpdate(sql);
		//(4)处理结果
		System.out.println(len>0?"成功":"失败");
		
		//4、关闭
		st.close();
		conn.close();
	

示例代码2:查询

public class TestSelect 
	public static void main(String[] args) throws Exception
		// 1、注册驱动
		Class.forName("com.mysql.jdbc.Driver");

		// 2、连接数据库
		Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "123456");

		// 3、执行sql
		String sql = "SELECT * FROM t_department";
		Statement st = conn.createStatement();
		
		ResultSet rs = st.executeQuery(sql);//ResultSet看成InputStream
		// 4、处理结果
		while(rs.next())//next()表示是否还有下一行
			Object did = rs.getObject(1);//获取第n列的值
			Object dname = rs.getObject(2);
			Object desc = rs.getObject(3);
			/*
			int did = rs.getInt("did");//也可以根据列名称,并且可以按照数据类型获取
			String dname = rs.getString("dname");
			String desc = rs.getString("description");
			 */
			
			System.out.println(did +"\\t" + dname + "\\t"+ desc);
		

		// 5、关闭
		rs.close();
		st.close();
		conn.close();
	

1.3 使用statement的一些问题

1.3.1 sql拼接问题

案例:我们希望键盘输入用户名,密码保存到sql的用户表中

class Demo2

    public static void main(String[] args) throws ClassNotFoundException, SQLException 
        // 创建驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 创建连接
        String url = "jdbc:mysql://localhost:3306/test2";
        Connection connection = DriverManager.getConnection(url, "root", "xxx");
        // 创建命令发射器
        Statement statement = connection.createStatement();
        // 准备sql
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String user = scanner.nextLine();
        System.out.println("请输入密码:");
        int password = scanner.nextInt();
        // 这种拼接是错误的,因为user和password是sql中的字符串,本身就需要引号括起来
        // String sql = "insert into user(user,password) values(" + user + "," + password + ")"; // 等价于"insert into user(user,password) values(张三,123456)"是错误的sql语句

        // 等价于 insert into user(user,password) values(' +  张三  +  ','  + 123456  +  ')
        // 拼接的sql是:insert into user(user,password) values('张三','123456')  是正确的sql
        String sql = "insert into user(user,password) values('" + user + "','" + password + "')";

        // 执行sql
        int res = statement.executeUpdate(sql);

        // 获取结果
        System.out.println(res>0?"注册成功":"注册失败");

        // 关闭资源
        scanner.close();
        statement.close();
        connection.close();
    


可以发现,statement要动态拼接sql非常麻烦。

1.3.2 sql注入

案例:键盘输入用户名密码,然后查询user表是否存在该用户名密码,如果存在则提示登录成功,否则登录失败

class Demo3
    public static void main(String[] args) throws ClassNotFoundException, SQLException 
        // 创建驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 创建连接
        String url = "jdbc:mysql://localhost:3306/test2";
        Connection connection = DriverManager.getConnection(url, "root", "xxx");
        // 创建命令发射器
        Statement statement = connection.createStatement();
        // 准备sql
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String user = scanner.nextLine();
        System.out.println("请输入密码:");
        String password = scanner.nextLine();

        String sql = "select * from user where (user='"+user+"'and password='"+password+"')";
        System.out.println("sql = " + sql);

        // 执行sql
        ResultSet res = statement.executeQuery(sql);

        // 获取结果
        String resUser = null;
        String resPassword = null;
        while (res.next())
            resUser = res.getString("user");
            resPassword = res.getString("password");
        
        if (resUser != null && resPassword != null)
            System.out.println("登录成功");
        else 
            System.out.println("登陆失败");
        

        // 关闭资源
        scanner.close();
        statement.close();
        connection.close();
    

然后我们来运行代码,实现sql注入:

请输入用户名:
张三
请输入密码:
123' or '1'='1
sql = select * from user where (user='张三'and password='123' or '1'='1')
登录成功

可以看到我们使用sql注入,即使输入错误的密码也登录成功了。

第二章 使用PreparedStatement处理CRUD

2.1 通过PreparedStatement来解决Statement的问题

Statement的问题:

(1)sql拼接

		String sql = "insert into t_employee(ename,tel,gender,salary) values('" + ename + "','" + tel + "','" + gender + "'," + salary +")";
		Statement st = conn.createStatement();
		int len = st.executeUpdate(sql);

(2)sql注入

		String sql = "SELECT * FROM t_employee where ename='" + ename + "'";
		//如果我此时从键盘输入ename值的时候,输入:张三' or '1'= '1
		//结果会把所有数据都查询出来
		Statement st = conn.createStatement();
		ResultSet rs = st.executeQuery(sql);

(3)无法处理blob等类型的数据(图片,文件等)

创建含有Blob字段类型的表 测试插入

String sql = "insert into user(username,photo) values('zs', 图片字节流)";
//此时photo是blob类型的数据时,无法在sql中直接拼接

PreparedStatement解决问题:

避免sql拼接

class Demo2

    public static void main(String[] args) throws ClassNotFoundException, SQLException 
        // 创建驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 创建连接
        String url = "jdbc:mysql://localhost:3306/test2";
        Connection connection = DriverManager.getConnection(url, "root", "xxx");
        // 准备sql
        String sql = "insert into user(user,password) values(?,?)"; //使用?来占用一个位置
        // 创建命令发射器
        PreparedStatement statement = connection.prepareStatement(sql);// 在创建发射器时,要传带?的sql,这样mysql端就会对这个sql进行预编译
        // 填充sql
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String user = scanner.nextLine();
        System.out.println("请输入密码:");
        int password = scanner.nextInt();
        statement.setObject(1,user); // 给第一个?赋值
        statement.setObject(2,password); // 给第2个?赋值
        // 执行sql
        int res = statement.executeUpdate(); // 因为sql在创建发射器时已经编译完成,不需要再次传入sql
        // 获取结果
        System.out.println(res>0?"注册成功":"注册失败");

        // 关闭资源
        scanner.close();
        statement.close();
        connection.close();
    

解决sql注入

class Demo3 
    public static void main(String[] args) throws ClassNotFoundException, SQLException 
        // 创建驱动
        Class.forName("com.mysql.cj.jdbc.Driver");
        // 创建连接
        String url = "jdbc:mysql://localhost:3306/test2";
        Connection connection = DriverManager.getConnection(url, "root", "xxx");

        // 准备sql
        String sql = "select * from user where (user=? and password=?)";
        // 创建命令发射器
        PreparedStatement statement = connection.prepareStatement(sql);
        // 填充sql
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String user = scanner.nextLine();
        System.out.println("请输入密码:");
        String password = scanner.nextLine();
        statement.setObject(1,user);
        statement.setObject(2,password);
        // 执行sql
        ResultSet res = statement.executeQuery();

        // 获取结果
        String resUser = null;
        String resPassword = null;
        while (res.next()) 
            resUser = res.getString("user");
            resPassword = res.getString("password");
        
        if (resUser != null && resPassword != null) 
            System.out.println("登录成功");
         else 
            System.out.println("登陆失败");
        

        // 关闭资源
        scanner.close();
        statement.close();
        connection.close();
    

运行一下代码。看看sql注入能否成功:

请输入用户名:
张三
请输入密码:
123 or 1==1 # 注入失败了,因为现在会将输入的所有内容作为密码进行校验
登陆失败

处理blob类型的数据(图片,文件等)

		String sql = "insert into user(username,photo) values(?,?)";
		PreparedStatement pst = conn.prepareStatement(sql);
		
		//设置?的值
		pst.setObject(1, "zs");
		FileInputStream fis = new FileInputStream("D:/QMDownload/img/美女/15.jpg");
		pst.setBlob(2, fis);
		
		int len = pst.executeUpdate();
		System.out.println(len>0?"成功":"失败");
  • 注意两个问题:

    ①my.ini关于上传的字节流文件有大小限制,可以在my.ini中配置变量

    ​ max_allowed_packet=16M

    ②mysql中使用tinyblob、blob、mediumblob、longblob四种数据类型来以二进制的形式储存图片,文件等数据。

    ③mysql中每一种blob有各自大小限制:

    tinyblob:255字节、blob:65k、mediumblob:16M、longblob:4G

练习:
新建一个img表,向表中储存图片,读取图片
①:新建img表

create table img(
id int primary key auto_increment,
img mediumblob,
iname char(20)
);

②:向表中插入数据

以上是关于Java全栈JavaSE:3.数据库之JDBC上的主要内容,如果未能解决你的问题,请参考以下文章

Java全栈数据库技术:3.数据库之JDBC上

Java全栈JavaSE:2.数据库之Mysql下

Java全栈JavaSE:1.数据库之MysqlL上

Java全栈JavaSE:19.常用类之大数运算日期和日历包装类

Java全栈JavaSE:20.集合之CollectionIterator迭代器ListSet

Java全栈数据库技术:4.数据库之JDBC下