JDBC

一、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&amp;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;
阅读 123
13 声望
6 粉丝
0 条评论
你知道吗?

13 声望
6 粉丝
宣传栏