一、JDBC实现CRUD
课前默写
1、写出SQL语句中的各种约束2、写出内连接的格式3、写出左外和右外连接的格式4、写出子查询
课程回顾
1、SQL语言数据的完整性2、多表查询3、多表查询操作4、SQL优化
今日内容
1、JDBC简介2、JDBC执行DML语句3、JDBC执行DQL语句4、JDBC的ResultSet
教学目标
1、了解JDBC简介2、掌握JDBC执行DML语句3、掌握JDBC执行DQL语句4、掌握JDBC的ResultSet
第一章 关于JDBC的简介
1.1 简介
JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序
Java 具有坚固、安全、易于使用、易于理解和可从网络上自动下载等特性,是编写数据库应用程序的杰出语言。所需要的只是 Java应用程序与各种不同数据库之间进行对话的方法。
JDBC可以在各种平台上使用Java,如Windows,Mac OS和各种版本的UNIX。
JDBC库包括通常与数据库使用相关的下面提到的每个任务的API。
- 连接数据库。
- 创建SQL或MySQL语句。
- 在数据库中执行SQL或MySQL查询。
- 查看和修改生成的记录。
8.2 JDBC体系结构
JDBC API支持用于数据库访问的两层和三层处理模型,但通常,JDBC体系结构由两层组成:
- JDBC API:这提供了应用程序到JDBC管理器连接。
- JDBC驱动程序API:这支持JDBC管理器到驱动程序连接。
JDBC API使用驱动程序管理器和特定于数据库的驱动程序来提供与异构数据库的透明连接。
8.3 JDBC核心组件
**DriverManager:** 此类管理数据库驱动程序列表。使用通信子协议将来自java应用程序的连接请求与适当的数据库驱动程序匹配。<br /> **Driver**:此接口处理与数据库服务器的通信,我们很少会直接与Driver对象进行交互。而是使用DriverManager对象来管理这种类型的对象。<br /> **Connection:**该界面具有用于联系数据库的所有方法。连接对象表示通信上下文,即,与数据库的所有通信仅通过连接对象。<br /> **Statement**:使用从此接口创建的对象将SQL语句提交到数据库。除了执行存储过程之外,一些派生接口还接受参数。<br /> **ResultSet:**在使用Statement对象执行SQL查询后,这些对象保存从数据库检索的数据。它作为一个迭代器,允许我们移动其数据。<br /> **SQLException:**此类处理数据库应用程序中发生的任何错误
第二章 JDBC相关的SQL语法
2.1 CRUD语法介绍
SQL 是一种标准化的语言,它允许你在数据库上执行操作,如创建项目,查询内容,更新内容,并删除条目等操作。<br /> Create, Read, Update, and Delete 通常称为CRUD操作。<br />CREATE DATABASE语句用于创建新的数据库:
SQL> CREATE DATABASE DATABASE_NAME;
DROP DATABASE语句用于删除现有数据库:
SQL> DROP DATABASE DATABASE_NAME;
CREATE TABLE语句用于创建新表。语法是 -
SQL> CREATE TABLE Employees(id INT NOT NULL,age INT NOT NULL,first VARCHAR(255),last VARCHAR(255),PRIMARY KEY ( id ));
DROP TABLE语句用于删除现有表。
SQL> DROP TABLE table_name;
INSERT的语法类似于以下内容,其中column1,column2等表示要显示在相应列中的新数据
SQL> INSERT INTO table_name VALUES (column1, column2, ...);
SELECT语句用于从数据库中检索数据。SELECT的语法是 -
SQL> SELECT column_name, column_name, ...FROM table_nameWHERE conditions;
WHERE子句可以使用比较运算符,例如=,!=,<,>,<=和> =,以及BETWEEN和LIKE运算符。
UPDATE语句用于更新数据。
SQL> UPDATE table_nameSET column_name = value, column_name = value, ...WHERE conditions;
WHERE子句可以使用比较运算符,例如=,!=,<,>,<=和> =,以及BETWEEN和LIKE运算符。
DELETE语句用于从表中删除数据。
SQL> DELETE FROM table_name WHERE conditions;
WHERE子句可以使用比较运算符,例如=,!=,<,>,<=和> =,以及BETWEEN和LIKE运算符。
第三章 JDBC初始
3.1 使用步骤
构建JDBC应用程序涉及以下六个步骤:
- 导入包:需要包含包含数据库编程所需的JDBC类的包。大多数情况下,使用import java.sql。*就足够了。
- 注册JDBC驱动程序:要求您初始化驱动程序,以便您可以打开与数据库的通信通道。
- 打开连接:需要使用DriverManager.getConnection()方法创建一个Connection对象,该对象表示与数据库的物理连接。
- 执行查询:需要使用类型为Statement的对象来构建和提交SQL语句到数据库。
- 从结果集中提取数据:需要使用相应的ResultSet.getXXX()方法从结果集中检索数据。
释放资源:需要明确地关闭所有数据库资源,而不依赖于JVM的垃圾收集。
3.2 JDBC连接步骤
建立JDBC连接所涉及的编程相当简单。这是简单的四个步骤
导入JDBC包:将Java语言的import语句添加到Java代码中导入所需的类。
- 注册JDBC驱动程序:此步骤将使JVM将所需的驱动程序实现加载到内存中,以便它可以满足您的JDBC请求。
- 数据库URL配置:这是为了创建一个格式正确的地址,指向要连接到的数据库。
- 创建连接对象:最后,调用DriverManager对象的getConnection()方法来建立实际的数据库连接。
Class.forName();
注册驱动程序最常见的方法是使用Java的Class.forName()方法,将驱动程序的类文件动态加载到内存中,并将其自动注册
try {Class.forName("oracle.jdbc.driver.OracleDriver");}catch(ClassNotFoundException ex) {System.out.println("Error: unable to load driver class!");System.exit(1);}
DriverManager.registerDriver()
可以用来注册驱动程序的第二种方法是使用静态DriverManager.registerDriver()方法。
try {Driver myDriver = new com.mysql.jdbc.Driver();DriverManager.registerDriver( myDriver );}catch(ClassNotFoundException ex) {System.out.println("Error: unable to load driver class!");System.exit(1);}
数据库URL配置
加载驱动程序后,可以使用DriverManager.getConnection()方法建立连接。为了方便参考,让我列出三个重载的DriverManager.getConnection()方法 -
- getConnection(String url)
- getConnection(String url,Properties prop)
- getConnection(String url,String user,String password) | RDBMS | JDBC驱动程序名称 | 网址格式 | | —- | —- | —- | | MySQL的 | com.mysql.jdbc.Driver | jdbc:mysql://hostname / databaseName | | ORACLE | oracle.jdbc.driver.OracleDriver | jdbc:oracle:thin:@ hostname:port Number:databaseName | | DB2 | COM.ibm.db2.jdbc.net.DB2Driver | jdbc:db2:hostname:port Number / databaseName | | SYBASE | com.sybase.jdbc.SybDriver | jdbc:sybase:Tds: hostname:port Number / databaseName |
创建数据库连接对象
String URL = "jdbc:mysql://localhost:3306/EMP";String USER = "username";String PASS = "password"Connection conn = DriverManager.getConnection(URL, USER, PASS);
使用数据库URL和属性对象
DriverManager.getConnection()方法的第三种形式需要一个数据库URL和一个Properties对象 -
DriverManager.getConnection(String url, Properties info);
import java.util.*;String URL = "jdbc:mysql://localhost:3306/EMP";Properties info = new Properties( );info.put( "user", "username" );info.put( "password", "password" );Connection conn = DriverManager.getConnection(URL, info);
关闭数据库连接
为确保连接关闭,您可以在代码中提供一个“finally”块。一个finally块总是执行,不管是否发生异常。
要关闭上面打开的连接,你应该调用close()方法如下 -
conn.close();
第四章 JDBC执行SQL语句
一旦获得了连接,我们可以与数据库进行交互。JDBC Statement和PreparedStatement接口定义了使您能够发送SQL命令并从数据库接收数据的方法和属性。
| 接口 | 推荐使用 |
|---|---|
| 声明 | 用于对数据库进行通用访问。在运行时使用静态SQL语句时很有用。Statement接口不能接受参数。 |
| PreparedStatement的 | 当您计划多次使用SQL语句时使用。PreparedStatement接口在运行时接受输入参数。 |
4.1 Statement
创建语句对象
在使用Statement对象执行SQL语句之前,需要使用Connection对象的createStatement()方法创建一个,如下例所示:
Statement stmt = null;try {stmt = conn.createStatement( );. . .}catch (SQLException e) {. . .}finally {. . .}
创建Statement对象后,您可以使用它来执行一个SQL语句,其中有三个执行方法之一。
- boolean execute(String SQL):如果可以检索到ResultSet对象,则返回一个布尔值true; 否则返回false。使用此方法执行SQL DDL语句或需要使用真正的动态SQL时。
- int executeUpdate(String SQL):返回受SQL语句执行影响的行数。使用此方法执行预期会影响多个行的SQL语句,例如INSERT,UPDATE或DELETE语句。
- ResultSet executeQuery(String SQL):返回一个ResultSet对象。当您希望获得结果集时,请使用此方法,就像使用SELECT语句一样。
关闭Statement对象
就像我们关闭一个Connection对象以保存数据库资源一样,由于同样的原因,还应该关闭Statement对象。
一个简单的调用close()方法将执行该作业。如果先关闭Connection对象,它也会关闭Statement对象。但是,应始终显式关闭Statement对象,以确保正确清理。
Statement stmt = null;try {stmt = conn.createStatement( );. . .}catch (SQLException e) {. . .}finally {stmt.close();}
4.2 SQL注入
就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击。
4.3 PreparedStatement
该PreparedStatement的接口扩展了Statement接口,它为您提供了一个通用的Statement对象有两个优点附加功能。
此语句使您可以动态地提供参数。
PreparedStatement pstmt = null;try {String SQL = "Update Employees SET age = ? WHERE id = ?";pstmt = conn.prepareStatement(SQL);. . .}catch (SQLException e) {. . .}finally {. . .}
JDBC中的所有参数都由?符号,这被称为参数标记。在执行SQL语句之前,必须为每个参数提供值。
所述的setXXX()方法将值绑定到所述参数,其中XXX代表要绑定到输入参数的值的Java数据类型。如果忘记提供值,将收到一个SQLException。
每个参数标记由其顺序位置引用。第一个标记表示位置1,下一个位置2等等。该方法与Java数组索引不同,从0开始。
关闭PreparedStatement对象
就像关闭Statement对象一样,由于同样的原因,还应该关闭PreparedStatement对象。
一个简单的调用close()方法将执行该作业。如果先关闭Connection对象,它也会关闭PreparedStatement对象。但是,应始终显式关闭PreparedStatement对象,以确保正确清理。
PreparedStatement pstmt = null;try {String SQL = "Update Employees SET age = ? WHERE id = ?";pstmt = conn.prepareStatement(SQL);. . .}catch (SQLException e) {. . .}finally {pstmt.close();}
4.4 ResultSet
SELECT语句是从数据库中选择行并在结果集中查看行的标准方法。该java.sql.ResultSet中的接口表示结果集数据库查询。
ResultSet对象维护指向结果集中当前行的游标。术语“结果集”是指包含在ResultSet对象中的行和列数据。
4.4.1 ResultSet类型
如果没有指定任何ResultSet类型,您将自动获得一个TYPE_FORWARD_ONLY。
| 类型 | 描述 |
|---|---|
| ResultSet.TYPE_FORWARD_ONLY | 光标只能在结果集中向前移动。 |
| ResultSet.TYPE_SCROLL_INSENSITIVE | 光标可以向前和向后滚动,结果集对创建结果集后发生的数据库的其他更改不敏感。 |
| ResultSet.TYPE_SCROLL_SENSITIVE。 | 光标可以向前和向后滚动,结果集对创建结果集之后发生的其他数据库所做的更改敏感。 |
try {Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,ResultSet.CONCUR_READ_ONLY);}catch(Exception ex) {....}finally {....}
作业题
1.登录创建user表id int 主键 自增username varcharpassword varchar创建user_leaf 用户个人信息表id int 主键 自增nameagesexu_id 外键控制台输入用户名 密码连接数据库 判断和数据库里的是否一致一致 显示 登录成功 不一致 显示登陆失败扩展 如登录成功显示user个人信息表信息2.根据id查询指定的学生记录创建学生信息表 创建方法 传入学生id 获取学生信息student 表sid(int 主键 自增)sname(varchar 非空)sage(int 默认值为10)ssex(varchar)birthday(date)score(double 保留小数点后2位)扩展 1.将 score单独存储到student_s表中一对多关系将数据存储到合适的集合中 遍历结合获取数据2.将集合中的数据 写出到student.txt文件中 以逗号分隔
一、复习
1)多表查询
union/union all, 内连接/外连接,子查询
2)三大范式
3)作业和练习
二、IDEA IDE工具的使用
IDE 工具就是开发软件使用的工具:Eclipse,MyEclipse,IDEA,NetBeans
将光标放到有异常的该行,alt + enter 有代码提示ctrl + d 复制当前行 ctrl + y 删除当前行ctrl + p 可以查看一个方法中有哪些参数如果代码比较乱 全部选中 ctrl + alt + L 对代码进行排版
三、JDBC
JDBC 是java 语言操作数据库的一种技术。 JDBC是Hiberante,Mybatis,JDBCTemplate,DBUtils 等工具或者框架的基础。
JDBC是一个接口,只定义接口方法,不实现内容。如果一个数据库厂商想让Java开发人员使用他们家的数据库,就需要实现
JDBC接口,打成一个工具包jar,这个工具包就称之为数据库驱动包。
JDBC 想要连接MySQL ,就需要一个MySQL的驱动包。
1、加载驱动
Class.forName(“com.mysql.jdbc.Driver”);
但是我们之前学过的反射,得知以上语句只是为了获得Driver类 的Class 类对象而已,毫无实际意义
通过源码:
static {try {DriverManager.registerDriver(new Driver());} catch (SQLException var1) {throw new RuntimeException("Can't register driver!");}}
该代码块是用来加载驱动的,我们通过Class.forName(“com.mysql.jdbc.Driver”),可以让虚拟机加载Driver,刚好出发Driver类中的静态代码块。
从JDBC3开始,不需要写加载数据库驱动这句话了。
2、获取数据库连接
两种写法:String url="jdbc:mysql://localhost:3306/jdbc?characterEncoding=utf8";String userName="root";String password="root";Connection connection = DriverManager.getConnection(url, userName, password);System.out.println(connection);第二种写法:String url="jdbc:mysql://localhost:3306/jdbc?characterEncoding=utf8";Properties properties = new Properties();properties.setProperty("user","root");properties.setProperty("password","root");/*** info a list of arbitrary string tag/value pairs asconnection arguments. Normally at least a "user" and"password" property should be included.*/Connection connection = DriverManager.getConnection(url, properties);System.out.println(connection);
3、创建Statement 只有statement对象才能执行SQL语句
DML语句 executeUpdate
DQL语句 executeQuery
4、ResultSet
resultSet.next() 将光标指向下一行数据,如果有数据,返回true
resultSet.getString(“列名”) 获取字符串类型的列的数据
resultSet.getInt(“列名”) 获取数字类型的列的数据
resultSet.getDouble(“”) 获取double类型的列的数据
四、注册案例
出现了SQL注入的问题,登录 人员只要进行一个稍微的变化,错误的账号和密码就能进入
请输入您的用户名:
skfjsdkfsd
请输入您的密码:
a’ or ‘’=’
select * from user where username=’skfjsdkfsd’ and password=’a’ or ‘’=’’
结论: Statement 这个类执行的SQL语句,有SQL注入的风险!!!!
五、PreparedStatement 接口
它是Statement 接口的子接口,可以有效的防止SQL注入
PreparedStatement
通过:connection.prepareStatement(sql); 获取该对象
SQL语句中需要传递值的地方先用?占位,然后通过statement对象传递 值。
执行SQL的时候,无需再次传入SQL:statement.executeQuery();
PreparedStatement 和 Statement 相比:它是一个预编译SQL的技术
Statement: 每一条SQL语句,都需要先编译,后运行,1W ,编译1W,执行1W次
PreparedStatement: 我先给数据库发送一个SQL模板,编译一次,后面每次执行无需再 编译。
二、复习JDBC
1、复习
jdbc— 一定要导入正确的jar包—mysql数据库驱动包
编写:分6步骤,代码无需强行记忆
1)数据库驱动
2)获取数据库连接
3)获取Statement/PreparedStatement
4) 执行SQL
5)获取结果集
6)关闭连接
但凡是重复的代码,我们都抽取成方法,抽取出来的类,就是工具类,类中的方法一般都是static方法。
Statement 无法防止SQL注入
PreparedStatement 防止SQL注入,SQL预编译,执行效率更高
2、JDBC事务
事务: 是一个非常重要的东西,一定要认真对待。一件事儿要么同时成,要么同时不成,不能出现事成一半的问题。<br /> 如果半道出现问题,请回滚到起初的样子,rollback.<br /> 问题:<br /> 演示的代码显示:Rose 扣款10元成功,但是因为中间出现异常,导致Jack账户上并没有增加10元。<br /> 原因是:jdbc的事务是自动提交的,每执行一次SQL就提交一次事务。<br /> 改:<br /> 1) 将自动提交事务,改为手动提交 connection.setAutoCommit(false);<br /> 2) 如果中间没有出现异常,再提交事务。connection.commit();<br /> 3) 如果中间有异常,就回滚事务 connection.rollback();
3、MySQL事务
1)默认情况下,我们的每一条SQL语句都是默认提交的。
update account set money=money-10 where name='Rose';update account set money=money+10 where name='Jack';
2)如果遇到类似于转账的操作,几条SQL要么同时成功,要么回滚<br /> 1、手动开启事务:start transaction;<br /> 2、执行需要执行的SQL:
update account set money=money-10 where name='Rose';
update account set money=money+10 where name='Jack';
我们发现,手动开启事务后,如果事务不提交,update语句更新的数据不显示。<br /> 3、手动开启事务后,必须手动提交 commit ,数据才算更新或保存。<br /> 4、如果中间有问题,可以选择不提交,使用回滚rollback操作,这个时候可以提交,但是数据已经变成原来的数据了。<br /> 3) 自动提交和手动提交的开关【了解即可,不要改】<br /> select @@autocommit; // 1代表SQL自动提交,0代表手动提交<br /> set @@autocommit=0;<br /> 可以修改,但是最好不要改,因为我们的代码大部分都是依赖自动提交的。<br /> 4)一旦我们的客户端发送给服务器指令: start transaction;<br /> 后面执行的所有SQL,都会存入到临时文件中,临时文件里面的数据只有等到commit,或者rollback 才会清空临时文件。<br /> commit 清空临时文件,将正确数据存入到真正的数据库中,rollback 只是清空记录,不做任何操作。<br /> 5)保存点 savapoint
start transaction;
update account set money=money-1 where name='Jack';
update account set money=money-1 where name='Jack';
update account set money=money-1 where name='Jack';
savepoint three_point;
update account set money=money-1 where name='Jack';
update account set money=money-1 where name='Jack';
rollback to three_point;
commit;// 发现真正的数据,只减了三次钱
4、事务的隔离级别
1、事务的四大特征 ACID<br /> 原子性,一致性,隔离性,持久性<br /> 2、事务的隔离级别<br /> 同一时间: 张三转账,李四转账,王五存钱,赵六取钱.....<br /> 多个人同时操作同一个数据库,虽然理想情况下我们不想让他们之前有任何 的联系,<br /> 但是事与愿违,因为数据库无法办到,所以我们只能取舍!!!<br /> 引发:脏读,幻读,不可重复读三大问题。<br /> 为了解决这些问题,各大数据库厂商推出了数据库的四个隔离级别来应对这些问题。<br /> 3、演示四大隔离级别 安全性和执行效率永远是对立的<br /> <br /> mySQL 默认的给力级别是:REPEATABLE-READ select @@tx_isolation;<br /> 1) 读未提交 read uncommitted 虽然引发的问题很多,但是运行效率极高 <br /> 修改数据库隔离级别:
set global transaction isolation level read uncommitted;
会出现脏读、幻读、不可重复读的问题。<br /> 脏读:一个事务读取到了另一个事务的尚未提交的数据。<br /> 一个问题:Rose 给Jack转钱,Rose没有提交的情况下,Jack能把钱取走吗?<br /> mysql 默认存储引擎是InnoDB, InnoDB数据库的锁是行级锁。<br /> 如果一个事务操作某一行数据,没有操作完的情况下,另一个事务是无法操作的。<br /> 结论:取不走。<br /> 2) 读已提交 read committed
set global transaction isolation level read committed;
这个隔离级别可以解决脏读的问题,但是无法解决不可重复读的问题。<br /> 不可重复读的问题:在我们的一个事务中,读取到的数据必须是一样的,不能变化。如果变化,说明出现了不可重复读的问题。<br /> 在同一个事务中,我们第一次读取Jack数据,和后面读取到的杰克的数据,不一样。<br /> 3)repeatable read 可重复度级别 --能够解决不可重复度的问题
set global transaction isolation level repeatable read;
我们看到,隔离级别一提升,在同一个事务中多次读取到的数据,一直没有发生变化,问题解决<br /> 4)幻读的问题,mysql无法演示。<br /> 5) serializable 串行化 可以解决所有安全问题,但是效果类似于锁表<br /> 每次只能有一个人操作你的数据库。
5、数据库管理 DCL
1、关于用户的操作
// 创建用户
create user 'laoyan'@'localhost' identified by 'laoyan';
// 给用户赋权限
grant all on *.* to 'laoyan'@'localhost';
// 收回用户的权限
revoke all on *.* from 'laoyan'@'localhost';
// 查看某个用户的权限:
show grants for 'root'@'localhost';
// 删除权限
drop user 'laoyan'@'localhost';
2、关于默认四张表
安装完数据库之后,默认会有四张表:mysql,test, information_xxx,performance_xxx
这四个表除了test,其余不要乱动,否则数据库崩溃。
3、如何修改数据库密码(了解)
// 修改用户的密码
第一种是可以登录进去,之前的密码还记得:
5.5 版本:
use mysql;
update user set password=password('123456');
5.7 版本:<br /> 没有password字段了,修改authentication_string
update user set authentication_string=password('123456');
重启服务才能生效。<br /> 第二种是把账号密码忘记了,可以试试下面的方式 :查看文档<br /> 4、如何配置mysql服务器的远程连接<br /> 如果远程连接没有开,如何开启?<br /> 第一种(改表法): % 找到mysql数据库中的user表,找到root用户,将Host中的localhost修改为%,重启服务。<br /> 第二种(授权法):
GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'root' WITH GRANT OPTION;
FLUSH PRIVILEGES;
三、数据库连接池
今日内容
1、自定义数据库连接池
2、使用DBCP连接池 【tomcat自带的连接池】
3、使用C3P0连接池 【全球人民都知道】
4、使用Druid连接池 【阿里巴巴荣誉出品,号称史上最快,性能最好】
教学目标
1、熟悉自定义数据库连接池
2、掌握DBCP连接池
3、掌握C3P0连接池的使用
4、掌握Druid连接池的使用
5、掌握JDBCTemplate的使用
第一章 自定义连接池
使用JDBC操作数据库,需要建立Connection,使用传统的JDBC操作需要每次创建Connection,创建Connection是一个非常性能和消耗时间的过程,我们需要在提高程序性能,那么就需要减少每次创建创建连接带来的负面影响,解决这个问题我们将利用池子概念,预先创建一些链接放入池子中,如果需要操作数据,不用创建新的Connection,只需要在池子中获取即可,使用完毕放入池子!这样就形成了复用!
1.1 自定义连接池
我们可以通过自定义的方式实现连接池!分析连接池类应该包含特定的属性和方法!
属性: 集合 放置Connection
方法: 获取连接方法
回收连接方法
具体实现代码:
public class JDBCUtils {
private static String url="jdbc:mysql://localhost:3306/jdbc?characterEncoding=utf8";
private static String userName="root";
private static String password="root";
// 通过 调用该方法可以获取 数据库连接
public static Connection getConnection() {
try {
return DriverManager.getConnection(url, userName, password);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public static void close(Connection connection, Statement statement, ResultSet resultSet){
try {
// 先开启的后关闭
// 判断不是null 再关闭,杜绝空指针异常
if(resultSet!=null){
resultSet.close();
}
if(statement!=null){
statement.close();
}
if(connection!=null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public class Pool{
static LinkedList<Connection> list = new LinkedList<Connection>();
static{
for (int i = 0; i < 10; i++) {
Connection connection = JDBCUtils.newInstance().getConnection();
list.add(connection);
}
}
/**
* 从连接池子中获取连接的方式
* @return
*/
public static Connection getConnection(){
if (list.isEmpty()) {
Connection connection = JDBCUtils.newInstance().getConnection();
list.addLast(connection);
}
Connection conn = list.removeFirst();
return conn;
}
/**
* 返回到连接池子中
*/
public static void addBack(Connection conn){
if (list.size() >= 10) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
list.addLast(conn); //10
}
}
/**
* 获取连接池子中连接数量的方法
*/
public static int getSize(){
return list.size();
}
}
第二章 DBCP连接池
DBCP(DataBase connection pool),数据库连接池。是 apache 上的一个 java 连接池项目,也是 tomcat 使用的连接池组件。单独使用dbcp需要2个包:commons-dbcp.jar,commons-pool.jar由于建立数据库连接是一个非常耗时耗资源的行为,所以通过连接池预先同数据库建立一些连接,放在内存中,应用程序需要建立数据库连接时直接到连接池中申请一个就行,用完后再放回去。
2.1 DBCP连接池的使用
2.1.1 创建项目
2.1.2 导入相应jar包
mysql-jdbc.jar<br /> commons-dbcp.jar<br /> commons-pool.jar
2.1.3 硬编码方式使用DBCP连接池
所谓的硬编码方式就是在代码中添加配置
@Test
public void testHard() throws SQLException{
//TODO 硬编码 使用DBCP连接池子
BasicDataSource source = new BasicDataSource();
//设置连接的信息
source.setDriverClassName("com.mysql.jdbc.Driver");
source.setUrl("jdbc:mysql://localhost:3306/day2");
source.setUsername("root");
source.setPassword("111");
Connection connection = source.getConnection();
String sql = "select * from student";
Statement createStatement = connection.createStatement();
ResultSet executeQuery = createStatement.executeQuery(sql);
while (executeQuery.next()) {
System.out.println(executeQuery.getString(2));
}
connection.close(); //回收
}
2.1.4 软编码方式使用DBCP连接池
所谓的软编码,就是在项目中添加配置文件,这样就不需要每次代码中添加配合!
项目中添加配置
文件名称: info.properties<br /> 文件位置: src下#连接设置 driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql://localhost:3306/day2 username=root password=111 #<!-- 初始化连接 --> initialSize=10 #最大连接数量 maxActive=50 #<!-- 最大空闲连接 --> maxIdle=20 #<!-- 最小空闲连接 --> minIdle=5 #<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 --> maxWait=6000代码实现
@Test public void testSoft() throws Exception{ //TODO DBCP软编码连接池子使用 BasicDataSourceFactory factory = new BasicDataSourceFactory(); Properties properties = new Properties(); //配置文件添加到properties对象中 javase properties.load(new FileInputStream("src/info.properties")); //生成连接池子 需要配置文件 DataSource source = factory.createDataSource(properties); Connection connection = source.getConnection(); String sql = "select * from student"; Statement createStatement = connection.createStatement(); ResultSet executeQuery = createStatement.executeQuery(sql); while (executeQuery.next()) { System.out.println(executeQuery.getString(2)); } connection.close(); //回收 }第三章 C3P0连接池
C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。
c3p0与dbcp区别
1.
dbcp没有自动回收空闲连接的功能
c3p0有自动回收空闲连接功能
2.
dbcp需要手动设置配置文件
c3p0不需要手动设置
3.1 使用步骤
3.1.1 创建项目
3.1.2 导入jar包
c3p0-0.9.1.2.jar
mysql-connector-java-5.0.8.jar
3.1.3.添加配置文件
c3p0是在外部添加配置文件,工具直接进行应用,因为直接引用,所以要求固定的命名和文件位置
文件位置: src
文件命名:c3p0-config.xml/c3p0-config.properties
<c3p0-config>
<!-- 默认配置,如果没有指定则使用这个配置 -->
<default-config>
<!-- 基本配置 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day2</property>
<property name="user">root</property>
<property name="password">111</property>
<!--扩展配置-->
<!-- 连接超过30秒报错-->
<property name="checkoutTimeout">30000</property>
<!--30秒检查空闲连接 -->
<property name="idleConnectionTestPeriod">30</property>
<property name="initialPoolSize">10</property>
<!-- 30秒不适用丢弃-->
<property name="maxIdleTime">30</property>
<property name="maxPoolSize">100</property>
<property name="minPoolSize">10</property>
<property name="maxStatements">200</property>
</default-config>
<!-- 命名的配置 -->
<named-config name="laoyan">
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/day2</property>
<property name="user">root</property>
<property name="password">111</property>
<!-- 如果池中数据连接不够时一次增长多少个 -->
<property name="acquireIncrement">5</property>
<property name="initialPoolSize">20</property>
<property name="minPoolSize">10</property>
<property name="maxPoolSize">40</property>
<property name="maxStatements">20</property>
<property name="maxStatementsPerConnection">5</property>
</named-config>
</c3p0-config>
注意: c3p0的配置文件内部可以包含命名配置文件和默认配置文件!默认是选择默认配置!如果需要切换命名配置可以在创建c3p0连接池的时候填入命名即可!
3.2 使用c3p0进行数据库操作
public class TestC3p0 {
public static void main(String[] args) throws Exception {
//1.创建C3P0连接池子
Connection connection = DataSourceUtils.getConnection();
Statement createStatement = connection.createStatement();
String sql = "select * from student;";
ResultSet resultSet = createStatement.executeQuery(sql);
while (resultSet.next()) {
System.out.println(resultSet.getString(1));
}
DataSourceUtils.close(connection, createStatement, resultSet);
}
}
3.4 使用c3p0连接池编写工具类
基于c3p0连接池编写数据库操作工具类!
/**
* 从连接池子中获取连接!
*
* C3P0的连接池子
* 0.获取连接池子对象 DBUtils
* 1.获取连接
* 2.关闭资源
*/
public class DataSourceUtils {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();
/**
* 返回连接池对象方法
* @return c3p0连接池子
*/
public static ComboPooledDataSource getDataSource(){
return dataSource;
}
/**
* 连接池中获取连接的方法
* @return 连接
*/
public static Connection getConnection(){
Connection conn = null;
try {
conn = dataSource.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return conn;
}
//关闭资源
public static void close(Connection conn){
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void close(Statement st){
if (st != null) {
try {
st.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void close(ResultSet set){
if (set != null) {
try {
set.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void close(Connection conn,Statement st){
close(conn);
close(st);
}
public static void close(Connection conn,Statement st,ResultSet rt){
close(conn);
close(st);
close(rt);
}
}
第四章 Druid连接池
Druid 是目前比较流行的高性能的,分布式列存储的OLAP框架(具体来说是MOLAP)。它有如下几个特点:
一. 亚秒级查询
druid提供了快速的聚合能力以及亚秒级的OLAP查询能力,多租户的设计,是面向用户分析应用的理想方式。
二.实时数据注入
druid支持流数据的注入,并提供了数据的事件驱动,保证在实时和离线环境下事件的实效性和统一性
三.可扩展的PB级存储
druid集群可以很方便的扩容到PB的数据量,每秒百万级别的数据注入。即便在加大数据规模的情况下,也能保证时其效性
四.多环境部署
druid既可以运行在商业的硬件上,也可以运行在云上。它可以从多种数据系统中注入数据,包括hadoop,spark,kafka,storm和samza等
五.丰富的社区
druid拥有丰富的社区,供大家学习
4.1 使用步骤
4.1.1 导入jar包
4.1.2 编写工具类
/**
* 阿里的数据库连接池
* 性能最好的
* Druid
* */
public class DruidUtils {
//声明连接池对象
private static DruidDataSource ds;
static{
///实例化数据库连接池对象
ds=new DruidDataSource();
//实例化配置对象
Properties properties=new Properties();
try {
//加载配置文件内容
properties.load(DruidUtils.class.getResourceAsStream("dbcpconfig.properties"));
//设置驱动类全称
ds.setDriverClassName(properties.getProperty("driverClassName"));
//设置连接的数据库
ds.setUrl(properties.getProperty("url"));
//设置用户名
ds.setUsername(properties.getProperty("username"));
//设置密码
ds.setPassword(properties.getProperty("password"));
//设置最大连接数量
ds.setMaxActive(Integer.parseInt(properties.getProperty("maxActive")));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
//获取连接对象
public static Connection getConnection() {
try {
return ds.getConnection();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
4.1.3 测试
public class Druid_Test{
@Test
public void test1(){
findAll();
find("小千千","47983");
}
public List<Master> findAll() {
// TODO Auto-generated method stub
Connection conn=null;
PreparedStatement pstat=null;
ResultSet rs=null;
List<Master> masters=new ArrayList<Master>();
try{
conn=DruidUtils.getConnection();
pstat=conn.prepareStatement("select * from master");
rs=pstat.executeQuery();
while(rs.next()){
int id=rs.getInt("id");
String name=rs.getString("name");
String password=rs.getString("password");
int money=rs.getInt("money");
masters.add(new Master(id, name, password, money));
}
return masters;
}catch(Exception e){
throw new RuntimeException(e);
}finally {
DbUtils.release(rs, pstat, conn);
}
}
public Master find(String name, String password) {
// TODO Auto-generated method stub
Connection conn=null;
PreparedStatement pstat=null;
ResultSet rs=null;
Master master=null;
try{
conn=DbUtils.getConnection();
pstat=conn.prepareStatement("select * from master where name=? and password=?");
pstat.setString(1, name);
pstat.setString(2, password);
rs=pstat.executeQuery();
if(rs.next()){
int id=rs.getInt("id");
int money=rs.getInt("money");
master=new Master(id, name, password, money);
}
return master;
}catch(Exception e){
throw new RuntimeException(e);
}finally {
DbUtils.release(rs, pstat, conn);
}
}
第五章:改进JdbcUtils工具类
核心代码
public class JDBCUtils {
//1.定义成员变量 DataSource
private static DataSource ds ;
static{
try {
//1.加载配置文件
Properties pro = new Properties();
pro.load(JDBCUtils.class.getClassLoader().getResourceAsStream("druid.properties"));
//2.获取DataSource
ds = DruidDataSourceFactory.createDataSource(pro);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取连接
*/
public static Connection getConnection() throws SQLException {
return ds.getConnection();
}
/**
* 获取连接池方法
*/
public static DataSource getDataSource(){
return ds;
}
}
第六章:JdbcTemplate工具类的使用
- Spring框架对JDBC的简单封装。提供了一个JDBCTemplate对象简化JDBC的开发
- 步骤:
- 导入jar包
- 创建JdbcTemplate对象。依赖于数据源DataSource
- JdbcTemplate template = new JdbcTemplate(ds);
- 调用JdbcTemplate的方法来完成CRUD的操作
- update():执行DML语句。增、删、改语句
- queryForMap():查询结果将结果集封装为map集合,将列名作为key,将值作为value 将这条记录封装为一个map集合
- 注意:这个方法查询的结果集长度只能是1
- queryForList():查询结果将结果集封装为list集合
- 注意:将每一条记录封装为一个Map集合,再将Map集合装载到List集合中
- query():查询结果,将结果封装为JavaBean对象
- query的参数:RowMapper
- 一般我们使用BeanPropertyRowMapper实现类。可以完成数据到JavaBean的自动封装
- new BeanPropertyRowMapper<类型>(类型.class)
- query的参数:RowMapper
- queryForObject:查询结果,将结果封装为对象
- 一般用于聚合函数的查询
- 练习:
- 需求: emp
- 修改1号数据的 salary 为 10000
- 添加一条记录
- 删除刚才添加的记录
- 查询id为1的记录,将其封装为Map集合
- 查询所有记录,将其封装为List
- 查询所有记录,将其封装为Emp对象的List集合
- 查询总记录数
- 代码: ```java import org.junit.Test; import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import java.sql.Date; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; import java.util.Map; public class JdbcTemplateDemo2 { //1. 获取JDBCTemplate对象 private JdbcTemplate template = new JdbcTemplate(JDBCUtils.getDataSource()); /**
- 需求: emp
- 调用JdbcTemplate的方法来完成CRUD的操作
- 修改1号数据的 salary 为 10000
*/
@Test
public void test1(){ for (Emp emp : list) {
} }System.out.println(emp);
- 修改1号数据的 salary 为 10000
*/
@Test
/**
* 2. 添加一条记录
*/
@Test
public void test2(){
String sql = "insert into emp(id,ename,dept_id) values(?,?,?)";
int count = template.update(sql, 1015, "郭靖", 10);
System.out.println(count);
}
/**
* 3.删除刚才添加的记录
*/
@Test
public void test3(){
String sql = "delete from emp where id = ?";
int count = template.update(sql, 1015);
System.out.println(count);
}
/**
* 4.查询id为1001的记录,将其封装为Map集合
* 注意:这个方法查询的结果集长度只能是1
*/
@Test
public void test4(){
String sql = "select * from emp where id = ? or id = ?";
Map<String, Object> map = template.queryForMap(sql, 1001,1002);
System.out.println(map);
//{id=1001, ename=孙悟空, job_id=4, mgr=1004, joindate=2000-12-17, salary=10000.00, bonus=null, dept_id=20}
}
/**
* 5. 查询所有记录,将其封装为List
*/
@Test
public void test5(){
String sql = "select * from emp";
List<Map<String, Object>> list = template.queryForList(sql);
for (Map<String, Object> stringObjectMap : list) {
System.out.println(stringObjectMap);
}
}
/**
* 6. 查询所有记录,将其封装为Emp对象的List集合
*/
@Test
public void test6(){
String sql = "select * from emp";
List<Emp> list = template.query(sql, new RowMapper<Emp>() {
@Override
public Emp mapRow(ResultSet rs, int i) throws SQLException {
Emp emp = new Emp();
int id = rs.getInt("id");
String ename = rs.getString("ename");
int job_id = rs.getInt("job_id");
int mgr = rs.getInt("mgr");
Date joindate = rs.getDate("joindate");
double salary = rs.getDouble("salary");
double bonus = rs.getDouble("bonus");
int dept_id = rs.getInt("dept_id");
emp.setId(id);
emp.setEname(ename);
emp.setJob_id(job_id);
emp.setMgr(mgr);
emp.setJoindate(joindate);
emp.setSalary(salary);
emp.setBonus(bonus);
emp.setDept_id(dept_id);
return emp;
}
});
}
@Test
public void test6_2(){
String sql = "select * from emp";
List<Emp> list = template.query(sql, new BeanPropertyRowMapper<Emp>(Emp.class));
for (Emp emp : list) {
System.out.println(emp);
}
}
/**
* 7. 查询总记录数
*/
@Test
public void test7(){
String sql = "select count(id) from emp";
Long total = template.queryForObject(sql, Long.class);
System.out.println(total);
}
// 8 批量删除
//将数组瞬间变为list集合,list集合只能读取,不能修改和删除
//List<String> asList = Arrays.asList(values);
int[] arr = template.batchUpdate(sql ,new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement stat, int i) throws SQLException {
//1 代表的是?的位置
// ? 的值
stat.setString(1, values[i]);
//stat.setString(2, "");
}
@Override
public int getBatchSize() {
return values.length;
}
});
一、数据库连接池<br /> 宏观:我们之前操作数据库,直接连接,用完之后断开连接。<br /> 频繁的进行数据库的连接与断开非常耗时,为了提高效率,创建出了数据库连接池。<br /> 思想:创建多个数据库连接,复用数据库连接,用完之后放回池子中。<br /> 微观:数据库连接池很多,dbcp,c3p0,druid等一堆数据库连接池,我们需要学习如何使用。<br /> 什么是jar包?<br /> 就是别人写好的类或者工具,我们直接导入到我们自己的项目中,就可以使用别人写好的类,从而提高开发效率。<br /> 学习数据库连接池一定要培养“池”的思想: 数据库连接池,线程池,跟缓存的思想是一样。<br /> 常用的数据库连接池:<br /> dbcp--tomcat使用的是这个。<br /> 任何数据库连接池使用都得导入mysql驱动包,然后再导入自己家的工具包。<br /> Caused by: java.lang.ClassNotFoundException: org.apache.commons.logging.LogFactory<br /> Caused by:由xxx引发的问题,我们一般找错误,都找这句话。<br /> ClassNotFoundException:类找不到而引发的异常。<br /> org.apache.commons.logging.LogFactory 哪个包下的哪个类找不到。<br /> 如果说一个类找不到,而这个类又不是你写的,那么就是缺jar包。<br /> c3p0--全球有名<br /> 1、导入两个jar包<br /> 2、导入配置文件
```properties
<c3p0-config>
<!-- 使用默认的配置读取连接池对象 -->
<default-config>
<!-- 连接参数 -->
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbc</property>
<property name="user">root</property>
<property name="password">123456</property>
<!-- 连接池参数 -->
<property name="initialPoolSize">5</property>
<property name="maxPoolSize">10</property>
<property name="checkoutTimeout">3000</property>
</default-config>
</c3p0-config>
3、API调用<br /> c3p0如果不指定要加载什么配置文件的话,<br /> 默认加载classes文件夹下的c3p0-config.xml文件,位置和名字都不允许改变。<br /> druid--阿里巴巴荣誉出品,德鲁伊,号称史上最快,其实也不算<br />二、单元测试<br /> 学习单元测试<br /> 作用就是:替代main方法,测试代码<br /> 语法:<br /> 编写一个普通类,里面的方法都必须是public 返回值必须是void<br /> 方法名字建议使用test开头。<br /> 在方法上添加@Test 表示该方法是单元测试方法<br /> 需要注意的点:<br /> 1、一个类中可以有多个单元测试方法<br /> 2、想运行哪个测试方法,就先选择,再运行,不选择就运行那是运行的所有测试方法<br /> 3、@Before 注解修饰的方法,每次运行单元测试方法的时候,都会先运行@Before修饰的方法。<br /> @After 修饰的方法,每次单元测试方法结束后,会执行@After修饰的方法<br /> before方法和after方法可以有多个。
三、复习
数据库连接池:管理数据库连接的工具。Connection 对象,是一个非常复杂的过程,频繁创建与关闭造成代码的性能很差。
常见的数据库连接池:DBCP,C3P0,Druid等。
思路:想法设法的拿到DataSource对象,通过该对象获取数据库连接。
四、JDBCTemplate
原生的JDBC开发效率不高,而且我们需要时刻关注数据库连接的开与关,一不留神就出现内存溢出。
所以在此基础之上产生了很多个封装了jdbc操作的框架或者工具:JdbcTemplate,DBUtils,Hibernate,Mybatis,JPA等一堆关于数据库的操作。
JdbcTemplate 该工具是一个简单的jdbc封装工具类,性能好,开发效率高,学习成本低。
Spring组织开发的一个项目,Java工程师永远离不开Spring.
JdbcTemplate 只是对jdbc做一个简易的封装,MyBatis,JPA是框架,对jdbc做更深一层次的封装。
用法:
获取数据源:DataSource dataSource = JDBCPoolUtils.getDataSource();
得到一个jdbcTemplate对象,如果要得到该对象,必须传递一个 数据源
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
// 执行SQL语句,完毕
int update = jdbcTemplate.update(“update account set money=money-10 where name=’Tom’”);
不管是新增,修改,还是删除,都是使用update方法
查询的执行:
1、queryForMap 结果是map集合,数据必须是一条,否则报错
2、queryForList 查询结果是list中有很多map
3、jdbcTemplate.query(sql,new BeanPropertyRowMapper<>(Account2.class));
前提条件是:数据库中的表字段名和实体中的属性名完全一致才能使用。
4、如果属性和表中的字段有不一样的,可以使用如下方法:
List
@Override
public Account mapRow(ResultSet resultSet, int i) throws SQLException {
Account account = new Account();<br /> account.setId(resultSet.getInt("id"));<br /> account.setUsername(resultSet.getString("name"));<br /> account.setMoney(resultSet.getInt("money"));<br /> return account;<br /> }<br /> });<br /> 5、关于统计类<br /> queryForObject<br /> 6、批量操作<br /> int[] arr={1,3};<br /> String sql = "delete from account where id=?";<br /> jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() {<br /> @Override<br /> public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {<br /> // 可以通过PreparedStatement 给 占位符? 赋值<br /> // 1 代表第一个问号<br /> // i 代表的是每次循环的一个下表 从0开始,有多少次,就会循环多少次<br /> preparedStatement.setInt(1,arr[i]);<br /> }
@Override<br /> public int getBatchSize() {<br /> // 告诉我们的jdbcTemplate 我这次需要执行多少次<br /> return arr.length;<br /> }<br /> });
