Java基础28–JDBC概念
JDBC:
Java Database Connectivity Java连接数据库的技术。
JDBC通常指的是SUN为各大数据库厂商Java程序如何连接和操作它这个DBMS软件指定的统一的标准,即公共接口。
这个公共接口由各大数据库厂商提供实现类,这些实现类就构成了数据库驱动。
Java程序员编写Java代码时,只要面向接口编程就可以了,运行时把驱动实现类加到项目中即可。
Java程序员只要学习SUN公司提供的JDBC的公共接口就可以了。
如果自己新开发了一款数据库,想要Java程序连接它,需要实现这些JDBC的公共接口,否则无法连接。
mysql提供的驱动:两个版本的,使用时一个就行
jar包打开就是各种各样的接口、类
JDBC编写步骤
java.sql包和javax.sql包
JDBC是代表一组公共的接口。
JDBC中的这些公共接口和DBMS数据库厂商提供的实现类(驱动jar),是为了实现Java代码可以连接DBMS,并且操作它里面的数据而声明的。
常用的接口:
Connection:连接
Statement和PreparedStatement:增删改查
ResultSet:接收和处理查询结果
辅助的类:
DriverManager:驱动管理类。
JDBC程序的编写步骤:
- 1、注册驱动(如果这步不做,编译不会报错,运行会报找不到驱动类等异常) java.sql.SQLException: No suitable driver found for jdbc:mysql://localhost:3306/test
- 2、连接数据库:
DriverManager和Connection- 3、操作数据库 (1)增、删、改:Statement或PreparedStatement (2)查询:Statement或PreparedStatement
ResultSet- 4、断开连接,关闭对应各种资源
url:统一资源定位符,通俗的讲就是网址
url是用来定位到①哪台电脑上的②哪种DBMS数据库软件,③访问哪个数据库,这个DBMS软件目前运行在④哪个断开号⑤连接这个DBMS软件是否还需要其他的参数。
url的标准格式:
协议://主机名:端口号/资源路径
mysql:
jdbc:mysql://主机名:端口号/数据库名?其他的参数
例如:
jdbc:mysql://localhost:3306/test
举例:
//添加案例,删除、修改只是sql语句不一样,其他步骤都一样
public static void main(String[] args) throws Exception {
//1、注册驱动:把驱动类加载到内存,并且初始化这个类
Class.forName("com.mysql.jdbc.Driver");//Class.forName()反射
//2、连接数据库
String url = "jdbc:mysql://localhost:3306/test";
Connection conn = DriverManager.getConnection(url, "root", "1234");
//java.sql.SQLException: Access denied for user 'root'@'localhost' (using password: YES)
//解决这个问题的思路:查看主机名,用户名,密码
System.out.println(conn.getClass());//获取运行时类型 class com.mysql.jdbc.Connection
//3、操作数据库
//这里我要添加一条记录到User
//(1)编写sql
String sql = "INSERT INTO t_stu VALUES(3,'yan')";
/*
* Connection好比网络编程中的Socket
* Statement好比和服务器进行数据读写的InputStream和OutputStream
*/
//(2)准备一个Statement
Statement st = conn.createStatement();
//(3)执行sql,通过Statement对象把Sql发给服务器,服务器执行Sql后,把结果返回
//insert ,update,delete语句都是通过这个方法执行
int len = st.executeUpdate(sql);//返回的是受影响的记录的行数
System.out.println(len>0?"添加成功":"添加失败");
//4、释放资源
st.close();
conn.close();
}
注册驱动三部曲:
(1)把jar放到项目的libs中
(2)把jar添加到build path中
在jar包上选择右键,Build Path–>Add to Build Path
不然会被当成普通文件
(3)在代码中注册驱动
Class.forName("com.mysql.jdbc.Driver");
JDBC实现查询
步骤:
1、注册驱动
三部曲:(1)把jar复制到libs(2)把jar添加到Build path中(3)加载驱动类
2、建立连接
获取Connection对象
3、执行sql
(1)编写sql
(2)创建Statement
(3)执行sql
增删改:int len = Statement对象.executeUpdate(sql)
查询:ResultSet rs = Statement对象.executeQuery(sql)
4、遍历结果集
5、关闭资源
@Test
public void test2()throws Exception{
//1、加载驱动类
Class.forName("com.mysql.jdbc.Driver");//之前那个
// Class.forName("org.gjt.mm.Driver");//两个驱动的包都可以,二选一
//2、建立连接
String url = "jdbc:mysql://localhost:3306/test";
Connection conn = DriverManager.getConnection(url,"root","1234");
//3、执行sql
String sql = "select * from t_stu";
//sql语句也可以写为:String sql = "select sid,sname from t_stu";
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
//4、遍历结果集
/*
* rs.next():还有没有下一行
* rs.getXxx(字段名)
*/
while(rs.next()){
int sid = rs.getInt("sid");
String sname = rs.getString("sname");
System.out.println(sid+"\t" +sname);
}
//5、关闭
rs.close();
st.close();
conn.close();
}
第二种方法:从查询结果里拿数据时也可以按字段的位置取值
@Test
public void test4()throws Exception{
//1、加载驱动类
Class.forName("com.mysql.jdbc.Driver");
// Class.forName("org.gjt.mm.Driver");
//2、建立连接
String url = "jdbc:mysql://localhost:3306/test";
Connection conn = DriverManager.getConnection(url,"root","1234");
//3、执行sql
String sql = "select sid,sname from t_stu";
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
//4、遍历结果集
/*
* rs.next():还有没有下一行
* rs.getXxx(字段名)
*/
while(rs.next()){
int sid = rs.getInt(1);//查询结果中的第一个字段
String sname = rs.getString(2);//查询结果中的第一个字段
System.out.println(sid+"\t" +sname);
}
//5、关闭
rs.close();
st.close();
conn.close();
}
甚至还可以写为:
public void test5()throws Exception{
//1、加载驱动类
Class.forName("com.mysql.jdbc.Driver");
// Class.forName("org.gjt.mm.Driver");
//2、建立连接
String url = "jdbc:mysql://localhost:3306/test";
Connection conn = DriverManager.getConnection(url,"root","1234");
//3、执行sql
String sql = "select sid,sname from t_stu";
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
//4、遍历结果集
/*
* rs.next():还有没有下一行
* rs.getXxx(字段名)
*/
while(rs.next()){
System.out.println(rs.getObject(1) + "\t" + rs.getObject(2));
//数据类型不清楚的时候就都用object就行
}
//5、关闭
rs.close();
st.close();
conn.close();
}
org.gjt.mm.Driver驱动包
Statement问题
Statement问题:
1、SQL拼接:太麻烦
2、SQL注入:欺骗服务器执行恶意的SQL命令
3、无法处理blob等二进制类型的数据
如何解决这些问题,使用Statement的子接口PreparedStatement
public class TestStatementProblem {
/*
* t_userinfo表
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(20) | NO | | NULL | |
| password | varchar(20) | NO | | NULL | |
| photo | blob | YES | | NULL | |
+----------+-------------+------+-----+---------+----------------+
*/
//三个问题示例
@Test
//无法处理blob等二进制类型的数据问题
/*
若是一张表中有字段是图片,图片存到数据库里不能是字符串、整数等类型,需要存储为二进制问题,比如blob类型,表结构如上图
*/
public void test04()throws Exception{
//往test库的t_userinfo表中添加一条记录
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "1234");
//3、编写sql
String sql = "insert into t_userinfo values(null,'chai','1234', )";//这里怎么拼都是字符串,但是表中却是blob类型,无法存储,无法处理blob
//这里id自增null就行
}
@Test
public void test03()throws Exception{
Scanner input = new Scanner(System.in);
System.out.print("请输入员工的姓名:");
String ename = input.nextLine();//若是inpur.next()的话遇到空格就输入结束,不正确,要用input.nextLine()
System.out.println(ename);
}
@Test
//sql注入问题
public void test02()throws Exception{
//员工自己查询自己的信息:从键盘输入员工的姓名,查询自己的信息
Scanner input = new Scanner(System.in);
System.out.print("请输入员工的姓名:");
String ename = input.nextLine();//输入:孙红雷' or '1'='1,若是inpur.next()的话遇到空格就输入结束,不正确
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "1234");
//3、编写sql
String sql = "select * from t_employee where ename = '" + ename + "'";
System.out.println(sql);//select * from t_employee where ename = '孙红雷' or '1'='1'
//这种编写sql语句的方法给了sql注入可乘之机!!!!!
//'1'='1 永远成立,前面所有条件都是失效
//会发现将整个表的数据都查出来了,不只是孙红雷的
//会发现查询的结果不是sql语句想要查询的结构,恶意输入语句也可以实现某些功能
//这种现象叫做sql注入
//4、创建Statement
Statement st = conn.createStatement();
//5、执行sql
ResultSet rs = st.executeQuery(sql);
//6、遍历结果集
while(rs.next()){
for (int i = 1; i <= 14; i++) {
System.out.print(rs.getObject(i) + "\t");
}
System.out.println();
}
//7、关闭
rs.close();
st.close();
conn.close();
input.close();
}
@Test
//sql语句拼接问题
public void test01()throws Exception{
//从键盘输入部门的信息,添加到test库的t_department表中
Scanner input = new Scanner(System.in);
System.out.print("请输入部门的名称:");
String dname = input.next();
System.out.print("请输入部门的简介:");
String desc = input.next();
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "1234");
//3、编写sql
String sql = "insert into t_department values(null,'" + dname + "','" + desc +"')";
//dname,desc都是字符串,都需要单引号括起来
//拼接麻烦
//4、创建Statement
Statement st = conn.createStatement();
//5、执行sql
int len = st.executeUpdate(sql);
System.out.println(len>0?"添加成功":"添加失败");
//6、关闭
st.close();
conn.close();
input.close();
}
}
PreparedStatement解决Statement问题
public class TestPreparedStatement {
/*
* t_userinfo表
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(20) | NO | | NULL | |
| password | varchar(20) | NO | | NULL | |
| photo | blob | YES | | NULL | |
+----------+-------------+------+-----+---------+----------------+
图片太大:
(1)表结构修改 把blob修改为mediumblob
mysql> desc t_userinfo;
+----------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| username | varchar(20) | NO | | NULL | |
| password | varchar(20) | NO | | NULL | |
| photo | mediumblob | YES | | NULL | |
+----------+-------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)
(2)修改my.ini(修改配置文件,默认从客户端传到服务器的数据量的大小的限制
增加一个参数
max_allowed_packet=16m
在修改my.ini之前,关闭服务,修改之后重新启动服务
*/
@Test
//解决blob问题
public void test04()throws Exception{
//往test库的t_userinfo标准添加一条记录
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "1234");
//3、编写sql
// String sql = "insert into t_userinfo values(null,'chai','1234', )";//无法处理blob
String sql = "insert into t_userinfo values(null,?,?,?)";//避免了拼接blob
//4、创建PreparedStatement
PreparedStatement pst = conn.prepareStatement(sql);
//5、设置?
pst.setString(1, "chailinyan");
pst.setString(2, "123456");
pst.setObject(3, new FileInputStream("D:/QMDownload/img/美女/15.jpg"));//不知道类型可以直接Object类型,这里报错图片太大,超过了
//有pst.setBlob()方法,这里没讲使用方法;
//6、执行sql
int len = pst.executeUpdate();
System.out.println(len>0?"添加成功":"添加失败");
//6、关闭
pst.close();
conn.close();
}
@Test
//避免sql注入
public void test02()throws Exception{
//员工自己查询自己的信息:从键盘输入员工的姓名,查询自己的信息
Scanner input = new Scanner(System.in);
System.out.print("请输入员工的姓名:");
String ename = input.nextLine();//输入:孙红雷' or '1'='1
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "1234");
//3、编写sql
// String sql = "select * from t_employee where ename = '" + ename + "'";
String sql = "select * from t_employee where ename = ?";
//4、创建Statement
// Statement st = conn.createStatement();
PreparedStatement pst = conn.prepareStatement(sql);
//插入一步,设置?
pst.setString(1, ename);
//5、执行sql
// ResultSet rs = st.executeQuery(sql);
ResultSet rs = pst.executeQuery();
//6、遍历结果集
while(rs.next()){
for (int i = 1; i <= 14; i++) {
System.out.print(rs.getObject(i) + "\t");
}
System.out.println();
}
//7、关闭
rs.close();
// st.close();
pst.close();
conn.close();
input.close();
}
@Test
//避免sql拼接
public void test01()throws Exception{
//从键盘输入部门的信息,添加到test库的t_department表中
Scanner input = new Scanner(System.in);
System.out.print("请输入部门的名称:");
String dname = input.next();
System.out.print("请输入部门的简介:");
String desc = input.next();
//1、注册驱动
Class.forName("com.mysql.jdbc.Driver");
//2、获取连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "1234");
//3、编写sql
// String sql = "insert into t_department values(null,'" + dname + "','" + desc +"')";//不要拼接
String sql = "insert into t_department values(null,?,?)";
//4、创建PreparedStatement
// Statement st = conn.createStatement();
PreparedStatement pst = conn.prepareStatement(sql);//此时的sql带?的
//加入一步,设置?的值
pst.setString(1, dname);//1表示第1个?
pst.setString(2, desc);//2表示第2个?
//5、执行sql
// int len = st.executeUpdate(sql);
int len = pst.executeUpdate();//这里没有sql
System.out.println(len>0?"添加成功":"添加失败");
//6、关闭
// st.close();
pst.close();
conn.close();
input.close();
}
}
修改my.ini文件
修改之前需要先将服务器停掉
停掉服务器
在文件的最后加上max_allowed_packet=16m(1
6是自己根据需要设置)然后开启服务器
补充:blob相关的类型