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全栈JavaSE:19.常用类之大数运算日期和日历包装类