一、JDBC概述
1、什么是JDBC、为什么要学习JDBC?
JDBC(Java DataBase Connectivity)Java数据库连接
其实就是利用Java语言/Java程序连接并访问数据库的一门技术
虽然之前我们可以通过 cmd或者navicat 连接数据库,也可以对数据库、表、表记录等进行操作。
但是,将来在开发中更多的是通过程序来连接数据库,如果是Java语言,通过Java程序连接数据库,就必须要学习JDBC这门技术。
2、如何通过Java程序连接mysql数据库?(快速入门)
创建一个测试类: com.tedu.JdbcTest01
//1.注册数据库驱动
//2.获取数据库连接
//3.获取传输器
//4.发送SQL语句到服务器执行,并返回结果
//5.处理执行的结果
//6.释放资源
3、JDBC API总结
1)Class.forName("com.mysql.cj.jdbc.Driver");
将mysql驱动包中的"com.mysql.cj.jdbc.Driver"类加载到内存中,Driver类中的静态代码块就会执行,而在Driver类的静态代码块中有一行代码是用于注册驱动的,因此这行代码可以注册驱动!
注册驱动: 将mysql驱动交给JDBC程序管理,以便于使用其中的功能
在JDBC4.0以后的版本中,这一步可以省略,但还是建议加上
2)DriverManager.getConnection(url,user,password)
url:指定要连接的是哪一个位置的哪一个库
jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
如果连接的数据库端口是3306,端口可以省略不写:
jdbc:mysql://localhost/jt_db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
如果是连接本机上的数据库,主机名/IP地址可以省略不写:
jdbc:mysql:///jt_db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
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表中添加一条记录:名称为'lucy',金额是3500
2、修改:修改account表中名称为'lucy'的金额,将金额改为2000
3、删除:删除account表中名称为'lucy'的记录
三、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对象
Statement 传输器
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=''
select * from user where username='tom' 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连接池?
dbcp/c3p0/druid/hikari
由于所有的连接池技术都实现了SUN公司所提供的DataSource接口
所以连接池也叫作"数据源"
第01步:在程序中创建一个c3p0连接池对象(存放连接的容器)
ComboPooledDataSource pool = new ComboPooledDataSource();
第02步:设置连接数据库的基本信息(四个参数)
方式一:将连接数据库的参数通过setXxx方法直接通过java代码写死在程序中
pool.setDriverClass("com.mysql.cj.jdbc.Driver");
pool.setJdbcUrl("jdbc:mysql:///jt_db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai");
pool.setUser("root");
pool.setPassword("root");
这种方式不推荐使用,因为这种方式将连接参数写死在程序中了,将来一旦参数发生变化,就需要我们去改程序,改完之后需要对项目重新编译、打包、部署、运行等,会提高维护成本!
方式二:将连接数据库的参数提取到 c3p0.properties(文件名是固定的) 文件中
并且需要将这个文件放在源码根目录(src根目录)下,文件内容如下:
-----------------------------------------
c3p0.driverClass=com.mysql.cj.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql:///jt_db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
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.cj.jdbc.Driver
</property>
<property name="jdbcUrl">
jdbc:mysql:///jt_db?characterEncoding=utf-8&serverTimezone=Asia/Shanghai
</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方法被改造成了还连接到连接池!
4、补充内容:xml文件和properties文件的区别
相同点:这两种文件在企业开发中都可以作为配置文件使用,而且用的特别多
不同点:1)xml文件缺点:配置信息多,编写起来比较麻烦,如果需要通过java程序读取,代码也比较麻烦。
2)xml文件的优点:可以保存有结构的数据(例如在xml文件中保存中国所有的省份以及省份包含的市区)
3)properties文件缺点:配置信息结构是key/value,无法保存有结构的数据
4)properties文件优点:配置信息简洁,如果需要通过java程序读取,读取起来也比较方便。
==================================================
一、什么是事务?
事务:简单的说,事务是将一堆的SQL语句绑定在一起执行,结果是要么全都执行成功,要么全都执行失败。而且是都成功了才算成功,但凡有一条执行失败,就按全失败来处理!
举例: 张三(1000)给李四(1000)转账100元
-- 开启事务(start transaction)
-- 给张三的账户金额减去100元
update 账户表 set money=money-100 where name='张三'; -- 1000
-- 给李四的账户金额加上100元
update 账户表 set money=money+100 where name='李四'; -- 1000
-- 提交事务(commit)/ 回滚事务(rollback)
举例: 网上购物
-- 开启事务
-- 往订单表中插入一条订单信息(用户、订单号、商品信息、商品数据量、单价、总金额等)
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账户(1000元)减去100元 -- A:900
B账户(1000元)加上100元 -- B:1100
-- 提交事务/回滚事务
三、MySQL中的事务
在mysql中默认一条SQL语句就是一个事务。
如果希望将多条SQL放在一个事务中执行,可以手动开启事务,并手动结束事务
开启事务: start transaction / begin;
结束事务:提交(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;
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。