一、JDBC概述
1、什么是JDBC、为什么要学习JDBC?
JDBC(Java DataBase Connectivity)Java数据库连接
其实就是利用Java语言/Java程序连接并访问数据库的一门技术
虽然之前我们可以通过 cmd或者navicat 连接数据库,也可以对数据库、表、表记录等进行操作。
但是,将来在开发中更多的是通过程序来连接数据库,如果是Java语言,通过Java程序连接数据库,就必须要学习JDBC这么技术。
2、如何通过Java程序连接mysql数据库?(快速入门)
2.1.准备数据
打开资料中的'SQL脚本.txt'文件,选中其中的所有sql语句并复制到 cmd 或者 navicat中执行
执行后会创建一个 jt_db 库, 其中包含了5张表(account、user、stu、tb_user、acc表)
2.2.创建工程并导入jar包
1)创建一个 JDBC-01 项目(Java基础工程或JavaWeb工程)
2)找到课前资料中的'resource/mysql-connector-java-5.1.32.jar'文件。
如果是Web项目:将上面的jar文件复制到项目的/WebContent/WEB-INF/lib目录下即可;
如果是Java基础项目:可以在项目中建一个lib目录,上面的jar文件复制到项目的lib目录下,再选中jar文件,右键--> Build Path-->Add to Build Path..,将jar包引用到项目中
2.3.创建一个测试类: com.JdbcTest01
//1.注册数据库驱动
//2.获取数据库连接
//3.获取传输器
//4.发送SQL语句到服务器执行,并返回结果
//5.处理执行的结果
//6.释放资源
3、JDBC API总结
1)Class.forName("com.mysql.jdbc.Driver");
将mysql驱动包中的"com.mysql.jdbc.Driver"类加载到内存中,Driver类中的静态代码块就会执行,而在Driver类的静态代码块中有一行代码是用于注册驱动的,因此这行代码可以注册驱动!
注册驱动: 将mysql驱动交给JDBC程序管理,以便于使用其中的功能
在JDBC4.0以后的版本中,这一步可以省略,但还是建议加上
2)DriverManager.getConnection(url,user,password)
url:指定要连接的是哪一个位置的哪一个库
jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8
如果连接的数据库端口是3306,端口可以省略不写:
jdbc:mysql://localhost/jt_db?characterEncoding=utf-8
如果是连接本机上的数据库,主机名/IP地址可以省略不写:
jdbc:mysql:///jt_db?characterEncoding=utf-8
getConnection方法返回一个Connection对象,用于表示Java程序和数据库服务器之间的连接。
3)Statement stat = conn.createStatement()
conn.createStatement() -- 用于获取向数据库发送SQL语句的传输器对象
stat.executeQuery(sql) -- 用于执行查询类型的SQL语句,返回的是一个ResultSet对象
stat.executeUpdate(sql) -- 用于执行新增、删除、修改类型的SQL语句,返回一个int值,表示影响的记录行数
4)rs.next() -- 用于将指向数据行的箭头往下挪动一行,并且返回布尔值(true或false),
true表示箭头往下挪动一行后,指向的行有数据;false表示箭头往下挪动一行后,指向的行没有数据;
5)在ResultSet结果集对象上提供了获取数据的方法,常见的有:
rs.getInt( colName );
rs.getInt( colCount );
rs.getString( colName );
rs.getString( colCount );
rs.getDobule( colName );
rs.getDobule( colCount );
...
rs.getObject( colName );
rs.getObject( colCount );
二、JDBC的增删改查
1、新增:往account表中添加一条记录:名称为'john',金额是3500
2、修改:修改account表中名称为'john'的金额,将金额改为1500
3、删除:删除account表中名称为'john'的记录
三、junit单元测试框架
junit(单元测试框架): 可以不用添加main函数,也不用创建类的实例就可以执行一个方法
能够用单元测试执行的方法必须满足如下条件:
1)方法必须是公共的(public)
2)方法必须是非静态的
3)方法必须是无返回值的(void)
4)方法必须是无参数的
如果执行的方法不满足以上任何一个条件,就会报如下错误:
java.lang.Exception: No tests found matching...
junit(单元测试框架)常用的三个注解: @Test、@before、@after
@Test:使用了该注解的方法,每次选中方法名,右键-->Run as-->junit test都可以执行该方法
@before:使用了该注解的方法,每次会在@Test标记的方法之前执行。也就是说,每次在执行@Test标记的方法之前都会执行@before标记的方法
@after:使用了该注解的方法,每次会在@Test标记的方法之后执行。也就是说,每次在执行@Test标记的方法之后都会执行@after标记的方法
四、PreparedStatement对象
1、模拟用户登录案例
PreparedStatement对象是Statement传输器对象的子对象
PreparedStatement对象比Statement对象更加安全,在某些方面执行的效率上也会更高一些!
下面以一个模拟登录的案例来讲解PreparedStatement对象
----------------------------------------------------------
请登录:
请输入用户名:
tom'#'
请输入密码:
select * from user where username='tom'#'' and password=''
恭喜您登录成功!
----------------------------------------------------------
请登录:
请输入用户名:
张飞' or '1=1
请输入密码:
select * from user where username='张飞' or '1=1' and password=''
恭喜您登录成功!
----------------------------------------------------------
2、SQL注入攻击
SQL注入攻击产生的原因:由于SQL语句中的参数是拼接而来的,其中的参数值(username和password的值)是用户提交过来的,
如果用户在提交参数时,在参数中掺杂一些SQL关键字或特殊字符(or、#、-- 、/* */等)就可能会导致SQL语句的语义被篡改,
从而执行一些意外的操作(比如用户名或密码错误也可以登录系统或网站)
delete from user where id=1 or 1=1;
3、如何解决SQL注入攻击问题
1)可以对用户提交过来的参数进行校验(例如通过正则表达式对用户名和密码进行校验),
如果用户名或密码中有类似于 or、#、-- 等符号,就不再登录,直接提示用户输入不合法,请重新登录!
2)或者使用JDBC中提供的PreparedStatement对象,可以解决SQL注入攻击问题!
PreparedStatement对象是如何解决SQL注入攻击的?
1)PreparedStatement对象是先将SQL语句的骨架(不包含参数)发送给服务器编译并确定下来。
String sql = "select * from user where username=? and password=?";
PreparedStatement stat = conn.prepareStatement(sql);
2)再将SQL语句中的参数值传递给服务器
//设置SQL语句中的参数值
stat.setString( 1, user );
stat.setString( 2, psw );
//执行SQL语句,返回执行结果
ResultSet rs = stat.executeQuery();
由于前面SQL语句的骨架已经被确定了,因此SQL参数中即使再包含SQL关键字或者特殊符号,
也不会影响SQL语句的骨架或语义,只会被当前普通的文本来处理,因此可以防止SQL注入!
五、数据库连接池
1、什么是连接池?
池:常量池、线程池、连接池等中的池都是一个容器。是指内存中的一片空间
连接池:就是将一批连接资源存入到一个容器中。目的是为了实现连接的复用,减少连接创建和关闭的次数,
以此来提高程序执行的效率!
2、为什么要使用连接池?
传统方式中,每次需要连接都是直接创建连接,再基于这个创建的连接去连接数据库,最后用完连接还要关闭!
而每次创建连接和关闭连接相比使用连接是要消耗大量的时间和资源,导致程序的执行效率非常低下!
为了提高程序执行的效率,我们可以在程序一启动时,就创建一批连接放在一个连接池中,供整个程序共享。
当用户需要连接时,不需要再创建连接,而是直接从连接池中获取一个连接进行使用,用完连接后,也不需要关闭,
而是直接将连接还回连接池中。这样一来,用来用去都是连接池中的这一批连接,必然可以实现连接的复用,
减少连接创建和关闭的次数。提高程序执行的效率!
3、如何使用c3p0连接池?
第01步:导入c3p0的jar包,在课前资料中找到"c3p0-0.9.1.2.jar"并导入到项目中
第02步:在程序中创建一个c3p0连接池对象(存放连接的容器)
ComboPooledDataSource pool = new ComboPooledDataSource();
第03步:设置连接数据库的基本信息(四个参数)
方式一:将连接数据库的参数通过setXxx方法直接通过java代码写死在程序中
pool.setDriverClass("com.mysql.jdbc.Driver");
pool.setJdbcUrl("jdbc:mysql:///jt_db?characterEncoding=utf-8");
pool.setUser("root");
pool.setPassword("root");
这种方式不推荐使用,因为这种方式将连接参数写死在程序中了,将来一旦参数发生变化,就需要我们去改程序,
改完之后需要对项目重新编译、打包、部署、运行等,会提高维护成本!
方式二:将连接数据库的参数提取到 c3p0.properties(文件名是固定的) 文件中
并且需要将这个文件放在源码根目录(src根目录)下,文件内容如下:
-----------------------------------------
c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql:///jt_db?characterEncoding=utf-8
c3p0.user=root
c3p0.password=root
-----------------------------------------
再次强调:这个文件的位置和名字都是固定的,因为底层c3p0会到指定的位置找指定名称的文件,
如果没有按照要求去存放文件或者没有按照要求去指定文件名称,都会导致c3p0找不到这个文件,
也就无法读取其中的配置信息,必然会导致连不上数据库!
方式三:将连接数据库的参数提取到 c3p0-config.xml(文件名也是固定的) 文件中
并且需要将这个文件放在源码根目录(src根目录)下,文件内容如下:
-----------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">
com.mysql.jdbc.Driver
</property>
<property name="jdbcUrl">
jdbc:mysql:///jt_db?characterEncoding=utf-8
</property>
<property name="user">root</property>
<property name="password">root</property>
</default-config>
</c3p0-config>
-----------------------------------------
再次强调:这个文件的位置和名字都是固定的,因为c3p0底层会到指定的位置找指定名称的文件,
如果没有按照要求去存放文件或者没有按照要求去指定文件名称,都会导致c3p0找不到这个文件,
也就无法读取其中的配置信息,必然会导致连不上数据库!
第04步:从连接池中获取一个连接对象进行使用
Connection conn = pool.getConnection();
第05步:将用完的连接对象还回到连接池中
conn.close();
如果在程序中没有使用任何连接池,需要连接就通过 DriverManager.getConnection获取(创建)一个连接,用完之后,调用conn.close()就是将连接资源关闭。
如果使用了连接池,通过 连接池对象调用 getConnection方法获取一个连接对象,此时获取的连接对象已经被改造了。用完之后,再调用conn.close()方法是将连接还回到连接池中。也就是说,从连接池中获取的连接对象上的close方法被改造成了还连接到连接池!
==================================================
一、什么是事务?
事务:简单的说,事务是将一堆的SQL语句绑定在一起执行,结果是要么全都执行成功,要么全都执行失败。而且是都成功了才算成功,但凡有一条执行失败,就按全失败来处理!
举例: 张三(1000)给李四(1000)转账100元
-- 开启事务
-- 给张三的账户金额减去100元
update 账户表 set money=money-100 where name='张三';
-- 给李四的账户金额加上100元
update 账户表 set money=money+100 where name='李四';
-- 提交事务/回滚事务
举例: 网上购物
-- 开启事务
-- 往订单表中插入一条订单信息(用户、订单号、商品信息、商品数据量、单价、总金额等)
insert into 订单表 value(....);
-- 减去商品库存表中的库存数量
update 商品库存表 set count=count-2 where...
-- 提交事务/回滚事务
二、事务的四大特性(重要)
1、原子性:表示事务中的所有操作(SQL)是一个整体,不能被分割,要么全都执行成功,要么全都执行失败!
2、一致性:在事务前后的业务数据之和是保持一致的。
在转账操作之前,张三账户金额(1000)和李四账户金额(1000)之和为2000元
在转账操作之后,无论事务提交了还是回滚了,张三和李四的账户金额之和还是2000元。
3、隔离性:是指所有的事务都是隔离开来的,在一个事务中看不到另外一个事务正在进行中的状态!
事务1: 查询A(1000)和B(1000)的账户总金额
事务2: -- 开启事务
A账户减去100元 -- A:900
B账户加上100元 -- B:1100
-- 提交事务/回滚事务
如果事务的隔离级别非常低,一个事务就可能会看到另外一个事务正在进行中的操作/状态(脏读)
4、持久性:在事务提交后,对数据的更新操作才会持久的保存到数据库中
-- 开启事务
A账户减去100元 -- A:900
B账户加上100元 -- B:1100
-- 提交事务/回滚事务
三、MySQL中的事务
在mysql中默认一条SQL语句就是一个事务。
如果希望将多条SQL放在一个事务中执行,可以手动开启事务,并手动结束事务
开启事务: start transaction;
结束事务:提交(commit) 和 回滚(rollback)
例子:使用转账演示mysql中如何开启事务以及如何结束事务
-- 开启事务
start transaction;
-- A账户减去100元
update acc set money=money-100 where name='A';
-- B账户加上100元
update acc set money=money+100 where name='B';
select * from acc;
-- 回滚事务 | 提交事务
rollback; | commit;
select * from acc;
补充:
Statement的。。
executeUpdate()方法会返回int类型,表示有多少条数据受到了影响。
executeQuery()方法会返回一个boolean类型,返回一个结果集
createPrepareStatement()创建
Statement preparedStatement是接口
JDBC执行查询语句没有查询到数据,ResultSet将返回result对象ResultSet有一个记录指针,
-0
855,初始状态下记录指针起始位置位于第一行之前